001// ***** This file is automatically generated from OneOf.java.jpp 002 003package daikon.inv.unary.string; 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 java.util.Comparator; 015 016 import java.util.regex.*; 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 String 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 */ 045 046@SuppressWarnings("UnnecessaryParentheses") // generated code, parentheses are sometimes needed 047public final class OneOfString extends SingleString implements OneOf { 048 static final long serialVersionUID = 20030822L; 049 050 /** Debugging logger. */ 051 public static final Logger debug = 052 Logger.getLogger(OneOfString.class.getName()); 053 054 // Variables starting with dkconfig_ should only be set via the 055 // daikon.config.Configuration interface. 056 /** Boolean. True iff OneOf invariants should be considered. */ 057 public static boolean dkconfig_enabled = Invariant.invariantEnabledDefault; 058 059 /** 060 * Positive integer. Specifies the maximum set size for this type of invariant (x is one of 061 * {@code size} items). 062 */ 063 064 public static int dkconfig_size = 3; 065 066 // Probably needs to keep its own list of the values, and number of each seen. 067 // (That depends on the slice; maybe not until the slice is cleared out. 068 // But so few values is cheap, so this is quite fine for now and long-term.) 069 070 @Unused(when=Prototype.class) 071 private @Interned String[] elts; 072 @Unused(when=Prototype.class) 073 private int num_elts; 074 075 @Prototype OneOfString() { 076 super(); 077 } 078 079 OneOfString(PptSlice slice) { 080 super(slice); 081 082 elts = new @Interned String[dkconfig_size]; 083 084 num_elts = 0; 085 086 } 087 088 private static @Prototype OneOfString proto = new @Prototype OneOfString(); 089 090 /** Returns the prototype invariant for OneOfString */ 091 public static @Prototype OneOfString get_proto() { 092 return proto; 093 } 094 095 @Override 096 public boolean enabled() { 097 return dkconfig_enabled; 098 } 099 100 @Override 101 public OneOfString instantiate_dyn(@Prototype OneOfString this, PptSlice slice) { 102 return new OneOfString(slice); 103 } 104 105 @Pure 106 public boolean is_boolean(@GuardSatisfied OneOfString this) { 107 return var().file_rep_type.elementType() == ProglangType.BOOLEAN; 108 } 109 @Pure 110 public boolean is_hashcode(@GuardSatisfied OneOfString this) { 111 return var().file_rep_type.elementType() == ProglangType.HASHCODE; 112 } 113 114 @SideEffectFree 115 @Override 116 public OneOfString clone(@GuardSatisfied OneOfString this) { 117 OneOfString result = (OneOfString) super.clone(); 118 result.elts = elts.clone(); 119 120 result.num_elts = this.num_elts; 121 return result; 122 } 123 124 @Override 125 public int num_elts() { 126 return num_elts; 127 } 128 129 @Override 130 public Object elt() { 131 return elt(0); 132 } 133 134 public Object elt(int index) { 135 if (num_elts <= index) { 136 throw new Error("Represents " + num_elts + " elements, index " + index + " not valid"); 137 } 138 139 return elts[index]; 140 } 141 142 static Comparator<@Nullable String> comparator = Comparator.nullsFirst(Comparator.naturalOrder()); 143 144 private void sort_rep(@GuardSatisfied OneOfString this) { 145 Arrays.sort(elts, 0, num_elts , comparator); 146 } 147 148 public @Interned String min_elt() { 149 if (num_elts == 0) { 150 throw new Error("Represents no elements"); 151 } 152 sort_rep(); 153 return elts[0]; 154 } 155 156 public @Interned String max_elt() { 157 if (num_elts == 0) { 158 throw new Error("Represents no elements"); 159 } 160 sort_rep(); 161 return elts[num_elts - 1]; 162 } 163 164 // Assumes the other array is already sorted 165 public boolean compare_rep(int num_other_elts, @Interned String[] other_elts) { 166 if (num_elts != num_other_elts) { 167 return false; 168 } 169 sort_rep(); 170 for (int i = 0; i < num_elts; i++) { 171 if (!((elts[i]) == (other_elts[i]))) { // elements are interned 172 return false; 173 } 174 } 175 return true; 176 } 177 178 private String subarray_rep(@GuardSatisfied OneOfString this) { 179 // Not so efficient an implementation, but simple; 180 // and how often will we need to print this anyway? 181 sort_rep(); 182 StringJoiner sj = new StringJoiner(", ", "{ ", " }"); 183 for (int i = 0; i < num_elts; i++) { 184 185 if (PrintInvariants.dkconfig_static_const_infer) { 186 boolean curVarMatch = false; 187 PptTopLevel pptt = ppt.parent; 188 for (VarInfo vi : pptt.var_infos) { 189 if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) { 190 Object constantVal = vi.constantValue(); 191 if (constantVal.equals(elts[i])) { 192 sj.add(vi.name()); 193 curVarMatch = true; 194 } 195 } 196 } 197 198 if (curVarMatch == false) { 199 sj.add(((elts[i] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[i]) + "\"")); 200 } 201 } else { 202 sj.add(((elts[i] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[i]) + "\"")); 203 } 204 205 } 206 return sj.toString(); 207 } 208 209 @Override 210 public String repr(@GuardSatisfied OneOfString this) { 211 return "OneOfString" + varNames() + ": falsified=" + falsified 212 + ", num_elts=" + num_elts 213 + ", elts=" + subarray_rep(); 214 } 215 216 public @Interned String[] getElts() { 217 @Interned String[] temp = new @Interned String[elts.length]; 218 for (int i = 0; i < elts.length; i++) { 219 temp[i] = elts[i]; 220 } 221 return temp; 222 } 223 224 @SideEffectFree 225 @Override 226 public String format_using(@GuardSatisfied OneOfString this, OutputFormat format) { 227 sort_rep(); 228 229 if (format.isJavaFamily()) { 230 return format_java_family(format); 231 } 232 233 if (format == OutputFormat.DAIKON) { 234 return format_daikon(); 235 } else if (format == OutputFormat.SIMPLIFY) { 236 return format_simplify(); 237 } else if (format == OutputFormat.ESCJAVA) { 238 String result = format_esc(); 239 return result; 240 } else if (format == OutputFormat.CSHARPCONTRACT) { 241 return format_csharp_contract(); 242 } else { 243 return format_unimplemented(format); 244 } 245 } 246 247 public String format_daikon(@GuardSatisfied OneOfString this) { 248 String varname = var().name(); 249 if (num_elts == 1) { 250 251 boolean is_type = is_type(); 252 if (!is_type) { 253 return varname + " == " + ((elts[0] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[0]) + "\""); 254 } else { 255 // It's a type 256 String str = elts[0]; 257 if ((str == null) || "null".equals(str)) { 258 return varname + " == null"; 259 } else { 260 if (str.startsWith("[")) { 261 @SuppressWarnings("signature") 262 String tempStr = Signatures.fieldDescriptorToBinaryName(str); 263 str = tempStr; 264 } 265 if (PrintInvariants.dkconfig_static_const_infer) { 266 PptTopLevel pptt = ppt.parent; 267 for (VarInfo vi : pptt.var_infos) { 268 if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) { 269 Object constantVal = vi.constantValue(); 270 if (constantVal.equals(str)) { 271 return varname + " == " + vi.name(); 272 } 273 } 274 } 275 } 276 // ".class" (which is a suffix for a type name) and not 277 // getClassSuffix (which is a suffix for an expression). 278 return varname + " == " + str + ".class"; 279 } 280 } 281 282 } else { 283 return varname + " one of " + subarray_rep(); 284 } 285 } 286 287 @Pure 288 private boolean is_type(@GuardSatisfied OneOfString this) { 289 return var().has_typeof(); 290 } 291 292 private static Pattern dollar_char_pat = Pattern.compile("\\$([A-Za-z])"); 293 294 private static String format_esc_string2type(String str) { 295 if ((str == null) || "null".equals(str)) { 296 return "\\typeof(null)"; 297 } 298 String type_str; 299 if (str.startsWith("[")) { 300 @SuppressWarnings("signature") 301 String temp_type_str = Signatures.fieldDescriptorToBinaryName(str); 302 type_str = temp_type_str; 303 } else { 304 type_str = str; 305 if (type_str.startsWith("\"") && type_str.endsWith("\"")) { 306 type_str = type_str.substring(1, type_str.length()-1); 307 } 308 } 309 310 // Inner classes 311 // type_str = type_str.replace('$', '.'); 312 // For named inner classes, convert "$" to ".". 313 // For anonymous inner classes, leave as "$". 314 Matcher m = dollar_char_pat.matcher(type_str); 315 type_str = m.replaceAll(".$1"); 316 317 return "\\type(" + type_str + ")"; 318 } 319 320 public String format_esc(@GuardSatisfied OneOfString this) { 321 sort_rep(); 322 323 String varname = var().esc_name(); 324 325 String result; 326 327 // We cannot say anything about Strings in ESC, just types (which 328 // Daikon stores as Strings). 329 if (!is_type()) { 330 result = format_unimplemented(OutputFormat.ESCJAVA); // "needs to be implemented" 331 } else { 332 // Format \typeof(theArray) = "[Ljava.lang.Object;" 333 // as \typeof(theArray) == \type(java.lang.Object[]) 334 // ... but still ... 335 // format \typeof(other) = "package.SomeClass;" 336 // as \typeof(other) == \type(package.SomeClass) 337 338 result = ""; 339 for (int i = 0; i < num_elts; i++) { 340 if (i != 0) { result += " || "; } 341 result += varname + " == " + format_esc_string2type(elts[i]); 342 } 343 } 344 345 return result; 346 } 347 348public String format_csharp_contract(@GuardSatisfied OneOfString this) { 349 350 @NonNull @Initialized String result; 351 352 String varname = var().csharp_name(); 353 354 if (is_type()) { 355 List<String> names = new ArrayList<>(); 356 for (int i = 0; i < num_elts; i++) { 357 String str = elts[i]; 358 if ((str == null) || "null".equals(str)) { 359 names.add(varname + " == null"); 360 } else { 361 names.add(varname + " == typeof(" + str + ")"); 362 } 363 } 364 result = String.join(" || ", names); 365 } else if (num_elts == 1) { 366 String str = elts[0]; 367 result = (str == null || "null".equals(str)) 368 ? (varname + " == null") 369 : (varname + ".Equals(" + ((str == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(str) + "\"") + ")"); 370 } else { 371 List<String> names = new ArrayList<>(); 372 for (int i = 0; i < num_elts; i++) { 373 String str = elts[i]; 374 names.add((str == null) ? "null" : ((str == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(str) + "\"")); 375 } 376 result = varname + ".OneOf(" + String.join(", ", names) + ")"; 377 } 378 379 return result; 380 } 381 382 public String format_java_family(@GuardSatisfied OneOfString this, OutputFormat format) { 383 384 String result; 385 386 // Setting up the name of the unary variable 387 String varname = var().name_using(format); 388 389 result = ""; 390 boolean is_type = is_type(); 391 for (int i = 0; i < num_elts; i++) { 392 if (i != 0) { 393 result += " || "; 394 } 395 String str = elts[i]; 396 if (!is_type) { 397 result += varname + ".equals(" + ((str == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(str) + "\"") + ")"; 398 } else { 399 // It's a type 400 if ((str == null) || "null".equals(str)) { 401 result += varname + " == null"; 402 } else { 403 if (str.startsWith("[")) { 404 @SuppressWarnings("signature") 405 String tempStr = Signatures.fieldDescriptorToBinaryName(str); 406 str = tempStr; 407 } 408 // ".class" (which is a suffix for a type name) and not 409 // getClassSuffix (which is a suffix for an expression). 410 // we need ".class.getName()" to make result a string 411 result += varname + " == " + str + ".class.getName()"; 412 } 413 } 414 } 415 416 return result; 417 } 418 419 public String format_simplify(@GuardSatisfied OneOfString this) { 420 421 sort_rep(); 422 423 String varname = 424 var().simplifyFixup(var().name_using(OutputFormat.SIMPLIFY)); 425 426 String result; 427 428 result = ""; 429 boolean is_type = is_type(); 430 for (int i = 0; i < num_elts; i++) { 431 String value = elts[i]; 432 if (is_type) { 433 if (value == null) { 434 // do nothing 435 } else if (value.startsWith("[")) { 436 @SuppressWarnings("signature") 437 String tempValue = Signatures.fieldDescriptorToBinaryName(value); 438 value = tempValue; 439 } else if (value.startsWith("\"") && value.endsWith("\"")) { 440 value = value.substring(1, value.length()-1); 441 } 442 value = "|T_" + value + "|"; 443 } else { 444 value = simplify_format_string(value); 445 } 446 result += " (EQ " + varname + " " + value + ")"; 447 } 448 if (num_elts > 1) { 449 result = "(OR" + result + ")"; 450 } else if (num_elts == 1) { 451 // chop leading space 452 result = result.substring(1); 453 } else if (num_elts == 0) { 454 return format_too_few_samples(OutputFormat.SIMPLIFY, null); 455 } 456 457 if (result.indexOf("format_simplify") == -1) { 458 daikon.simplify.SimpUtil.assert_well_formed(result); 459 } 460 return result; 461 } 462 463 @Override 464 public InvariantStatus add_modified(@Interned String a, int count) { 465 return runValue(a, count, true); 466 } 467 468 @Override 469 public InvariantStatus check_modified(@Interned String a, int count) { 470 return runValue(a, count, false); 471 } 472 473 private InvariantStatus runValue(@Interned String v, int count, boolean mutate) { 474 InvariantStatus status; 475 if (mutate) { 476 status = add_mod_elem(v, count); 477 } else { 478 status = check_mod_elem(v, count); 479 } 480 if (status == InvariantStatus.FALSIFIED) { 481 if (logOn() && mutate) { 482 StringBuilder eltString = new StringBuilder(); 483 for (int i = 0; i < num_elts; i++) { 484 eltString.append(((elts[i] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[i]) + "\"") + " "); 485 } 486 log("destroyed on sample %s previous vals = {%s} num_elts = %s", 487 ((v == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(v) + "\""), eltString, num_elts); 488 } 489 return InvariantStatus.FALSIFIED; 490 } 491 return status; 492 } 493 494 /** 495 * Adds a single sample to the invariant. Returns 496 * the appropriate InvariantStatus from the result 497 * of adding the sample to this. 498 */ 499 public InvariantStatus add_mod_elem(@Interned String v, int count) { 500 InvariantStatus status = check_mod_elem(v, count); 501 if (status == InvariantStatus.WEAKENED) { 502 elts[num_elts] = v; 503 num_elts++; 504 } 505 return status; 506 } 507 508 /** 509 * Checks a single sample to the invariant. Returns 510 * the appropriate InvariantStatus from the result 511 * of adding the sample to this. 512 */ 513 public InvariantStatus check_mod_elem(@Interned String v, int count) { 514 515 // Look for v in our list of previously seen values. If it's 516 // found, we're all set. 517 for (int i = 0; i < num_elts; i++) { 518 // if (logDetail()) 519 // log ("add_modified (" + v + ")"); 520 if (((elts[i]) == ( v))) { 521 return InvariantStatus.NO_CHANGE; 522 } 523 } 524 525 if (num_elts == dkconfig_size) { 526 return InvariantStatus.FALSIFIED; 527 } 528 529 if (is_type() && (num_elts == 1)) { 530 return InvariantStatus.FALSIFIED; 531 } 532 533 return InvariantStatus.WEAKENED; 534 } 535 536 @Override 537 protected double computeConfidence() { 538 // This is not ideal. 539 if (num_elts == 0) { 540 return Invariant.CONFIDENCE_UNJUSTIFIED; 541 } else { 542 return Invariant.CONFIDENCE_JUSTIFIED; 543 } 544 } 545 546 @Pure 547 @Override 548 public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) { 549 // Static constants are necessarily OneOf precisely one value. 550 // This removes static constants from the output, which might not be 551 // desirable if the user doesn't know their actual value. 552 if (vis[0].isStaticConstant()) { 553 assert num_elts <= 1; 554 return new DiscardInfo(this, DiscardCode.obvious, vis[0].name() + " is a static constant."); 555 } 556 return super.isObviousStatically(vis); 557 } 558 559 @Override 560 public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) { 561 DiscardInfo super_result = super.isObviousDynamically(vis); 562 if (super_result != null) { 563 return super_result; 564 } 565 566 VarInfo v = vis[0]; 567 568 // We can check if all values in the list match with the ones we know about 569 // (useful for booleans and numeric enumerations). 570 if (v.aux.hasValue(VarInfoAux.VALID_VALUES)) { 571 String[] vsValidValues = v.aux.getList(VarInfoAux.VALID_VALUES); 572 Set<@Interned String> setValidValues = new TreeSet<>(); 573 for (String s : vsValidValues) { 574 setValidValues.add(s.intern()); 575 } 576 Set<@Interned String> setValuesInvariant = new TreeSet<>(); 577 for (@Interned String e : elts) { 578 if (e == null) { 579 continue; 580} 581 setValuesInvariant.add(e); 582 } 583 584 if (setValidValues.equals(setValuesInvariant)) { 585 return new DiscardInfo(this, DiscardCode.obvious, 586 "The value list consists of all possible values"); 587 } 588 } 589 590 return null; 591 } 592 593 /** 594 * Oneof can merge different formulas from lower points to create a single formula at an upper 595 * point. 596 */ 597 @Override 598 public boolean mergeFormulasOk() { 599 return true; 600 } 601 602 @Pure 603 @Override 604 public boolean isSameFormula(Invariant o) { 605 OneOfString other = (OneOfString) o; 606 if (num_elts != other.num_elts) { 607 return false; 608 } 609 if (num_elts == 0 && other.num_elts == 0) { 610 return true; 611 } 612 613 sort_rep(); 614 other.sort_rep(); 615 616 for (int i = 0; i < num_elts; i++) { 617 if (!((elts[i]) == (other.elts[i]))) { 618 return false; 619 } 620 } 621 622 return true; 623 } 624 625 @Pure 626 @Override 627 public boolean isExclusiveFormula(Invariant o) { 628 if (o instanceof OneOfString) { 629 OneOfString other = (OneOfString) o; 630 631 if (num_elts == 0 || other.num_elts == 0) { 632 return false; 633 } 634 for (int i = 0; i < num_elts; i++) { 635 for (int j = 0; j < other.num_elts; j++) { 636 if (((elts[i]) == (other.elts[j]))) // elements are interned 637 return false; 638 } 639 } 640 641 return true; 642 } 643 644 return false; 645 } 646 647 @Override 648 public boolean hasUninterestingConstant() { 649 650 return false; 651 } 652 653 @Pure 654 @Override 655 public boolean isExact() { 656 return num_elts == 1; 657 } 658 659 // Look up a previously instantiated invariant. 660 public static @Nullable OneOfString find(PptSlice ppt) { 661 assert ppt.arity() == 1; 662 for (Invariant inv : ppt.invs) { 663 if (inv instanceof OneOfString) { 664 return (OneOfString) inv; 665 } 666 } 667 return null; 668 } 669 670 // Interning is lost when an object is serialized and deserialized. 671 // Manually re-intern any interned fields upon deserialization. 672 private void readObject(ObjectInputStream in) throws IOException, 673 ClassNotFoundException { 674 in.defaultReadObject(); 675 676 for (int i = 0; i < num_elts; i++) { 677 elts[i] = Intern.intern(elts[i]); 678 } 679 } 680 681 /** 682 * Merge the invariants in invs to form a new invariant. Each must be a OneOfString invariant. 683 * This code finds all of the oneof values from each of the invariants and returns the merged 684 * invariant (if any). 685 * 686 * @param invs list of invariants to merge. The invariants must all be of the same type and should 687 * come from the children of parent_ppt. They should also all be permuted to match the 688 * variable order in parent_ppt. 689 * @param parent_ppt slice that will contain the new invariant 690 */ 691 @Override 692 public @Nullable OneOfString merge(List<Invariant> invs, PptSlice parent_ppt) { 693 694 // Create the initial parent invariant from the first child 695 OneOfString first = (OneOfString) invs.get(0); 696 OneOfString result = first.clone(); 697 result.ppt = parent_ppt; 698 699 // Loop through the rest of the child invariants 700 for (int i = 1; i < invs.size(); i++ ) { 701 702 // Get this invariant 703 OneOfString inv = (OneOfString) invs.get(i); 704 705 // Loop through each distinct value found in this child and add 706 // it to the parent. If the invariant is falsified, there is no parent 707 // invariant 708 for (int j = 0; j < inv.num_elts; j++) { 709 @Interned String val = inv.elts[j]; 710 711 InvariantStatus status = result.add_mod_elem(val, 1); 712 if (status == InvariantStatus.FALSIFIED) { 713 714 result.log("%s", "child value '" + val + "' destroyed oneof"); 715 716 return null; 717 } 718 } 719 } 720 721 result.log("Merged '%s' from %s child invariants", result.format(), invs.size()); 722 return result; 723 } 724 725 /** 726 * Setup the invariant with the specified elements. Normally used when searching for a specified 727 * OneOf. The elements of vals are not necessarily interned; this method interns each element. 728 */ 729 public void set_one_of_val(String[] vals) { 730 731 num_elts = vals.length; 732 for (int i = 0; i < num_elts; i++) { 733 elts[i] = Intern.intern(vals[i]); 734 } 735 } 736 737 /** 738 * Returns true if every element in this invariant is contained in the specified state. For 739 * example if x = 1 and the state contains 1 and 2, true will be returned. 740 */ 741 @Override 742 public boolean state_match(Object state) { 743 744 if (num_elts == 0) { 745 return false; 746 } 747 748 if (!(state instanceof @Interned String[])) { 749 // Daikon is about to crash. Produce some debugging output. 750 System.out.printf("state = %s [%s]%n", state, state.getClass()); 751 System.out.printf("problem with %s%n", this); 752 } 753 @Interned String[] e = (@Interned String[]) state; 754 for (int i = 0; i < num_elts; i++) { 755 boolean match = false; 756 for (int j = 0; j < e.length; j++) { 757 if (elts[i] == e[j]) { 758 match = true; 759 break; 760 } 761 } 762 if (!match) { 763 return false; 764 } 765 } 766 return true; 767 } 768 769}