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 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 String variables that take on only a few distinct values. Prints as either 040 * {@code x == c} (when there is only one value) or as {@code x one of {c1, c2, c3}} 041 * (when there are multiple values). 042 043 */ 044 045@SuppressWarnings("UnnecessaryParentheses") // generated code, parentheses are sometimes needed 046public final class OneOfString extends SingleString implements OneOf { 047 // We are Serializable, so we specify a version to allow changes to 048 // method signatures without breaking serialization. If you add or 049 // remove fields, you should change this number to the current date. 050 static final long serialVersionUID = 20030822L; 051 052 /** Debugging logger. */ 053 public static final Logger debug = 054 Logger.getLogger(OneOfString.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 // Probably needs to keep its own list of the values, and number of each seen. 069 // (That depends on the slice; maybe not until the slice is cleared out. 070 // But so few values is cheap, so this is quite fine for now and long-term.) 071 072 @Unused(when=Prototype.class) 073 private @Interned String[] elts; 074 @Unused(when=Prototype.class) 075 private int num_elts; 076 077 @Prototype OneOfString() { 078 super(); 079 } 080 081 OneOfString(PptSlice slice) { 082 super(slice); 083 084 elts = new @Interned String[dkconfig_size]; 085 086 num_elts = 0; 087 088 } 089 090 private static @Prototype OneOfString proto = new @Prototype OneOfString(); 091 092 /** Returns the prototype invariant for OneOfString */ 093 public static @Prototype OneOfString get_proto() { 094 return proto; 095 } 096 097 /** returns whether or not this invariant is enabled */ 098 @Override 099 public boolean enabled() { 100 return dkconfig_enabled; 101 } 102 103 /** instantiate an invariant on the specified slice */ 104 @Override 105 public OneOfString instantiate_dyn(@Prototype OneOfString this, PptSlice slice) { 106 return new OneOfString(slice); 107 } 108 109 @Pure 110 public boolean is_boolean(@GuardSatisfied OneOfString this) { 111 return var().file_rep_type.elementType() == ProglangType.BOOLEAN; 112 } 113 @Pure 114 public boolean is_hashcode(@GuardSatisfied OneOfString this) { 115 return var().file_rep_type.elementType() == ProglangType.HASHCODE; 116 } 117 118 @SideEffectFree 119 @Override 120 public OneOfString clone(@GuardSatisfied OneOfString this) { 121 OneOfString result = (OneOfString) super.clone(); 122 result.elts = elts.clone(); 123 124 result.num_elts = this.num_elts; 125 return result; 126 } 127 128 @Override 129 public int num_elts() { 130 return num_elts; 131 } 132 133 @Override 134 public Object elt() { 135 return elt(0); 136 } 137 138 public Object elt(int index) { 139 if (num_elts <= index) { 140 throw new Error("Represents " + num_elts + " elements, index " + index + " not valid"); 141 } 142 143 return elts[index]; 144 } 145 146 static Comparator<@Nullable String> comparator = Comparator.nullsFirst(Comparator.naturalOrder()); 147 148 private void sort_rep(@GuardSatisfied OneOfString this) { 149 Arrays.sort(elts, 0, num_elts , comparator); 150 } 151 152 public @Interned String min_elt() { 153 if (num_elts == 0) { 154 throw new Error("Represents no elements"); 155 } 156 sort_rep(); 157 return elts[0]; 158 } 159 160 public @Interned String max_elt() { 161 if (num_elts == 0) { 162 throw new Error("Represents no elements"); 163 } 164 sort_rep(); 165 return elts[num_elts - 1]; 166 } 167 168 // Assumes the other array is already sorted 169 public boolean compare_rep(int num_other_elts, @Interned String[] other_elts) { 170 if (num_elts != num_other_elts) { 171 return false; 172 } 173 sort_rep(); 174 for (int i = 0; i < num_elts; i++) { 175 if (!((elts[i]) == (other_elts[i]))) { // elements are interned 176 return false; 177 } 178 } 179 return true; 180 } 181 182 private String subarray_rep(@GuardSatisfied OneOfString this) { 183 // Not so efficient an implementation, but simple; 184 // and how often will we need to print this anyway? 185 sort_rep(); 186 StringBuilder sb = new StringBuilder(); 187 sb.append("{ "); 188 for (int i = 0; i < num_elts; i++) { 189 if (i != 0) { 190 sb.append(", "); 191 } 192 193 if (PrintInvariants.dkconfig_static_const_infer) { 194 boolean curVarMatch = false; 195 PptTopLevel pptt = ppt.parent; 196 for (VarInfo vi : pptt.var_infos) { 197 if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) { 198 Object constantVal = vi.constantValue(); 199 if (constantVal.equals(elts[i])) { 200 sb.append(vi.name()); 201 curVarMatch = true; 202 } 203 } 204 } 205 206 if (curVarMatch == false) { 207 sb.append(((elts[i] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[i]) + "\"")); 208 } 209 } else { 210 sb.append(((elts[i] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[i]) + "\"")); 211 } 212 213 } 214 sb.append(" }"); 215 return sb.toString(); 216 } 217 218 @Override 219 public String repr(@GuardSatisfied OneOfString this) { 220 return "OneOfString" + varNames() + ": falsified=" + falsified 221 + ", num_elts=" + num_elts 222 + ", elts=" + subarray_rep(); 223 } 224 225 public @Interned String[] getElts() { 226 @Interned String[] temp = new @Interned String[elts.length]; 227 for (int i = 0; i < elts.length; i++) { 228 temp[i] = elts[i]; 229 } 230 return temp; 231 } 232 233 @SideEffectFree 234 @Override 235 public String format_using(@GuardSatisfied OneOfString this, OutputFormat format) { 236 sort_rep(); 237 238 if (format.isJavaFamily()) { 239 return format_java_family(format); 240 } 241 242 if (format == OutputFormat.DAIKON) { 243 return format_daikon(); 244 } else if (format == OutputFormat.SIMPLIFY) { 245 return format_simplify(); 246 } else if (format == OutputFormat.ESCJAVA) { 247 String result = format_esc(); 248 return result; 249 } else if (format == OutputFormat.CSHARPCONTRACT) { 250 return format_csharp_contract(); 251 } else { 252 return format_unimplemented(format); 253 } 254 } 255 256 public String format_daikon(@GuardSatisfied OneOfString this) { 257 String varname = var().name(); 258 if (num_elts == 1) { 259 260 boolean is_type = is_type(); 261 if (!is_type) { 262 return varname + " == " + ((elts[0] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[0]) + "\""); 263 } else { 264 // It's a type 265 String str = elts[0]; 266 if ((str == null) || "null".equals(str)) { 267 return varname + " == null"; 268 } else { 269 if (str.startsWith("[")) { 270 str = Signatures.fieldDescriptorToBinaryName(str); 271 } 272 if (PrintInvariants.dkconfig_static_const_infer) { 273 PptTopLevel pptt = ppt.parent; 274 for (VarInfo vi : pptt.var_infos) { 275 if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) { 276 Object constantVal = vi.constantValue(); 277 if (constantVal.equals(str)) { 278 return varname + " == " + vi.name(); 279 } 280 } 281 } 282 } 283 // ".class" (which is a suffix for a type name) and not 284 // getClassSuffix (which is a suffix for an expression). 285 return varname + " == " + str + ".class"; 286 } 287 } 288 289 } else { 290 return varname + " one of " + subarray_rep(); 291 } 292 } 293 294 @Pure 295 private boolean is_type(@GuardSatisfied OneOfString this) { 296 return var().has_typeof(); 297 } 298 299 private static Pattern dollar_char_pat = Pattern.compile("\\$([A-Za-z])"); 300 301 private static String format_esc_string2type(String str) { 302 if ((str == null) || "null".equals(str)) { 303 return "\\typeof(null)"; 304 } 305 String type_str; 306 if (str.startsWith("[")) { 307 type_str = Signatures.fieldDescriptorToBinaryName(str); 308 } else { 309 type_str = str; 310 if (type_str.startsWith("\"") && type_str.endsWith("\"")) { 311 type_str = type_str.substring(1, type_str.length()-1); 312 } 313 } 314 315 // Inner classes 316 // type_str = type_str.replace('$', '.'); 317 // For named inner classes, convert "$" to ".". 318 // For anonymous inner classes, leave as "$". 319 Matcher m = dollar_char_pat.matcher(type_str); 320 type_str = m.replaceAll(".$1"); 321 322 return "\\type(" + type_str + ")"; 323 } 324 325 public String format_esc(@GuardSatisfied OneOfString this) { 326 sort_rep(); 327 328 String varname = var().esc_name(); 329 330 String result; 331 332 // We cannot say anything about Strings in ESC, just types (which 333 // Daikon stores as Strings). 334 if (!is_type()) { 335 result = format_unimplemented(OutputFormat.ESCJAVA); // "needs to be implemented" 336 } else { 337 // Format \typeof(theArray) = "[Ljava.lang.Object;" 338 // as \typeof(theArray) == \type(java.lang.Object[]) 339 // ... but still ... 340 // format \typeof(other) = "package.SomeClass;" 341 // as \typeof(other) == \type(package.SomeClass) 342 343 result = ""; 344 for (int i = 0; i < num_elts; i++) { 345 if (i != 0) { result += " || "; } 346 result += varname + " == " + format_esc_string2type(elts[i]); 347 } 348 } 349 350 return result; 351 } 352 353public String format_csharp_contract(@GuardSatisfied OneOfString this) { 354 355 @NonNull @Initialized String result; 356 357 String varname = var().csharp_name(); 358 359 if (is_type()) { 360 List<String> names = new ArrayList<>(); 361 for (int i = 0; i < num_elts; i++) { 362 String str = elts[i]; 363 if ((str == null) || "null".equals(str)) { 364 names.add(varname + " == null"); 365 } else { 366 names.add(varname + " == typeof(" + str + ")"); 367 } 368 } 369 result = String.join(" || ", names); 370 } else if (num_elts == 1) { 371 String str = elts[0]; 372 result = (str == null || "null".equals(str)) 373 ? (varname + " == null") 374 : (varname + ".Equals(" + ((str == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(str) + "\"") + ")"); 375 } else { 376 List<String> names = new ArrayList<>(); 377 for (int i = 0; i < num_elts; i++) { 378 String str = elts[i]; 379 names.add((str == null) ? "null" : ((str == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(str) + "\"")); 380 } 381 result = varname + ".OneOf(" + String.join(", ", names) + ")"; 382 } 383 384 return result; 385 } 386 387 public String format_java_family(@GuardSatisfied OneOfString this, OutputFormat format) { 388 389 String result; 390 391 // Setting up the name of the unary variable 392 String varname = var().name_using(format); 393 394 result = ""; 395 boolean is_type = is_type(); 396 for (int i = 0; i < num_elts; i++) { 397 if (i != 0) { result += " || "; } 398 String str = elts[i]; 399 if (!is_type) { 400 result += varname + ".equals(" + ((str == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(str) + "\"") + ")"; 401 } else { 402 // It's a type 403 if ((str == null) || "null".equals(str)) { 404 result += varname + " == null"; 405 } else { 406 if (str.startsWith("[")) { 407 str = Signatures.fieldDescriptorToBinaryName(str); 408 } 409 // ".class" (which is a suffix for a type name) and not 410 // getClassSuffix (which is a suffix for an expression). 411 // we need ".class.getName()" to make result a string 412 result += varname + " == " + str + ".class.getName()"; 413 } 414 } 415 } 416 417 return result; 418 } 419 420 public String format_simplify(@GuardSatisfied OneOfString this) { 421 422 sort_rep(); 423 424 String varname = 425 var().simplifyFixup(var().name_using(OutputFormat.SIMPLIFY)); 426 427 String result; 428 429 result = ""; 430 boolean is_type = is_type(); 431 for (int i = 0; i < num_elts; i++) { 432 String value = elts[i]; 433 if (is_type) { 434 if (value == null) { 435 // do nothing 436 } else if (value.startsWith("[")) { 437 value = Signatures.fieldDescriptorToBinaryName(value); 438 } else if (value.startsWith("\"") && value.endsWith("\"")) { 439 value = value.substring(1, value.length()-1); 440 } 441 value = "|T_" + value + "|"; 442 } else { 443 value = simplify_format_string(value); 444 } 445 result += " (EQ " + varname + " " + value + ")"; 446 } 447 if (num_elts > 1) { 448 result = "(OR" + result + ")"; 449 } else if (num_elts == 1) { 450 // chop leading space 451 result = result.substring(1); 452 } else if (num_elts == 0) { 453 return format_too_few_samples(OutputFormat.SIMPLIFY, null); 454 } 455 456 if (result.indexOf("format_simplify") == -1) { 457 daikon.simplify.SimpUtil.assert_well_formed(result); 458 } 459 return result; 460 } 461 462 @Override 463 public InvariantStatus add_modified(@Interned String a, int count) { 464 return runValue(a, count, true); 465 } 466 467 @Override 468 public InvariantStatus check_modified(@Interned String a, int count) { 469 return runValue(a, count, false); 470 } 471 472 private InvariantStatus runValue(@Interned String v, int count, boolean mutate) { 473 InvariantStatus status; 474 if (mutate) { 475 status = add_mod_elem(v, count); 476 } else { 477 status = check_mod_elem(v, count); 478 } 479 if (status == InvariantStatus.FALSIFIED) { 480 if (logOn() && mutate) { 481 StringBuilder eltString = new StringBuilder(); 482 for (int i = 0; i < num_elts; i++) { 483 eltString.append(((elts[i] == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(elts[i]) + "\"") + " "); 484 } 485 log("destroyed on sample %s previous vals = {%s} num_elts = %s", 486 ((v == null) ? "null" : "\"" + StringsPlume.escapeNonASCII(v) + "\""), eltString, num_elts); 487 } 488 return InvariantStatus.FALSIFIED; 489 } 490 return status; 491 } 492 493 /** 494 * Adds a single sample to the invariant. Returns 495 * the appropriate InvariantStatus from the result 496 * of adding the sample to this. 497 */ 498 public InvariantStatus add_mod_elem(@Interned String v, int count) { 499 InvariantStatus status = check_mod_elem(v, count); 500 if (status == InvariantStatus.WEAKENED) { 501 elts[num_elts] = v; 502 num_elts++; 503 } 504 return status; 505 } 506 507 /** 508 * Checks a single sample to the invariant. Returns 509 * the appropriate InvariantStatus from the result 510 * of adding the sample to this. 511 */ 512 public InvariantStatus check_mod_elem(@Interned String v, int count) { 513 514 // Look for v in our list of previously seen values. If it's 515 // found, we're all set. 516 for (int i = 0; i < num_elts; i++) { 517 // if (logDetail()) 518 // log ("add_modified (" + v + ")"); 519 if (((elts[i]) == ( v))) { 520 return InvariantStatus.NO_CHANGE; 521 } 522 } 523 524 if (num_elts == dkconfig_size) { 525 return InvariantStatus.FALSIFIED; 526 } 527 528 if (is_type() && (num_elts == 1)) { 529 return InvariantStatus.FALSIFIED; 530 } 531 532 return InvariantStatus.WEAKENED; 533 } 534 535 @Override 536 protected double computeConfidence() { 537 // This is not ideal. 538 if (num_elts == 0) { 539 return Invariant.CONFIDENCE_UNJUSTIFIED; 540 } else { 541 return Invariant.CONFIDENCE_JUSTIFIED; 542 } 543 } 544 545 @Pure 546 @Override 547 public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) { 548 // Static constants are necessarily OneOf precisely one value. 549 // This removes static constants from the output, which might not be 550 // desirable if the user doesn't know their actual value. 551 if (vis[0].isStaticConstant()) { 552 assert num_elts <= 1; 553 return new DiscardInfo(this, DiscardCode.obvious, vis[0].name() + " is a static constant."); 554 } 555 return super.isObviousStatically(vis); 556 } 557 558 /** {@inheritDoc} */ 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 Invariant 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}