001package daikon; 002 003import static daikon.FileIO.VarDefinition; 004 005import daikon.Quantify.QuantFlags; 006import daikon.Quantify.QuantifyReturn; 007import daikon.VarInfoName.Add; 008import daikon.VarInfoName.Elements; 009import daikon.VarInfoName.ElementsFinder; 010import daikon.VarInfoName.Field; 011import daikon.VarInfoName.FunctionOf; 012import daikon.VarInfoName.FunctionOfN; 013import daikon.VarInfoName.Poststate; 014import daikon.VarInfoName.Prestate; 015import daikon.VarInfoName.Simple; 016import daikon.VarInfoName.SizeOf; 017import daikon.VarInfoName.Slice; 018import daikon.VarInfoName.Subscript; 019import daikon.VarInfoName.TypeOf; 020import daikon.VarInfoName.Visitor; 021import daikon.chicory.DaikonVariableInfo; 022import daikon.derive.Derivation; 023import daikon.derive.binary.SequenceScalarSubscript; 024import daikon.derive.binary.SequenceScalarSubsequence; 025import daikon.derive.binary.SequenceSubsequence; 026import daikon.derive.ternary.SequenceScalarArbitrarySubsequence; 027import daikon.derive.unary.SequenceInitial; 028import daikon.derive.unary.SequenceLength; 029import daikon.derive.unary.SequenceMax; 030import daikon.derive.unary.SequenceMin; 031import daikon.derive.unary.SequenceSum; 032import daikon.inv.Equality; 033import daikon.inv.Invariant; 034import daikon.inv.OutputFormat; 035import daikon.inv.ValueSet; 036import daikon.inv.binary.twoScalar.IntGreaterEqual; 037import daikon.inv.binary.twoScalar.IntGreaterThan; 038import daikon.inv.binary.twoScalar.IntLessEqual; 039import daikon.inv.binary.twoScalar.IntLessThan; 040import daikon.inv.binary.twoScalar.LinearBinary; 041import daikon.inv.unary.scalar.NonZero; 042import daikon.inv.unary.scalar.SingleScalar; 043import daikon.inv.unary.sequence.EltNonZero; 044import daikon.inv.unary.sequence.SingleScalarSequence; 045import java.io.IOException; 046import java.io.ObjectInputStream; 047import java.io.Serializable; 048import java.util.ArrayList; 049import java.util.Arrays; 050import java.util.Comparator; 051import java.util.EnumSet; 052import java.util.HashMap; 053import java.util.HashSet; 054import java.util.LinkedHashSet; 055import java.util.List; 056import java.util.Map; 057import java.util.Set; 058import java.util.StringJoiner; 059import java.util.logging.Level; 060import java.util.logging.Logger; 061import org.checkerframework.checker.formatter.qual.FormatMethod; 062import org.checkerframework.checker.interning.qual.Interned; 063import org.checkerframework.checker.lock.qual.GuardSatisfied; 064import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 065import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 066import org.checkerframework.checker.nullness.qual.NonNull; 067import org.checkerframework.checker.nullness.qual.Nullable; 068import org.checkerframework.checker.signedness.qual.PolySigned; 069import org.checkerframework.dataflow.qual.Pure; 070import org.checkerframework.dataflow.qual.SideEffectFree; 071import org.plumelib.util.ArraysPlume; 072 073/** 074 * Represents information about a particular variable for a program point. This object doesn't hold 075 * the value of the variable at a particular step of the program point, but can get the value it 076 * holds when given a ValueTuple using the getValue() method. VarInfo also includes info about the 077 * variable's name, its declared type, its file representation type, its internal type, and its 078 * comparability. 079 */ 080@SuppressWarnings({ 081 "nullness", // nullness properties in this file are hairy; save for later 082 "interning" 083}) 084public final @Interned class VarInfo implements Cloneable, Serializable { 085 static final long serialVersionUID = 20060815L; 086 087 /** 088 * If true, then variables are only considered comparable if they are declared with the same type. 089 * For example, java.util.List is not comparable to java.util.ArrayList and float is not 090 * comparable to double. This may miss valid invariants, but significant time can be saved and 091 * many variables with different declared types are not comparable (e.g., java.util.Date and 092 * java.util.ArrayList). 093 */ 094 public static boolean dkconfig_declared_type_comparability = true; 095 096 /** 097 * If true, the treat static constants (such as MapQuick.GeoPoint.FACTOR) as fields within an 098 * object rather than as a single name. Not correct, but used to obtain compatibility with 099 * VarInfoName. 100 */ 101 public static boolean dkconfig_constant_fields_simplify = true; 102 103 /** Debug missing vals. */ 104 public static final Logger debugMissing = Logger.getLogger("daikon.VarInfo.missing"); 105 106 // Ppts read from version 1 files never have their ppt set. We should stop supporting version 1 107 // files so that the invariants of this class are better maintained. 108 /** 109 * The program point this variable is in. Is null until set by {@link PptTopLevel#init_vars} and 110 * {@link PptTopLevel#addVarInfos}. 111 */ 112 public PptTopLevel ppt; 113 114 /** 115 * Name. Do not compare names of invariants from different program points, because two different 116 * program points could contain unrelated variables named "x". 117 */ 118 private VarInfoName var_info_name; // interned 119 120 /** 121 * Name as specified in the program point declaration. VarInfoName sometimes changes this name as 122 * part of parsing so that VarInfoName.name() doesn't return the original name. 123 */ 124 private @Interned String str_name; // interned 125 126 /** returns the interned name of the variable. */ 127 @Pure 128 public @Interned String name(@GuardSatisfied VarInfo this) { 129 if (FileIO.new_decl_format) { 130 return str_name; 131 } else { 132 return var_info_name.name().intern(); // vin ok 133 } 134 } 135 136 /** Returns the original name of the variable from the program point declaration. */ 137 public @Interned String str_name() { 138 return str_name; 139 } 140 141 /** 142 * Type as declared in the target program. This is seldom used within Daikon as these types vary 143 * with program language and the like. It's here more for information than anything else. 144 */ 145 public ProglangType type; // interned (as are all ProglangType objects) 146 147 /** 148 * Type as written in the data trace file -- i.e., it is the source variable type mapped into the 149 * set of basic types recognized by Daikon. In particular, it includes boolean and hashcode 150 * (pointer). This is the type that is normally used when determining if an invariant is 151 * applicable to a variable. For example, the less-than invariant is not applicable to booleans or 152 * hashcodes, but is applicable to integers (of various sizes) and floats. (In the variable name, 153 * "rep" stands for "representation".) 154 */ 155 public ProglangType file_rep_type; // interned (as are all ProglangType objects) 156 157 /** 158 * Type as internally stored by Daikon. It contains less information than file_rep_type (for 159 * example, boolean and hashcode are both stored as integers). (In the variable name, "rep" stands 160 * for "representation".) 161 * 162 * @see ProglangType#fileTypeToRepType() 163 */ 164 public ProglangType rep_type; // interned (as are all ProglangType objects) 165 166 /** Comparability info. */ 167 @SuppressWarnings("serial") 168 public VarComparability comparability; 169 170 /** Auxiliary info. */ 171 public VarInfoAux aux; 172 173 /** The index in lists of VarInfo objects. */ 174 public int varinfo_index; 175 176 /** 177 * The index in a ValueTuple (more generally, in a list of values). It can differ from 178 * varinfo_index due to constants (and possibly other factors). It is -1 iff is_static_constant or 179 * not yet set. 180 */ 181 public int value_index; 182 183 /** 184 * Invariants: <br> 185 * is_static_constant == (value_index == -1);<br> 186 * is_static_constant == (static_constant_value != null). 187 */ 188 // queried via isStaticConstant() method, so consider making this field private 189 public boolean is_static_constant; 190 191 /** Null if not statically constant. */ 192 @SuppressWarnings("serial") 193 @Nullable @Interned Object static_constant_value; 194 195 /** Whether and how derived. Null if this is not derived. */ 196 public @MonotonicNonNull Derivation derived; 197 198 // Various enums used for information about variables 199 public enum RefType { 200 POINTER, 201 OFFSET 202 }; 203 204 public enum LangFlags { 205 PUBLIC, 206 PRIVATE, 207 PROTECTED, 208 STATIC, 209 FINAL, 210 SYNCHRONIZED, 211 VOLATILE, 212 TRANSIENT, 213 ANNOTATION, 214 ENUM 215 }; 216 217 // These enums are intentionally duplicated in Chicory and other 218 // front-ends. These values are written into decl files, and as 219 // such, should stay constant between front-ends. They should not be 220 // changed without good reason; if you do change them, make sure to 221 // also change the corresponding constants in Daikon front ends! 222 // Adding a new enum is fine, but should also be done in all locations. 223 public enum VarKind { 224 FIELD, 225 FUNCTION, 226 ARRAY, 227 VARIABLE, 228 RETURN 229 }; 230 231 public enum VarFlags { 232 IS_PARAM, 233 NO_DUPS, 234 NOT_ORDERED, 235 NO_SIZE, 236 NOMOD, 237 SYNTHETIC, 238 CLASSNAME, 239 TO_STRING, 240 NON_NULL, 241 IS_PROPERTY, 242 IS_ENUM, 243 IS_READONLY 244 }; 245 246 public @Nullable RefType ref_type; 247 public VarKind var_kind; 248 public EnumSet<VarFlags> var_flags = EnumSet.noneOf(VarFlags.class); 249 public EnumSet<LangFlags> lang_flags = EnumSet.noneOf(LangFlags.class); 250 251 public VarDefinition vardef; 252 253 /** 254 * For documentation, see {@link #get_enclosing_var()}. Null if no variable encloses this one -- 255 * that is, this is not a field of another variable, nor a "method call" like tostring or class. 256 */ 257 public @Nullable VarInfo enclosing_var; 258 259 /** Number of array dimensions (0 or 1). */ 260 public int arr_dims; 261 262 /** 263 * The arguments that were used to create this function application. Null if this variable is not 264 * a function application. 265 */ 266 @SuppressWarnings("serial") 267 public @MonotonicNonNull List<VarInfo> function_args = null; 268 269 /** Parent program points in ppt hierarchy (optional) */ 270 @SuppressWarnings("serial") 271 public List<VarParent> parents; 272 273 /** 274 * The relative name of this variable with respect to its enclosing variable. Field name for 275 * fields, method name for instance methods. 276 */ 277 public @Nullable String relative_name = null; 278 279 /** 280 * Returns whether or not we have encountered to date any missing values due to array indices 281 * being out of bounds. This can happen with both subscripts and subsequences. Note that this 282 * becomes true as we are running, it cannot be set in advance without a first pass. 283 * 284 * <p>This is used as we are processing data to destroy any invariants that use this variable. 285 * 286 * @see Derivation#missingOutOfBounds() 287 */ 288 public boolean missingOutOfBounds() { 289 if ((derived != null) && derived.missingOutOfBounds()) { 290 return true; 291 } 292 return false; 293 } 294 295 /** 296 * True if a missing/nonsensical value was ever observed for this variable. This starts out false 297 * and is set dynamically, while reading the trace file. 298 */ 299 public boolean canBeMissing = false; 300 301 /** 302 * Which equality group this belongs to. Replaces equal_to. Never null after this is put inside 303 * equalitySet. 304 */ 305 public Equality equalitySet; 306 307 /** Cached value for sequenceSize() */ 308 private VarInfo sequenceSize; 309 310 /** 311 * non-null if this is an orig() variable. 312 * 313 * <p><b>Do not test equality! Only use its .name slot.</b> 314 */ 315 public @Nullable VarInfo postState; 316 317 /** 318 * Throws an exception if this object is malformed. Requires that the VarInfo has been installed 319 * into a program point (the {@code ppt} field is set). 320 * 321 * @exception RuntimeException if representation invariant on this is broken 322 */ 323 public void checkRep() { 324 checkRepNoPpt(); 325 assert ppt != null; 326 assert 0 <= varinfo_index && varinfo_index < ppt.var_infos.length; 327 assert -1 <= value_index && value_index <= varinfo_index 328 : "" + this + " value_index=" + value_index + ", varinfo_index=" + varinfo_index; 329 assert is_static_constant == (value_index == -1); 330 assert is_static_constant || (static_constant_value == null); 331 } 332 333 /** 334 * Throws an exception if this object is malformed. 335 * 336 * <p>Does not require the {@code ppt} field to be set; can be called on VarInfos that have not 337 * been installed into a program point. 338 * 339 * @exception RuntimeException if representation invariant on this is broken 340 */ 341 public void checkRepNoPpt() { 342 try { 343 assert var_info_name != null; // vin ok 344 assert var_info_name == var_info_name.intern(); // vin ok 345 assert type != null; 346 assert file_rep_type != null; 347 assert rep_type != null; 348 assert var_kind != null; 349 assert comparability != null; // anything else ?? 350 assert (comparability.alwaysComparable() 351 || (((VarComparabilityImplicit) comparability).dimensions 352 == file_rep_type.dimensions())) 353 : "Dimensions mismatch for " 354 + this 355 + ": " 356 + ((VarComparabilityImplicit) comparability).dimensions 357 + " " 358 + file_rep_type.dimensions(); 359 if ((var_kind == VarKind.FIELD || var_kind == VarKind.ARRAY) && enclosing_var == null) { 360 throw new AssertionError( 361 "enclosing-var not specified for variable " + var_info_name + " of kind " + var_kind); 362 } 363 } catch (Throwable e) { 364 throw new AssertionError( 365 "checkRepNoPpt failed for variable " 366 + var_info_name 367 + (ppt == null ? "" : (" in " + ppt)) 368 + ": " 369 + repr(), 370 e); 371 } 372 } 373 374 /** Returns whether or not rep_type is a legal type. */ 375 static boolean legalRepType(ProglangType rep_type) { 376 return ((rep_type == ProglangType.INT) 377 || (rep_type == ProglangType.DOUBLE) 378 || (rep_type == ProglangType.STRING) 379 || (rep_type == ProglangType.INT_ARRAY) 380 || (rep_type == ProglangType.DOUBLE_ARRAY) 381 || (rep_type == ProglangType.STRING_ARRAY)); 382 } 383 384 /** Returns whether or not constant_value is a legal constant. */ 385 @EnsuresNonNullIf(result = false, expression = "#1") 386 static boolean legalConstant(@Nullable Object constant_value) { 387 return ((constant_value == null) 388 || (constant_value instanceof Long) 389 || (constant_value instanceof Double)); 390 } 391 392 /** 393 * Returns whether or not file_rep_type is a legal file_rep_type. The file_rep_type matches 394 * rep_type except that it also allows the more detailed scalar types (HASHCODE, BOOLEAN, etc). 395 */ 396 static boolean legalFileRepType(ProglangType file_rep_type) { 397 return (legalRepType(file_rep_type) 398 // The below types are converted into one of the rep types 399 // by ProglangType.fileTypeToRepType(). 400 || (file_rep_type == ProglangType.HASHCODE) 401 || (file_rep_type == ProglangType.HASHCODE_ARRAY) 402 || ((file_rep_type.dimensions() <= 1) && file_rep_type.baseIsPrimitive())); 403 } 404 405 /** 406 * Create VarInfo from VarDefinition. 407 * 408 * <p>This does not create a fully initialized VarInfo. For example, its ppt and enclosing_var 409 * fields are not yet set. Callers need to do some work to complete the construction of the 410 * VarInfo. 411 */ 412 public VarInfo(VarDefinition vardef) { 413 vardef.checkRep(); 414 415 this.vardef = vardef; 416 417 // Create a VarInfoName from the external name. This probably gets 418 // removed in the long run. 419 try { 420 var_info_name = VarInfoName.parse(vardef.name); // vin ok 421 } catch (Exception e) { 422 @SuppressWarnings("nullness") // error case, likely to crash later anyway 423 @NonNull VarInfoName vin = null; 424 var_info_name = vin; 425 System.out.printf("Warning: Can't parse %s as a VarInfoName", vardef.name); 426 } 427 str_name = vardef.name.intern(); 428 429 // Copy info from vardef 430 var_kind = vardef.kind; 431 relative_name = vardef.relative_name; 432 ref_type = vardef.ref_type; 433 arr_dims = vardef.arr_dims; 434 comparability = vardef.comparability; 435 file_rep_type = vardef.rep_type; 436 type = vardef.declared_type; 437 var_flags = vardef.flags; 438 lang_flags = vardef.lang_flags; 439 parents = new ArrayList<VarParent>(vardef.parents); 440 441 // If a static constant value was specified, set it 442 if (vardef.static_constant_value != null) { 443 is_static_constant = true; 444 static_constant_value = vardef.static_constant_value; 445 } else { 446 is_static_constant = false; 447 } 448 449 // Create the rep_type from the file rep type 450 rep_type = file_rep_type.fileTypeToRepType(); 451 452 // Create the VarInfoAux information 453 final List<String> auxstrs = new ArrayList<>(); 454 if (var_flags.contains(VarFlags.IS_PARAM)) { 455 auxstrs.add(VarInfoAux.IS_PARAM + "=true"); 456 } 457 if (var_flags.contains(VarFlags.NON_NULL)) { 458 auxstrs.add(VarInfoAux.IS_NON_NULL + "=true"); 459 } 460 if (vardef.min_value != null) { 461 auxstrs.add(VarInfoAux.MINIMUM_VALUE + "=" + vardef.min_value); 462 } 463 if (vardef.max_value != null) { 464 auxstrs.add(VarInfoAux.MAXIMUM_VALUE + "=" + vardef.max_value); 465 } 466 if (vardef.min_length != null) { 467 auxstrs.add(VarInfoAux.MINIMUM_LENGTH + "=" + vardef.min_length); 468 } 469 if (vardef.max_length != null) { 470 auxstrs.add(VarInfoAux.MAXIMUM_LENGTH + "=" + vardef.max_length); 471 } 472 if (vardef.valid_values != null) { 473 auxstrs.add(VarInfoAux.VALID_VALUES + "=" + vardef.valid_values); 474 } 475 // Sadly, String.join is only available from Java 8: 476 // https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#join-java.lang.CharSequence-java.lang.Iterable- 477 final String auxstr = String.join(", ", auxstrs); 478 479 try { 480 aux = VarInfoAux.parse(auxstr); 481 } catch (Exception e) { 482 throw new RuntimeException("unexpected aux error", e); 483 } 484 } 485 486 /** 487 * Finishes defining the variable by relating it to other variables. This cannot be done when 488 * creating the variable because the other variables it is related to, may not yet exist. 489 * Variables are related to their enclosing variables (for fields, arrays, and functions) and to 490 * their parent variables in the PptHierarchy. RuntimeExceptions are thrown if any related 491 * variables do not exist. 492 */ 493 public void relate_var() { 494 495 if (vardef == null) { 496 return; 497 } 498 499 // System.out.printf("enclosing var for %s is %s%n", str_name, 500 // vardef.enclosing_var); 501 502 // Find and set the enclosing variable (if any) 503 if (vardef.enclosing_var_name != null) { 504 enclosing_var = ppt.find_var_by_name(vardef.enclosing_var_name); 505 if (enclosing_var == null) { 506 for (int i = 0; i < ppt.var_infos.length; i++) { 507 System.out.printf("var = '%s'%n", ppt.var_infos[i]); 508 } 509 throw new RuntimeException( 510 String.format( 511 "enclosing variable '%s' for variable '%s' in ppt '%s' cannot be found", 512 vardef.enclosing_var_name, vardef.name, ppt.name)); 513 } 514 } 515 516 // Convert vardef.function_args, which is a list of Strings, 517 // into this.function_args, which is a list of VarInfos. 518 if (vardef.function_args != null) { 519 function_args = new ArrayList<VarInfo>(vardef.function_args.size()); 520 for (String varname : vardef.function_args) { 521 VarInfo vi = ppt.find_var_by_name(varname); 522 if (vi == null) { 523 throw new RuntimeException( 524 String.format( 525 "function argument '%s' for variable '%s' in ppt '%s' cannot be found", 526 varname, vardef.name, ppt.name)); 527 } 528 function_args.add(vi); 529 } 530 } 531 532 // do something appropriate with the ppt/var hierarchy. It may be 533 // that this is better done within PptRelation 534 } 535 536 /** 537 * Setup information normally specified in the declaration record for derived variables where the 538 * new variable is the result of applying a function to the other variables. Much of the 539 * information is inferred from (arbitrarily) the first argument to the function. 540 * 541 * <p>The parent_ppt field is set if each VarInfo in the derivation has the same parent. The 542 * parent_variable field is set if there is a parent_ppt and one or more of the bases has a 543 * non-default parent variable. The parent variable name is formed as function_name(arg1,arg2,...) 544 * where arg1, arg2, etc are the parent variable names of each of the arguments. 545 */ 546 public void setup_derived_function(String name, VarInfo... bases) { 547 548 // Copy variable info from the base. 549 // Might some of these need to be overridden later, because they are just guesses? 550 VarInfo base = bases[0]; 551 ref_type = null; 552 var_flags = base.var_flags.clone(); 553 lang_flags = base.lang_flags.clone(); 554 for (int ii = 1; ii < bases.length; ii++) { 555 var_flags.retainAll(bases[ii].var_flags); 556 lang_flags.retainAll(bases[ii].lang_flags); 557 } 558 enclosing_var = null; 559 arr_dims = base.arr_dims; 560 var_kind = VarKind.FUNCTION; 561 function_args = Arrays.asList(bases); 562 563 // Build the string name 564 List<String> arg_names = new ArrayList<>(); 565 for (VarInfo vi : bases) { 566 arg_names.add(vi.name()); 567 } 568 str_name = String.format("%s(%s)", name, String.join(",", arg_names)).intern(); 569 570 // The parent ppt is the same as the base if each varinfo in the 571 // derivation has the same parent 572 for (VarParent bp : base.parents) { 573 int parent_relation_id = bp.parent_relation_id; 574 String parent_ppt = bp.parent_ppt; 575 576 if (allHaveRelation(parent_relation_id)) { 577 parents.add(new VarParent(parent_ppt, parent_relation_id, null)); 578 } 579 } 580 581 // If there is a parent_ppt, determine the parent_variable name. 582 // If all of the argument names are the default, then the parent_variable 583 // is the default as well. Otherwise, build up the name from the 584 // function name and the name of each arguments parent variable name. 585 // TWS: the code doesn't appear to handle the case where some are default and some aren't. 586 for (VarParent p : parents) { 587 boolean parent_vars_specified = false; 588 for (VarInfo vi : bases) { 589 for (VarParent vp : vi.parents) { 590 if (vp.parent_variable != null && vp.parent_relation_id == p.parent_relation_id) { 591 parent_vars_specified = true; 592 } 593 } 594 } 595 if (!parent_vars_specified) { 596 p.parent_variable = null; 597 } else { 598 StringJoiner args = new StringJoiner(","); 599 for (VarInfo vi : bases) { 600 boolean found = false; 601 for (VarParent vp : vi.parents) { 602 if (vp.parent_relation_id == p.parent_relation_id) { 603 args.add(vp.parent_variable); 604 found = true; 605 } 606 } 607 if (!found) { 608 args.add(vi.name()); 609 } 610 } 611 p.parent_variable = String.format("%s(%s)", name, args.toString()); 612 } 613 } 614 } 615 616 /** 617 * Setup information normally specified in the declaration record for derived variables where one 618 * of the variables is the base of the derivation. In general this information is inferred from 619 * the base variable of the derived variables. Note that parent_ppt is set if each VarInfo in the 620 * derivation has the same parent, but parent_variable is not set. This has to be set based on the 621 * particular derivation. 622 */ 623 public void setup_derived_base(VarInfo base, @Nullable VarInfo... others) { 624 625 // Copy variable info from the base 626 ref_type = base.ref_type; 627 var_kind = base.var_kind; 628 var_flags = base.var_flags.clone(); 629 lang_flags = base.lang_flags.clone(); 630 enclosing_var = base.enclosing_var; 631 arr_dims = base.arr_dims; 632 function_args = base.function_args; 633 634 // The parent ppt is the same as the base if each varinfo in the 635 // derivation has the same parent 636 for (VarParent bp : base.parents) { 637 int parent_relation_id = bp.parent_relation_id; 638 String parent_ppt = bp.parent_ppt; 639 640 if (allHaveRelation(parent_relation_id, others)) { 641 parents.add(new VarParent(parent_ppt, parent_relation_id, null)); 642 } 643 } 644 } 645 646 /** Returns true if all variables have a parent relation with the specified id. */ 647 private static boolean allHaveRelation(int parent_relation_id, VarInfo... vs) { 648 for (VarInfo vi : vs) { 649 if (vi == null) { 650 continue; 651 } 652 boolean hasRelId = false; 653 for (VarParent vp : vi.parents) { 654 if (parent_relation_id == vp.parent_relation_id) { 655 hasRelId = true; 656 break; 657 } 658 } 659 if (!hasRelId) { 660 return false; 661 } 662 } 663 return true; 664 } 665 666 /** 667 * Create the specified VarInfo. The resulting VarInfo does not have its ppt field set. 668 * 669 * @param name the variable name 670 * @param type type as declared in the program point 671 * @param file_rep_type type as written in the data trace file 672 * @param comparability comparability info 673 * @param is_static_constant true if the variable always has the same, known value 674 * @param static_constant_value the static constant value, or null if not statically constant 675 * @param aux auxiliary info 676 */ 677 private VarInfo( 678 VarInfoName name, 679 ProglangType type, 680 ProglangType file_rep_type, 681 VarComparability comparability, 682 boolean is_static_constant, 683 @Nullable @Interned Object static_constant_value, 684 VarInfoAux aux) { 685 686 assert name != null; 687 assert file_rep_type != null; 688 assert legalFileRepType(file_rep_type) 689 : "Unsupported representation type " 690 + file_rep_type.format() 691 + "/" 692 + file_rep_type.getClass() 693 + " " 694 + ProglangType.HASHCODE.getClass() 695 + " for variable " 696 + name; 697 assert type != null; 698 assert comparability != null; 699 // COMPARABILITY TEST 700 // assert 701 // comparability.alwaysComparable() || ((VarComparabilityImplicit)comparability).dimensions == 702 // file_rep_type.dimensions() 703 // : "Types dimensions incompatibility: " 704 // + type 705 // + " vs. " 706 // + file_rep_type; 707 assert aux != null; 708 assert legalConstant(static_constant_value) 709 : "unexpected constant class " + static_constant_value.getClass(); 710 711 // Possibly the call to intern() isn't necessary; but it's safest to 712 // make the call to intern() rather than running the risk that a caller 713 // didn't. 714 this.var_info_name = name.intern(); // vin ok 715 this.type = type; 716 this.file_rep_type = file_rep_type; 717 this.rep_type = file_rep_type.fileTypeToRepType(); 718 arr_dims = rep_type.isArray() ? 1 : 0; 719 this.comparability = comparability; 720 this.is_static_constant = is_static_constant; 721 this.static_constant_value = static_constant_value; 722 this.aux = aux; 723 this.parents = new ArrayList<VarParent>(); 724 725 if (debug.isLoggable(Level.FINE)) { 726 debug.fine("Var " + name + " aux: " + aux); 727 } 728 729 // Indicates that these haven't yet been set to reasonable values. 730 value_index = -1; 731 varinfo_index = -1; 732 733 canBeMissing = false; 734 } 735 736 /** Create the specified VarInfo. */ 737 public VarInfo( 738 String name, 739 ProglangType type, 740 ProglangType file_rep_type, 741 VarComparability comparability, 742 boolean is_static_constant, 743 @Nullable @Interned Object static_constant_value, 744 VarInfoAux aux) { 745 this( 746 VarInfoName.parse(name), 747 type, 748 file_rep_type, 749 comparability, 750 is_static_constant, 751 static_constant_value, 752 aux); 753 assert name != null; 754 this.str_name = name.intern(); 755 } 756 757 /** Create the specified non-static VarInfo. */ 758 private VarInfo( 759 VarInfoName name, 760 ProglangType type, 761 ProglangType file_rep_type, 762 VarComparability comparability, 763 VarInfoAux aux) { 764 this(name, type, file_rep_type, comparability, false, null, aux); 765 } 766 767 /** Create the specified non-static VarInfo. */ 768 public VarInfo( 769 String name, 770 ProglangType type, 771 ProglangType file_rep_type, 772 VarComparability comparability, 773 VarInfoAux aux) { 774 this(name, type, file_rep_type, comparability, false, null, aux); 775 assert name != null; 776 this.str_name = name.intern(); 777 } 778 779 /** Create a VarInfo with the same values as vi. */ 780 public VarInfo(VarInfo vi) { 781 this( 782 vi.name(), 783 vi.type, 784 vi.file_rep_type, 785 vi.comparability, 786 vi.is_static_constant, 787 vi.static_constant_value, 788 vi.aux); 789 str_name = vi.str_name; 790 canBeMissing = vi.canBeMissing; 791 postState = vi.postState; 792 equalitySet = vi.equalitySet; 793 ref_type = vi.ref_type; 794 var_kind = vi.var_kind; 795 var_flags = vi.var_flags.clone(); 796 lang_flags = vi.lang_flags.clone(); 797 vardef = vi.vardef; 798 enclosing_var = vi.enclosing_var; 799 arr_dims = vi.arr_dims; 800 function_args = vi.function_args; 801 parents = new ArrayList<VarParent>(); 802 for (VarParent parent : vi.parents) { 803 parents.add( 804 new VarParent(parent.parent_ppt, parent.parent_relation_id, parent.parent_variable)); 805 } 806 relative_name = vi.relative_name; 807 } 808 809 // /** Creates and returns a copy of this. */ 810 // // Default implementation to quiet Findbugs. 811 // @SuppressWarnings("interning") // temporary? 812 // public VarInfo clone() throws CloneNotSupportedException { 813 // return (VarInfo) super.clone(); 814 // } 815 816 /** 817 * Create the prestate, or "orig()", version of the variable. Note that the returned value is not 818 * completely initialized. The caller is still responsible for setting some fields of it, such as 819 * enclosing_var. 820 */ 821 public static VarInfo origVarInfo(VarInfo vi) { 822 // At an exit point, parameters are uninteresting, but orig(param) is not. 823 // So don't call orig(param) a parameter. 824 // VIN (below should be removed) 825 // VarInfoAux aux_nonparam = 826 // vi.aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.FALSE); 827 828 VarInfo result; 829 if (FileIO.new_decl_format) { 830 831 // Build a Variable Definition from the poststate vardef 832 VarDefinition result_vardef = vi.vardef.copy(); 833 result_vardef.name = vi.prestate_name(); 834 835 // The only hierarchy relation for orig variables is to the enter 836 // ppt. Remove any specified relations. 837 result_vardef.clear_parent_relation(); 838 839 // Fix the VarDefinition enclosing variable, if any, to point to the 840 // prestate version. This code does not affect the VarInfo yet, but 841 // the side-effected VarDefinition will be passed to "new VarInfo". 842 if (result_vardef.enclosing_var_name != null) { 843 assert vi.enclosing_var != null 844 : "@AssumeAssertion(nullness): dependent: result_vardef was copied from vi and their" 845 + " enclosing_var fields are the same"; 846 result_vardef.enclosing_var_name = vi.enclosing_var.prestate_name(); 847 assert result_vardef.enclosing_var_name != null : "" + result_vardef; 848 } 849 850 // Build the prestate VarInfo from the VarDefinition. 851 result = new VarInfo(result_vardef); 852 853 // Copy the missing flag from the original variable. This is necessary 854 // for combined exit points which are built after processing is 855 // complete. In most cases the missing flag will be set correctly 856 // by merging the missing flag from the numbered exit points. But 857 // this will fail if the method terminates early each time the variable 858 // is missing. A better fix would be to instrument early exits and 859 // merge them in as well, but this matches what we did previously. 860 result.canBeMissing = vi.canBeMissing; 861 862 } else { 863 VarInfoName newname = vi.var_info_name.applyPrestate(); // vin ok 864 result = 865 new VarInfo(newname, vi.type, vi.file_rep_type, vi.comparability.makeAlias(), vi.aux); 866 result.canBeMissing = vi.canBeMissing; 867 result.postState = vi; 868 result.equalitySet = vi.equalitySet; 869 result.arr_dims = vi.arr_dims; 870 result.str_name = vi.prestate_name(); 871 } 872 873 // At an exit point, parameters are uninteresting, but orig(param) is not. 874 // So don't call orig(param) a parameter. 875 result.set_is_param(false); 876 return result; 877 } 878 879 /** 880 * Given an array of VarInfo objects, return an array of clones, where references to the originals 881 * have been modified into references to the new ones (so that the new set is self-consistent). 882 * The originals should not be modified by this operation. 883 */ 884 public static VarInfo[] arrayclone_simple(VarInfo[] a_old) { 885 int len = a_old.length; 886 VarInfo[] a_new = new VarInfo[len]; 887 for (int i = 0; i < len; i++) { 888 a_new[i] = new VarInfo(a_old[i]); 889 if (a_old[i].derived != null) { 890 assert a_new[i].derived != null; 891 } 892 a_new[i].varinfo_index = a_old[i].varinfo_index; 893 a_new[i].value_index = a_old[i].value_index; 894 } 895 return a_new; 896 } 897 898 /** Trims the collections used by this VarInfo. */ 899 public void trimToSize() { 900 // if (derivees != null) { derivees.trimToSize(); } 901 // Derivation derived; probably can't be trimmed 902 } 903 904 /** 905 * Returns the name of the variable. 906 * 907 * @see #name 908 */ 909 @SideEffectFree 910 @Override 911 public String toString(@GuardSatisfied VarInfo this) { 912 return name(); 913 } 914 915 /** 916 * Returns the argument or, if it is null, {@code "null"}. 917 * 918 * @param o a reference 919 * @return the argument or, if it is null, {@code "null"} 920 */ 921 private @PolySigned Object checkNull( 922 @GuardSatisfied VarInfo this, @Nullable @PolySigned Object o) { 923 return (o == null) ? (@PolySigned Object) "null" : o; 924 } 925 926 /** 927 * Returns a complete string description of the variable. 928 * 929 * @return a complete string description of the variable 930 */ 931 public String repr() { 932 return "<VarInfo " 933 + var_info_name // vin ok 934 + ": " 935 + "type=" 936 + type 937 + ",file_rep_type=" 938 + file_rep_type 939 + ",rep_type=" 940 + rep_type 941 + ",comparability=" 942 + comparability 943 + ",value_index=" 944 + value_index 945 + ",varinfo_index=" 946 + varinfo_index 947 + ",is_static_constant=" 948 + is_static_constant 949 + ",static_constant_value=" 950 + static_constant_value 951 + ",derived=" 952 + checkNull(derived) 953 + ",derivees=" 954 + derivees() 955 + ",ppt=" 956 // This method is only called for debugging, so let's 957 // protect ourselves from a mistake somewhere else, such as ppt being null. 958 // + ppt.name() 959 + ppt 960 + ",canBeMissing=" 961 + canBeMissing 962 + (",equal_to=" + (equalitySet == null ? "null" : equalitySet.toString())) 963 + ",PostState=" 964 + postState 965 + ",isCanonical()=" 966 + isCanonical() 967 + ">"; 968 } 969 970 /** Returns whether or not this variable is a static constant. */ 971 @EnsuresNonNullIf( 972 result = true, 973 expression = {"constantValue()", "static_constant_value"}) 974 @Pure 975 public boolean isStaticConstant() { 976 return is_static_constant; 977 } 978 979 /** 980 * Returns the static constant value of this variable. The variable must be a static constant. The 981 * result is non-null. 982 */ 983 public Object constantValue() { 984 if (isStaticConstant()) { 985 assert static_constant_value != null; 986 return static_constant_value; 987 } else { 988 throw new Error("Variable " + name() + " is not constant"); 989 } 990 } 991 992 /** Returns true if this is an "orig()" variable. */ 993 @EnsuresNonNullIf(result = true, expression = "postState") 994 @Pure 995 public boolean isPrestate() { 996 return postState != null; 997 } 998 999 /** Returns true if this variable is derived from prestate variables. */ 1000 @SuppressWarnings("all:not.deterministic") // nondeterminism does not affect result 1001 @Pure 1002 public boolean isPrestateDerived() { 1003 if (postState != null) { 1004 return true; 1005 } 1006 if (isDerived()) { 1007 for (VarInfo vi : derived.getBases()) { 1008 if (!vi.isPrestate()) { 1009 return false; 1010 } 1011 } 1012 return true; 1013 } else { 1014 return isPrestate(); 1015 } 1016 1017 // return name.isAllPrestate(); 1018 } 1019 1020 /** Returns true if this variable is a derived variable. */ 1021 @EnsuresNonNullIf(result = true, expression = "this.derived") 1022 @Pure 1023 public boolean isDerived() { 1024 return (derived != null); 1025 } 1026 1027 /** 1028 * Returns the depth of derivation. 1029 * 1030 * @return the depth of derivation 1031 */ 1032 public int derivedDepth() { 1033 if (derived == null) { 1034 return 0; 1035 } else { 1036 return derived.derivedDepth(); 1037 } 1038 } 1039 1040 /** 1041 * Return all derived variables that build off this one. 1042 * 1043 * @return all derived variables that build off this one 1044 */ 1045 public List<Derivation> derivees() { 1046 ArrayList<Derivation> result = new ArrayList<>(); 1047 // This method is only called from the debugging routine 'repr()'. 1048 // So let's protect ourselves from a mistake somewhere else. 1049 if (ppt == null) { 1050 return result; 1051 } 1052 VarInfo[] vis = ppt.var_infos; 1053 for (int i = 0; i < vis.length; i++) { 1054 VarInfo vi = vis[i]; 1055 Derivation der = vi.derived; 1056 if (der == null) { 1057 continue; 1058 } 1059 if (ArraysPlume.indexOf(der.getBases(), this) >= 0) { 1060 result.add(der); 1061 } 1062 } 1063 return result; 1064 } 1065 1066 /** 1067 * Returns a list of all of the basic (non-derived) variables that are used to make up this 1068 * variable. If this variable is not derived, it is just this variable. Otherwise it is all of the 1069 * the bases of this derivation. 1070 */ 1071 public List<VarInfo> get_all_constituent_vars() { 1072 List<VarInfo> vars = new ArrayList<>(); 1073 if (isDerived()) { 1074 for (VarInfo vi : derived.getBases()) { 1075 vars.addAll(vi.get_all_constituent_vars()); 1076 } 1077 } else { 1078 vars.add(this); 1079 } 1080 return vars; 1081 } 1082 1083 /** 1084 * Returns a list of all of the simple names that make up this variable. this includes each field 1085 * and function name in the variable. If this variable is derived it includes the simple names 1086 * from each of its bases. For example, 'this.item.a' would return a list with 'this', 'item', and 1087 * 'a' and 'this.theArray[i]' would return 'this', 'theArray' and 'i'. 1088 */ 1089 public List<String> get_all_simple_names() { 1090 assert FileIO.new_decl_format; 1091 List<String> names = new ArrayList<>(); 1092 if (isDerived()) { 1093 for (VarInfo vi : derived.getBases()) { 1094 names.addAll(vi.get_all_simple_names()); 1095 } 1096 } else { 1097 VarInfo start = (isPrestate() ? postState : this); 1098 for (VarInfo vi = start; vi != null; vi = vi.enclosing_var) { 1099 if (relative_name == null) { 1100 names.add(vi.name()); 1101 } else { 1102 names.add(vi.relative_name); 1103 } 1104 } 1105 } 1106 return names; 1107 } 1108 1109 @Pure 1110 public boolean isClosure() { 1111 // This should eventually turn into 1112 // return name.indexOf("closure(") != -1; 1113 // when I rename those variables to "closure(...)". 1114 return name().indexOf("~") != -1; // XXX 1115 } 1116 1117 /** Cached value for getDerivedParam(). */ 1118 public @Nullable VarInfo derivedParamCached = null; 1119 1120 /** Cached value for isDerivedParam(). */ 1121 // Boolean rather than boolean so we can use "null" to indicate "not yet set". 1122 public @MonotonicNonNull Boolean isDerivedParamCached = null; 1123 1124 /** 1125 * Returns true if this is a param according to aux info, or this is a front end derivation such 1126 * that one of its bases is a param. To figure this out, what we do is get all the param variables 1127 * at this's program point. Then we search in this's name to see if the name contains any of the 1128 * variables. We have to do this because we only have name info, and we assume that x and x.a are 1129 * related from the names alone. 1130 * 1131 * <p>Effects: Sets isDerivedParamCached and derivedParamCached to values the first time this 1132 * method is called. Subsequent calls use these cached values. 1133 */ 1134 @EnsuresNonNullIf(result = true, expression = "getDerivedParam()") 1135 @SuppressWarnings("all:purity") // created object is not returned 1136 @Pure 1137 public boolean isDerivedParam() { 1138 if (isDerivedParamCached != null) { 1139 // System.out.printf("var %s is-derived-param = %b%n", name(), 1140 // isDerivedParamCached); 1141 return isDerivedParamCached.booleanValue(); 1142 } 1143 1144 boolean result = false; 1145 if (isParam() && !isPrestate()) { 1146 result = true; 1147 } 1148 1149 if (!FileIO.new_decl_format) { 1150 // Determine the result from VarInfoName 1151 Set<VarInfo> paramVars = ppt.getParamVars(); 1152 Set<VarInfoName> param_names = new LinkedHashSet<>(); 1153 for (VarInfo vi : paramVars) { 1154 param_names.add(vi.var_info_name); // vin ok 1155 } 1156 1157 VarInfoName.Finder finder = new VarInfoName.Finder(param_names); 1158 Object baseMaybe = finder.getPart(var_info_name); // vin ok 1159 if (baseMaybe != null) { 1160 VarInfoName base = (VarInfoName) baseMaybe; 1161 derivedParamCached = this.ppt.find_var_by_name(base.name()); 1162 if (Global.debugSuppressParam.isLoggable(Level.FINE)) { 1163 Global.debugSuppressParam.fine(name() + " is a derived param"); 1164 Global.debugSuppressParam.fine("derived from " + base.name()); 1165 Global.debugSuppressParam.fine(paramVars.toString()); 1166 } 1167 result = true; 1168 } 1169 } else { // new format 1170 derivedParamCached = enclosing_param(); 1171 if (derivedParamCached != null) { 1172 result = true; 1173 } else if (derived != null) { 1174 for (VarInfo vi : derived.getBases()) { 1175 derivedParamCached = vi.enclosing_param(); 1176 if (derivedParamCached != null) { 1177 result = true; 1178 break; 1179 } 1180 } 1181 } 1182 } 1183 1184 // System.out.printf("var %s is-derived-param = %b%n", name(), result); 1185 isDerivedParamCached = result ? Boolean.TRUE : Boolean.FALSE; 1186 return result; 1187 } 1188 1189 /** 1190 * Returns the param variable that encloses this variable (if any). Returns null otherwise. Only 1191 * valid in the new decl format. 1192 */ 1193 private @Nullable VarInfo enclosing_param() { 1194 // System.out.printf("Considering %s%n", this); 1195 assert FileIO.new_decl_format; 1196 if (isPrestate()) { 1197 return postState.enclosing_param(); 1198 } 1199 for (VarInfo evi = this; evi != null; evi = evi.enclosing_var) { 1200 // System.out.printf("%s isParam=%b%n", evi, evi.isParam()); 1201 if (evi.isParam()) { 1202 return evi; 1203 } 1204 } 1205 return null; 1206 } 1207 1208 /** 1209 * Return a VarInfo that has two properties: this is a derivation of it, and it is a parameter 1210 * variable. If this is a parameter, then this is returned. For example, "this" is always a 1211 * parameter. The return value of getDerivedParam for "this.a" (which is not a parameter) is 1212 * "this". 1213 * 1214 * <p>Effects: Sets isDerivedParamCached and derivedParamCached to values the first time this 1215 * method is called. Subsequent calls use these cached values. 1216 * 1217 * @return null if the above condition doesn't hold 1218 */ 1219 @Pure 1220 public @Nullable VarInfo getDerivedParam() { 1221 if (isDerivedParamCached == null) { 1222 // fill in the cache 1223 isDerivedParam(); 1224 } 1225 return derivedParamCached; 1226 } 1227 1228 private @MonotonicNonNull Boolean isDerivedParamAndUninterestingCached = null; 1229 1230 /** 1231 * Returns true if a given VarInfo is a parameter or derived from one in such a way that changes 1232 * to it wouldn't be visible to the method's caller. There are 3 such cases: 1233 * 1234 * <ul> 1235 * <li>The variable is a pass-by-value parameter "p". 1236 * <li>The variable is of the form "p.prop" where "prop" is an immutable property of an object, 1237 * like its type, or (for a Java array) its size. 1238 * <li>The variable is of the form "p.prop", and "p" has been modified to point to a different 1239 * object. We assume "p" has been modified if we don't have an invariant "orig(p) == p". 1240 * </ul> 1241 * 1242 * In any case, the variable must have a postState VarInfoName, and equality invariants need to 1243 * have already been computed. 1244 */ 1245 @SuppressWarnings("all:purity") // set cache field 1246 @Pure 1247 public boolean isDerivedParamAndUninteresting() { 1248 if (isDerivedParamAndUninterestingCached != null) { 1249 return isDerivedParamAndUninterestingCached.booleanValue(); 1250 } else { 1251 isDerivedParamAndUninterestingCached = 1252 _isDerivedParamAndUninteresting() ? Boolean.TRUE : Boolean.FALSE; 1253 return isDerivedParamAndUninterestingCached.booleanValue(); 1254 } 1255 } 1256 1257 /** Implementation of {@link #isDerivedParamAndUninteresting()}. */ 1258 @Pure 1259 private boolean _isDerivedParamAndUninteresting() { 1260 if (PrintInvariants.debugFiltering.isLoggable(Level.FINE)) { 1261 PrintInvariants.debugFiltering.fine("isDPAU: name is " + name()); 1262 PrintInvariants.debugFiltering.fine(" isPrestate is " + String.valueOf(isPrestate())); 1263 } 1264 1265 // Orig variables are not considered parameters. We only check the 1266 // first variable in a derivation because that is the sequence variable in 1267 // sequence-subscript or sequence-subsequence derivations and we don't 1268 // care if the index into the sequence is prestate or not. 1269 if (isPrestate() || (isDerived() && derived.getBase(0).isPrestate())) { 1270 return false; 1271 } 1272 1273 if (isParam()) { 1274 PrintInvariants.debugFiltering.fine(" not interesting, IS_PARAM == true for " + name()); 1275 return true; 1276 } 1277 if (Global.debugSuppressParam.isLoggable(Level.FINE)) { 1278 Global.debugSuppressParam.fine("Testing isDerivedParamAndUninteresting for: " + name()); 1279 Global.debugSuppressParam.fine(aux.toString()); 1280 Global.debugSuppressParam.fine("At ppt " + ppt.name()); 1281 } 1282 if (isDerivedParam()) { 1283 // I am uninteresting if I'm a derived param from X and X's 1284 // type or X's size, because these things are boring if X 1285 // changes (the default for the rest of the code here), and 1286 // boring if X stays the same (because it's obviously true). 1287 if (!FileIO.new_decl_format) { 1288 if (var_info_name instanceof VarInfoName.TypeOf) { // vin ok 1289 VarInfoName base = ((VarInfoName.TypeOf) var_info_name).term; // vin ok 1290 VarInfo baseVar = ppt.find_var_by_name(base.name()); 1291 if ((baseVar != null) && baseVar.isParam()) { 1292 Global.debugSuppressParam.fine("TypeOf returning true"); 1293 PrintInvariants.debugFiltering.fine(" not interesting, first dpf case"); 1294 return true; 1295 } 1296 } 1297 if (var_info_name instanceof VarInfoName.SizeOf) { // vin ok 1298 VarInfoName base = ((VarInfoName.SizeOf) var_info_name).get_term(); // vin ok 1299 VarInfo baseVar = ppt.find_var_by_name(base.name()); 1300 if (baseVar != null && baseVar.isParam()) { 1301 Global.debugSuppressParam.fine("SizeOf returning true"); 1302 PrintInvariants.debugFiltering.fine(" not interesting, second dpf case"); 1303 return true; 1304 } 1305 } 1306 } else { // new decl format 1307 assert enclosing_var != null : this; 1308 assert enclosing_var != null : "@AssumeAssertion(nullness)"; 1309 1310 // The class of a parameter can't change in the caller 1311 if (var_flags.contains(VarFlags.CLASSNAME) && enclosing_var.isParam()) { 1312 return true; 1313 } 1314 1315 // The size of a parameter can't change in the caller. We shouldn't 1316 // have the shift==0 test, but need it to match the old code 1317 if (is_size() && enclosing_var.get_base_array_hashcode().isParam()) { 1318 if (((SequenceLength) derived).shift == 0) { 1319 return true; 1320 } 1321 } 1322 } 1323 1324 VarInfo base = getDerivedParam(); 1325 assert base != null : "can't find base for " + name(); 1326 // Actually we should be getting all the derivations that could 1327 // be params, and if any of them are uninteresting, this is 1328 // uninteresting. 1329 1330 // Remember that if this is derived from a true param, then this 1331 // is a param too, so we don't need to worry. However, if this 1332 // is derived from a derivedParam, then we need to find all 1333 // derivation parents that could possibly fail under these 1334 // rules. Right now, we just get the first one. 1335 1336 // So if x = Foo(this.y, p.y) and this hasn't changed then we 1337 // will be ignoring the fact that y has changed. 1338 1339 // Henceforth only interesting if it's true that base = orig(base) 1340 if (base.name().equals("this")) { 1341 return false; 1342 } 1343 Global.debugSuppressParam.fine("Base is " + base.name()); 1344 VarInfo origBase = ppt.find_var_by_name(base.prestate_name()); 1345 if (origBase == null) { 1346 Global.debugSuppressParam.fine("No orig variable for base, returning true "); 1347 PrintInvariants.debugFiltering.fine(" not interesting, no orig variable for base"); 1348 return true; // There can't be an equal invariant without orig 1349 } 1350 if (base.isEqualTo(origBase)) { 1351 Global.debugSuppressParam.fine("Saw equality. Derived worth printing."); 1352 return false; 1353 } else { 1354 Global.debugSuppressParam.fine("Didn't see equality in base, so uninteresting"); 1355 PrintInvariants.debugFiltering.fine(" didn't see equality in base"); 1356 return true; 1357 } 1358 1359 } else { 1360 Global.debugSuppressParam.fine(" Not a derived param."); 1361 } 1362 return false; 1363 } 1364 1365 /** Convenience methods that return information from the ValueTuple. */ 1366 @Pure 1367 public int getModified(ValueTuple vt) { 1368 if (is_static_constant) { 1369 // return ValueTuple.STATIC_CONSTANT; 1370 return ValueTuple.MODIFIED; 1371 } else { 1372 return vt.getModified(value_index); 1373 } 1374 } 1375 1376 @Pure 1377 public boolean isUnmodified(ValueTuple vt) { 1378 return ValueTuple.modIsUnmodified(getModified(vt)); 1379 } 1380 1381 @Pure 1382 public boolean isModified(ValueTuple vt) { 1383 return ValueTuple.modIsModified(getModified(vt)); 1384 } 1385 1386 @Pure 1387 public boolean isMissingNonsensical(ValueTuple vt) { 1388 return ValueTuple.modIsMissingNonsensical(getModified(vt)); 1389 } 1390 1391 @Pure 1392 public boolean isMissingFlow(ValueTuple vt) { 1393 return ValueTuple.modIsMissingFlow(getModified(vt)); 1394 } 1395 1396 @Pure 1397 public boolean isMissing(ValueTuple vt) { 1398 return isMissingNonsensical(vt) || isMissingFlow(vt); 1399 } 1400 1401 /** 1402 * Get the value of this variable from a particular sample (ValueTuple). 1403 * 1404 * @param vt the ValueTuple from which to extract the value 1405 */ 1406 public @Interned Object getValue(ValueTuple vt) { 1407 if (is_static_constant) { 1408 @SuppressWarnings("nullness") // derived: is_static_constant == true 1409 @NonNull Object result = static_constant_value; 1410 return result; 1411 } else { 1412 return vt.getValue(value_index); 1413 } 1414 } 1415 1416 /** Use of this method is discouraged. */ 1417 public @Nullable @Interned Object getValueOrNull(ValueTuple vt) { 1418 if (is_static_constant) { 1419 return static_constant_value; 1420 } else { 1421 return vt.getValueOrNull(value_index); 1422 } 1423 } 1424 1425 /** 1426 * Returns the parent relation with the specified parent_relation_id; returns null if the relation 1427 * is not specified. 1428 */ 1429 private @Nullable VarParent get_parent(int parent_relation_id) { 1430 for (VarParent vp : parents) { 1431 if (vp.parent_relation_id == parent_relation_id) { 1432 return vp; 1433 } 1434 } 1435 return null; 1436 } 1437 1438 /** Returns the parent variable name for the specified parent_relation_id. */ 1439 private String parent_var_name(int parent_relation_id) { 1440 VarParent parent = get_parent(parent_relation_id); 1441 if (parent == null) { 1442 throw new IllegalArgumentException( 1443 "invalid parent_relation_id " + parent_relation_id + " for variable " + name()); 1444 } 1445 return parent.parent_variable != null ? parent.parent_variable : name(); 1446 } 1447 1448 /** Returns true iff the variable has a parent with the given parent_relation_id. */ 1449 private boolean has_parent(int parent_relation_id) { 1450 return get_parent(parent_relation_id) != null; 1451 } 1452 1453 private @Nullable String parent_var(int parent_relation_id) { 1454 VarParent parent = get_parent(parent_relation_id); 1455 if (parent == null) { 1456 throw new IllegalArgumentException( 1457 "invalid parent_relation_id " + parent_relation_id + " for variable " + name()); 1458 } 1459 return parent.parent_variable; 1460 } 1461 1462 /** Return the value of this long variable (as an integer) */ 1463 public int getIndexValue(ValueTuple vt) { 1464 Object raw = getValue(vt); 1465 if (raw == null) { 1466 throw new Error( 1467 "getIndexValue: getValue returned null " 1468 + this.name() 1469 + " index=" 1470 + this.varinfo_index 1471 + " vt=" 1472 + vt); 1473 } 1474 return ((Long) raw).intValue(); 1475 } 1476 1477 /** Return the value of this long variable (as a long) */ 1478 public long getIntValue(ValueTuple vt) { 1479 Object raw = getValue(vt); 1480 if (raw == null) { 1481 throw new Error( 1482 "getIntValue: getValue returned null " 1483 + this.name() 1484 + " index=" 1485 + this.varinfo_index 1486 + " vt=" 1487 + vt); 1488 } 1489 return ((Long) raw).longValue(); 1490 } 1491 1492 /** Return the value of an long[] variable. */ 1493 public long[] getIntArrayValue(ValueTuple vt) { 1494 Object raw = getValue(vt); 1495 if (raw == null) { 1496 throw new Error( 1497 "getIntArrayValue: getValue returned null " 1498 + this.name() 1499 + " index=" 1500 + this.varinfo_index 1501 + " vt=" 1502 + vt); 1503 } 1504 return (long[]) raw; 1505 } 1506 1507 /** Return the value of a double variable. */ 1508 public double getDoubleValue(ValueTuple vt) { 1509 Object raw = getValue(vt); 1510 if (raw == null) { 1511 throw new Error( 1512 "getDoubleValue: getValue returned null " 1513 + this.name() 1514 + " index=" 1515 + this.varinfo_index 1516 + " vt=" 1517 + vt); 1518 } 1519 return ((Double) raw).doubleValue(); 1520 } 1521 1522 /** Return the value of a double[] variable. */ 1523 public double[] getDoubleArrayValue(ValueTuple vt) { 1524 Object raw = getValue(vt); 1525 if (raw == null) { 1526 throw new Error( 1527 "getDoubleArrayValue: getValue returned null " 1528 + this.name() 1529 + " index=" 1530 + this.varinfo_index 1531 + " vt=" 1532 + vt); 1533 } 1534 return (double[]) raw; 1535 } 1536 1537 /** Return the value of a String variable. */ 1538 public String getStringValue(ValueTuple vt) { 1539 return (String) getValue(vt); 1540 } 1541 1542 /** Reteurn the value of a String[] array variable. */ 1543 public String[] getStringArrayValue(ValueTuple vt) { 1544 Object raw = getValue(vt); 1545 if (raw == null) { 1546 throw new Error( 1547 "getDoubleArrayValue: getValue returned null " 1548 + this.name() 1549 + " index=" 1550 + this.varinfo_index 1551 + " vt=" 1552 + vt); 1553 } 1554 return (String[]) raw; 1555 } 1556 1557 /** 1558 * Whether this VarInfo is the leader of its equality set. 1559 * 1560 * @return true if this VarInfo is the leader of its equality set 1561 */ 1562 @Pure 1563 public boolean isCanonical() { 1564 if (equalitySet == null) { 1565 return true; 1566 } 1567 return (equalitySet.leader() == this); 1568 } 1569 1570 /** Canonical representative that's equal to this variable. */ 1571 @Pure 1572 public VarInfo canonicalRep() { 1573 if (equalitySet == null) { 1574 System.out.println("equality sets = " + ppt.equality_sets_txt()); 1575 assert equalitySet != null 1576 : "Variable " + name() + " in ppt " + ppt.name() + " index = " + varinfo_index; 1577 } 1578 return equalitySet.leader(); 1579 } 1580 1581 /** Return true if this is a pointer or reference to another object. */ 1582 @Pure 1583 public boolean is_reference() { 1584 1585 // This used to check to see if the item was a list and some other 1586 // odd things, but hashcode seems like the right check. 1587 return rep_type.isHashcode(); 1588 } 1589 1590 /** 1591 * Returns the VarInfo for the sequence from which this was derived, or null if this wasn't 1592 * derived from a sequence. Only works for scalars. 1593 * 1594 * @return the VarInfo for the sequence from which this was derived, or null 1595 */ 1596 public @Nullable VarInfo isDerivedSequenceMember() { 1597 if (derived == null) { 1598 return null; 1599 } 1600 1601 if (derived instanceof SequenceScalarSubscript) { 1602 SequenceScalarSubscript sss = (SequenceScalarSubscript) derived; 1603 return sss.seqvar(); 1604 } else if (derived instanceof SequenceInitial) { 1605 SequenceInitial se = (SequenceInitial) derived; 1606 return se.seqvar(); 1607 } else if (derived instanceof SequenceMax) { 1608 SequenceMax sm = (SequenceMax) derived; 1609 return sm.base; 1610 } else if (derived instanceof SequenceMin) { 1611 SequenceMin sm = (SequenceMin) derived; 1612 return sm.base; 1613 } else { 1614 return null; 1615 } 1616 } 1617 1618 @Pure 1619 public boolean isDerivedSequenceMinMaxSum() { 1620 return ((derived != null) 1621 && ((derived instanceof SequenceMax) 1622 || (derived instanceof SequenceMin) 1623 || (derived instanceof SequenceSum))); 1624 } 1625 1626 /** 1627 * Return the original sequence variable from which this derived sequence was derived. Only works 1628 * for sequences. 1629 * 1630 * @return the VarInfo for the original sequence from which this sequence was derived, or null 1631 */ 1632 public @Nullable VarInfo isDerivedSubSequenceOf() { 1633 1634 if (derived == null) { 1635 return null; 1636 } 1637 1638 if (derived instanceof SequenceScalarSubsequence) { 1639 SequenceScalarSubsequence sss = (SequenceScalarSubsequence) derived; 1640 return sss.seqvar(); 1641 } else if (derived instanceof SequenceScalarArbitrarySubsequence) { 1642 SequenceScalarArbitrarySubsequence ssas = (SequenceScalarArbitrarySubsequence) derived; 1643 return ssas.seqvar(); 1644 } else { 1645 return null; 1646 } 1647 } 1648 1649 /** Returns the variable (if any) that represents the size of this sequence. */ 1650 public @Nullable VarInfo sequenceSize() { 1651 if (sequenceSize != null) { 1652 return sequenceSize; 1653 } 1654 assert rep_type.isArray(); 1655 // we know the size follows the variable itself in the list 1656 VarInfo[] vis = ppt.var_infos; 1657 for (int i = varinfo_index + 1; i < vis.length; i++) { 1658 VarInfo vi = vis[i]; 1659 if ((vi.derived instanceof SequenceLength) && (((SequenceLength) vi.derived).base == this)) { 1660 sequenceSize = vi; 1661 return sequenceSize; 1662 } 1663 } 1664 // It is possible that this VarInfo never had its size derived, 1665 // since it looked something like this.ary[].field. In this case, 1666 // we should return size(this.ary[]), since it was derived and 1667 // must be the same values. 1668 if (FileIO.new_decl_format) { 1669 VarInfo base = get_base_array(); 1670 VarInfo size = ppt.find_var_by_name("size(" + base.name() + ")"); 1671 return size; 1672 } else { 1673 VarInfoName search = this.var_info_name; // vin ok 1674 boolean pre = false; 1675 if (search instanceof VarInfoName.Prestate) { 1676 search = ((VarInfoName.Prestate) search).term; 1677 pre = true; 1678 } 1679 while (search instanceof VarInfoName.Field) { 1680 search = ((VarInfoName.Field) search).term; 1681 } 1682 if (pre) { 1683 search = search.applyPrestate(); 1684 } 1685 search = search.applySize(); 1686 VarInfo result = ppt.find_var_by_name(search.name()); 1687 if (result != null) { 1688 return result; 1689 // } else { 1690 // System.out.println("Warning: Size variable " + search + " not found."); 1691 // System.out.print("Variables: "); 1692 // for (int i = 0; i<ppt.var_infos.length; i++) { 1693 // VarInfo vi = ppt.var_infos[i]; 1694 // System.out.print(vi.name + " "); 1695 // } 1696 // System.out.println(); 1697 } 1698 } 1699 // throw new Error("Couldn't find size of " + name); 1700 return null; 1701 } 1702 1703 /** 1704 * Returns true if the type in the original program is integer. Should perhaps check 1705 * Daikon.check_program_types and behave differently depending on that. 1706 */ 1707 @Pure 1708 public boolean isIndex() { 1709 return (file_rep_type == ProglangType.INT) && type.isIndex(); 1710 } 1711 1712 /** 1713 * Returns true if this variable is an array. 1714 * 1715 * @return true if this variable is an array 1716 */ 1717 @Pure 1718 public boolean is_array() { 1719 return arr_dims > 0; 1720 } 1721 1722 /** 1723 * Returns false if this variable expression is not legal ESC syntax, except for any necessary 1724 * quantifications (subscripting). We err on the side of returning true, for now. 1725 * 1726 * @return false if this variable expression is not legal ESC syntax, except for any necessary 1727 * quantifications (subscripting) 1728 */ 1729 @Pure 1730 public boolean isValidEscExpression() { 1731 // "myList.length" is invalid 1732 if (derived instanceof SequenceLength) { 1733 SequenceLength sl = (SequenceLength) derived; 1734 if (!sl.base.type.isArray()) { 1735 // VarInfo base = sl.base; 1736 // System.out.printf("%s is not an array%n", base); 1737 // System.out.printf("type = %s%n", base.type); 1738 return false; 1739 } 1740 } 1741 1742 // "myList[]" is invalid, as is myList[foo] (when myList is a list 1743 // of some sort and not an array) 1744 if (FileIO.new_decl_format) { 1745 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 1746 if (vi.file_rep_type.isArray() && !vi.type.isArray()) { 1747 return false; 1748 } 1749 if (vi.isDerived()) { 1750 VarInfo base = vi.derived.getBase(0); 1751 if (base.file_rep_type.isArray() && !base.type.isArray()) { 1752 return false; 1753 } 1754 } 1755 } 1756 } else { 1757 for (VarInfoName next : var_info_name.inOrderTraversal()) { // vin ok 1758 if (next instanceof VarInfoName.Elements) { 1759 VarInfoName.Elements elems = (VarInfoName.Elements) next; 1760 VarInfo seq = ppt.find_var_by_name(elems.term.name()); 1761 if (!seq.type.isArray()) { 1762 return false; 1763 } 1764 } 1765 } 1766 } 1767 1768 return true; 1769 } 1770 1771 /** 1772 * Return true if invariants about this quantity are really properties of a pointer, but derived 1773 * variables can refer to properties of the thing pointed to. This distinction is important when 1774 * making logical statements about the object, because in the presence of side effects, the 1775 * pointed-to object can change even when the pointer doesn't. For instance, we might have "obj == 1776 * orig(obj)", but "obj.color != orig(obj.color)". In such a case, isPointer() would be true of 1777 * obj, and for some forms of output we'd need to translate "obj == orig(obj)" into something like 1778 * "location(obj) == location(orig(obj))". 1779 */ 1780 @Pure 1781 public boolean isPointer() { 1782 // This used to check whether the program type had a higher 1783 // dimension than the rep type, or if the rep type was integral 1784 // but the program type wasn't primitive. These rules worked 1785 // pretty well for Java, but not so well for C, where for instance 1786 // you might have rep_type = int and type = size_t. 1787 1788 return file_rep_type.isPointerFileRep(); 1789 } 1790 1791 /** 1792 * A wrapper around VarInfoName.simplify_name() that also uses VarInfo information to guess 1793 * whether "obj" should logically be treated as just the hash code of "obj", rather than the whole 1794 * object. 1795 */ 1796 public String simplifyFixup(String str) { 1797 if (isPointer()) { 1798 str = "(hash " + str + ")"; 1799 } 1800 return str; 1801 } 1802 1803 public String simplifyFixedupName() { 1804 return simplifyFixup(simplify_name()); 1805 } 1806 1807 // /////////////////////////////////////////////////////////////////////////// 1808 // Utility functions 1809 // 1810 1811 // Where do these really belong? 1812 1813 /** 1814 * Given two variables I and J, indicate whether it is necessarily the case that i≤j or i≥j. 1815 * The variables also each have a shift, so the test can really be something like (i+1)≤(j-1). 1816 * The test is one of: 1817 * 1818 * <ul> 1819 * <li>i + i_shift ≤ j + j_shift (if test_lessequal) 1820 * <li>i + i_shift ≥ j + j_shift (if !test_lessequal) 1821 * </ul> 1822 * 1823 * This is a dynamic check, and so must not be called while Daikon is inferencing. 1824 */ 1825 public static boolean compare_vars( 1826 VarInfo vari, int vari_shift, VarInfo varj, int varj_shift, boolean test_lessequal) { 1827 1828 // System.out.printf("comparing variables %s and %s in ppt %s%n", 1829 // vari.name(), varj.name(), vari.ppt.name()); 1830 // Throwable stack = new Throwable("debug traceback"); 1831 // stack.fillInStackTrace(); 1832 // stack.printStackTrace(); 1833 1834 assert !Daikon.isInferencing; 1835 // System.out.println("compare_vars(" + vari.name + ", " + vari_shift + ", "+ varj.name + ", " + 1836 // varj_shift + ", " + (test_lessequal?"<=":">=") + ")"); 1837 if (vari == varj) { 1838 // same variable 1839 return test_lessequal ? (vari_shift <= varj_shift) : (vari_shift >= varj_shift); 1840 } 1841 // different variables 1842 boolean samePpt = (vari.ppt == varj.ppt); 1843 assert samePpt; 1844 PptSlice indices_ppt = vari.ppt.findSlice_unordered(vari, varj); 1845 if (indices_ppt == null) { 1846 return false; 1847 } 1848 1849 boolean vari_is_var1 = (vari == indices_ppt.var_infos[0]); 1850 LinearBinary lb = LinearBinary.find(indices_ppt); 1851 long index_vari_minus_seq = -2222; // valid only if lb != null 1852 if (lb != null) { 1853 if (!lb.enoughSamples()) { 1854 lb = null; 1855 } else if (lb.core.a != 1 || lb.core.b != -1) { 1856 // Do not attempt to deal with anything but y=x+b, aka x-y+b=0. 1857 lb = null; 1858 } else { 1859 // System.out.println("justified LinearBinary: " + lb.format()); 1860 // lb.b is var2()-var1(). 1861 1862 // a is 1 or -1, and the values are integers, so c must be an integer 1863 long c_int = (long) lb.core.c; 1864 assert lb.core.c == c_int; 1865 index_vari_minus_seq = (vari_is_var1 ? -c_int : c_int); 1866 index_vari_minus_seq += vari_shift - varj_shift; 1867 } 1868 } 1869 1870 boolean vari_lt = false; 1871 boolean vari_le = false; 1872 boolean vari_gt = false; 1873 boolean vari_ge = false; 1874 { 1875 IntLessEqual ile = IntLessEqual.find(indices_ppt); 1876 IntLessThan ilt = IntLessThan.find(indices_ppt); 1877 IntGreaterEqual ige = IntGreaterEqual.find(indices_ppt); 1878 IntGreaterThan igt = IntGreaterThan.find(indices_ppt); 1879 if (ile != null && !ile.enoughSamples()) { 1880 ile = null; 1881 } 1882 if (ilt != null && !ilt.enoughSamples()) { 1883 ilt = null; 1884 } 1885 if (ige != null && !ige.enoughSamples()) { 1886 ige = null; 1887 } 1888 if (igt != null && !igt.enoughSamples()) { 1889 igt = null; 1890 } 1891 1892 if (vari_is_var1) { 1893 vari_lt = ilt != null; 1894 vari_le = ile != null; 1895 vari_gt = igt != null; 1896 vari_ge = ige != null; 1897 } else { 1898 vari_lt = igt != null; 1899 vari_le = ige != null; 1900 vari_gt = ilt != null; 1901 vari_ge = ile != null; 1902 } 1903 } 1904 1905 // System.out.println("test_lessequal=" + test_lessequal 1906 // + ", vari_can_be_lt=" + vari_can_be_lt 1907 // + ", vari_can_be_eq=" + vari_can_be_eq 1908 // + ", vari_can_be_gt=" + vari_can_be_gt); 1909 1910 if (test_lessequal) { 1911 if (lb != null) { 1912 return (index_vari_minus_seq <= 0); 1913 } else { 1914 return ((vari_le && (vari_shift <= varj_shift)) 1915 || (vari_lt && (vari_shift - 1 <= varj_shift))); 1916 } 1917 } else { 1918 if (lb != null) { 1919 return (index_vari_minus_seq >= 0); 1920 } else { 1921 return ((vari_ge && (vari_shift >= varj_shift)) 1922 || (vari_gt && (vari_shift + 1 >= varj_shift))); 1923 } 1924 } 1925 } 1926 1927 // // takes an "orig()" var and gives a VarInfoName for a variable or 1928 // // expression in the post-state which is equal to this one. 1929 // public VarInfoName postStateEquivalent() { 1930 // return otherStateEquivalent(true); 1931 // } 1932 1933 // takes a non-"orig()" var and gives a VarInfoName for a variable 1934 // or expression in the pre-state which is equal to this one. 1935 public @Nullable VarInfoName preStateEquivalent() { 1936 return otherStateEquivalent(false); 1937 } 1938 1939 /** 1940 * Return some variable in the other state (pre-state if this is post-state, or vice versa) that 1941 * equals this one, or null if no equal variable exists. 1942 */ 1943 // This does *not* try the obvious thing of converting "foo" to 1944 // "orig(foo)"; it creates something new. I need to clarify the 1945 // documentation. 1946 public @Nullable VarInfoName otherStateEquivalent(boolean post) { 1947 1948 assert !FileIO.new_decl_format; 1949 1950 // Below is equivalent to: 1951 // assert post == isPrestate(); 1952 if (post != isPrestate()) { 1953 throw new Error( 1954 "Shouldn't happen (should it?): " 1955 + (post ? "post" : "pre") 1956 + "StateEquivalent(" 1957 + name() 1958 + ")"); 1959 } 1960 1961 { 1962 List<LinearBinary> lbs = LinearBinary.findAll(this); 1963 for (LinearBinary lb : lbs) { 1964 if (this.equals(lb.var2()) && (post != lb.var1().isPrestate())) { 1965 1966 // a * v1 + b * this + c = 0 or this == (-a/b) * v1 - c/b 1967 double a = lb.core.a, b = lb.core.b, c = lb.core.c; 1968 // if (a == 1) { // match } for vim 1969 if (-a / b == 1) { 1970 // this = v1 - c/b 1971 // int add = (int) b; 1972 int add = (int) -c / (int) b; 1973 return lb.var1().var_info_name.applyAdd(add); // vin ok 1974 } 1975 } 1976 1977 if (this.equals(lb.var1()) && (post != lb.var2().isPrestate())) { 1978 // v2 = a * this + b <-- not true anymore 1979 // a * this + b * v2 + c == 0 or v2 == (-a/b) * this - c/b 1980 double a = lb.core.a, b = lb.core.b, c = lb.core.c; 1981 // if (a == 1) { // match } for vim 1982 if (-a / b == 1) { 1983 // this = v2 + c/b 1984 // int add = - ((int) b); 1985 int add = (int) c / (int) b; 1986 return lb.var2().var_info_name.applyAdd(add); // vin ok 1987 } 1988 } 1989 } 1990 1991 // Should also try other exact invariants... 1992 } 1993 1994 // Can't find post-state equivalent. 1995 return null; 1996 } 1997 1998 /** Check if two VarInfos are truly (non guarded) equal to each other right now. */ 1999 @Pure 2000 public boolean isEqualTo(VarInfo other) { 2001 assert equalitySet != null; 2002 return this.equalitySet == other.equalitySet; 2003 } 2004 2005 /** Debug tracer. */ 2006 private static final Logger debug = Logger.getLogger("daikon.VarInfo"); 2007 2008 /** Debug tracer for simplifying expressions. */ 2009 private static final Logger debugSimplifyExpression = 2010 Logger.getLogger("daikon.VarInfo.simplifyExpression"); 2011 2012 /** Enable assertions that would otherwise reduce run time performance. */ 2013 private static final Logger debugEnableAssertions = 2014 Logger.getLogger("daikon.VarInfo.enableAssertions"); 2015 2016 // This is problematic because it also enables some debugging output. 2017 // I need something that only enables assertions. 2018 // Slightly gross implementation, using a logger; but the command-line 2019 // options processing code already exists for it: 2020 // --dbg daikon.VarInfo 2021 public static boolean assertionsEnabled() { 2022 return debugEnableAssertions.isLoggable(Level.FINE); 2023 } 2024 2025 /** 2026 * Change the name of this VarInfo by side effect into a more simplified form, which is easier to 2027 * read on display. Don't call this during processing, as I think the system assumes that names 2028 * don't change over time (?). 2029 */ 2030 public void simplify_expression() { 2031 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2032 debugSimplifyExpression.fine("** Simplify: " + name()); 2033 } 2034 2035 if (!isDerived()) { 2036 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2037 debugSimplifyExpression.fine("** Punt because not derived variable"); 2038 } 2039 return; 2040 } 2041 2042 // find a ...post(...)... expression to simplify 2043 VarInfoName.Poststate postexpr = null; 2044 for (VarInfoName node : new VarInfoName.InorderFlattener(var_info_name).nodes()) { // vin ok 2045 if (node instanceof VarInfoName.Poststate) { 2046 // Remove temporary var when bug is fixed. 2047 VarInfoName.Poststate tempNode = (VarInfoName.Poststate) node; 2048 postexpr = tempNode; 2049 // old code; reinstate when bug is fixed 2050 // postexpr = (VarInfoName.Poststate) node; 2051 break; 2052 } 2053 } 2054 if (postexpr == null) { 2055 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2056 debugSimplifyExpression.fine("** Punt because no post()"); 2057 } 2058 return; 2059 } 2060 2061 // if we have post(...+k) rewrite as post(...)+k 2062 if (postexpr.term instanceof VarInfoName.Add) { 2063 VarInfoName.Add add = (VarInfoName.Add) postexpr.term; 2064 VarInfoName swapped = add.term.applyPoststate().applyAdd(add.amount); 2065 var_info_name = 2066 new VarInfoName.Replacer(postexpr, swapped) 2067 .replace(var_info_name) 2068 .intern(); // vin ok // interning bugfix 2069 // start over 2070 simplify_expression(); 2071 return; 2072 } 2073 2074 // Stop now if we don't want to replace post vars with equivalent orig 2075 // vars 2076 if (!PrintInvariants.dkconfig_remove_post_vars) { 2077 return; 2078 } 2079 2080 // [[ find the ppt context for the post() term ]] (I used to 2081 // search the expression for this, but upon further reflection, 2082 // there is only one EXIT point which could possibly be associated 2083 // with this VarInfo, so "this.ppt" must be correct. 2084 PptTopLevel post_context = this.ppt; 2085 2086 // see if the contents of the post(...) have an equivalent orig() 2087 // expression. 2088 VarInfo postvar = post_context.find_var_by_name(postexpr.term.name()); 2089 if (postvar == null) { 2090 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2091 debugSimplifyExpression.fine("** Punt because no VarInfo for postvar " + postexpr.term); 2092 } 2093 return; 2094 } 2095 VarInfoName pre_expr = postvar.preStateEquivalent(); 2096 if (pre_expr != null) { 2097 // strip off any orig() so we don't get orig(a[orig(i)]) 2098 if (pre_expr instanceof VarInfoName.Prestate) { 2099 pre_expr = ((VarInfoName.Prestate) pre_expr).term; 2100 } else if (pre_expr instanceof VarInfoName.Add) { 2101 VarInfoName.Add add = (VarInfoName.Add) pre_expr; 2102 if (add.term instanceof VarInfoName.Prestate) { 2103 pre_expr = ((VarInfoName.Prestate) add.term).term.applyAdd(add.amount); 2104 } 2105 } 2106 var_info_name = 2107 new VarInfoName.Replacer(postexpr, pre_expr) 2108 .replace(var_info_name) 2109 .intern(); // vin ok // interning bugfix 2110 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2111 debugSimplifyExpression.fine("** Replaced with: " + var_info_name); // vin ok 2112 } 2113 } 2114 2115 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2116 debugSimplifyExpression.fine("** Nothing to do (no state equlivalent)"); 2117 } 2118 } 2119 2120 /** 2121 * Two variables are "compatible" if their declared types are castable and their comparabilities 2122 * are comparable. This is a reflexive relationship, because it calls 2123 * ProglangType.comparableOrSuperclassEitherWay. However, it is not transitive because it might 2124 * not hold for two children of a superclass, even though it would for each child and the 2125 * superclass. 2126 */ 2127 public boolean compatible(VarInfo var2) { 2128 VarInfo var1 = this; 2129 // Can only compare in the same ppt because otherwise 2130 // comparability info may not make sense. 2131 boolean samePpt = (var1.ppt == var2.ppt); 2132 assert samePpt; 2133 2134 if (!comparableByType(var2)) { 2135 return false; 2136 } 2137 2138 if (!Daikon.ignore_comparability && !VarComparability.comparable(var1, var2)) { 2139 return false; 2140 } 2141 2142 return true; 2143 } 2144 2145 /** 2146 * Return true if this sequence variable's element type is compatible with the scalar variable. 2147 */ 2148 public boolean eltsCompatible(VarInfo sclvar) { 2149 VarInfo seqvar = this; 2150 if (Daikon.check_program_types) { 2151 ProglangType elttype = seqvar.type.elementType(); 2152 if (!elttype.comparableOrSuperclassEitherWay(sclvar.type)) { 2153 // System.out.printf("eltsCompatible: bad program types; elttype(%s)=%s, scltype(%s)=%s%n", 2154 // seqvar, elttype, sclvar, sclvar.type); 2155 return false; 2156 } 2157 } 2158 if (!Daikon.ignore_comparability) { 2159 if (!VarComparability.comparable(seqvar.comparability.elementType(), sclvar.comparability)) { 2160 // System.out.printf("eltsCompatible: eltcomp(%s;%s)=%s, sclcomp(%s)=%s%n", 2161 // seqvar, seqvar.comparability.elementType(), 2162 // seqvar.comparability.elementType(), sclvar, sclvar.comparability); 2163 return false; 2164 } 2165 } 2166 return true; 2167 } 2168 2169 /** 2170 * Without using comparability info, check that this is comparable to var2. This is a reflexive 2171 * relationship, because it calls ProglangType.comparableOrSuperclassEitherWay. However, it is not 2172 * transitive because it might not hold for two children of a superclass, even though it would for 2173 * each child and the superclass. Does not check comparabilities. 2174 */ 2175 public boolean comparableByType(VarInfo var2) { 2176 VarInfo var1 = this; 2177 2178 // System.out.printf("comparableByType(%s, %s)%n", var1, var2); 2179 2180 // the check ensures that a scalar or string and elements of an array of the same type are 2181 // labelled as comparable 2182 if (Daikon.check_program_types 2183 && (var1.file_rep_type.isArray() && !var2.file_rep_type.isArray())) { 2184 2185 // System.out.printf("comparableByType: case 1 %s%n", var1.eltsCompatible(var2)); 2186 if (var1.eltsCompatible(var2)) { 2187 return true; 2188 } 2189 } 2190 2191 // the check ensures that a scalar or string and elements of an array of the same type are 2192 // labelled as comparable 2193 if (Daikon.check_program_types 2194 && (!var1.file_rep_type.isArray() && var2.file_rep_type.isArray())) { 2195 2196 // System.out.printf("comparableByType: case 2 %s%n", var2.eltsCompatible(var1)); 2197 if (var2.eltsCompatible(var1)) { 2198 return true; 2199 } 2200 } 2201 2202 if (Daikon.check_program_types && (var1.file_rep_type != var2.file_rep_type)) { 2203 // System.out.printf("comparableByType: case 4 return false%n"); 2204 return false; 2205 } 2206 2207 // If the file rep types match then the variables are comparable unless 2208 // their dimensions are different. 2209 if (!dkconfig_declared_type_comparability) { 2210 if (var1.type.dimensions() != var2.type.dimensions()) { 2211 // debug_print_once ("types %s and %s are not comparable", 2212 // var1.type, var2.type); 2213 return false; 2214 } 2215 return true; 2216 } 2217 2218 if (Daikon.check_program_types && !var1.type.comparableOrSuperclassEitherWay(var2.type)) { 2219 // debug_print_once ("types %s and %s are not comparable", 2220 // var1.type, var2.type); 2221 return false; 2222 } 2223 // debug_print_once ("types %s and %s are comparable", 2224 // var1.type, var2.type); 2225 2226 // System.out.printf("comparableByType: fallthough return true%n"); 2227 return true; 2228 } 2229 2230 /** 2231 * Without using comparability info, check that this is comparable to var2. This is a reflexive 2232 * and transitive relationship. Does not check comparabilities. 2233 * 2234 * @param var2 the variable to test comparability with 2235 * @return true if this is comparable to var2 2236 */ 2237 public boolean comparableNWay(VarInfo var2) { 2238 VarInfo var1 = this; 2239 if (Daikon.check_program_types && !var1.type.comparableOrSuperclassOf(var2.type)) { 2240 return false; 2241 } 2242 if (Daikon.check_program_types && !var2.type.comparableOrSuperclassOf(var1.type)) { 2243 return false; 2244 } 2245 if (Daikon.check_program_types && (var1.file_rep_type != var2.file_rep_type)) { 2246 return false; 2247 } 2248 return true; 2249 } 2250 2251 /** Return true if this sequence's first index type is compatible with the scalar variable. */ 2252 public boolean indexCompatible(VarInfo sclvar) { 2253 VarInfo seqvar = this; 2254 if (Daikon.check_program_types) { 2255 if (!seqvar.is_array() || !sclvar.isIndex()) { 2256 return false; 2257 } 2258 } 2259 if (!Daikon.ignore_comparability) { 2260 if (!VarComparability.comparable(seqvar.comparability.indexType(0), sclvar.comparability)) { 2261 return false; 2262 } 2263 } 2264 return true; 2265 } 2266 2267 // Interning is lost when an object is serialized and deserialized. 2268 // Manually re-intern any interned fields upon deserialization. 2269 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 2270 in.defaultReadObject(); 2271 var_info_name = var_info_name.intern(); // vin ok 2272 str_name = str_name.intern(); 2273 2274 for (VarParent parent : parents) { 2275 parent.parent_ppt = parent.parent_ppt.intern(); 2276 if (parent.parent_variable != null) { 2277 parent.parent_variable = parent.parent_variable.intern(); 2278 } 2279 } 2280 2281 if (relative_name != null) { 2282 relative_name = relative_name.intern(); 2283 } 2284 } 2285 2286 // /** 2287 // * It is <b>not</b> safe in general to compare based on VarInfoName 2288 // * alone, because it is possible for two different program points to have 2289 // * unrelated variables of the same name. 2290 // */ 2291 // public static class LexicalComparator implements Comparator<VarInfo> { 2292 // @Pure 2293 // public int compare(VarInfo vi1, VarInfo vi2) { 2294 // VarInfoName name1 = vi1.name; 2295 // VarInfoName name2 = vi2.name; 2296 // return name1.compareTo(name2); 2297 // } 2298 // } 2299 2300 // Is this property always guaranteed to be true? It's placed in a 2301 // slice, but then might it get printed or treated as true? 2302 /** 2303 * Create a guarding predicate for this VarInfo, that is, an invariant that ensures that this 2304 * object is available for access to variables that reference it, such as fields. (The invariant 2305 * is placed in the appropriate slice.) Returns null if no guarding is needed. 2306 */ 2307 // Adding a test against null is not quite right for C programs, where *p 2308 // could be nonsensical (uninitialized or freed) even when p is non-null. 2309 // But this is a decent approximation to start with. 2310 public @Nullable Invariant createGuardingPredicate(boolean install) { 2311 // Later for the array, make sure index in bounds 2312 if (!(type.isArray() || type.isObject())) { 2313 String message = 2314 String.format("Unexpected guarding based on %s with type %s%n", name(), type); 2315 System.err.print(message); 2316 throw new Error(message); 2317 } 2318 2319 // For now associating with the variable's PptSlice 2320 PptSlice slice = ppt.get_or_instantiate_slice(this); 2321 2322 Invariant result = Invariant.find(NonZero.class, slice); 2323 2324 // Check whether the predicate already exists 2325 if (result == null) { 2326 // If it doesn't, create a "fake" invariant, which should 2327 // never be printed. Is it a good idea even to set 2328 // result.falsified to true? We know it's true because 2329 // result's children were missing. However, some forms of 2330 // filtering might remove it from slice. 2331 VarInfo[] vis = slice.var_infos; 2332 if (SingleScalar.valid_types_static(vis)) { 2333 result = NonZero.get_proto().instantiate(slice); 2334 } else if (SingleScalarSequence.valid_types_static(vis)) { 2335 result = EltNonZero.get_proto().instantiate(slice); 2336 } else { 2337 throw new Error("Bad VarInfos"); 2338 } 2339 if (result == null) { 2340 // Return null if NonZero invariant is not applicable to this variable. 2341 return null; 2342 } 2343 result.isGuardingPredicate = true; 2344 // System.out.printf("Created a guarding predicate: %s at %s%n", result, slice); 2345 // new Error().printStackTrace(System.out); 2346 if (install) { 2347 slice.addInvariant(result); 2348 } 2349 } 2350 2351 return result; 2352 } 2353 2354 static Set<String> addVarMessages = new HashSet<>(); 2355 2356 /** 2357 * Finds a list of variables that must be guarded for this VarInfo to be guaranteed to not be 2358 * missing. This list never includes "this", as it can never be null. The variables are returned 2359 * in the order in which their guarding prefixes are supposed to print. 2360 * 2361 * <p>For example, if this VarInfo is "a.b.c", then the guarding list consists of the variables 2362 * "a" and "a.b". If "a" is null or "a.b" is null, then "a.b.c" is missing (does not exist). 2363 * 2364 * @return a list of varables that must be guarded 2365 */ 2366 public List<VarInfo> getGuardingList() { 2367 2368 // The list returned by this visitor always includes the argument itself (if it is testable 2369 // against null; for example, derived variables are not). If the caller does not want the 2370 // argument to be in the list, the caller must must remove the argument. 2371 2372 // Inner class because it uses the "ppt" variable. 2373 // Basic structure of each visitor: 2374 // If the argument should be guarded, recurse. 2375 // If the argument is testable against null, add it to the result. 2376 // Recursing first arranges that the argument goes at the end, 2377 // after its subparts that need to be guarded. 2378 2379 class GuardingVisitor implements Visitor<List<VarInfo>> { 2380 boolean inPre = false; 2381 2382 private boolean shouldBeGuarded(VarInfoName viname) { 2383 // Not "shouldBeGuarded(ppt.findVar(viname))" because that 2384 // unnecessarily computes ppt.findVar(viname), if 2385 // dkconfig_guardNulls is "always". 2386 // System.out.printf("viname = %s, applyPreMaybe=%s, findvar=%s%n", 2387 // viname, applyPreMaybe(viname), 2388 // ppt.findVar(applyPreMaybe(viname))); 2389 if (Daikon.dkconfig_guardNulls == "always") { // interned 2390 return true; 2391 } 2392 if (Daikon.dkconfig_guardNulls == "missing") { // interned 2393 VarInfo vi = ppt.find_var_by_name(applyPreMaybe(viname).name()); 2394 // Don't guard variables that don't exist. This happends when 2395 // we incorrectly parse static variable package names as field names 2396 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2397 Invariant.debugGuarding.fine( 2398 String.format( 2399 "shouldBeGuarded(%s) [%s] %s %b", 2400 viname, applyPreMaybe(viname), vi, ((vi == null) ? false : vi.canBeMissing))); 2401 } 2402 if (vi == null) { 2403 return false; 2404 } 2405 return vi.canBeMissing; 2406 } 2407 return false; 2408 } 2409 2410 @Override 2411 public List<VarInfo> visitSimple(Simple o) { 2412 List<VarInfo> result = new ArrayList<>(); 2413 // No recursion: no children 2414 if (!o.name.equals("this")) { 2415 result = addVar(result, o); 2416 } 2417 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2418 Invariant.debugGuarding.fine(String.format("visitSimple(%s) => %s", o.name(), result)); 2419 } 2420 return result; 2421 } 2422 2423 @Override 2424 public List<VarInfo> visitSizeOf(SizeOf o) { 2425 List<VarInfo> result = new ArrayList<>(); 2426 if (shouldBeGuarded(o)) { 2427 result.addAll(o.sequence.accept(this)); 2428 } 2429 // No call to addVar: derived variable 2430 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2431 Invariant.debugGuarding.fine(String.format("visitSizeOf(%s) => %s", o.name(), result)); 2432 } 2433 return result; 2434 } 2435 2436 @Override 2437 public List<VarInfo> visitFunctionOf(FunctionOf o) { 2438 List<VarInfo> result = new ArrayList<>(); 2439 if (shouldBeGuarded(o)) { 2440 result.addAll(o.argument.accept(this)); 2441 } 2442 result = addVar(result, o); 2443 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2444 Invariant.debugGuarding.fine( 2445 String.format("visitFunctionOf(%s) => %s", o.name(), result)); 2446 } 2447 return result; 2448 } 2449 2450 @Override 2451 public List<VarInfo> visitFunctionOfN(FunctionOfN o) { 2452 List<VarInfo> result = new ArrayList<>(); 2453 if (shouldBeGuarded(o)) { 2454 for (VarInfoName arg : o.args) { 2455 result.addAll(arg.accept(this)); 2456 } 2457 } 2458 result = addVar(result, o); 2459 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2460 Invariant.debugGuarding.fine( 2461 String.format("visitFunctionOfN(%s) => %s", o.name(), result)); 2462 } 2463 return result; 2464 } 2465 2466 @Override 2467 public List<VarInfo> visitField(Field o) { 2468 List<VarInfo> result = new ArrayList<>(); 2469 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2470 Invariant.debugGuarding.fine( 2471 String.format("visitField: shouldBeGuarded(%s) => %s", o.name(), shouldBeGuarded(o))); 2472 } 2473 if (shouldBeGuarded(o)) { 2474 result.addAll(o.term.accept(this)); 2475 } 2476 result = addVar(result, o); 2477 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2478 Invariant.debugGuarding.fine(String.format("visitField(%s) => %s", o.name(), result)); 2479 } 2480 return result; 2481 } 2482 2483 @Override 2484 public List<VarInfo> visitTypeOf(TypeOf o) { 2485 List<VarInfo> result = new ArrayList<>(); 2486 if (shouldBeGuarded(o)) { 2487 result.addAll(o.term.accept(this)); 2488 } 2489 // No call to addVar: derived variable 2490 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2491 Invariant.debugGuarding.fine(String.format("visitTypeOf(%s) => %s", o.name(), result)); 2492 } 2493 return result; 2494 } 2495 2496 @Override 2497 public List<VarInfo> visitPrestate(Prestate o) { 2498 assert inPre == false; 2499 inPre = true; 2500 List<VarInfo> result = o.term.accept(this); 2501 assert inPre == true; 2502 inPre = false; 2503 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2504 Invariant.debugGuarding.fine(String.format("visitPrestate(%s) => %s", o.name(), result)); 2505 } 2506 return result; 2507 } 2508 2509 @Override 2510 public List<VarInfo> visitPoststate(Poststate o) { 2511 assert inPre == true; 2512 inPre = false; 2513 List<VarInfo> result = o.term.accept(this); 2514 assert inPre == false; 2515 inPre = true; 2516 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2517 Invariant.debugGuarding.fine(String.format("visitPostState(%s) => %s", o.name(), result)); 2518 } 2519 return result; 2520 } 2521 2522 @Override 2523 public List<VarInfo> visitAdd(Add o) { 2524 List<VarInfo> result = new ArrayList<>(); 2525 if (shouldBeGuarded(o)) { 2526 result.addAll(o.term.accept(this)); 2527 } 2528 // No call to addVar: derived variable 2529 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2530 Invariant.debugGuarding.fine(String.format("visitAdd(%s) => %s", o.name(), result)); 2531 } 2532 return result; 2533 } 2534 2535 @Override 2536 public List<VarInfo> visitElements(Elements o) { 2537 List<VarInfo> result = new ArrayList<>(); 2538 if (shouldBeGuarded(o)) { 2539 result.addAll(o.term.accept(this)); 2540 } 2541 // No call to addVar: derived variable 2542 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2543 Invariant.debugGuarding.fine(String.format("visitElements(%s) => %s", o.name(), result)); 2544 } 2545 return result; 2546 } 2547 2548 @Override 2549 public List<VarInfo> visitSubscript(Subscript o) { 2550 List<VarInfo> result = new ArrayList<>(); 2551 if (shouldBeGuarded(o)) { 2552 result.addAll(o.sequence.accept(this)); 2553 result.addAll(o.index.accept(this)); 2554 } 2555 result = addVar(result, o); 2556 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2557 Invariant.debugGuarding.fine(String.format("visitSubscript(%s) => %s", o.name(), result)); 2558 } 2559 return result; 2560 } 2561 2562 @Override 2563 public List<VarInfo> visitSlice(Slice o) { 2564 List<VarInfo> result = new ArrayList<>(); 2565 if (shouldBeGuarded(o)) { 2566 result.addAll(o.sequence.accept(this)); 2567 if (o.i != null) { 2568 result.addAll(o.i.accept(this)); 2569 } 2570 if (o.j != null) { 2571 result.addAll(o.j.accept(this)); 2572 } 2573 } 2574 // No call to addVar: derived variable 2575 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2576 Invariant.debugGuarding.fine(String.format("visitSlice(%s) => %s", o.name(), result)); 2577 } 2578 return result; 2579 } 2580 2581 // Convert to prestate variable name if appropriate 2582 VarInfoName applyPreMaybe(VarInfoName vin) { 2583 if (inPre) { 2584 return vin.applyPrestate(); 2585 } else { 2586 return vin; 2587 } 2588 } 2589 2590 private List<VarInfo> addVar(List<VarInfo> result, VarInfoName vin) { 2591 VarInfo vi = ppt.find_var_by_name(applyPreMaybe(vin).name()); 2592 // vi could be null because some variable's prefix is not a 2593 // variable. Example: for static variable "Class.staticvar", 2594 // "Class" is not a varible, even though for variable "a.b.c", 2595 // typically "a" and "a.b" are also variables. 2596 if (vi == null) { 2597 // String message = 2598 // String.format( 2599 // "getGuardingList(%s, %s): did not find variable %s [inpre=%s]", 2600 // name(), ppt.name(), vin.name(), inPre); 2601 // // Only print the error message at most once per variable. 2602 // if (addVarMessages.add(vin.name())) { 2603 // // For now, don't print at all: it's generally innocuous 2604 // // (class prefix of a static variable). 2605 // // System.err.println(message); 2606 // } 2607 // // System.out.println("vars: " + ppt.varNames()); 2608 // // System.out.flush(); 2609 // // throw new Error(String.format(message)); 2610 return result; 2611 } else { 2612 return addVarInfo(result, vi); 2613 } 2614 } 2615 2616 /** 2617 * Add the given variable to the result list. Does nothing if the variable is of primitive 2618 * type. 2619 */ 2620 // Should this operate by side effect on a global variable? 2621 // (Then what is the type of the visitor; what does everything return?) 2622 private List<VarInfo> addVarInfo(List<VarInfo> result, VarInfo vi) { 2623 assert vi != null; 2624 assert !vi.isDerived() || vi.isDerived() : "addVar on derived variable: " + vi; 2625 // Don't guard primitives 2626 if ( // TODO: ***** make changes here ***** 2627 // vi.file_rep_type.isScalar() && 2628 !vi.type.isScalar() 2629 // (vi.type.isArray() || vi.type.isObject()) 2630 ) { 2631 result.add(vi); 2632 } else { 2633 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2634 Invariant.debugGuarding.fine( 2635 String.format( 2636 "addVarInfo did not add %s: %s (%s) %s (%s)", 2637 vi, 2638 vi.file_rep_type.isScalar(), 2639 vi.file_rep_type, 2640 vi.type.isScalar(), 2641 vi.type)); 2642 } 2643 } 2644 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2645 Invariant.debugGuarding.fine(String.format("addVarInfo(%s) => %s", vi, result)); 2646 } 2647 return result; 2648 } 2649 } // end of class GuardingVisitor 2650 2651 if (!FileIO.new_decl_format) { 2652 List<VarInfo> result = var_info_name.accept(new GuardingVisitor()); // vin ok 2653 result.remove(ppt.find_var_by_name(var_info_name.name())); // vin ok 2654 assert !ArraysPlume.anyNull(result); 2655 return result; 2656 } else { // new format 2657 List<VarInfo> result = new ArrayList<>(); 2658 2659 if (Daikon.dkconfig_guardNulls == "never") { // interned 2660 return result; 2661 } 2662 2663 // If this is never missing, nothing to guard 2664 if ((Daikon.dkconfig_guardNulls == "missing") // interned 2665 && !canBeMissing) { 2666 return result; 2667 } 2668 2669 // Create a list of variables to be guarded from the list of all 2670 // enclosing variables. 2671 for (VarInfo vi : get_all_enclosing_vars()) { 2672 // if (var_flags.contains(VarFlags.CLASSNAME)) { 2673 // System.err.printf( 2674 // "%s file_rep_type = %s, canbemissing = %b%n", vi, vi.file_rep_type, 2675 // vi.canBeMissing); 2676 // } 2677 if (!vi.file_rep_type.isHashcode()) { 2678 continue; 2679 } 2680 result.add(0, vi); 2681 if ((Daikon.dkconfig_guardNulls == "missing") // interned 2682 && !vi.canBeMissing) { 2683 break; 2684 } 2685 } 2686 return result; 2687 } 2688 } 2689 2690 /** 2691 * Returns a list of all of the variables that enclose this one. If this is derived, this includes 2692 * all of the enclosing variables of all of the bases. 2693 */ 2694 public List<VarInfo> get_all_enclosing_vars() { 2695 List<VarInfo> result = new ArrayList<>(); 2696 if (isDerived()) { 2697 for (VarInfo base : derived.getBases()) { 2698 result.addAll(base.get_all_enclosing_vars()); 2699 } 2700 } else { // not derived 2701 for (VarInfo vi = this.enclosing_var; vi != null; vi = vi.enclosing_var) { 2702 result.add(vi); 2703 } 2704 } 2705 return result; 2706 } 2707 2708 /** Compare names by index. */ 2709 public static final class IndexComparator implements Comparator<VarInfo>, Serializable { 2710 // This needs to be serializable because Equality invariants keep 2711 // a TreeSet of variables sorted by theInstance. 2712 2713 // We are Serializable, so we specify a version to allow changes to 2714 // method signatures without breaking serialization. If you add or 2715 // remove fields, you should change this number to the current date. 2716 static final long serialVersionUID = 20050923L; 2717 2718 private IndexComparator() {} 2719 2720 @Pure 2721 @Override 2722 public int compare(VarInfo vi1, VarInfo vi2) { 2723 if (vi1.varinfo_index < vi2.varinfo_index) { 2724 return -1; 2725 } else if (vi1.varinfo_index == vi2.varinfo_index) { 2726 return 0; 2727 } else { 2728 return 1; 2729 } 2730 } 2731 2732 public static IndexComparator getInstance() { 2733 return theInstance; 2734 } 2735 2736 public static final IndexComparator theInstance = new IndexComparator(); 2737 } 2738 2739 /** 2740 * Looks for an OBJECT ppt that corresponds to the type of this variable. Returns null if such a 2741 * point is not found. 2742 * 2743 * @param all_ppts map of all program points 2744 */ 2745 public @Nullable PptTopLevel find_object_ppt(PptMap all_ppts) { 2746 2747 // Arrays don't have types 2748 if (is_array()) { 2749 return null; 2750 } 2751 2752 // build the name of the object ppt based on the variable type 2753 String type_str = type.base().replaceFirst("\\$", "."); 2754 PptName objname = new PptName(type_str, null, FileIO.object_suffix); 2755 return all_ppts.get(objname); 2756 } 2757 2758 /** 2759 * Class used to contain a pair of VarInfos and their sample count. Currently used for equality 2760 * set merging as a way to store pairs of equal variables. The variable with the smaller index is 2761 * always stored first. 2762 * 2763 * <p>Pairs are equal if both of their VarInfos are identical. Note that the content of the 2764 * VarInfos are not compared, only their pointer values. 2765 */ 2766 public static class Pair { 2767 2768 public VarInfo v1; 2769 public VarInfo v2; 2770 public int samples; 2771 2772 public Pair(VarInfo v1, VarInfo v2, int samples) { 2773 if (v1.varinfo_index < v2.varinfo_index) { 2774 this.v1 = v1; 2775 this.v2 = v2; 2776 } else { 2777 this.v1 = v2; 2778 this.v2 = v1; 2779 } 2780 this.samples = samples; 2781 } 2782 2783 @EnsuresNonNullIf(result = true, expression = "#1") 2784 @Pure 2785 @Override 2786 public boolean equals(@GuardSatisfied Pair this, @GuardSatisfied @Nullable Object obj) { 2787 if (!(obj instanceof Pair)) { 2788 return false; 2789 } 2790 2791 Pair o = (Pair) obj; 2792 return (o.v1 == v1) && (o.v2 == v2); 2793 } 2794 2795 @Pure 2796 @Override 2797 public int hashCode(@GuardSatisfied Pair this) { 2798 return v1.hashCode() + v2.hashCode(); 2799 } 2800 2801 @SideEffectFree 2802 @Override 2803 public String toString(@GuardSatisfied Pair this) { 2804 return v1.name() + " = " + v2.name(); 2805 } 2806 } 2807 2808 /** Return the set of values that have been seen so far for this variable. */ 2809 public ValueSet get_value_set() { 2810 2811 // Static constants don't have value sets, so we must make one 2812 if (is_static_constant) { 2813 ValueSet vs = ValueSet.factory(this); 2814 assert static_constant_value != null 2815 : "@AssumeAssertion(nullness): dependent: is_static_constant"; 2816 vs.add(static_constant_value); 2817 return vs; 2818 } 2819 2820 return ppt.value_sets[value_index]; 2821 } 2822 2823 public String get_value_info() { 2824 return name() + "- " + get_value_set().repr_short(); 2825 } 2826 2827 /** 2828 * Returns the number of elements in the variable's equality set. Returns 1 if the equality 2829 * optimization is turned off. 2830 */ 2831 public int get_equalitySet_size() { 2832 if (equalitySet == null) { 2833 return 1; 2834 } else { 2835 return equalitySet.size(); 2836 } 2837 } 2838 2839 /** 2840 * Returns the vars_info in the variable's equality set. Returns a set with just itself if the 2841 * equality optimization is turned off. 2842 */ 2843 public Set<VarInfo> get_equalitySet_vars() { 2844 if (equalitySet == null) { 2845 HashSet<VarInfo> set = new HashSet<>(); 2846 set.add(this); 2847 return set; 2848 } else { 2849 return equalitySet.getVars(); 2850 } 2851 } 2852 2853 /** 2854 * Returns the leader in the variable's equality set. Returns itself if the equality optimization 2855 * is turned off. 2856 */ 2857 public VarInfo get_equalitySet_leader() { 2858 // if (equalitySet == null && VarInfo.use_equality_optimization == false) { // match } for vim 2859 if (equalitySet == null) { 2860 return this; 2861 } else { 2862 return equalitySet.leader(); 2863 } 2864 } 2865 2866 private static Set<String> out_strings = new LinkedHashSet<>(); 2867 2868 /** If the message is new print it, otherwise discard it. */ 2869 @FormatMethod 2870 static void debug_print_once(String format, @Nullable Object... args) { 2871 String msg = String.format(format, args); 2872 if (!out_strings.contains(msg)) { 2873 System.out.println(msg); 2874 out_strings.add(msg); 2875 } 2876 } 2877 2878 /** Returns whether or not this variable is a parameter. */ 2879 @Pure 2880 public boolean isParam() { 2881 if (FileIO.new_decl_format) { 2882 return var_flags.contains(VarFlags.IS_PARAM); 2883 } else { 2884 return aux.isParam(); // VIN 2885 } 2886 } 2887 2888 /** Set this variable as a parameter. */ 2889 public void set_is_param() { 2890 // System.out.printf("setting is_param for %s %n", name()); 2891 if (FileIO.new_decl_format) { 2892 var_flags.add(VarFlags.IS_PARAM); 2893 } 2894 aux = aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.TRUE); // VIN 2895 } 2896 2897 /** Set whether or not this variable is a parameter. */ 2898 public void set_is_param(boolean set) { 2899 if (set) { 2900 set_is_param(); 2901 } else { 2902 if (FileIO.new_decl_format) { 2903 var_flags.remove(VarFlags.IS_PARAM); 2904 } 2905 aux = aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.FALSE); // VIN 2906 } 2907 } 2908 2909 /** 2910 * Adds a subscript (or sequence) to an array variable. This should really just just substitute 2911 * for '..', but the dots are currently removed for back compatability. 2912 */ 2913 public String apply_subscript(String subscript) { 2914 if (FileIO.new_decl_format) { 2915 assert arr_dims == 1 : "Can't apply subscript to " + name(); 2916 return name().replace("..", subscript); 2917 } else { 2918 assert name().contains("[]") : "Can't apply subscript to " + name(); 2919 return apply_subscript(name(), subscript); 2920 } 2921 } 2922 2923 /** 2924 * Adds a subscript (or subsequence) to an array name. This should really just substitute for 2925 * '..', but the dots are currently removed for back compatibility. 2926 */ 2927 public static String apply_subscript(String sequence, String subscript) { 2928 if (FileIO.new_decl_format) { 2929 return sequence.replace("[..]", "[" + subscript + "]"); 2930 } else { 2931 return sequence.replace("[]", "[" + subscript + "]"); 2932 } 2933 } 2934 2935 /** 2936 * For array variables, returns the variable that is a simple array. If this variable is a slice, 2937 * it returns the array variable that is being sliced. If this variable is a simple array itself, 2938 * returns this. 2939 */ 2940 public VarInfo get_array_var() { 2941 assert file_rep_type.isArray(); 2942 if (isDerived()) { 2943 return derived.get_array_var(); 2944 } else { 2945 return this; 2946 } 2947 } 2948 2949 /** 2950 * Returns the VarInfo that represents the base array of this array. For example, if the array is 2951 * a[].b.c, returns a[]. 2952 */ 2953 @Pure 2954 public VarInfo get_base_array() { 2955 assert file_rep_type.isArray() : this; 2956 if (FileIO.new_decl_format) { 2957 VarInfo var = this; 2958 while (var.var_kind != VarKind.ARRAY) { 2959 if (var.enclosing_var == null) { 2960 // error condition; print some debugging output before assertion failure 2961 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 2962 System.out.printf("%s %s%n", vi, vi.var_kind); 2963 } 2964 assert var.enclosing_var != null : this + " " + var; 2965 } 2966 assert var.enclosing_var != null : "@AssumeAssertion(nullness): just tested"; 2967 var = var.enclosing_var; 2968 } 2969 return var; 2970 } else { 2971 Elements elems = new ElementsFinder(var_info_name).elems(); // vin ok 2972 return ppt.find_var_by_name(elems.name()); 2973 } 2974 } 2975 2976 /** 2977 * Returns the VarInfo that represents the hashcode of the base array of this array. For example, 2978 * if the array is a[].b.c, returns a. Returns null if there is no such variable. 2979 */ 2980 @Pure 2981 public @Nullable VarInfo get_base_array_hashcode() { 2982 if (FileIO.new_decl_format) { 2983 return get_base_array().enclosing_var; 2984 } else { 2985 Elements elems = new ElementsFinder(var_info_name).elems(); // vin ok 2986 // System.out.printf("term.name() = %s%n", elems.term.name()); 2987 return ppt.find_var_by_name(elems.term.name()); 2988 } 2989 } 2990 2991 /** Returns the lower bound of the array or slice. */ 2992 public Quantify.Term get_lower_bound() { 2993 assert file_rep_type.isArray() : "var " + name() + " rep " + file_rep_type; 2994 if (isDerived()) { 2995 return derived.get_lower_bound(); 2996 } else { 2997 return new Quantify.Constant(0); 2998 } 2999 } 3000 3001 /** Returns the upper bound of the array or slice. */ 3002 public Quantify.Term get_upper_bound() { 3003 assert file_rep_type.isArray(); 3004 if (isDerived()) { 3005 return derived.get_upper_bound(); 3006 } else { 3007 return new Quantify.Length(this, -1); 3008 } 3009 } 3010 3011 /** 3012 * Returns the length of this array. The array can be an array or a list. It cannot be a slice. 3013 */ 3014 public Quantify.Term get_length() { 3015 assert file_rep_type.isArray() && !isDerived() : this; 3016 return new Quantify.Length(this, 0); 3017 } 3018 3019 /** 3020 * Updates any references to other variables that should be within this ppt by looking them up 3021 * within the ppt. Necessary if a variable is moved to a different program point or if cloned 3022 * variable is placed in a new program point (such as is done when combined exits are created). 3023 */ 3024 public void update_after_moving_to_new_ppt() { 3025 if (enclosing_var != null) { 3026 // enclosing_var exists but is in the wrong ppt; update it 3027 enclosing_var = ppt.find_var_by_name(enclosing_var.name()); 3028 assert enclosing_var != null; 3029 } 3030 } 3031 3032 /** 3033 * Temporary to let things compile now that name is private. Eventually this should be removed. 3034 */ 3035 public VarInfoName get_VarInfoName() { 3036 return var_info_name; // vin ok 3037 } 3038 3039 private static boolean isStatic(String variable, String enclosing) { 3040 return !variable.startsWith(enclosing) || variable.charAt(enclosing.length()) != '.'; 3041 } 3042 3043 // Map java objects to C# objects. 3044 private static final Map<String, String> csharp_types = new HashMap<>(); 3045 3046 static { 3047 csharp_types.put("java.lang.String", "string"); 3048 csharp_types.put("java.lang.String[]", "string[]"); 3049 csharp_types.put("java.lang.Object", "object"); 3050 csharp_types.put("java.lang.Object[]", "object[]"); 3051 csharp_types.put("boolean", "bool"); 3052 } 3053 3054 /** Transforms a Daikon type representation into a valid C# type. */ 3055 public static String fix_csharp_type_name(String type) { 3056 if (csharp_types.containsKey(type)) { 3057 return csharp_types.get(type); 3058 } else { 3059 return type; 3060 } 3061 } 3062 3063 /** 3064 * If the variable is an array, returns a valid C# 'Select' statement representing the array. For 3065 * example, this.Array[].field would become this.Array.Select(x ⇒ x.field) 3066 * 3067 * <p>If the variable is not an array, csharp_name() is returned. 3068 */ 3069 public String csharp_collection_string() { 3070 String[] split = csharp_array_split(); 3071 if (split[1].equals("")) { 3072 return split[0]; 3073 } else { 3074 return split[0] + ".Select(x => x" + split[1] + ")"; 3075 } 3076 } 3077 3078 /** 3079 * Splits an array variable into the array and field portions. For example, if the variable 3080 * this.Array[].field then 3081 * 3082 * <pre> 3083 * result[0] = this.Array[] 3084 * result[1] = field 3085 * </pre> 3086 * 3087 * If the variable is not an array then 3088 * 3089 * <pre> 3090 * result[0] = csharp_name() 3091 * result[1] = "" 3092 * </pre> 3093 * 3094 * (there is no splitting). 3095 */ 3096 public String[] csharp_array_split() { 3097 String[] results = new String[2]; 3098 3099 if (!is_array()) { 3100 results[0] = csharp_name(); 3101 results[1] = ""; 3102 return results; 3103 } 3104 3105 String fields = ""; 3106 VarInfo v = this; 3107 // Go backwards from v until we reach the array portion. 3108 while (v.var_kind != VarInfo.VarKind.ARRAY && v.enclosing_var != null) { 3109 if (v.relative_name != null) { 3110 if (v.relative_name.equals("GetType()")) { 3111 fields = "." + v.relative_name; 3112 } else { 3113 fields = "." + v.relative_name + fields; 3114 } 3115 } 3116 v = v.enclosing_var; 3117 } 3118 3119 results[0] = v.csharp_name(); 3120 results[1] = fields; 3121 return results; 3122 } 3123 3124 /** Returns the name of this variable in the specified format. */ 3125 public String name_using(OutputFormat format) { 3126 if (format == OutputFormat.DAIKON) { 3127 return name(); 3128 } 3129 if (format == OutputFormat.SIMPLIFY) { 3130 return simplify_name(); 3131 } 3132 if (format == OutputFormat.ESCJAVA) { 3133 return esc_name(); 3134 } 3135 if (format == OutputFormat.JAVA) { 3136 return java_name(); 3137 } 3138 if (format == OutputFormat.JML) { 3139 return jml_name(); 3140 } 3141 if (format == OutputFormat.DBCJAVA) { 3142 return dbc_name(); 3143 } 3144 if (format == OutputFormat.CSHARPCONTRACT) { 3145 return csharp_name(); 3146 } 3147 throw new UnsupportedOperationException("Unknown format requested: " + format); 3148 } 3149 3150 /** Returns the name of this variable as a valid C# Code Contract. */ 3151 @SideEffectFree 3152 public String csharp_name() { 3153 return csharp_name(null); 3154 } 3155 3156 /** 3157 * Returns the name of this variable as a valid C# Code Contract. 3158 * 3159 * @param index an an array index. Must be null for a non-array variable. 3160 * @return the name of this variable as a valid C# Code Contract 3161 */ 3162 @SideEffectFree 3163 public String csharp_name(@Nullable String index) { 3164 if (index != null) { 3165 assert file_rep_type.isArray(); 3166 } 3167 3168 if (postState != null) { 3169 return "Contract.OldValue(" + postState.csharp_name(index) + ")"; 3170 } 3171 3172 if (derived != null) { 3173 return derived.csharp_name(index); 3174 } 3175 3176 switch (var_kind) { 3177 case FIELD: 3178 assert relative_name != null : this; 3179 3180 if (enclosing_var != null) { 3181 if (isStatic(str_name, enclosing_var.name())) { 3182 return str_name; 3183 } 3184 return enclosing_var.csharp_name(index) + "." + relative_name; 3185 } 3186 3187 return str_name; 3188 3189 case FUNCTION: 3190 if (var_flags.contains(VarFlags.TO_STRING)) { 3191 return enclosing_var.csharp_name(index); 3192 } 3193 3194 if (var_flags.contains(VarFlags.CLASSNAME)) { 3195 if (arr_dims > 0) { 3196 return csharp_collection_string(); 3197 } else { 3198 return enclosing_var.csharp_name(index) + ".GetType()"; 3199 } 3200 } 3201 3202 if (enclosing_var != null) { 3203 3204 if (isStatic(str_name, enclosing_var.name())) { 3205 String qualifiedName = str_name.substring(0, str_name.indexOf("(")); 3206 return qualifiedName + "(" + enclosing_var.csharp_name(index) + ")"; 3207 } else if (var_flags.contains(VarFlags.IS_PROPERTY)) { 3208 return enclosing_var.csharp_name(index) + "." + relative_name; 3209 } else { 3210 return enclosing_var.csharp_name(index) + "." + relative_name + "()"; 3211 } 3212 } else { 3213 return str_name; 3214 } 3215 3216 case ARRAY: 3217 if (index == null) { 3218 return enclosing_var.csharp_name(null); 3219 } 3220 return enclosing_var.csharp_name(null) + "[" + index + "]"; 3221 3222 case VARIABLE: 3223 assert enclosing_var == null; 3224 return str_name; 3225 3226 case RETURN: 3227 return "Contract.Result<" + fix_csharp_type_name(type.toString()) + ">()"; 3228 3229 default: 3230 throw new Error("can't drop through switch statement."); 3231 } 3232 } 3233 3234 /** Returns the name in Java format. This is the same as JML. */ 3235 public String java_name() { 3236 if (!FileIO.new_decl_format) { 3237 return var_info_name.java_name(this); // vin ok 3238 } 3239 3240 return jml_name(); 3241 } 3242 3243 /** Returns the name in DBC format. This is the same as JML. */ 3244 public String dbc_name() { 3245 if (!FileIO.new_decl_format) { 3246 return var_info_name.dbc_name(this); // vin ok 3247 } 3248 3249 return jml_name(); 3250 } 3251 3252 /** Returns the name of this variable in ESC format. */ 3253 @SideEffectFree 3254 public String esc_name() { 3255 if (!FileIO.new_decl_format) { 3256 return var_info_name.esc_name(); // vin ok 3257 } 3258 3259 return esc_name(null); 3260 } 3261 3262 /** 3263 * Returns the name of this variable in ESC format. If an index is specified, it is used as an 3264 * array index. It is an error to specify an index on a non-array variable. 3265 */ 3266 @SideEffectFree 3267 public String esc_name(@Nullable String index) { 3268 3269 // System.out.printf("esc_name for %s, flags %s, enclosing-var %s " 3270 // + " poststate %s index %s rname %s ppt %s%n", str_name, 3271 // var_flags, enclosing_var, postState, index, 3272 // relative_name, ppt.name()); 3273 if (index != null) { 3274 assert file_rep_type.isArray(); 3275 } 3276 3277 // If this is an orig variable, use the post version to generate the name 3278 if (postState != null) { 3279 return "\\old(" + postState.esc_name(index) + ")"; 3280 } 3281 3282 // If this is a derived variable, the derivations builds the name 3283 if (derived != null) { 3284 return derived.esc_name(index); 3285 } 3286 3287 // Build the name by processing back through all of the enclosing variables 3288 switch (var_kind) { 3289 case FIELD: 3290 assert relative_name != null : this; 3291 if (enclosing_var != null) { 3292 return enclosing_var.esc_name(index) + "." + relative_name; 3293 } 3294 return str_name; 3295 case FUNCTION: 3296 // function_args assert function_args == null : "function args not implemented"; 3297 if (var_flags.contains(VarFlags.CLASSNAME)) { 3298 return ("\\typeof(" + enclosing_var.esc_name(index) + ")"); 3299 } 3300 if (var_flags.contains(VarFlags.TO_STRING)) { 3301 return enclosing_var.esc_name(index) + ".toString"; 3302 } 3303 if (enclosing_var != null) { 3304 return enclosing_var.esc_name(index) + "." + relative_name + "()"; 3305 } 3306 return str_name; 3307 case ARRAY: 3308 if (index == null) { 3309 return enclosing_var.esc_name(null) + "[]"; 3310 } 3311 return enclosing_var.esc_name(null) + "[" + index + "]"; 3312 case VARIABLE: 3313 assert enclosing_var == null; 3314 return str_name; 3315 case RETURN: 3316 return "\\result"; 3317 default: 3318 throw new Error("can't drop through switch statement"); 3319 } 3320 } 3321 3322 /** Returns the name of this variable in JML format. */ 3323 @SideEffectFree 3324 public String jml_name() { 3325 if (!FileIO.new_decl_format) { 3326 return var_info_name.jml_name(this); // vin ok 3327 } 3328 3329 return jml_name(null); 3330 } 3331 3332 /** 3333 * Returns the name of this variable in JML format. 3334 * 3335 * @param index an array index. Must be null for a non-array variable. 3336 * @return the name of this variable in JML format 3337 */ 3338 public String jml_name(@Nullable String index) { 3339 3340 if (index != null) { 3341 assert file_rep_type.isArray(); 3342 } 3343 3344 // If this is an orig variable, use the post version to generate the name 3345 if (postState != null) { 3346 return "\\old(" + postState.jml_name(index) + ")"; 3347 } 3348 3349 // If this is a derived variable, the derivations builds the name 3350 if (derived != null) { 3351 return derived.jml_name(index); 3352 } 3353 3354 // If this is an array of fields, collect the fields into a collection 3355 if ((arr_dims > 0) && (var_kind != VarKind.ARRAY) && !var_flags.contains(VarFlags.CLASSNAME)) { 3356 String field_name = relative_name; 3357 ; 3358 VarInfo vi = this.enclosing_var; 3359 for (; vi.var_kind != VarKind.ARRAY; vi = vi.enclosing_var) { 3360 field_name = vi.relative_name + "." + field_name; 3361 } 3362 return String.format("daikon.Quant.collectObject(%s, \"%s\")", vi.jml_name(), field_name); 3363 } 3364 3365 // Build the name by processing back through all of the enclosing variables 3366 switch (var_kind) { 3367 case FIELD: 3368 assert relative_name != null : this; 3369 if (enclosing_var != null) { 3370 return enclosing_var.jml_name(index) + "." + relative_name; 3371 } 3372 return str_name; 3373 case FUNCTION: 3374 // function_args assert function_args == null : "function args not implemented"; 3375 if (var_flags.contains(VarFlags.CLASSNAME)) { 3376 if (arr_dims > 0) { 3377 return String.format("daikon.Quant.typeArray(%s)", enclosing_var.jml_name(index)); 3378 } else { 3379 return enclosing_var.jml_name(index) + DaikonVariableInfo.class_suffix; 3380 } 3381 } 3382 if (var_flags.contains(VarFlags.TO_STRING)) { 3383 return enclosing_var.jml_name(index) + ".toString()"; 3384 } 3385 if (enclosing_var != null) { 3386 return enclosing_var.jml_name(index) + "." + relative_name + "()"; 3387 } 3388 return str_name; 3389 case ARRAY: 3390 if (index == null) { 3391 return enclosing_var.jml_name(null); 3392 } 3393 return enclosing_var.jml_name(null) + "[" + index + "]"; 3394 case VARIABLE: 3395 assert enclosing_var == null; 3396 return str_name; 3397 case RETURN: 3398 return "\\result"; 3399 default: 3400 throw new Error("can't drop through switch statement"); 3401 } 3402 } 3403 3404 /** Returns the name of this variable in simplify format. */ 3405 @SideEffectFree 3406 public String simplify_name() { 3407 return simplify_name(null); 3408 } 3409 3410 /** 3411 * Returns the name of this variable in simplify format. If an index is specified, it is used as 3412 * an array index. It is an error to specify an index on a non-array variable. 3413 */ 3414 public String simplify_name(@Nullable String index) { 3415 if (!FileIO.new_decl_format) { 3416 return var_info_name.simplify_name(); // vin ok 3417 } 3418 3419 assert (index == null) || file_rep_type.isArray() : index + " " + name(); 3420 3421 // If this is a derived variable, the derivations builds the name 3422 if (derived != null) { 3423 return derived.simplify_name(); 3424 } 3425 3426 // Build the name by processing back through all of the enclosing variables 3427 switch (var_kind) { 3428 case FIELD: 3429 assert relative_name != null : this; 3430 return String.format("(select |%s| %s)", relative_name, enclosing_var.simplify_name(index)); 3431 case FUNCTION: 3432 // function_args assert function_args == null : "function args not implemented"; 3433 if (var_flags.contains(VarFlags.CLASSNAME)) { 3434 return ("(typeof " + enclosing_var.simplify_name(index) + ")"); 3435 } 3436 if (var_flags.contains(VarFlags.TO_STRING)) { 3437 return String.format("(select |toString| %s)", enclosing_var.simplify_name(index)); 3438 } 3439 if (enclosing_var != null) { 3440 return enclosing_var.simplify_name(index) + "." + relative_name + "()"; 3441 } 3442 return str_name; 3443 case ARRAY: 3444 if (index == null) { 3445 return String.format("(select elems %s)", enclosing_var.simplify_name()); 3446 } 3447 // if (index.equals("|0|")) { 3448 // System.err.printf("index = %s%n", index); 3449 // Throwable t = new Throwable(); 3450 // t.printStackTrace(); 3451 // } 3452 return String.format("(select (select elems %s) %s)", enclosing_var.simplify_name(), index); 3453 case VARIABLE: 3454 if (dkconfig_constant_fields_simplify && str_name.contains(".")) { 3455 String sel; 3456 String[] fields; 3457 if (postState != null) { 3458 fields = postState.name().split("\\."); 3459 sel = String.format("(select |%s| |__orig__%s|)", fields[1], fields[0]); 3460 } else { // not orig variable 3461 fields = str_name.split("\\."); 3462 sel = String.format("(select |%s| |%s|)", fields[1], fields[0]); 3463 } 3464 for (int ii = 2; ii < fields.length; ii++) { 3465 sel = String.format("(select |%s| %s)", fields[ii], sel); 3466 } 3467 return sel; 3468 } 3469 3470 assert enclosing_var == null; 3471 if (postState != null) { 3472 return "|__orig__" + postState.name() + "|"; 3473 } 3474 return "|" + str_name + "|"; 3475 case RETURN: 3476 return "|return|"; 3477 default: 3478 throw new Error("can't drop through switch statement"); 3479 } 3480 } 3481 3482 /** Return the name of this variable in its prestate (orig). */ 3483 @SideEffectFree 3484 public @Interned String prestate_name() { 3485 return ("orig(" + name() + ")").intern(); 3486 } 3487 3488 /** 3489 * Returns the name of the size variable that correponds to this array variable in simplify 3490 * format. Returns null if this variable is not an array or the size name can't be constructed for 3491 * other reasons. Note that isArray seems to distinguish between actual arrays and other sequences 3492 * (such as java.util.list). Simplify uses (it seems) the same length approach for both, so we 3493 * don't check isArray(). 3494 */ 3495 public @Nullable String get_simplify_size_name() { 3496 // Implement the method in two ways, to double-check results. 3497 3498 @Interned String result; 3499 if (!file_rep_type.isArray() || isDerived()) { 3500 result = null; 3501 } else { 3502 // System.out.printf("Getting size name for %s [%s]%n", name(), 3503 // get_length()); 3504 result = get_length().simplify_name().intern(); 3505 } 3506 3507 @Interned String old_result; 3508 if (!var_info_name.isApplySizeSafe()) { // vin ok 3509 old_result = null; 3510 } else { 3511 old_result = var_info_name.applySize().simplify_name().intern(); // vin ok 3512 } 3513 if (FileIO.new_decl_format && (old_result != result)) { 3514 throw new Error( 3515 String.format( 3516 "%s: '%s' '%s'%n basehashcode = %s%n", 3517 this, result, old_result, get_base_array_hashcode())); 3518 } 3519 3520 return old_result; 3521 } 3522 3523 /** Returns true if this variable contains a simple variable whose name is varname. */ 3524 public boolean includes_simple_name(String varname) { 3525 if (!FileIO.new_decl_format) { 3526 return var_info_name.includesSimpleName(varname); // vin ok 3527 } 3528 3529 if (isDerived()) { 3530 for (VarInfo base : derived.getBases()) { 3531 if (base.includes_simple_name(varname)) { 3532 return true; 3533 } 3534 } 3535 } else { 3536 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 3537 if ((vi.var_kind == VarKind.VARIABLE) && vi.name().equals(varname)) { 3538 return true; 3539 } 3540 } 3541 } 3542 return false; 3543 } 3544 3545 /** 3546 * Quantifies over the specified array variables in ESC format. Returns an array with 2 more 3547 * elements than the argument. Element 0 is the quantification, Element 1 is the indexed form of 3548 * variable 1, Element 2 is the indexed form of variable 3, and Element 4 is syntax such as close 3549 * parentheses. 3550 */ 3551 public static String[] esc_quantify(VarInfo... vars) { 3552 return esc_quantify(true, vars); 3553 } 3554 3555 /** 3556 * Quantifies over the specified array variables in ESC format. Returns an array with 2 more 3557 * elements than the argument. Element 0 is the quantification, Element 1 is the indexed form of 3558 * variable 1, Element 2 is the indexed form of variable 3, and Element 4 is syntax such as close 3559 * parentheses. 3560 */ 3561 public static String[] esc_quantify(boolean elementwise, VarInfo... vars) { 3562 3563 if (FileIO.new_decl_format) { 3564 Quantify.ESCQuantification quant = 3565 new Quantify.ESCQuantification(Quantify.get_flags(elementwise), vars); 3566 if (vars.length == 1) { 3567 return new String[] {quant.get_quantification(), quant.get_arr_vars_indexed(0), ")"}; 3568 } else if ((vars.length == 2) && vars[1].file_rep_type.isArray()) { 3569 return new String[] { 3570 quant.get_quantification(), 3571 quant.get_arr_vars_indexed(0), 3572 quant.get_arr_vars_indexed(1), 3573 ")" 3574 }; 3575 } else { 3576 return new String[] { 3577 quant.get_quantification(), quant.get_arr_vars_indexed(0), vars[1].esc_name(), ")" 3578 }; 3579 } 3580 } else { 3581 VarInfoName vin[] = new VarInfoName[vars.length]; 3582 for (int ii = 0; ii < vars.length; ii++) { 3583 vin[ii] = vars[ii].var_info_name; // vin ok 3584 } 3585 return VarInfoName.QuantHelper.format_esc(vin, elementwise); 3586 } 3587 } 3588 3589 /** 3590 * Returns a string array with 3 elements. The first element is the sequence, the second element 3591 * is the lower bound, and the third element is the upper bound. Returns null if this is not a 3592 * direct array or slice. 3593 */ 3594 public String @Nullable [] simplifyNameAndBounds() { 3595 if (!FileIO.new_decl_format) { 3596 return VarInfoName.QuantHelper.simplifyNameAndBounds(var_info_name); // vin ok 3597 } 3598 3599 String[] results = new String[3]; 3600 if (is_direct_non_slice_array() || (derived instanceof SequenceSubsequence)) { 3601 results[0] = get_base_array_hashcode().simplify_name(); 3602 results[1] = get_lower_bound().simplify_name(); 3603 results[2] = get_upper_bound().simplify_name(); 3604 return results; 3605 } 3606 3607 return null; 3608 } 3609 3610 /** 3611 * Returns the upper and lower bounds of the slice in simplify format. The implementation is 3612 * somewhat different that simplifyNameAndBounds (I don't know why). 3613 */ 3614 public String @Nullable [] get_simplify_slice_bounds() { 3615 if (!FileIO.new_decl_format) { 3616 @Interned VarInfoName[] bounds = var_info_name.getSliceBounds(); // vin ok 3617 if (bounds == null) { 3618 return null; 3619 } 3620 String[] str_bounds = new String[2]; 3621 str_bounds[0] = bounds[0].simplify_name(); 3622 str_bounds[1] = bounds[1].simplify_name(); 3623 return str_bounds; 3624 } 3625 3626 String[] results; 3627 if (derived instanceof SequenceSubsequence) { 3628 results = new String[2]; 3629 results[0] = get_lower_bound().simplify_name().intern(); 3630 results[1] = get_upper_bound().simplify_name().intern(); 3631 } else { 3632 results = null; 3633 } 3634 3635 return results; 3636 } 3637 3638 /** 3639 * Return a string in simplify format that will seclect the (index_base + index_off)-th element of 3640 * the sequence specified by this variable. 3641 * 3642 * @param simplify_index_name name of the index. If free is false, this must be a number or null 3643 * (null implies an index of 0). 3644 * @param free true of simplify_index_name is variable name 3645 * @param index_off offset from the index 3646 */ 3647 public String get_simplify_selectNth(String simplify_index_name, boolean free, int index_off) { 3648 3649 // Remove the simplify bars if present from the index name 3650 if ((simplify_index_name != null) 3651 && simplify_index_name.startsWith("|") 3652 && simplify_index_name.endsWith("|")) 3653 simplify_index_name = simplify_index_name.substring(1, simplify_index_name.length() - 1); 3654 3655 // Use VarInfoName to handle the old format 3656 if (!FileIO.new_decl_format) { 3657 VarInfoName select = 3658 VarInfoName.QuantHelper.selectNth( 3659 this.var_info_name, // vin ok 3660 simplify_index_name, 3661 free, 3662 index_off); 3663 // System.out.printf("sNth: index %s, free %b, off %d, result '%s'%n", 3664 // simplify_index_name, free, index_off, 3665 // select.simplify_name()); 3666 return select.simplify_name(); 3667 } 3668 3669 // Calculate the index (including the offset if non-zero) 3670 String complete_index; 3671 if (!free) { 3672 int index = 0; 3673 if (simplify_index_name != null) { 3674 index = Integer.decode(simplify_index_name); 3675 } 3676 index += index_off; 3677 complete_index = String.format("%d", index); 3678 } else { 3679 if (index_off != 0) { 3680 complete_index = String.format("(+ |%s| %d)", simplify_index_name, index_off); 3681 } else { 3682 complete_index = String.format("|%s|", simplify_index_name); 3683 } 3684 } 3685 3686 // Return the array properly indexed 3687 return simplify_name(complete_index); 3688 } 3689 3690 /** 3691 * Return a string in simplify format that will seclect the index_off element in a sequence that 3692 * has a lower bound. 3693 * 3694 * @param index_off offset from the index 3695 */ 3696 public String get_simplify_selectNth_lower(int index_off) { 3697 3698 // Use VarInfoName to handle the old format 3699 if (!FileIO.new_decl_format) { 3700 @Interned VarInfoName[] bounds = var_info_name.getSliceBounds(); 3701 VarInfoName lower = null; 3702 if (bounds != null) { 3703 lower = bounds[0]; 3704 } 3705 VarInfoName select = 3706 VarInfoName.QuantHelper.selectNth( 3707 var_info_name, // vin ok 3708 lower, 3709 index_off); 3710 return select.simplify_name(); 3711 } 3712 3713 // Calculate the index (including the offset if non-zero) 3714 String complete_index; 3715 Quantify.Term lower = get_lower_bound(); 3716 String lower_name = lower.simplify_name(); 3717 if (!(lower instanceof Quantify.Constant)) { 3718 lower_name = String.format("|%s|", lower_name); 3719 } 3720 if (index_off != 0) { 3721 if (lower instanceof Quantify.Constant) { 3722 complete_index = String.format("%d", ((Quantify.Constant) lower).get_value() + index_off); 3723 } else { 3724 complete_index = String.format("(+ %s %d)", lower_name, index_off); 3725 } 3726 } else { 3727 complete_index = String.format("%s", lower_name); 3728 } 3729 3730 // Return the array properly indexed 3731 // System.err.printf("lower bound type = %s [%s] %s%n", lower, 3732 // lower.getClass(), complete_index); 3733 return simplify_name(complete_index); 3734 } 3735 3736 /** Get a fresh variable name that doesn't appear in the given variable in simplify format. */ 3737 public static String get_simplify_free_index(VarInfo... vars) { 3738 if (!FileIO.new_decl_format) { 3739 VarInfoName[] vins = new VarInfoName[vars.length]; 3740 for (int ii = 0; ii < vars.length; ii++) { 3741 vins[ii] = vars[ii].var_info_name; // vin ok 3742 } 3743 return VarInfoName.QuantHelper.getFreeIndex(vins).simplify_name(); 3744 } 3745 3746 // Get a free variable for each variable and return the first one 3747 QuantifyReturn[] qret = Quantify.quantify(vars); 3748 return qret[0].index.simplify_name(); 3749 } 3750 3751 /** Get a 2 fresh variable names that doesn't appear in the given variable in simplify format. */ 3752 public static String[] get_simplify_free_indices(VarInfo... vars) { 3753 if (!FileIO.new_decl_format) { 3754 if (vars.length == 1) { 3755 VarInfoName index1_vin = 3756 VarInfoName.QuantHelper.getFreeIndex(vars[0].var_info_name); // vin ok 3757 String index2 = 3758 VarInfoName.QuantHelper.getFreeIndex(vars[0].var_info_name, index1_vin) 3759 .simplify_name(); // vin ok 3760 return new String[] {index1_vin.name(), index2}; 3761 } else if (vars.length == 2) { 3762 VarInfoName index1_vin = 3763 VarInfoName.QuantHelper.getFreeIndex( 3764 vars[0].var_info_name, vars[1].var_info_name); // vin ok 3765 String index2 = 3766 VarInfoName.QuantHelper.getFreeIndex( 3767 vars[0].var_info_name, vars[1].var_info_name, index1_vin) // vin ok 3768 .simplify_name(); 3769 return new String[] {index1_vin.name(), index2}; 3770 } else { 3771 throw new Error("unexpected length " + vars.length); 3772 } 3773 } 3774 3775 // Get a free variable for each variable 3776 if (vars.length == 1) { 3777 vars = new VarInfo[] {vars[0], vars[0]}; 3778 } 3779 QuantifyReturn qret[] = Quantify.quantify(vars); 3780 return new String[] {qret[0].index.simplify_name(), qret[1].index.simplify_name()}; 3781 } 3782 3783 /** 3784 * Quantifies over the specified array variables in Simplify format. Returns a string array that 3785 * contains the quantification, indexed form of each variable, optionally the index itself, and 3786 * the closer. 3787 * 3788 * <p>If elementwise is true, include the additional contraint that the indices (there must be 3789 * exactly two in this case) refer to corresponding positions. If adjacent is true, include the 3790 * additional constraint that the second index be one more than the first. If distinct is true, 3791 * include the constraint that the two indices are different. If includeIndex is true, return 3792 * additional strings, after the roots but before the closer, with the names of the index 3793 * variables. 3794 */ 3795 public static String[] simplify_quantify(EnumSet<QuantFlags> flags, VarInfo... vars) { 3796 3797 if (!FileIO.new_decl_format) { 3798 // Get the names for each variable. 3799 VarInfoName vin[] = new VarInfoName[vars.length]; 3800 for (int ii = 0; ii < vars.length; ii++) { 3801 vin[ii] = vars[ii].var_info_name; // vin ok 3802 } 3803 3804 return VarInfoName.QuantHelper.format_simplify( 3805 vin, 3806 flags.contains(QuantFlags.ELEMENT_WISE), 3807 flags.contains(QuantFlags.ADJACENT), 3808 flags.contains(QuantFlags.DISTINCT), 3809 flags.contains(QuantFlags.INCLUDE_INDEX)); 3810 } 3811 3812 Quantify.SimplifyQuantification quant = new Quantify.SimplifyQuantification(flags, vars); 3813 boolean include_index = flags.contains(QuantFlags.INCLUDE_INDEX); 3814 if ((vars.length == 1) && include_index) { 3815 return new String[] { 3816 quant.get_quantification(), 3817 quant.get_arr_vars_indexed(0), 3818 quant.get_index(0), 3819 quant.get_closer() 3820 }; 3821 } else if (vars.length == 1) { 3822 return new String[] { 3823 quant.get_quantification(), quant.get_arr_vars_indexed(0), quant.get_closer() 3824 }; 3825 } else if ((vars.length == 2) && include_index) { 3826 return new String[] { 3827 quant.get_quantification(), 3828 quant.get_arr_vars_indexed(0), 3829 quant.get_arr_vars_indexed(1), 3830 quant.get_index(0), 3831 quant.get_index(1), 3832 quant.get_closer() 3833 }; 3834 } else { // must be length 2 and no index 3835 return new String[] { 3836 quant.get_quantification(), 3837 quant.get_arr_vars_indexed(0), 3838 quant.get_arr_vars_indexed(1), 3839 quant.get_closer() 3840 }; 3841 } 3842 } 3843 3844 /** 3845 * See {@link #simplify_quantify(EnumSet, VarInfo[])}. 3846 * 3847 * @see #simplify_quantify(EnumSet, VarInfo[]) 3848 */ 3849 public static String[] simplify_quantify(VarInfo... vars) { 3850 return simplify_quantify(EnumSet.noneOf(QuantFlags.class), vars); 3851 } 3852 3853 /** 3854 * Returns a rough indication of the complexity of the variable. Higher numbers indicate more 3855 * complexity. 3856 */ 3857 public int complexity() { 3858 if (!FileIO.new_decl_format) { 3859 // System.out.printf("%s - %s%n", this, var_info_name.repr()); 3860 return var_info_name.inOrderTraversal().size(); // vin ok 3861 } 3862 3863 int cnt = 0; 3864 if (isDerived()) { 3865 cnt += derived.complexity(); 3866 VarInfo[] bases = derived.getBases(); 3867 for (VarInfo vi : bases) { 3868 cnt += vi.complexity(); 3869 } 3870 // Adjust for the complexity change when a prestate is nested in 3871 // another prestate. This is just done to match the old version 3872 if ((bases.length == 2) && bases[0].isPrestate()) { 3873 if (bases[1].isPrestate()) { 3874 cnt--; 3875 } else { 3876 cnt++; 3877 } 3878 } 3879 } else { 3880 if (isPrestate()) { 3881 cnt++; 3882 } 3883 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 3884 cnt++; 3885 } 3886 } 3887 3888 // int old_cnt = var_info_name.inOrderTraversal().size(); 3889 // if (cnt != old_cnt) 3890 // System.out.printf("var %s, new cnt = %d, old cnt = %d [%s]%n", 3891 // name(), cnt, old_cnt, var_info_name.inOrderTraversal()); 3892 return cnt; 3893 } 3894 3895 /** 3896 * Returns true if this variable can be assigned to. Currently this is presumed true of all 3897 * variable except the special variable for the type of a variable and the size of a sequence. It 3898 * should include pure functions as well. 3899 */ 3900 @Pure 3901 public boolean is_assignable_var() { 3902 if (!FileIO.new_decl_format) { 3903 return !((var_info_name instanceof VarInfoName.TypeOf) // vin ok 3904 || (var_info_name instanceof VarInfoName.SizeOf)); // vin ok 3905 } 3906 3907 return !(is_typeof() || is_size()); 3908 } 3909 3910 /** 3911 * Returns whether or not this variable represents the type of a variable (eg, 3912 * a.getClass().getName()). Note that this will miss prestate variables such as 3913 * 'orig(a.getClass().getName())'. 3914 */ 3915 @Pure 3916 public boolean is_typeof() { 3917 if (!FileIO.new_decl_format) { 3918 return (var_info_name instanceof VarInfoName.TypeOf); // vin ok 3919 } 3920 3921 // The isPrestate check doesn't seem necessary, but is required to 3922 // match old behavior. 3923 return !isPrestate() && var_flags.contains(VarFlags.CLASSNAME); 3924 } 3925 3926 /** 3927 * Returns whether or not this variable represents the type of a variable (eg, 3928 * a.getClass().getName()). This version finds prestate variables such as 3929 * 'org(a.getClass().getName())'. 3930 */ 3931 public boolean has_typeof() { 3932 if (!FileIO.new_decl_format) { 3933 return var_info_name.hasTypeOf(); // vin ok 3934 } 3935 3936 if (isPrestate()) { 3937 return postState.has_typeof(); 3938 } 3939 return is_typeof(); 3940 } 3941 3942 /** Returns whether or not this variable is the 'this' variable. */ 3943 @Pure 3944 public boolean is_this() { 3945 return name().equals("this"); 3946 // return get_VarInfoName().equals(VarInfoName.THIS); 3947 } 3948 3949 /** 3950 * Returns whether or not this variable is the 'this' variable. True for both normal and prestate 3951 * versions of the variable. 3952 */ 3953 @Pure 3954 public boolean isThis() { 3955 return var_info_name.isThis(); 3956 } 3957 3958 /** Returns whether this is a size of an array or a prestate thereof. */ 3959 @Pure 3960 public boolean is_size() { 3961 return (derived instanceof SequenceLength); 3962 } 3963 3964 /** Returns wehther or not this variable is a field. */ 3965 @Pure 3966 public boolean is_field() { 3967 return (var_info_name instanceof VarInfoName.Field); 3968 } 3969 3970 /** Returns whether or not this variable has an integer offset (eg, a+2) */ 3971 @Pure 3972 public boolean is_add() { 3973 return (var_info_name instanceof VarInfoName.Add); 3974 } 3975 3976 /** 3977 * Returns the integer offset if this variable is an addition such as a+2. Throws an exception of 3978 * this variable is not an addition. 3979 * 3980 * @see #is_add() 3981 */ 3982 public int get_add_amount() { 3983 return ((VarInfoName.Add) var_info_name).amount; 3984 } 3985 3986 /** 3987 * Returns whether or not this variable is an actual array as opposed to an array that is created 3988 * over fields/methods of an array. For example, 'a[]' is a direct array, but 'a[].b' is not. 3989 */ 3990 @Pure 3991 public boolean is_direct_array() { 3992 // Must be an array to be a direct array 3993 if (!rep_type.isArray()) { 3994 return false; 3995 } 3996 3997 // If $Field or $Type appears before $Elements, false. 3998 // System.out.printf("%s flatten %s%n", name(), name); 3999 for (VarInfoName node : new VarInfoName.InorderFlattener(var_info_name).nodes()) { 4000 if (node instanceof VarInfoName.Field) { 4001 return false; 4002 } 4003 if (node instanceof VarInfoName.TypeOf) { 4004 return false; 4005 } 4006 if (node instanceof VarInfoName.Elements) { 4007 break; 4008 } 4009 } 4010 4011 return true; 4012 } 4013 4014 /** 4015 * Returns whether or not this variable is an actual array as opposed to an array that is created 4016 * over fields/methods of an array or a slice. For example, 'a[]' is a direct array, but 'a[].b' 4017 * and 'a[i..]' are not. 4018 */ 4019 @Pure 4020 public boolean is_direct_non_slice_array() { 4021 return (var_info_name instanceof VarInfoName.Elements); 4022 } 4023 4024 /** 4025 * Returns whether or not two variables have the same enclosing variable. If either variable is 4026 * not a field, returns false. 4027 */ 4028 public boolean has_same_parent(VarInfo other) { 4029 if (!is_field() || !other.is_field()) { 4030 return false; 4031 } 4032 4033 VarInfoName.Field name1 = (VarInfoName.Field) var_info_name; 4034 VarInfoName.Field name2 = (VarInfoName.Field) other.var_info_name; 4035 4036 return name1.term.equals(name2.term); 4037 } 4038 4039 /** 4040 * Returns the variable that encloses this one. For example if this variable is 'x.a.b', the 4041 * enclosing variable is 'x.a'. 4042 */ 4043 public @Nullable VarInfo get_enclosing_var() { 4044 if (FileIO.new_decl_format) { 4045 return enclosing_var; 4046 } else { 4047 List<VarInfoName> traversal = new VarInfoName.InorderFlattener(var_info_name).nodes(); 4048 if (traversal.size() <= 1) { 4049 // System.out.printf("size <= 1, traversal = %s%n", traversal); 4050 return null; 4051 } else { 4052 VarInfo enclosing_vi = ppt.find_var_by_name(traversal.get(1).name()); 4053 // if (enclosing_vi == null) 4054 // System.out.printf("Can't find '%s' in %s%n", 4055 // traversal.get(1).name(), ppt.varNames()); 4056 return enclosing_vi; 4057 } 4058 } 4059 } 4060 4061 /** 4062 * Replaces all instances of 'this' in the variable with the name of arg. Used to match up 4063 * enter/exit variables with object variables. 4064 */ 4065 public String replace_this(VarInfo arg) { 4066 VarInfoName parent_name = var_info_name.replaceAll(VarInfoName.THIS, arg.var_info_name); 4067 return parent_name.name(); 4068 } 4069 4070 /** 4071 * Creates a VarInfo that is a subsequence that begins at begin and ends at end with the specified 4072 * shifts. The begin or the end can be null, but a non-zero shift is only allowed with non-null 4073 * variables. 4074 */ 4075 public static VarInfo make_subsequence( 4076 VarInfo seq, @Nullable VarInfo begin, int begin_shift, @Nullable VarInfo end, int end_shift) { 4077 4078 String begin_str = inside_name(begin, seq.isPrestate(), begin_shift); 4079 if (begin_str.equals("")) { // interned if the null string, not interned otherwise 4080 begin_str = "0"; 4081 } 4082 String end_str = inside_name(end, seq.isPrestate(), end_shift); 4083 4084 VarInfoName begin_name; 4085 String parent_format = "%s.."; 4086 if (begin == null) { 4087 begin_name = null; 4088 } else { 4089 begin_name = (begin != null) ? begin.var_info_name : null; 4090 if (begin_shift == -1) { 4091 begin_name = begin_name.applyDecrement(); 4092 parent_format = "%s-1.."; 4093 } else if (begin_shift == 1) { 4094 begin_name = begin_name.applyIncrement(); 4095 parent_format = "%s+1.."; 4096 } else { 4097 assert begin_shift == 0; 4098 } 4099 } 4100 4101 VarInfoName end_name; 4102 if (end == null) { 4103 end_name = null; 4104 parent_format += "%s"; 4105 } else { 4106 end_name = end.var_info_name; 4107 if (end_shift == -1) { 4108 end_name = end_name.applyDecrement(); 4109 parent_format += "%s-1"; 4110 } else if (end_shift == 1) { 4111 end_name = end_name.applyIncrement(); 4112 parent_format += "%s+1"; 4113 } else { 4114 assert end_shift == 0; 4115 parent_format += "%s"; 4116 } 4117 } 4118 4119 VarInfoName new_name = seq.var_info_name.applySlice(begin_name, end_name); 4120 4121 VarInfo vi = new VarInfo(new_name, seq.type, seq.file_rep_type, seq.comparability, seq.aux); 4122 vi.setup_derived_base(seq, begin, end); 4123 vi.str_name = 4124 seq.apply_subscript(String.format("%s..%s", begin_str, end_str)) 4125 .intern(); // interning bugfix 4126 4127 // If there is a parent ppt (set in setup_derived_base), set the 4128 // parent variable accordingly. If all of the original variables used 4129 // the default name, this can as well. Otherwise, build the parent 4130 // name. 4131 for (VarParent parent : vi.parents) { 4132 int rid = parent.parent_relation_id; 4133 4134 if ((seq.get_parent(rid) == null) 4135 && ((begin == null) || !begin.has_parent(rid) || (begin.parent_var(rid) == null)) 4136 && ((end == null) || !end.has_parent(rid) || (end.parent_var(rid) == null))) { 4137 4138 parent.parent_variable = null; 4139 } else { 4140 String begin_pname = 4141 (begin == null || !begin.has_parent(rid)) ? "0" : begin.parent_var_name(rid); 4142 String end_pname = (end == null || !end.has_parent(rid)) ? "" : end.parent_var_name(rid); 4143 @SuppressWarnings( 4144 "formatter") // format string is constructed above using make_subsequence's arguments 4145 String res = 4146 apply_subscript( 4147 seq.parent_var_name(rid), String.format(parent_format, begin_pname, end_pname)); 4148 parent.parent_variable = res; 4149 // System.out.printf("-- set parent var from '%s' '%s' '%s' '%s'%n", 4150 // seq.parent_var_name(), parent_format, begin_pname, end_pname); 4151 } 4152 // System.out.printf("Parent for %s:%s is %s:%s%n", 4153 // ((seq.ppt != null)? seq.ppt.name() : "none"), vi.name(), 4154 // vi.parent_ppt, vi.parent_variable); 4155 } 4156 4157 return vi; 4158 } 4159 4160 /** 4161 * Returns the name to use for vi inside of a array reference. If the array reference is orig, 4162 * then orig is implied. This removes orig from orig variales and adds post to post variables. 4163 */ 4164 private static String inside_name(@Nullable VarInfo vi, boolean in_orig, int shift) { 4165 if (vi == null) { 4166 return ""; 4167 } 4168 4169 String shift_str = ""; 4170 if (shift != 0) { 4171 shift_str = String.format("%+d", shift); 4172 } 4173 4174 if (in_orig) { 4175 if (vi.isPrestate()) { 4176 return vi.postState.name() + shift_str; 4177 } else { 4178 return String.format("post(%s)%s", vi.name(), shift_str); 4179 } 4180 } else { 4181 return vi.name() + shift_str; 4182 } 4183 } 4184 4185 /** 4186 * Creates a VarInfo that is an index into a sequence. The type, file_rep_type, etc are taken from 4187 * the element type of the sequence. 4188 */ 4189 public static VarInfo make_subscript(VarInfo seq, @Nullable VarInfo index, int index_shift) { 4190 4191 String index_str = inside_name(index, seq.isPrestate(), index_shift); 4192 4193 VarInfoName index_name; 4194 if (index == null) { 4195 index_name = VarInfoName.parse(String.valueOf(index_shift)); 4196 } else { 4197 index_name = index.var_info_name; 4198 if (index_shift == -1) { 4199 index_name = index_name.applyDecrement(); 4200 } else { 4201 assert index_shift == 0 : "bad shift " + index_shift + " for " + index; 4202 } 4203 } 4204 4205 VarInfoName new_name = seq.var_info_name.applySubscript(index_name); 4206 VarInfo vi = 4207 new VarInfo( 4208 new_name, 4209 seq.type.elementType(), 4210 seq.file_rep_type.elementType(), 4211 seq.comparability.elementType(), 4212 VarInfoAux.getDefault()); 4213 vi.setup_derived_base(seq, index); 4214 vi.var_kind = VarInfo.VarKind.FIELD; 4215 vi.str_name = seq.apply_subscript(index_str).intern(); // interning bugfix 4216 for (VarParent parent : vi.parents) { 4217 int rid = parent.parent_relation_id; 4218 4219 if ((seq.parent_var(rid) == null) 4220 && ((index == null) || !index.has_parent(rid) || (index.parent_var(rid) == null))) { 4221 parent.parent_variable = null; 4222 } else { // one of the two bases has a different parent variable name 4223 String subscript_parent = String.valueOf(index_shift); 4224 if (index != null && index.has_parent(rid)) { 4225 subscript_parent = index.parent_var_name(rid); 4226 4227 if (seq.isPrestate() && !index.isPrestate()) { 4228 // Wrap the index in POST if the sequence is original 4229 subscript_parent = VarInfoName.parse(subscript_parent).applyPoststate().name_impl(); 4230 } else if (seq.isPrestate() && index.isPrestate()) { 4231 // Remove redundant ORIG 4232 subscript_parent = ((Prestate) VarInfoName.parse(subscript_parent)).term.name_impl(); 4233 } 4234 4235 if (index_shift == -1) { 4236 subscript_parent = subscript_parent + "-1"; 4237 } 4238 } 4239 parent.parent_variable = apply_subscript(seq.parent_var_name(rid), subscript_parent); 4240 } 4241 } 4242 return vi; 4243 } 4244 4245 /** 4246 * Create a VarInfo that is a function over one or more other variables. The type, rep_type, etc. 4247 * of the new function are taken from the first variable. 4248 */ 4249 public static VarInfo make_function(String function_name, VarInfo... vars) { 4250 4251 VarInfoName[] vin = new VarInfoName[vars.length]; 4252 for (int ii = 0; ii < vars.length; ii++) { 4253 vin[ii] = vars[ii].var_info_name; 4254 } 4255 4256 VarInfo vi = 4257 new VarInfo( 4258 VarInfoName.applyFunctionOfN(function_name, vin), 4259 vars[0].type, 4260 vars[0].file_rep_type, 4261 vars[0].comparability, 4262 vars[0].aux); 4263 vi.setup_derived_function(function_name, vars); 4264 return vi; 4265 } 4266 4267 /* 4268 * Creates the derived variable func(seq) from seq. 4269 * 4270 * @param func_name name of the function 4271 * @param type return type of the function. If null, the return type is 4272 * the element type of the sequence. 4273 * @param seq sequence variable 4274 * @param shift value to add or subtract from the function. Legal values 4275 * are -1, 0, and 1. 4276 */ 4277 public static VarInfo make_scalar_seq_func( 4278 String func_name, @Nullable ProglangType type, VarInfo seq, int shift) { 4279 4280 VarInfoName viname = seq.var_info_name.applyFunction(func_name); 4281 if (func_name.equals("size")) { 4282 viname = seq.var_info_name.applySize(); 4283 } 4284 String shift_name = ""; 4285 if (shift == -1) { 4286 viname = viname.applyDecrement(); 4287 shift_name = "_minus1"; 4288 } else if (shift == 1) { 4289 viname = viname.applyIncrement(); 4290 shift_name = "_plus1"; 4291 } else { 4292 assert shift == 0; 4293 } 4294 4295 @NonNull ProglangType ptype = type; 4296 @NonNull ProglangType frtype = type; 4297 VarComparability comp = seq.comparability.indexType(0); 4298 VarInfoAux aux = VarInfoAux.getDefault(); 4299 if (type == null) { 4300 ptype = seq.type.elementType(); 4301 frtype = seq.file_rep_type.elementType(); 4302 comp = seq.comparability.elementType(); 4303 aux = seq.aux; 4304 } 4305 VarInfo vi = new VarInfo(viname, ptype, frtype, comp, aux); 4306 vi.setup_derived_base(seq); 4307 vi.var_kind = VarInfo.VarKind.FUNCTION; 4308 vi.enclosing_var = seq; 4309 vi.arr_dims = 0; 4310 // null is initial value: vi.function_args = null; 4311 vi.relative_name = func_name + shift_name; 4312 4313 // Calculate the string to add for the shift. 4314 String shift_str = ""; 4315 if (shift != 0) { 4316 shift_str = String.format("%+d", shift); 4317 } 4318 4319 // Determine whether orig should be swapped with the function. 4320 // The original VarInfoName code did this only for the size 4321 // function (though it makes the same sense for all functions over 4322 // sequences). 4323 boolean swap_orig = 4324 func_name.equals("size") && seq.isPrestate() && !VarInfoName.dkconfig_direct_orig; 4325 4326 // Force orig to the outside if specified. 4327 if (swap_orig) { 4328 vi.str_name = 4329 String.format("orig(%s(%s))%s", func_name, seq.postState.name(), shift_str) 4330 .intern(); // interning bugfix 4331 } else { 4332 vi.str_name = 4333 String.format("%s(%s)%s", func_name, seq.name(), shift_str).intern(); // interning bugfix 4334 } 4335 4336 for (VarParent parent : vi.parents) { 4337 int rid = parent.parent_relation_id; 4338 if (!seq.has_parent(rid) || seq.parent_var(rid) == null) { 4339 parent.parent_variable = null; 4340 } else { 4341 if (func_name.equals("size")) { 4342 // Special handling for the case where the parent var name is orig(array[...]). 4343 // With swapping, the parent should be orig(size(array[..])), however it's stored 4344 // as a string so the swap can't be done textually. 4345 VarInfoName parentName = VarInfoName.parse(seq.parent_var_name(rid)); 4346 4347 // Can't use the more general applyFunction method here because it doesn't take into 4348 // account prestate values as the applySize method explicitly does 4349 parentName = parentName.applySize(); 4350 4351 parent.parent_variable = String.format("%s%s", parentName.name(), shift_str); 4352 } else { 4353 assert !swap_orig : "swap orig with parent " + vi; 4354 parent.parent_variable = 4355 String.format("%s(%s)%s", func_name, seq.parent_var_name(rid), shift_str); 4356 } 4357 } 4358 } 4359 return vi; 4360 } 4361 4362 /* 4363 * Creates the derived variable func(str) from string. 4364 * 4365 * @param func_name name of the function 4366 * @param type return type of the function 4367 * @param str sequence variable 4368 */ 4369 public static VarInfo make_scalar_str_func(String func_name, ProglangType type, VarInfo str) { 4370 4371 VarInfoName viname = str.var_info_name.applyFunction(func_name); 4372 4373 ProglangType ptype = type; 4374 ProglangType frtype = type; 4375 VarComparability comp = str.comparability.string_length_type(); 4376 VarInfoAux aux = VarInfoAux.getDefault(); 4377 VarInfo vi = new VarInfo(viname, ptype, frtype, comp, aux); 4378 vi.setup_derived_base(str); 4379 vi.var_kind = VarInfo.VarKind.FUNCTION; 4380 vi.enclosing_var = str; 4381 vi.arr_dims = 0; 4382 // null is initial value: vi.function_args = null; 4383 vi.relative_name = func_name; 4384 4385 vi.str_name = String.format("%s.%s()", str.name(), func_name).intern(); // interning bugfix 4386 4387 for (VarParent parent : vi.parents) { 4388 int rid = parent.parent_relation_id; 4389 if (str.get_parent(rid).parent_variable == null) { 4390 parent.parent_variable = null; 4391 } else { 4392 parent.parent_variable = 4393 String.format("%s.%s()", str.get_parent(rid).parent_variable, func_name); 4394 } 4395 } 4396 return vi; 4397 } 4398 4399 /** 4400 * Returns true if vi is the prestate version of this. If this is a derived variable, vi must be 4401 * the same derivation using prestate versions of each base variable. 4402 */ 4403 @Pure 4404 public boolean is_prestate_version(VarInfo vi) { 4405 4406 // If both variables are not derived 4407 if ((derived == null) && (vi.derived == null)) { 4408 4409 // true if vi is the prestate version of this 4410 return !isPrestate() && vi.isPrestate() && name().equals(vi.postState.name()); 4411 4412 // else if both variables are derived 4413 } else if ((derived != null) && (vi.derived != null)) { 4414 4415 return derived.is_prestate_version(vi.derived); 4416 4417 // one is derived and the other isn't 4418 } else { 4419 return false; 4420 } 4421 } 4422 4423 /** Returns true if this is an array or a slice. */ 4424 @Pure 4425 public boolean isArray() { 4426 return type.isArray(); 4427 } 4428 4429 /** Returns true if this is a slice. */ 4430 @Pure 4431 public boolean isSlice() { 4432 return isArray() && isDerived(); 4433 } 4434 4435 /** Converts a variable name or expression to the old style of names. */ 4436 public static String old_var_names(String name) { 4437 if (PrintInvariants.dkconfig_old_array_names && FileIO.new_decl_format) { 4438 return name.replace("[..]", "[]"); 4439 } else { 4440 return name; 4441 } 4442 } 4443 4444 /** Returns the old style variable name for this name. */ 4445 public String old_var_name() { 4446 return old_var_names(name()); 4447 } 4448 4449 /** Rough check to ensure that the variable name and derivation match up. */ 4450 public void var_check() { 4451 4452 if (false) { 4453 if (derived instanceof SequenceSubsequence) { 4454 if (name().contains("-1")) { 4455 SequenceSubsequence ss = (SequenceSubsequence) derived; 4456 // System.out.printf("checking %s [%s] with derived %s[%s]%n", 4457 // this, System.identityHashCode(this), derived, 4458 // System.identityHashCode(derived)); 4459 assert ss.index_shift == -1 4460 : "bad var " 4461 + this 4462 + " derived " 4463 + derived 4464 + " shift " 4465 + ss.index_shift 4466 + " in ppt " 4467 + ppt.name(); 4468 } 4469 } 4470 } 4471 } 4472}