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