001// ***** This file is automatically generated from OneOf.java.jpp 002 003package daikon.inv.unary.sequence; 004 005import daikon.*; 006import daikon.inv.*; 007import daikon.inv.unary.OneOf; 008 009import java.io.*; 010import java.util.logging.Logger; 011import java.util.logging.Level; 012import java.util.*; 013 014import org.checkerframework.checker.initialization.qual.Initialized; 015import org.checkerframework.checker.interning.qual.Interned; 016import org.checkerframework.checker.lock.qual.GuardSatisfied; 017import org.checkerframework.checker.nullness.qual.NonNull; 018import org.checkerframework.checker.nullness.qual.Nullable; 019import org.checkerframework.dataflow.qual.Pure; 020import org.checkerframework.dataflow.qual.SideEffectFree; 021import org.checkerframework.framework.qual.Unused; 022import org.plumelib.reflection.Signatures; 023import org.plumelib.util.ArraysPlume; 024import org.plumelib.util.Intern; 025import org.plumelib.util.StringsPlume; 026import org.plumelib.util.UtilPlume; 027import typequals.prototype.qual.NonPrototype; 028import typequals.prototype.qual.Prototype; 029 030// This subsumes an "exact" invariant that says the value is always exactly 031// a specific value. Do I want to make that a separate invariant 032// nonetheless? Probably not, as this will simplify implication and such. 033 034 /** 035 * Represents long[] variables that take on only a few distinct values. Prints as either 036 * {@code x == c} (when there is only one value) or as {@code x one of {c1, c2, c3}} 037 * (when there are multiple values). 038 039 */ 040 041@SuppressWarnings("UnnecessaryParentheses") // generated code, parentheses are sometimes needed 042public final class OneOfSequence extends SingleScalarSequence implements OneOf { 043 // We are Serializable, so we specify a version to allow changes to 044 // method signatures without breaking serialization. If you add or 045 // remove fields, you should change this number to the current date. 046 static final long serialVersionUID = 20030822L; 047 048 /** Debugging logger. */ 049 public static final Logger debug = 050 Logger.getLogger(OneOfSequence.class.getName()); 051 052 // Variables starting with dkconfig_ should only be set via the 053 // daikon.config.Configuration interface. 054 /** Boolean. True iff OneOf invariants should be considered. */ 055 public static boolean dkconfig_enabled = Invariant.invariantEnabledDefault; 056 057 /** 058 * Positive integer. Specifies the maximum set size for this type of invariant (x is one of 059 * {@code size} items). 060 */ 061 062 public static int dkconfig_size = 3; 063 064 /** 065 * Boolean. If true, invariants describing hashcode-typed variables as having any particular value 066 * will have an artificial value substituted for the exact hashhode values. The artificial values 067 * will stay the same from run to run even if the actual hashcode values change (as long as the 068 * OneOf invariants remain the same). If false, hashcodes will be formatted as the application of 069 * a hashcode uninterpreted function to an integer representing the bit pattern of the hashcode. 070 * One might wish to omit the exact values of the hashcodes because they are usually 071 * uninteresting; this is the same reason they print in the native Daikon format, for instance, as 072 * {@code var has only one value} rather than {@code var == 150924732}. 073 */ 074 public static boolean dkconfig_omit_hashcode_values_Simplify = false; 075 076 // Probably needs to keep its own list of the values, and number of each seen. 077 // (That depends on the slice; maybe not until the slice is cleared out. 078 // But so few values is cheap, so this is quite fine for now and long-term.) 079 080 @Unused(when=Prototype.class) 081 private long[] @Interned [] elts; 082 @Unused(when=Prototype.class) 083 private int num_elts; 084 085 @Prototype OneOfSequence() { 086 super(); 087 } 088 089 OneOfSequence(PptSlice slice) { 090 super(slice); 091 092 // Elements are interned, so can test with == 093 // (in the general online case, it's not worth interning). 094 elts = new long[dkconfig_size] @Interned []; 095 096 num_elts = 0; 097 098 // var() is initialized by the super constructor 099 assert var().is_array() : 100 String.format("In %s constructor, var %s (type=%s, rep_type=%s) should be an array", 101 "OneOfSequence", var().name(), var().type, var().rep_type); 102 103 } 104 105 private static @Prototype OneOfSequence proto = new @Prototype OneOfSequence(); 106 107 /** Returns the prototype invariant for OneOfSequence */ 108 public static @Prototype OneOfSequence get_proto() { 109 return proto; 110 } 111 112 /** returns whether or not this invariant is enabled */ 113 @Override 114 public boolean enabled() { 115 return dkconfig_enabled; 116 } 117 118 /** instantiate an invariant on the specified slice */ 119 @Override 120 public OneOfSequence instantiate_dyn(@Prototype OneOfSequence this, PptSlice slice) { 121 return new OneOfSequence(slice); 122 } 123 124 @Pure 125 public boolean is_boolean(@GuardSatisfied OneOfSequence this) { 126 return var().file_rep_type.elementType() == ProglangType.BOOLEAN; 127 } 128 @Pure 129 public boolean is_hashcode(@GuardSatisfied OneOfSequence this) { 130 return var().file_rep_type.elementType() == ProglangType.HASHCODE; 131 } 132 133 @SideEffectFree 134 @Override 135 public OneOfSequence clone(@GuardSatisfied OneOfSequence this) { 136 OneOfSequence result = (OneOfSequence) super.clone(); 137 result.elts = elts.clone(); 138 139 for (int i = 0; i < num_elts; i++) { 140 result.elts[i] = Intern.intern(elts[i].clone()); 141 } 142 143 result.num_elts = this.num_elts; 144 return result; 145 } 146 147 @Override 148 public int num_elts() { 149 return num_elts; 150 } 151 152 @Override 153 public Object elt() { 154 return elt(0); 155 } 156 157 public Object elt(int index) { 158 if (num_elts <= index) { 159 throw new Error("Represents " + num_elts + " elements, index " + index + " not valid"); 160 } 161 162 return elts[index]; 163 } 164 165 static Comparator<long[]> comparator = ArraysPlume.LongArrayComparatorLexical.it; 166 167 private void sort_rep(@GuardSatisfied OneOfSequence this) { 168 Arrays.sort(elts, 0, num_elts , comparator); 169 } 170 171 public long @Interned [] min_elt() { 172 if (num_elts == 0) { 173 throw new Error("Represents no elements"); 174 } 175 sort_rep(); 176 return elts[0]; 177 } 178 179 public long @Interned [] max_elt() { 180 if (num_elts == 0) { 181 throw new Error("Represents no elements"); 182 } 183 sort_rep(); 184 return elts[num_elts - 1]; 185 } 186 187 // Assumes the other array is already sorted 188 public boolean compare_rep(int num_other_elts, long[] @Interned [] other_elts) { 189 if (num_elts != num_other_elts) { 190 return false; 191 } 192 sort_rep(); 193 for (int i = 0; i < num_elts; i++) { 194 if (!((elts[i]) == (other_elts[i]))) { // elements are interned 195 return false; 196 } 197 } 198 return true; 199 } 200 201 private String subarray_rep(@GuardSatisfied OneOfSequence this) { 202 // Not so efficient an implementation, but simple; 203 // and how often will we need to print this anyway? 204 sort_rep(); 205 StringBuilder sb = new StringBuilder(); 206 sb.append("{ "); 207 for (int i = 0; i < num_elts; i++) { 208 if (i != 0) { 209 sb.append(", "); 210 } 211 212 if (PrintInvariants.dkconfig_static_const_infer) { 213 boolean curVarMatch = false; 214 PptTopLevel pptt = ppt.parent; 215 for (VarInfo vi : pptt.var_infos) { 216 if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) { 217 Object constantVal = vi.constantValue(); 218 if (constantVal.equals(elts[i])) { 219 sb.append(vi.name()); 220 curVarMatch = true; 221 } 222 } 223 } 224 225 if (curVarMatch == false) { 226 sb.append(Arrays.toString(elts[i])); 227 } 228 } else { 229 sb.append(Arrays.toString(elts[i])); 230 } 231 232 } 233 sb.append(" }"); 234 return sb.toString(); 235 } 236 237 @Override 238 public String repr(@GuardSatisfied OneOfSequence this) { 239 return "OneOfSequence" + varNames() + ": falsified=" + falsified 240 + ", num_elts=" + num_elts 241 + ", elts=" + subarray_rep(); 242 } 243 244 private boolean all_nulls(@GuardSatisfied OneOfSequence this, int value_no) { 245 long @Interned [] seq = elts[value_no]; 246 for (int i = 0; i < seq.length; i++) { 247 if (seq[i] != 0) { 248 return false; 249 } 250 } 251 return true; 252 } 253 private boolean no_nulls(@GuardSatisfied OneOfSequence this, int value_no) { 254 long @Interned [] seq = elts[value_no]; 255 for (int i = 0; i < seq.length; i++) { 256 if (seq[i] == 0) { 257 return false; 258 } 259 } 260 return true; 261 } 262 263 @SideEffectFree 264 @Override 265 public String format_using(@GuardSatisfied OneOfSequence this, OutputFormat format) { 266 sort_rep(); 267 268 if (format.isJavaFamily()) { 269 return format_java_family(format); 270 } 271 272 if (format == OutputFormat.DAIKON) { 273 return format_daikon(); 274 } else if (format == OutputFormat.SIMPLIFY) { 275 return format_simplify(); 276 } else if (format == OutputFormat.ESCJAVA) { 277 String result = format_esc(); 278 return result; 279 } else if (format == OutputFormat.CSHARPCONTRACT) { 280 return format_csharp_contract(); 281 } else { 282 return format_unimplemented(format); 283 } 284 } 285 286 public String format_daikon(@GuardSatisfied OneOfSequence this) { 287 String varname = var().name(); 288 if (num_elts == 1) { 289 290 if (is_hashcode()) { 291 // we only have one value, because add_modified dies if more 292 assert num_elts == 1; 293 long @Interned [] value = elts[0]; 294 if (value.length == 0) { 295 return varname + " == []"; 296 } else if ((value.length == 1) && (value[0] == 0)) { 297 return varname + " == [null]"; 298 } else if (no_nulls(0)) { 299 return varname + " contains no nulls and has only one value, of length " + value.length; 300 } else if (all_nulls(0)) { 301 return varname + " contains only nulls and has only one value, of length " + value.length; 302 } else { 303 return varname + " has only one value, of length " + value.length; 304 } 305 } else { 306 if (PrintInvariants.dkconfig_static_const_infer) { 307 PptTopLevel pptt = ppt.parent; 308 for (VarInfo vi : pptt.var_infos) { 309 if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) { 310 Object constantVal = vi.constantValue(); 311 if (constantVal.equals(elts[0])) { 312 return varname + " == " + vi.name(); 313 } 314 } 315 } 316 } 317 return varname + " == " + Arrays.toString(elts[0]); 318 } 319 320 } else { 321 return varname + " one of " + subarray_rep(); 322 } 323 } 324 325 public String format_esc(@GuardSatisfied OneOfSequence this) { 326 sort_rep(); 327 328 String result; 329 330 String length = null; 331 String forall = null; 332 333 if (is_hashcode()) { 334 // we only have one value, because add_modified dies if more 335 assert num_elts == 1; 336 long @Interned [] value = elts[0]; 337 if (var().isArray()) { 338 length = null; 339 if (!var().isSlice()) { 340 length = var().get_length().esc_name() + " == " + value.length; 341 } 342 String[] form = VarInfo.esc_quantify(var()); 343 if (no_nulls(0)) { 344 forall = form[0] + "(" + form[1] + " != null)" + form[2]; 345 } else if (all_nulls(0)) { 346 forall = form[0] + "(" + form[1] + " == null)" + form[2]; 347 } 348 } 349 } 350 351 if (length == null) { 352 if (forall == null) { 353 return format_unimplemented(OutputFormat.ESCJAVA); // "needs to be implemented" 354 } else { 355 result = forall; 356 } 357 } else { 358 if (forall == null) { 359 result = length; 360 } else { 361 result = "(" + length + ") && (" + forall + ")"; 362 } 363 } 364 365 return result; 366 } 367 368public String format_csharp_contract(@GuardSatisfied OneOfSequence this) { 369 370 @NonNull @Initialized String result; 371 372 result = "(\"oneOf.java.jpp: SEQUENCE unimplemented\" != null)"; // "interned" 373 374 return result; 375 } 376 377 public String format_java_family(@GuardSatisfied OneOfSequence this, OutputFormat format) { 378 379 String result; 380 381 result = "(\"oneOf.java.jpp: SEQUENCE unimplemented\" != null)"; // "interned" 382 383 return result; 384 } 385 386 public String format_simplify(@GuardSatisfied OneOfSequence this) { 387 388 // if (is_hashcode() && dkconfig_omit_hashcode_values_Simplify) 389 // return "(AND)"; 390 391 sort_rep(); 392 393 String result; 394 395 StringBuilder resultBuf = new StringBuilder(); 396 for (int i = 0; i < num_elts; i++) { 397 long @Interned [] seq = elts[i]; 398 String offset = null; 399 String contents = null; 400 String[] bounds_name; 401 String length = var().get_simplify_size_name(); 402 // if ((length == null) && var().name.isApplySizeSafe()) 403 // System.out.printf("var %s, type %s, is_array %b%n", var().name(), 404 // var().type, var().type.isArray()); 405 if (length != null) { 406 length = "(EQ " + length + " "+ simplify_format_long(seq.length) + ")"; 407 } else if ((bounds_name = var().get_simplify_slice_bounds()) != null) { 408 String size = "(+ 1 (- " + bounds_name[1] +" " + bounds_name[0] + "))"; 409 length = "(EQ " + size + " " + simplify_format_long(seq.length) + ")"; 410 offset = bounds_name[0]; 411 } 412 413 if (is_hashcode()) { 414 String[] form = VarInfo.simplify_quantify(var()); 415 if (no_nulls(i)) { 416 contents = form[0] + "(NEQ " + form[1] + " null)" + form[2]; 417 } else if (all_nulls(i)) { 418 contents = form[0] + "(EQ " + form[1] + " null)" + form[2]; 419 } 420 } else { 421 StringBuilder contentsBuf = new StringBuilder(); 422 for (int j = 0; j < seq.length; j++) { 423 if (j + 3 < seq.length 424 && ((seq[j]) == ( seq[j + 1])) 425 && ((seq[j]) == ( seq[j + 2])) 426 && ((seq[j]) == ( seq[j + 3]))) { 427 // Compress a sequence of adjacent values 428 int k = j + 4; 429 for (; k < seq.length; k++) { 430 if (!((seq[j]) == ( seq[k]))) { 431 break; 432 } 433 } 434 k--; 435 String index_name = VarInfo.get_simplify_free_index(var()); 436 String cond_left, cond_right; 437 if (offset == null) { 438 cond_left = "(<= " + j + " " + index_name + ")"; 439 cond_right = "(<= " + index_name + " " + k + ")"; 440 } else { 441 cond_left = "(<= (+ " + offset + " " + j + ") " 442 + index_name + ")"; 443 cond_right = "(>= (+ " + offset + " " + k + ") " 444 + index_name + ")"; 445 } 446 String cond = "(AND " + cond_left + " " + cond_right + ")"; 447 String nth = var().get_simplify_selectNth(index_name, true, 0); 448 String eq = "(EQ " + nth + " " + simplify_format_long(seq[j]) + ")"; 449 String implies = "(IMPLIES " + cond + " " + eq + ")"; 450 String forall = "(FORALL (" + index_name + ") " + implies + ")"; 451 contentsBuf.append(" " + forall); 452 j = k; 453 } else { 454 // Output a single value 455 String nth = var().get_simplify_selectNth_lower(j); 456 if (nth == null) { 457 String classname = this.getClass().toString().substring(6); 458 result = "warning: method " + classname 459 + ".format_simplify() needs to fix selectNth(): " + format(); 460 return result; 461 } 462 String value = simplify_format_long(seq[j]); 463 contentsBuf.append(" (EQ " + nth + " " + value + ")"); 464 // if (nth.contains ("--orig__a")) 465 // System.out.printf("regular orig__a%n"); 466 467 } 468 } 469 if (seq.length > 1) { 470 contents = "(AND " + contentsBuf.toString() + ")"; 471 } else if (seq.length == 1) { 472 contents = contentsBuf.toString().substring(1); 473 } else if (seq.length == 0) { 474 contents = null; // back from "" 475 } 476 } 477 if (length == null && contents == null) { 478 resultBuf.append(" "); 479 } else if (length == null && contents != null) { 480 resultBuf.append(" " + contents); 481 } else if (length != null && contents == null) { 482 resultBuf.append(" " + length); 483 } else { 484 assert length != null && contents != null; 485 resultBuf.append(" (AND " + length + " " + contents + ")"); 486 } 487 488 } 489 if (num_elts > 1) { 490 result = "(OR" + resultBuf.toString() + ")"; 491 } else if (num_elts == 1) { 492 // chop leading space 493 result = resultBuf.toString().substring(1); 494 } else if (num_elts == 0) { 495 return format_too_few_samples(OutputFormat.SIMPLIFY, null); 496 } else { 497 throw new Error("this can't happen"); 498 // result = null; 499 } 500 if (result.trim().equals("")) { 501 result = "format_simplify() failed on a weird OneOf"; 502 } 503 504 if (result.indexOf("format_simplify") == -1) { 505 daikon.simplify.SimpUtil.assert_well_formed(result); 506 } 507 return result; 508 } 509 510 @Override 511 public InvariantStatus add_modified(long @Interned [] a, int count) { 512 return runValue(a, count, true); 513 } 514 515 @Override 516 public InvariantStatus check_modified(long @Interned [] a, int count) { 517 return runValue(a, count, false); 518 } 519 520 private InvariantStatus runValue(long @Interned [] v, int count, boolean mutate) { 521 InvariantStatus status; 522 if (mutate) { 523 status = add_mod_elem(v, count); 524 } else { 525 status = check_mod_elem(v, count); 526 } 527 if (status == InvariantStatus.FALSIFIED) { 528 if (logOn() && mutate) { 529 StringBuilder eltString = new StringBuilder(); 530 for (int i = 0; i < num_elts; i++) { 531 eltString.append(Arrays.toString(elts[i]) + " "); 532 } 533 log("destroyed on sample %s previous vals = {%s} num_elts = %s", 534 Arrays.toString(v), eltString, num_elts); 535 } 536 return InvariantStatus.FALSIFIED; 537 } 538 return status; 539 } 540 541 /** 542 * Adds a single sample to the invariant. Returns 543 * the appropriate InvariantStatus from the result 544 * of adding the sample to this. 545 */ 546 public InvariantStatus add_mod_elem(long @Interned [] v, int count) { 547 InvariantStatus status = check_mod_elem(v, count); 548 if (status == InvariantStatus.WEAKENED) { 549 elts[num_elts] = v; 550 num_elts++; 551 } 552 return status; 553 } 554 555 /** 556 * Checks a single sample to the invariant. Returns 557 * the appropriate InvariantStatus from the result 558 * of adding the sample to this. 559 */ 560 public InvariantStatus check_mod_elem(long @Interned [] v, int count) { 561 562 // Look for v in our list of previously seen values. If it's 563 // found, we're all set. 564 for (int i = 0; i < num_elts; i++) { 565 // if (logDetail()) 566 // log ("add_modified (" + v + ")"); 567 if (((elts[i]) == ( v))) { 568 return InvariantStatus.NO_CHANGE; 569 } 570 } 571 572 if (num_elts == dkconfig_size) { 573 return InvariantStatus.FALSIFIED; 574 } 575 576 if (is_hashcode() && (num_elts == 1)) { 577 return InvariantStatus.FALSIFIED; 578 } 579 580 return InvariantStatus.WEAKENED; 581 } 582 583 @Override 584 protected double computeConfidence() { 585 // This is not ideal. 586 if (num_elts == 0) { 587 return Invariant.CONFIDENCE_UNJUSTIFIED; 588 } else { 589 return Invariant.CONFIDENCE_JUSTIFIED; 590 } 591 } 592 593 @Pure 594 @Override 595 public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) { 596 // Static constants are necessarily OneOf precisely one value. 597 // This removes static constants from the output, which might not be 598 // desirable if the user doesn't know their actual value. 599 if (vis[0].isStaticConstant()) { 600 assert num_elts <= 1; 601 return new DiscardInfo(this, DiscardCode.obvious, vis[0].name() + " is a static constant."); 602 } 603 return super.isObviousStatically(vis); 604 } 605 606 /** {@inheritDoc} */ 607 @Override 608 public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) { 609 DiscardInfo super_result = super.isObviousDynamically(vis); 610 if (super_result != null) { 611 return super_result; 612 } 613 614 VarInfo v = vis[0]; 615 616 // We can check if all values in the element sequence match 617 // with the ones we know about (useful for booleans and numeric 618 // enumerations). 619 if (v.aux.hasValue(VarInfoAux.VALID_VALUES) 620 && v.aux.hasValue(VarInfoAux.MAXIMUM_LENGTH) 621 && v.aux.hasValue(VarInfoAux.MINIMUM_LENGTH) 622 && v.aux.getInt(VarInfoAux.MAXIMUM_LENGTH) == 1 623 && v.aux.getInt(VarInfoAux.MINIMUM_LENGTH) == 1) { 624 625 String[] vsValidValues = v.aux.getList(VarInfoAux.VALID_VALUES); 626 Set<Long> setValidValues = new TreeSet<>(); 627 for (String s : vsValidValues) { 628 setValidValues.add(Long.valueOf(s)); 629 } 630 Set<Long> setValuesInvariant = new TreeSet<>(); 631 for (long @Interned [] e : elts) { 632 if (e == null) { 633 continue; 634} 635 for (Long b : e) { 636 setValuesInvariant.add(b); 637 } 638 } 639 if (setValidValues.equals(setValuesInvariant)) { 640 return new DiscardInfo(this, DiscardCode.obvious, 641 "The value list matches the allowed value list"); 642 } 643 } 644 645 return null; 646 } 647 648 /** 649 * Oneof can merge different formulas from lower points to create a single formula at an upper 650 * point. 651 */ 652 @Override 653 public boolean mergeFormulasOk() { 654 return true; 655 } 656 657 @Pure 658 @Override 659 public boolean isSameFormula(Invariant o) { 660 OneOfSequence other = (OneOfSequence) o; 661 if (num_elts != other.num_elts) { 662 return false; 663 } 664 if (num_elts == 0 && other.num_elts == 0) { 665 return true; 666 } 667 668 sort_rep(); 669 other.sort_rep(); 670 671 // All nonzero hashcode values should be considered equal to each other 672 // 673 // Examples: 674 // inv1 inv2 result 675 // ------- ------- ------ 676 // {19,23} {91,0} false 677 // {19,23} {91,32} true 678 // {19,0} {91,0} true 679 // {0,0} {0,0} true 680 681 if (is_hashcode() && other.is_hashcode()) { 682 // we only have one value, because add_modified dies if more 683 assert num_elts == 1 && other.num_elts == 1; 684 685 long @Interned [] thisSeq = elts[0]; 686 long @Interned [] otherSeq = other.elts[0]; 687 if (thisSeq.length != otherSeq.length) { 688 return false; 689 } 690 691 for (int i = 0; i < thisSeq.length; i++) { 692 if ((thisSeq[i] == 0 && otherSeq[i] != 0) 693 || (thisSeq[i] != 0 && otherSeq[i] == 0)) { 694 return false; 695 } 696 } 697 698 return true; 699 } 700 701 for (int i = 0; i < num_elts; i++) { 702 if (!((elts[i]) == (other.elts[i]))) { 703 return false; 704 } 705 } 706 707 return true; 708 } 709 710 @Pure 711 @Override 712 public boolean isExclusiveFormula(Invariant o) { 713 if (o instanceof OneOfSequence) { 714 OneOfSequence other = (OneOfSequence) o; 715 716 if (num_elts == 0 || other.num_elts == 0) { 717 return false; 718 } 719 for (int i = 0; i < num_elts; i++) { 720 for (int j = 0; j < other.num_elts; j++) { 721 if (((elts[i]) == (other.elts[j]))) // elements are interned 722 return false; 723 } 724 } 725 726 // Be even more aggressive about rejecting these for use in 727 // implications in this case, since, we'd be printing them as 728 // "true" 729 /* 730 if (dkconfig_omit_hashcode_values_Simplify 731 && (is_hashcode() || other.is_hashcode())) { 732 return false; 733 } 734 */ 735 736 return true; 737 } 738 739 return false; 740 } 741 742 @Override 743 public boolean hasUninterestingConstant() { 744 745 for (int i = 0; i < num_elts; i++) { 746 for (int j = 0; j < elts[i].length; j++) { 747 if (elts[i][j] < -1 || elts[i][j] > 2) { 748 return true; 749 } 750 } 751 } 752 753 return false; 754 } 755 756 @Pure 757 @Override 758 public boolean isExact() { 759 return num_elts == 1; 760 } 761 762 // Look up a previously instantiated invariant. 763 public static @Nullable OneOfSequence find(PptSlice ppt) { 764 assert ppt.arity() == 1; 765 for (Invariant inv : ppt.invs) { 766 if (inv instanceof OneOfSequence) { 767 return (OneOfSequence) inv; 768 } 769 } 770 return null; 771 } 772 773 // Interning is lost when an object is serialized and deserialized. 774 // Manually re-intern any interned fields upon deserialization. 775 private void readObject(ObjectInputStream in) throws IOException, 776 ClassNotFoundException { 777 in.defaultReadObject(); 778 779 for (int i = 0; i < num_elts; i++) { 780 elts[i] = Intern.intern(elts[i]); 781 } 782 } 783 784 /** 785 * Merge the invariants in invs to form a new invariant. Each must be a OneOfSequence invariant. 786 * This code finds all of the oneof values from each of the invariants and returns the merged 787 * invariant (if any). 788 * 789 * @param invs list of invariants to merge. The invariants must all be of the same type and should 790 * come from the children of parent_ppt. They should also all be permuted to match the 791 * variable order in parent_ppt. 792 * @param parent_ppt slice that will contain the new invariant 793 */ 794 @Override 795 public @Nullable Invariant merge(List<Invariant> invs, PptSlice parent_ppt) { 796 797 // Create the initial parent invariant from the first child 798 OneOfSequence first = (OneOfSequence) invs.get(0); 799 OneOfSequence result = first.clone(); 800 result.ppt = parent_ppt; 801 802 for (int i = 0; i < result.num_elts; i++) { 803 result.elts[i] = Intern.intern(result.elts[i]); 804 } 805 806 // Loop through the rest of the child invariants 807 for (int i = 1; i < invs.size(); i++ ) { 808 809 // Get this invariant 810 OneOfSequence inv = (OneOfSequence) invs.get(i); 811 812 // Loop through each distinct value found in this child and add 813 // it to the parent. If the invariant is falsified, there is no parent 814 // invariant 815 for (int j = 0; j < inv.num_elts; j++) { 816 long @Interned [] val = inv.elts[j]; 817 818 val = Intern.intern(val); 819 820 InvariantStatus status = result.add_mod_elem(val, 1); 821 if (status == InvariantStatus.FALSIFIED) { 822 823 result.log("%s", "child value '" + Arrays.toString(val) + "' destroyed oneof"); 824 825 return null; 826 } 827 } 828 } 829 830 result.log("Merged '%s' from %s child invariants", result.format(), invs.size()); 831 return result; 832 } 833 834 /** 835 * Setup the invariant with the specified elements. Normally used when searching for a specified 836 * OneOf. The elements of vals are not necessarily interned; this method interns each element. 837 */ 838 public void set_one_of_val(long[][] vals) { 839 840 num_elts = vals.length; 841 for (int i = 0; i < num_elts; i++) { 842 elts[i] = Intern.intern(vals[i]); 843 } 844 } 845 846 /** 847 * Returns true if every element in this invariant is contained in the specified state. For 848 * example if x = 1 and the state contains 1 and 2, true will be returned. 849 */ 850 @Override 851 public boolean state_match(Object state) { 852 853 if (num_elts == 0) { 854 return false; 855 } 856 857 if (!(state instanceof long[] @Interned [])) { 858 // Daikon is about to crash. Produce some debugging output. 859 System.out.printf("state = %s [%s]%n", state, state.getClass()); 860 System.out.printf("problem with %s%n", this); 861 } 862 long[] @Interned [] e = (long[] @Interned []) state; 863 for (int i = 0; i < num_elts; i++) { 864 boolean match = false; 865 for (int j = 0; j < e.length; j++) { 866 if (elts[i] == e[j]) { 867 match = true; 868 break; 869 } 870 } 871 if (!match) { 872 return false; 873 } 874 } 875 return true; 876 } 877 878}