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