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 public @Nullable VarInfo isDerivedSequenceMember() { 1595 if (derived == null) { 1596 return null; 1597 } 1598 1599 if (derived instanceof SequenceScalarSubscript) { 1600 SequenceScalarSubscript sss = (SequenceScalarSubscript) derived; 1601 return sss.seqvar(); 1602 } else if (derived instanceof SequenceInitial) { 1603 SequenceInitial se = (SequenceInitial) derived; 1604 return se.seqvar(); 1605 } else if (derived instanceof SequenceMax) { 1606 SequenceMax sm = (SequenceMax) derived; 1607 return sm.base; 1608 } else if (derived instanceof SequenceMin) { 1609 SequenceMin sm = (SequenceMin) derived; 1610 return sm.base; 1611 } else { 1612 return null; 1613 } 1614 } 1615 1616 @Pure 1617 public boolean isDerivedSequenceMinMaxSum() { 1618 return ((derived != null) 1619 && ((derived instanceof SequenceMax) 1620 || (derived instanceof SequenceMin) 1621 || (derived instanceof SequenceSum))); 1622 } 1623 1624 /** 1625 * Return the original sequence variable from which this derived sequence was derived. Only works 1626 * for sequences. 1627 */ 1628 public @Nullable VarInfo isDerivedSubSequenceOf() { 1629 1630 if (derived == null) { 1631 return null; 1632 } 1633 1634 if (derived instanceof SequenceScalarSubsequence) { 1635 SequenceScalarSubsequence sss = (SequenceScalarSubsequence) derived; 1636 return sss.seqvar(); 1637 } else if (derived instanceof SequenceScalarArbitrarySubsequence) { 1638 SequenceScalarArbitrarySubsequence ssas = (SequenceScalarArbitrarySubsequence) derived; 1639 return ssas.seqvar(); 1640 } else { 1641 return null; 1642 } 1643 } 1644 1645 /** Returns the variable (if any) that represents the size of this sequence. */ 1646 public @Nullable VarInfo sequenceSize() { 1647 if (sequenceSize != null) { 1648 return sequenceSize; 1649 } 1650 assert rep_type.isArray(); 1651 // we know the size follows the variable itself in the list 1652 VarInfo[] vis = ppt.var_infos; 1653 for (int i = varinfo_index + 1; i < vis.length; i++) { 1654 VarInfo vi = vis[i]; 1655 if ((vi.derived instanceof SequenceLength) && (((SequenceLength) vi.derived).base == this)) { 1656 sequenceSize = vi; 1657 return sequenceSize; 1658 } 1659 } 1660 // It is possible that this VarInfo never had its size derived, 1661 // since it looked something like this.ary[].field. In this case, 1662 // we should return size(this.ary[]), since it was derived and 1663 // must be the same values. 1664 if (FileIO.new_decl_format) { 1665 VarInfo base = get_base_array(); 1666 VarInfo size = ppt.find_var_by_name("size(" + base.name() + ")"); 1667 return size; 1668 } else { 1669 VarInfoName search = this.var_info_name; // vin ok 1670 boolean pre = false; 1671 if (search instanceof VarInfoName.Prestate) { 1672 search = ((VarInfoName.Prestate) search).term; 1673 pre = true; 1674 } 1675 while (search instanceof VarInfoName.Field) { 1676 search = ((VarInfoName.Field) search).term; 1677 } 1678 if (pre) { 1679 search = search.applyPrestate(); 1680 } 1681 search = search.applySize(); 1682 VarInfo result = ppt.find_var_by_name(search.name()); 1683 if (result != null) { 1684 return result; 1685 // } else { 1686 // System.out.println("Warning: Size variable " + search + " not found."); 1687 // System.out.print("Variables: "); 1688 // for (int i = 0; i<ppt.var_infos.length; i++) { 1689 // VarInfo vi = ppt.var_infos[i]; 1690 // System.out.print(vi.name + " "); 1691 // } 1692 // System.out.println(); 1693 } 1694 } 1695 // throw new Error("Couldn't find size of " + name); 1696 return null; 1697 } 1698 1699 /** 1700 * Returns true if the type in the original program is integer. Should perhaps check 1701 * Daikon.check_program_types and behave differently depending on that. 1702 */ 1703 @Pure 1704 public boolean isIndex() { 1705 return (file_rep_type == ProglangType.INT) && type.isIndex(); 1706 } 1707 1708 /** 1709 * Returns true if this variable is an array. 1710 * 1711 * @return true if this variable is an array 1712 */ 1713 @Pure 1714 public boolean is_array() { 1715 return arr_dims > 0; 1716 } 1717 1718 /** 1719 * Returns false if this variable expression is not legal ESC syntax, except for any necessary 1720 * quantifications (subscripting). We err on the side of returning true, for now. 1721 * 1722 * @return false if this variable expression is not legal ESC syntax, except for any necessary 1723 * quantifications (subscripting) 1724 */ 1725 @Pure 1726 public boolean isValidEscExpression() { 1727 // "myList.length" is invalid 1728 if (derived instanceof SequenceLength) { 1729 SequenceLength sl = (SequenceLength) derived; 1730 if (!sl.base.type.isArray()) { 1731 // VarInfo base = sl.base; 1732 // System.out.printf("%s is not an array%n", base); 1733 // System.out.printf("type = %s%n", base.type); 1734 return false; 1735 } 1736 } 1737 1738 // "myList[]" is invalid, as is myList[foo] (when myList is a list 1739 // of some sort and not an array) 1740 if (FileIO.new_decl_format) { 1741 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 1742 if (vi.file_rep_type.isArray() && !vi.type.isArray()) { 1743 return false; 1744 } 1745 if (vi.isDerived()) { 1746 VarInfo base = vi.derived.getBase(0); 1747 if (base.file_rep_type.isArray() && !base.type.isArray()) { 1748 return false; 1749 } 1750 } 1751 } 1752 } else { 1753 for (VarInfoName next : var_info_name.inOrderTraversal()) { // vin ok 1754 if (next instanceof VarInfoName.Elements) { 1755 VarInfoName.Elements elems = (VarInfoName.Elements) next; 1756 VarInfo seq = ppt.find_var_by_name(elems.term.name()); 1757 if (!seq.type.isArray()) { 1758 return false; 1759 } 1760 } 1761 } 1762 } 1763 1764 return true; 1765 } 1766 1767 /** 1768 * Return true if invariants about this quantity are really properties of a pointer, but derived 1769 * variables can refer to properties of the thing pointed to. This distinction is important when 1770 * making logical statements about the object, because in the presence of side effects, the 1771 * pointed-to object can change even when the pointer doesn't. For instance, we might have "obj == 1772 * orig(obj)", but "obj.color != orig(obj.color)". In such a case, isPointer() would be true of 1773 * obj, and for some forms of output we'd need to translate "obj == orig(obj)" into something like 1774 * "location(obj) == location(orig(obj))". 1775 */ 1776 @Pure 1777 public boolean isPointer() { 1778 // This used to check whether the program type had a higher 1779 // dimension than the rep type, or if the rep type was integral 1780 // but the program type wasn't primitive. These rules worked 1781 // pretty well for Java, but not so well for C, where for instance 1782 // you might have rep_type = int and type = size_t. 1783 1784 return file_rep_type.isPointerFileRep(); 1785 } 1786 1787 /** 1788 * A wrapper around VarInfoName.simplify_name() that also uses VarInfo information to guess 1789 * whether "obj" should logically be treated as just the hash code of "obj", rather than the whole 1790 * object. 1791 */ 1792 public String simplifyFixup(String str) { 1793 if (isPointer()) { 1794 str = "(hash " + str + ")"; 1795 } 1796 return str; 1797 } 1798 1799 public String simplifyFixedupName() { 1800 return simplifyFixup(simplify_name()); 1801 } 1802 1803 /////////////////////////////////////////////////////////////////////////// 1804 /// Utility functions 1805 /// 1806 1807 // Where do these really belong? 1808 1809 /** 1810 * Given two variables I and J, indicate whether it is necessarily the case that i≤j or i≥j. 1811 * The variables also each have a shift, so the test can really be something like (i+1)≤(j-1). 1812 * The test is one of: 1813 * 1814 * <ul> 1815 * <li>i + i_shift ≤ j + j_shift (if test_lessequal) 1816 * <li>i + i_shift ≥ j + j_shift (if !test_lessequal) 1817 * </ul> 1818 * 1819 * This is a dynamic check, and so must not be called while Daikon is inferencing. 1820 */ 1821 public static boolean compare_vars( 1822 VarInfo vari, int vari_shift, VarInfo varj, int varj_shift, boolean test_lessequal) { 1823 1824 // System.out.printf("comparing variables %s and %s in ppt %s%n", 1825 // vari.name(), varj.name(), vari.ppt.name()); 1826 // Throwable stack = new Throwable("debug traceback"); 1827 // stack.fillInStackTrace(); 1828 // stack.printStackTrace(); 1829 1830 assert !Daikon.isInferencing; 1831 // System.out.println("compare_vars(" + vari.name + ", " + vari_shift + ", "+ varj.name + ", " + 1832 // varj_shift + ", " + (test_lessequal?"<=":">=") + ")"); 1833 if (vari == varj) { 1834 // same variable 1835 return test_lessequal ? (vari_shift <= varj_shift) : (vari_shift >= varj_shift); 1836 } 1837 // different variables 1838 boolean samePpt = (vari.ppt == varj.ppt); 1839 assert samePpt; 1840 PptSlice indices_ppt = vari.ppt.findSlice_unordered(vari, varj); 1841 if (indices_ppt == null) { 1842 return false; 1843 } 1844 1845 boolean vari_is_var1 = (vari == indices_ppt.var_infos[0]); 1846 LinearBinary lb = LinearBinary.find(indices_ppt); 1847 long index_vari_minus_seq = -2222; // valid only if lb != null 1848 if (lb != null) { 1849 if (!lb.enoughSamples()) { 1850 lb = null; 1851 } else if (lb.core.a != 1 || lb.core.b != -1) { 1852 // Do not attempt to deal with anything but y=x+b, aka x-y+b=0. 1853 lb = null; 1854 } else { 1855 // System.out.println("justified LinearBinary: " + lb.format()); 1856 // lb.b is var2()-var1(). 1857 1858 // a is 1 or -1, and the values are integers, so c must be an integer 1859 long c_int = (long) lb.core.c; 1860 assert lb.core.c == c_int; 1861 index_vari_minus_seq = (vari_is_var1 ? -c_int : c_int); 1862 index_vari_minus_seq += vari_shift - varj_shift; 1863 } 1864 } 1865 1866 boolean vari_lt = false; 1867 boolean vari_le = false; 1868 boolean vari_gt = false; 1869 boolean vari_ge = false; 1870 { 1871 IntLessEqual ile = IntLessEqual.find(indices_ppt); 1872 IntLessThan ilt = IntLessThan.find(indices_ppt); 1873 IntGreaterEqual ige = IntGreaterEqual.find(indices_ppt); 1874 IntGreaterThan igt = IntGreaterThan.find(indices_ppt); 1875 if (ile != null && !ile.enoughSamples()) { 1876 ile = null; 1877 } 1878 if (ilt != null && !ilt.enoughSamples()) { 1879 ilt = null; 1880 } 1881 if (ige != null && !ige.enoughSamples()) { 1882 ige = null; 1883 } 1884 if (igt != null && !igt.enoughSamples()) { 1885 igt = null; 1886 } 1887 1888 if (vari_is_var1) { 1889 vari_lt = ilt != null; 1890 vari_le = ile != null; 1891 vari_gt = igt != null; 1892 vari_ge = ige != null; 1893 } else { 1894 vari_lt = igt != null; 1895 vari_le = ige != null; 1896 vari_gt = ilt != null; 1897 vari_ge = ile != null; 1898 } 1899 } 1900 1901 // System.out.println("test_lessequal=" + test_lessequal 1902 // + ", vari_can_be_lt=" + vari_can_be_lt 1903 // + ", vari_can_be_eq=" + vari_can_be_eq 1904 // + ", vari_can_be_gt=" + vari_can_be_gt); 1905 1906 if (test_lessequal) { 1907 if (lb != null) { 1908 return (index_vari_minus_seq <= 0); 1909 } else { 1910 return ((vari_le && (vari_shift <= varj_shift)) 1911 || (vari_lt && (vari_shift - 1 <= varj_shift))); 1912 } 1913 } else { 1914 if (lb != null) { 1915 return (index_vari_minus_seq >= 0); 1916 } else { 1917 return ((vari_ge && (vari_shift >= varj_shift)) 1918 || (vari_gt && (vari_shift + 1 >= varj_shift))); 1919 } 1920 } 1921 } 1922 1923 // // takes an "orig()" var and gives a VarInfoName for a variable or 1924 // // expression in the post-state which is equal to this one. 1925 // public VarInfoName postStateEquivalent() { 1926 // return otherStateEquivalent(true); 1927 // } 1928 1929 // takes a non-"orig()" var and gives a VarInfoName for a variable 1930 // or expression in the pre-state which is equal to this one. 1931 public @Nullable VarInfoName preStateEquivalent() { 1932 return otherStateEquivalent(false); 1933 } 1934 1935 /** 1936 * Return some variable in the other state (pre-state if this is post-state, or vice versa) that 1937 * equals this one, or null if no equal variable exists. 1938 */ 1939 // This does *not* try the obvious thing of converting "foo" to 1940 // "orig(foo)"; it creates something new. I need to clarify the 1941 // documentation. 1942 public @Nullable VarInfoName otherStateEquivalent(boolean post) { 1943 1944 assert !FileIO.new_decl_format; 1945 1946 // Below is equivalent to: 1947 // assert post == isPrestate(); 1948 if (post != isPrestate()) { 1949 throw new Error( 1950 "Shouldn't happen (should it?): " 1951 + (post ? "post" : "pre") 1952 + "StateEquivalent(" 1953 + name() 1954 + ")"); 1955 } 1956 1957 { 1958 List<LinearBinary> lbs = LinearBinary.findAll(this); 1959 for (LinearBinary lb : lbs) { 1960 if (this.equals(lb.var2()) && (post != lb.var1().isPrestate())) { 1961 1962 // a * v1 + b * this + c = 0 or this == (-a/b) * v1 - c/b 1963 double a = lb.core.a, b = lb.core.b, c = lb.core.c; 1964 // if (a == 1) { // match } for vim 1965 if (-a / b == 1) { 1966 // this = v1 - c/b 1967 // int add = (int) b; 1968 int add = (int) -c / (int) b; 1969 return lb.var1().var_info_name.applyAdd(add); // vin ok 1970 } 1971 } 1972 1973 if (this.equals(lb.var1()) && (post != lb.var2().isPrestate())) { 1974 // v2 = a * this + b <-- not true anymore 1975 // a * this + b * v2 + c == 0 or v2 == (-a/b) * this - c/b 1976 double a = lb.core.a, b = lb.core.b, c = lb.core.c; 1977 // if (a == 1) { // match } for vim 1978 if (-a / b == 1) { 1979 // this = v2 + c/b 1980 // int add = - ((int) b); 1981 int add = (int) c / (int) b; 1982 return lb.var2().var_info_name.applyAdd(add); // vin ok 1983 } 1984 } 1985 } 1986 1987 // Should also try other exact invariants... 1988 } 1989 1990 // Can't find post-state equivalent. 1991 return null; 1992 } 1993 1994 /** Check if two VarInfos are truly (non guarded) equal to each other right now. */ 1995 @Pure 1996 public boolean isEqualTo(VarInfo other) { 1997 assert equalitySet != null; 1998 return this.equalitySet == other.equalitySet; 1999 } 2000 2001 /** Debug tracer. */ 2002 private static final Logger debug = Logger.getLogger("daikon.VarInfo"); 2003 2004 /** Debug tracer for simplifying expressions. */ 2005 private static final Logger debugSimplifyExpression = 2006 Logger.getLogger("daikon.VarInfo.simplifyExpression"); 2007 2008 /** Enable assertions that would otherwise reduce run time performance. */ 2009 private static final Logger debugEnableAssertions = 2010 Logger.getLogger("daikon.VarInfo.enableAssertions"); 2011 2012 // This is problematic because it also enables some debugging output. 2013 // I need something that only enables assertions. 2014 // Slightly gross implementation, using a logger; but the command-line 2015 // options processing code already exists for it: 2016 // --dbg daikon.VarInfo 2017 public static boolean assertionsEnabled() { 2018 return debugEnableAssertions.isLoggable(Level.FINE); 2019 } 2020 2021 /** 2022 * Change the name of this VarInfo by side effect into a more simplified form, which is easier to 2023 * read on display. Don't call this during processing, as I think the system assumes that names 2024 * don't change over time (?). 2025 */ 2026 public void simplify_expression() { 2027 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2028 debugSimplifyExpression.fine("** Simplify: " + name()); 2029 } 2030 2031 if (!isDerived()) { 2032 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2033 debugSimplifyExpression.fine("** Punt because not derived variable"); 2034 } 2035 return; 2036 } 2037 2038 // find a ...post(...)... expression to simplify 2039 VarInfoName.Poststate postexpr = null; 2040 for (VarInfoName node : new VarInfoName.InorderFlattener(var_info_name).nodes()) { // vin ok 2041 if (node instanceof VarInfoName.Poststate) { 2042 // Remove temporary var when bug is fixed. 2043 VarInfoName.Poststate tempNode = (VarInfoName.Poststate) node; 2044 postexpr = tempNode; 2045 // old code; reinstate when bug is fixed 2046 // postexpr = (VarInfoName.Poststate) node; 2047 break; 2048 } 2049 } 2050 if (postexpr == null) { 2051 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2052 debugSimplifyExpression.fine("** Punt because no post()"); 2053 } 2054 return; 2055 } 2056 2057 // if we have post(...+k) rewrite as post(...)+k 2058 if (postexpr.term instanceof VarInfoName.Add) { 2059 VarInfoName.Add add = (VarInfoName.Add) postexpr.term; 2060 VarInfoName swapped = add.term.applyPoststate().applyAdd(add.amount); 2061 var_info_name = 2062 new VarInfoName.Replacer(postexpr, swapped) 2063 .replace(var_info_name) 2064 .intern(); // vin ok // interning bugfix 2065 // start over 2066 simplify_expression(); 2067 return; 2068 } 2069 2070 // Stop now if we don't want to replace post vars with equivalent orig 2071 // vars 2072 if (!PrintInvariants.dkconfig_remove_post_vars) { 2073 return; 2074 } 2075 2076 // [[ find the ppt context for the post() term ]] (I used to 2077 // search the expression for this, but upon further reflection, 2078 // there is only one EXIT point which could possibly be associated 2079 // with this VarInfo, so "this.ppt" must be correct. 2080 PptTopLevel post_context = this.ppt; 2081 2082 // see if the contents of the post(...) have an equivalent orig() 2083 // expression. 2084 VarInfo postvar = post_context.find_var_by_name(postexpr.term.name()); 2085 if (postvar == null) { 2086 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2087 debugSimplifyExpression.fine("** Punt because no VarInfo for postvar " + postexpr.term); 2088 } 2089 return; 2090 } 2091 VarInfoName pre_expr = postvar.preStateEquivalent(); 2092 if (pre_expr != null) { 2093 // strip off any orig() so we don't get orig(a[orig(i)]) 2094 if (pre_expr instanceof VarInfoName.Prestate) { 2095 pre_expr = ((VarInfoName.Prestate) pre_expr).term; 2096 } else if (pre_expr instanceof VarInfoName.Add) { 2097 VarInfoName.Add add = (VarInfoName.Add) pre_expr; 2098 if (add.term instanceof VarInfoName.Prestate) { 2099 pre_expr = ((VarInfoName.Prestate) add.term).term.applyAdd(add.amount); 2100 } 2101 } 2102 var_info_name = 2103 new VarInfoName.Replacer(postexpr, pre_expr) 2104 .replace(var_info_name) 2105 .intern(); // vin ok // interning bugfix 2106 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2107 debugSimplifyExpression.fine("** Replaced with: " + var_info_name); // vin ok 2108 } 2109 } 2110 2111 if (debugSimplifyExpression.isLoggable(Level.FINE)) { 2112 debugSimplifyExpression.fine("** Nothing to do (no state equlivalent)"); 2113 } 2114 } 2115 2116 /** 2117 * Two variables are "compatible" if their declared types are castable and their comparabilities 2118 * are comparable. This is a reflexive relationship, because it calls 2119 * ProglangType.comparableOrSuperclassEitherWay. However, it is not transitive because it might 2120 * not hold for two children of a superclass, even though it would for each child and the 2121 * superclass. 2122 */ 2123 public boolean compatible(VarInfo var2) { 2124 VarInfo var1 = this; 2125 // Can only compare in the same ppt because otherwise 2126 // comparability info may not make sense. 2127 boolean samePpt = (var1.ppt == var2.ppt); 2128 assert samePpt; 2129 2130 if (!comparableByType(var2)) { 2131 return false; 2132 } 2133 2134 if (!Daikon.ignore_comparability && !VarComparability.comparable(var1, var2)) { 2135 return false; 2136 } 2137 2138 return true; 2139 } 2140 2141 /** 2142 * Return true if this sequence variable's element type is compatible with the scalar variable. 2143 */ 2144 public boolean eltsCompatible(VarInfo sclvar) { 2145 VarInfo seqvar = this; 2146 if (Daikon.check_program_types) { 2147 ProglangType elttype = seqvar.type.elementType(); 2148 if (!elttype.comparableOrSuperclassEitherWay(sclvar.type)) { 2149 // System.out.printf("eltsCompatible: bad program types; elttype(%s)=%s, scltype(%s)=%s%n", 2150 // seqvar, elttype, sclvar, sclvar.type); 2151 return false; 2152 } 2153 } 2154 if (!Daikon.ignore_comparability) { 2155 if (!VarComparability.comparable(seqvar.comparability.elementType(), sclvar.comparability)) { 2156 // System.out.printf("eltsCompatible: eltcomp(%s;%s)=%s, sclcomp(%s)=%s%n", 2157 // seqvar, seqvar.comparability.elementType(), 2158 // seqvar.comparability.elementType(), sclvar, sclvar.comparability); 2159 return false; 2160 } 2161 } 2162 return true; 2163 } 2164 2165 /** 2166 * Without using comparability info, check that this is comparable to var2. This is a reflexive 2167 * relationship, because it calls ProglangType.comparableOrSuperclassEitherWay. However, it is not 2168 * transitive because it might not hold for two children of a superclass, even though it would for 2169 * each child and the superclass. Does not check comparabilities. 2170 */ 2171 public boolean comparableByType(VarInfo var2) { 2172 VarInfo var1 = this; 2173 2174 // System.out.printf("comparableByType(%s, %s)%n", var1, var2); 2175 2176 // the check ensures that a scalar or string and elements of an array of the same type are 2177 // labelled as comparable 2178 if (Daikon.check_program_types 2179 && (var1.file_rep_type.isArray() && !var2.file_rep_type.isArray())) { 2180 2181 // System.out.printf("comparableByType: case 1 %s%n", var1.eltsCompatible(var2)); 2182 if (var1.eltsCompatible(var2)) { 2183 return true; 2184 } 2185 } 2186 2187 // the check ensures that a scalar or string and elements of an array of the same type are 2188 // labelled as comparable 2189 if (Daikon.check_program_types 2190 && (!var1.file_rep_type.isArray() && var2.file_rep_type.isArray())) { 2191 2192 // System.out.printf("comparableByType: case 2 %s%n", var2.eltsCompatible(var1)); 2193 if (var2.eltsCompatible(var1)) { 2194 return true; 2195 } 2196 } 2197 2198 if (Daikon.check_program_types && (var1.file_rep_type != var2.file_rep_type)) { 2199 // System.out.printf("comparableByType: case 4 return false%n"); 2200 return false; 2201 } 2202 2203 // If the file rep types match then the variables are comparable unless 2204 // their dimensions are different. 2205 if (!dkconfig_declared_type_comparability) { 2206 if (var1.type.dimensions() != var2.type.dimensions()) { 2207 // debug_print_once ("types %s and %s are not comparable", 2208 // var1.type, var2.type); 2209 return false; 2210 } 2211 return true; 2212 } 2213 2214 if (Daikon.check_program_types && !var1.type.comparableOrSuperclassEitherWay(var2.type)) { 2215 // debug_print_once ("types %s and %s are not comparable", 2216 // var1.type, var2.type); 2217 return false; 2218 } 2219 // debug_print_once ("types %s and %s are comparable", 2220 // var1.type, var2.type); 2221 2222 // System.out.printf("comparableByType: fallthough return true%n"); 2223 return true; 2224 } 2225 2226 /** 2227 * Without using comparability info, check that this is comparable to var2. This is a reflexive 2228 * and transitive relationship. Does not check comparabilities. 2229 * 2230 * @param var2 the variable to test comparability with 2231 * @return true if this is comparable to var2 2232 */ 2233 public boolean comparableNWay(VarInfo var2) { 2234 VarInfo var1 = this; 2235 if (Daikon.check_program_types && !var1.type.comparableOrSuperclassOf(var2.type)) { 2236 return false; 2237 } 2238 if (Daikon.check_program_types && !var2.type.comparableOrSuperclassOf(var1.type)) { 2239 return false; 2240 } 2241 if (Daikon.check_program_types && (var1.file_rep_type != var2.file_rep_type)) { 2242 return false; 2243 } 2244 return true; 2245 } 2246 2247 /** Return true if this sequence's first index type is compatible with the scalar variable. */ 2248 public boolean indexCompatible(VarInfo sclvar) { 2249 VarInfo seqvar = this; 2250 if (Daikon.check_program_types) { 2251 if (!seqvar.is_array() || !sclvar.isIndex()) { 2252 return false; 2253 } 2254 } 2255 if (!Daikon.ignore_comparability) { 2256 if (!VarComparability.comparable(seqvar.comparability.indexType(0), sclvar.comparability)) { 2257 return false; 2258 } 2259 } 2260 return true; 2261 } 2262 2263 // Interning is lost when an object is serialized and deserialized. 2264 // Manually re-intern any interned fields upon deserialization. 2265 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 2266 in.defaultReadObject(); 2267 var_info_name = var_info_name.intern(); // vin ok 2268 str_name = str_name.intern(); 2269 2270 for (VarParent parent : parents) { 2271 parent.parent_ppt = parent.parent_ppt.intern(); 2272 if (parent.parent_variable != null) { 2273 parent.parent_variable = parent.parent_variable.intern(); 2274 } 2275 } 2276 2277 if (relative_name != null) { 2278 relative_name = relative_name.intern(); 2279 } 2280 } 2281 2282 // /** 2283 // * It is <b>not</b> safe in general to compare based on VarInfoName 2284 // * alone, because it is possible for two different program points to have 2285 // * unrelated variables of the same name. 2286 // */ 2287 // public static class LexicalComparator implements Comparator<VarInfo> { 2288 // @Pure 2289 // public int compare(VarInfo vi1, VarInfo vi2) { 2290 // VarInfoName name1 = vi1.name; 2291 // VarInfoName name2 = vi2.name; 2292 // return name1.compareTo(name2); 2293 // } 2294 // } 2295 2296 // Is this property always guaranteed to be true? It's placed in a 2297 // slice, but then might it get printed or treated as true? 2298 /** 2299 * Create a guarding predicate for this VarInfo, that is, an invariant that ensures that this 2300 * object is available for access to variables that reference it, such as fields. (The invariant 2301 * is placed in the appropriate slice.) Returns null if no guarding is needed. 2302 */ 2303 // Adding a test against null is not quite right for C programs, where *p 2304 // could be nonsensical (uninitialized or freed) even when p is non-null. 2305 // But this is a decent approximation to start with. 2306 public @Nullable Invariant createGuardingPredicate(boolean install) { 2307 // Later for the array, make sure index in bounds 2308 if (!(type.isArray() || type.isObject())) { 2309 String message = 2310 String.format("Unexpected guarding based on %s with type %s%n", name(), type); 2311 System.err.print(message); 2312 throw new Error(message); 2313 } 2314 2315 // For now associating with the variable's PptSlice 2316 PptSlice slice = ppt.get_or_instantiate_slice(this); 2317 2318 Invariant result = Invariant.find(NonZero.class, slice); 2319 2320 // Check whether the predicate already exists 2321 if (result == null) { 2322 // If it doesn't, create a "fake" invariant, which should 2323 // never be printed. Is it a good idea even to set 2324 // result.falsified to true? We know it's true because 2325 // result's children were missing. However, some forms of 2326 // filtering might remove it from slice. 2327 VarInfo[] vis = slice.var_infos; 2328 if (SingleScalar.valid_types_static(vis)) { 2329 result = NonZero.get_proto().instantiate(slice); 2330 } else if (SingleScalarSequence.valid_types_static(vis)) { 2331 result = EltNonZero.get_proto().instantiate(slice); 2332 } else { 2333 throw new Error("Bad VarInfos"); 2334 } 2335 if (result == null) { 2336 // Return null if NonZero invariant is not applicable to this variable. 2337 return null; 2338 } 2339 result.isGuardingPredicate = true; 2340 // System.out.printf("Created a guarding predicate: %s at %s%n", result, slice); 2341 // new Error().printStackTrace(System.out); 2342 if (install) { 2343 slice.addInvariant(result); 2344 } 2345 } 2346 2347 return result; 2348 } 2349 2350 static Set<String> addVarMessages = new HashSet<>(); 2351 2352 /** 2353 * Finds a list of variables that must be guarded for this VarInfo to be guaranteed to not be 2354 * missing. This list never includes "this", as it can never be null. The variables are returned 2355 * in the order in which their guarding prefixes are supposed to print. 2356 * 2357 * <p>For example, if this VarInfo is "a.b.c", then the guarding list consists of the variables 2358 * "a" and "a.b". If "a" is null or "a.b" is null, then "a.b.c" is missing (does not exist). 2359 */ 2360 public List<VarInfo> getGuardingList() { 2361 2362 /** 2363 * The list returned by this visitor always includes the argument itself (if it is testable 2364 * against null; for example, derived variables are not). If the caller does not want the 2365 * argument to be in the list, the caller must must remove the argument. 2366 */ 2367 // Inner class because it uses the "ppt" variable. 2368 // Basic structure of each visitor: 2369 // If the argument should be guarded, recurse. 2370 // If the argument is testable against null, add it to the result. 2371 // Recursing first arranges that the argument goes at the end, 2372 // after its subparts that need to be guarded. 2373 2374 class GuardingVisitor implements Visitor<List<VarInfo>> { 2375 boolean inPre = false; 2376 2377 private boolean shouldBeGuarded(VarInfoName viname) { 2378 // Not "shouldBeGuarded(ppt.findVar(viname))" because that 2379 // unnecessarily computes ppt.findVar(viname), if 2380 // dkconfig_guardNulls is "always". 2381 // System.out.printf("viname = %s, applyPreMaybe=%s, findvar=%s%n", 2382 // viname, applyPreMaybe(viname), 2383 // ppt.findVar(applyPreMaybe(viname))); 2384 if (Daikon.dkconfig_guardNulls == "always") { // interned 2385 return true; 2386 } 2387 if (Daikon.dkconfig_guardNulls == "missing") { // interned 2388 VarInfo vi = ppt.find_var_by_name(applyPreMaybe(viname).name()); 2389 // Don't guard variables that don't exist. This happends when 2390 // we incorrectly parse static variable package names as field names 2391 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2392 Invariant.debugGuarding.fine( 2393 String.format( 2394 "shouldBeGuarded(%s) [%s] %s %b", 2395 viname, applyPreMaybe(viname), vi, ((vi == null) ? false : vi.canBeMissing))); 2396 } 2397 if (vi == null) { 2398 return false; 2399 } 2400 return vi.canBeMissing; 2401 } 2402 return false; 2403 } 2404 2405 @Override 2406 public List<VarInfo> visitSimple(Simple o) { 2407 List<VarInfo> result = new ArrayList<>(); 2408 // No recursion: no children 2409 if (!o.name.equals("this")) { 2410 result = addVar(result, o); 2411 } 2412 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2413 Invariant.debugGuarding.fine(String.format("visitSimple(%s) => %s", o.name(), result)); 2414 } 2415 return result; 2416 } 2417 2418 @Override 2419 public List<VarInfo> visitSizeOf(SizeOf o) { 2420 List<VarInfo> result = new ArrayList<>(); 2421 if (shouldBeGuarded(o)) { 2422 result.addAll(o.sequence.accept(this)); 2423 } 2424 // No call to addVar: derived variable 2425 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2426 Invariant.debugGuarding.fine(String.format("visitSizeOf(%s) => %s", o.name(), result)); 2427 } 2428 return result; 2429 } 2430 2431 @Override 2432 public List<VarInfo> visitFunctionOf(FunctionOf o) { 2433 List<VarInfo> result = new ArrayList<>(); 2434 if (shouldBeGuarded(o)) { 2435 result.addAll(o.argument.accept(this)); 2436 } 2437 result = addVar(result, o); 2438 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2439 Invariant.debugGuarding.fine( 2440 String.format("visitFunctionOf(%s) => %s", o.name(), result)); 2441 } 2442 return result; 2443 } 2444 2445 @Override 2446 public List<VarInfo> visitFunctionOfN(FunctionOfN o) { 2447 List<VarInfo> result = new ArrayList<>(); 2448 if (shouldBeGuarded(o)) { 2449 for (VarInfoName arg : o.args) { 2450 result.addAll(arg.accept(this)); 2451 } 2452 } 2453 result = addVar(result, o); 2454 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2455 Invariant.debugGuarding.fine( 2456 String.format("visitFunctionOfN(%s) => %s", o.name(), result)); 2457 } 2458 return result; 2459 } 2460 2461 @Override 2462 public List<VarInfo> visitField(Field o) { 2463 List<VarInfo> result = new ArrayList<>(); 2464 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2465 Invariant.debugGuarding.fine( 2466 String.format("visitField: shouldBeGuarded(%s) => %s", o.name(), shouldBeGuarded(o))); 2467 } 2468 if (shouldBeGuarded(o)) { 2469 result.addAll(o.term.accept(this)); 2470 } 2471 result = addVar(result, o); 2472 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2473 Invariant.debugGuarding.fine(String.format("visitField(%s) => %s", o.name(), result)); 2474 } 2475 return result; 2476 } 2477 2478 @Override 2479 public List<VarInfo> visitTypeOf(TypeOf o) { 2480 List<VarInfo> result = new ArrayList<>(); 2481 if (shouldBeGuarded(o)) { 2482 result.addAll(o.term.accept(this)); 2483 } 2484 // No call to addVar: derived variable 2485 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2486 Invariant.debugGuarding.fine(String.format("visitTypeOf(%s) => %s", o.name(), result)); 2487 } 2488 return result; 2489 } 2490 2491 @Override 2492 public List<VarInfo> visitPrestate(Prestate o) { 2493 assert inPre == false; 2494 inPre = true; 2495 List<VarInfo> result = o.term.accept(this); 2496 assert inPre == true; 2497 inPre = false; 2498 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2499 Invariant.debugGuarding.fine(String.format("visitPrestate(%s) => %s", o.name(), result)); 2500 } 2501 return result; 2502 } 2503 2504 @Override 2505 public List<VarInfo> visitPoststate(Poststate o) { 2506 assert inPre == true; 2507 inPre = false; 2508 List<VarInfo> result = o.term.accept(this); 2509 assert inPre == false; 2510 inPre = true; 2511 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2512 Invariant.debugGuarding.fine(String.format("visitPostState(%s) => %s", o.name(), result)); 2513 } 2514 return result; 2515 } 2516 2517 @Override 2518 public List<VarInfo> visitAdd(Add o) { 2519 List<VarInfo> result = new ArrayList<>(); 2520 if (shouldBeGuarded(o)) { 2521 result.addAll(o.term.accept(this)); 2522 } 2523 // No call to addVar: derived variable 2524 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2525 Invariant.debugGuarding.fine(String.format("visitAdd(%s) => %s", o.name(), result)); 2526 } 2527 return result; 2528 } 2529 2530 @Override 2531 public List<VarInfo> visitElements(Elements o) { 2532 List<VarInfo> result = new ArrayList<>(); 2533 if (shouldBeGuarded(o)) { 2534 result.addAll(o.term.accept(this)); 2535 } 2536 // No call to addVar: derived variable 2537 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2538 Invariant.debugGuarding.fine(String.format("visitElements(%s) => %s", o.name(), result)); 2539 } 2540 return result; 2541 } 2542 2543 @Override 2544 public List<VarInfo> visitSubscript(Subscript o) { 2545 List<VarInfo> result = new ArrayList<>(); 2546 if (shouldBeGuarded(o)) { 2547 result.addAll(o.sequence.accept(this)); 2548 result.addAll(o.index.accept(this)); 2549 } 2550 result = addVar(result, o); 2551 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2552 Invariant.debugGuarding.fine(String.format("visitSubscript(%s) => %s", o.name(), result)); 2553 } 2554 return result; 2555 } 2556 2557 @Override 2558 public List<VarInfo> visitSlice(Slice o) { 2559 List<VarInfo> result = new ArrayList<>(); 2560 if (shouldBeGuarded(o)) { 2561 result.addAll(o.sequence.accept(this)); 2562 if (o.i != null) { 2563 result.addAll(o.i.accept(this)); 2564 } 2565 if (o.j != null) { 2566 result.addAll(o.j.accept(this)); 2567 } 2568 } 2569 // No call to addVar: derived variable 2570 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2571 Invariant.debugGuarding.fine(String.format("visitSlice(%s) => %s", o.name(), result)); 2572 } 2573 return result; 2574 } 2575 2576 // Convert to prestate variable name if appropriate 2577 VarInfoName applyPreMaybe(VarInfoName vin) { 2578 if (inPre) { 2579 return vin.applyPrestate(); 2580 } else { 2581 return vin; 2582 } 2583 } 2584 2585 private List<VarInfo> addVar(List<VarInfo> result, VarInfoName vin) { 2586 VarInfo vi = ppt.find_var_by_name(applyPreMaybe(vin).name()); 2587 // vi could be null because some variable's prefix is not a 2588 // variable. Example: for static variable "Class.staticvar", 2589 // "Class" is not a varible, even though for variable "a.b.c", 2590 // typically "a" and "a.b" are also variables. 2591 if (vi == null) { 2592 // String message = 2593 // String.format( 2594 // "getGuardingList(%s, %s): did not find variable %s [inpre=%s]", 2595 // name(), ppt.name(), vin.name(), inPre); 2596 // // Only print the error message at most once per variable. 2597 // if (addVarMessages.add(vin.name())) { 2598 // // For now, don't print at all: it's generally innocuous 2599 // // (class prefix of a static variable). 2600 // // System.err.println(message); 2601 // } 2602 // // System.out.println("vars: " + ppt.varNames()); 2603 // // System.out.flush(); 2604 // // throw new Error(String.format(message)); 2605 return result; 2606 } else { 2607 return addVarInfo(result, vi); 2608 } 2609 } 2610 2611 /** 2612 * Add the given variable to the result list. Does nothing if the variable is of primitive 2613 * type. 2614 */ 2615 // Should this operate by side effect on a global variable? 2616 // (Then what is the type of the visitor; what does everything return?) 2617 private List<VarInfo> addVarInfo(List<VarInfo> result, VarInfo vi) { 2618 assert vi != null; 2619 assert !vi.isDerived() || vi.isDerived() : "addVar on derived variable: " + vi; 2620 // Don't guard primitives 2621 if ( // TODO: ***** make changes here ***** 2622 // vi.file_rep_type.isScalar() && 2623 !vi.type.isScalar() 2624 // (vi.type.isArray() || vi.type.isObject()) 2625 ) { 2626 result.add(vi); 2627 } else { 2628 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2629 Invariant.debugGuarding.fine( 2630 String.format( 2631 "addVarInfo did not add %s: %s (%s) %s (%s)", 2632 vi, 2633 vi.file_rep_type.isScalar(), 2634 vi.file_rep_type, 2635 vi.type.isScalar(), 2636 vi.type)); 2637 } 2638 } 2639 if (Invariant.debugGuarding.isLoggable(Level.FINE)) { 2640 Invariant.debugGuarding.fine(String.format("addVarInfo(%s) => %s", vi, result)); 2641 } 2642 return result; 2643 } 2644 } // end of class GuardingVisitor 2645 2646 if (!FileIO.new_decl_format) { 2647 List<VarInfo> result = var_info_name.accept(new GuardingVisitor()); // vin ok 2648 result.remove(ppt.find_var_by_name(var_info_name.name())); // vin ok 2649 assert !ArraysPlume.anyNull(result); 2650 return result; 2651 } else { // new format 2652 List<VarInfo> result = new ArrayList<>(); 2653 2654 if (Daikon.dkconfig_guardNulls == "never") { // interned 2655 return result; 2656 } 2657 2658 // If this is never missing, nothing to guard 2659 if ((Daikon.dkconfig_guardNulls == "missing") // interned 2660 && !canBeMissing) { 2661 return result; 2662 } 2663 2664 // Create a list of variables to be guarded from the list of all 2665 // enclosing variables. 2666 for (VarInfo vi : get_all_enclosing_vars()) { 2667 // if (var_flags.contains(VarFlags.CLASSNAME)) { 2668 // System.err.printf( 2669 // "%s file_rep_type = %s, canbemissing = %b%n", vi, vi.file_rep_type, 2670 // vi.canBeMissing); 2671 // } 2672 if (!vi.file_rep_type.isHashcode()) { 2673 continue; 2674 } 2675 result.add(0, vi); 2676 if ((Daikon.dkconfig_guardNulls == "missing") // interned 2677 && !vi.canBeMissing) { 2678 break; 2679 } 2680 } 2681 return result; 2682 } 2683 } 2684 2685 /** 2686 * Returns a list of all of the variables that enclose this one. If this is derived, this includes 2687 * all of the enclosing variables of all of the bases. 2688 */ 2689 public List<VarInfo> get_all_enclosing_vars() { 2690 List<VarInfo> result = new ArrayList<>(); 2691 if (isDerived()) { 2692 for (VarInfo base : derived.getBases()) { 2693 result.addAll(base.get_all_enclosing_vars()); 2694 } 2695 } else { // not derived 2696 for (VarInfo vi = this.enclosing_var; vi != null; vi = vi.enclosing_var) { 2697 result.add(vi); 2698 } 2699 } 2700 return result; 2701 } 2702 2703 /** Compare names by index. */ 2704 public static final class IndexComparator implements Comparator<VarInfo>, Serializable { 2705 // This needs to be serializable because Equality invariants keep 2706 // a TreeSet of variables sorted by theInstance. 2707 2708 // We are Serializable, so we specify a version to allow changes to 2709 // method signatures without breaking serialization. If you add or 2710 // remove fields, you should change this number to the current date. 2711 static final long serialVersionUID = 20050923L; 2712 2713 private IndexComparator() {} 2714 2715 @Pure 2716 @Override 2717 public int compare(VarInfo vi1, VarInfo vi2) { 2718 if (vi1.varinfo_index < vi2.varinfo_index) { 2719 return -1; 2720 } else if (vi1.varinfo_index == vi2.varinfo_index) { 2721 return 0; 2722 } else { 2723 return 1; 2724 } 2725 } 2726 2727 public static IndexComparator getInstance() { 2728 return theInstance; 2729 } 2730 2731 public static final IndexComparator theInstance = new IndexComparator(); 2732 } 2733 2734 /** 2735 * Looks for an OBJECT ppt that corresponds to the type of this variable. Returns null if such a 2736 * point is not found. 2737 * 2738 * @param all_ppts map of all program points 2739 */ 2740 public @Nullable PptTopLevel find_object_ppt(PptMap all_ppts) { 2741 2742 // Arrays don't have types 2743 if (is_array()) { 2744 return null; 2745 } 2746 2747 // build the name of the object ppt based on the variable type 2748 String type_str = type.base().replaceFirst("\\$", "."); 2749 PptName objname = new PptName(type_str, null, FileIO.object_suffix); 2750 return all_ppts.get(objname); 2751 } 2752 2753 /** 2754 * Class used to contain a pair of VarInfos and their sample count. Currently used for equality 2755 * set merging as a way to store pairs of equal variables. The variable with the smaller index is 2756 * always stored first. 2757 * 2758 * <p>Pairs are equal if both of their VarInfos are identical. Note that the content of the 2759 * VarInfos are not compared, only their pointer values. 2760 */ 2761 public static class Pair { 2762 2763 public VarInfo v1; 2764 public VarInfo v2; 2765 public int samples; 2766 2767 public Pair(VarInfo v1, VarInfo v2, int samples) { 2768 if (v1.varinfo_index < v2.varinfo_index) { 2769 this.v1 = v1; 2770 this.v2 = v2; 2771 } else { 2772 this.v1 = v2; 2773 this.v2 = v1; 2774 } 2775 this.samples = samples; 2776 } 2777 2778 @EnsuresNonNullIf(result = true, expression = "#1") 2779 @Pure 2780 @Override 2781 public boolean equals(@GuardSatisfied Pair this, @GuardSatisfied @Nullable Object obj) { 2782 if (!(obj instanceof Pair)) { 2783 return false; 2784 } 2785 2786 Pair o = (Pair) obj; 2787 return (o.v1 == v1) && (o.v2 == v2); 2788 } 2789 2790 @Pure 2791 @Override 2792 public int hashCode(@GuardSatisfied Pair this) { 2793 return v1.hashCode() + v2.hashCode(); 2794 } 2795 2796 @SideEffectFree 2797 @Override 2798 public String toString(@GuardSatisfied Pair this) { 2799 return v1.name() + " = " + v2.name(); 2800 } 2801 } 2802 2803 /** Return the set of values that have been seen so far for this variable. */ 2804 public ValueSet get_value_set() { 2805 2806 // Static constants don't have value sets, so we must make one 2807 if (is_static_constant) { 2808 ValueSet vs = ValueSet.factory(this); 2809 assert static_constant_value != null 2810 : "@AssumeAssertion(nullness): dependent: is_static_constant"; 2811 vs.add(static_constant_value); 2812 return vs; 2813 } 2814 2815 return ppt.value_sets[value_index]; 2816 } 2817 2818 public String get_value_info() { 2819 return name() + "- " + get_value_set().repr_short(); 2820 } 2821 2822 /** 2823 * Returns the number of elements in the variable's equality set. Returns 1 if the equality 2824 * optimization is turned off. 2825 */ 2826 public int get_equalitySet_size() { 2827 if (equalitySet == null) { 2828 return 1; 2829 } else { 2830 return equalitySet.size(); 2831 } 2832 } 2833 2834 /** 2835 * Returns the vars_info in the variable's equality set. Returns a set with just itself if the 2836 * equality optimization is turned off. 2837 */ 2838 public Set<VarInfo> get_equalitySet_vars() { 2839 if (equalitySet == null) { 2840 HashSet<VarInfo> set = new HashSet<>(); 2841 set.add(this); 2842 return set; 2843 } else { 2844 return equalitySet.getVars(); 2845 } 2846 } 2847 2848 /** 2849 * Returns the leader in the variable's equality set. Returns itself if the equality optimization 2850 * is turned off. 2851 */ 2852 public VarInfo get_equalitySet_leader() { 2853 // if (equalitySet == null && VarInfo.use_equality_optimization == false) { // match } for vim 2854 if (equalitySet == null) { 2855 return this; 2856 } else { 2857 return equalitySet.leader(); 2858 } 2859 } 2860 2861 private static Set<String> out_strings = new LinkedHashSet<>(); 2862 2863 /** If the message is new print it, otherwise discard it. */ 2864 @FormatMethod 2865 static void debug_print_once(String format, @Nullable Object... args) { 2866 String msg = String.format(format, args); 2867 if (!out_strings.contains(msg)) { 2868 System.out.println(msg); 2869 out_strings.add(msg); 2870 } 2871 } 2872 2873 /** Returns whether or not this variable is a parameter. */ 2874 @Pure 2875 public boolean isParam() { 2876 if (FileIO.new_decl_format) { 2877 return var_flags.contains(VarFlags.IS_PARAM); 2878 } else { 2879 return aux.isParam(); // VIN 2880 } 2881 } 2882 2883 /** Set this variable as a parameter. */ 2884 public void set_is_param() { 2885 // System.out.printf("setting is_param for %s %n", name()); 2886 if (FileIO.new_decl_format) { 2887 var_flags.add(VarFlags.IS_PARAM); 2888 } 2889 aux = aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.TRUE); // VIN 2890 } 2891 2892 /** Set whether or not this variable is a parameter. */ 2893 public void set_is_param(boolean set) { 2894 if (set) { 2895 set_is_param(); 2896 } else { 2897 if (FileIO.new_decl_format) { 2898 var_flags.remove(VarFlags.IS_PARAM); 2899 } 2900 aux = aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.FALSE); // VIN 2901 } 2902 } 2903 2904 /** 2905 * Adds a subscript (or sequence) to an array variable. This should really just just substitute 2906 * for '..', but the dots are currently removed for back compatability. 2907 */ 2908 public String apply_subscript(String subscript) { 2909 if (FileIO.new_decl_format) { 2910 assert arr_dims == 1 : "Can't apply subscript to " + name(); 2911 return name().replace("..", subscript); 2912 } else { 2913 assert name().contains("[]") : "Can't apply subscript to " + name(); 2914 return apply_subscript(name(), subscript); 2915 } 2916 } 2917 2918 /** 2919 * Adds a subscript (or subsequence) to an array name. This should really just substitute for 2920 * '..', but the dots are currently removed for back compatibility. 2921 */ 2922 public static String apply_subscript(String sequence, String subscript) { 2923 if (FileIO.new_decl_format) { 2924 return sequence.replace("[..]", "[" + subscript + "]"); 2925 } else { 2926 return sequence.replace("[]", "[" + subscript + "]"); 2927 } 2928 } 2929 2930 /** 2931 * For array variables, returns the variable that is a simple array. If this variable is a slice, 2932 * it returns the array variable that is being sliced. If this variable is a simple array itself, 2933 * returns this. 2934 */ 2935 public VarInfo get_array_var() { 2936 assert file_rep_type.isArray(); 2937 if (isDerived()) { 2938 return derived.get_array_var(); 2939 } else { 2940 return this; 2941 } 2942 } 2943 2944 /** 2945 * Returns the VarInfo that represents the base array of this array. For example, if the array is 2946 * a[].b.c, returns a[]. 2947 */ 2948 @Pure 2949 public VarInfo get_base_array() { 2950 assert file_rep_type.isArray() : this; 2951 if (FileIO.new_decl_format) { 2952 VarInfo var = this; 2953 while (var.var_kind != VarKind.ARRAY) { 2954 if (var.enclosing_var == null) { 2955 // error condition; print some debugging output before assertion failure 2956 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 2957 System.out.printf("%s %s%n", vi, vi.var_kind); 2958 } 2959 assert var.enclosing_var != null : this + " " + var; 2960 } 2961 assert var.enclosing_var != null : "@AssumeAssertion(nullness): just tested"; 2962 var = var.enclosing_var; 2963 } 2964 return var; 2965 } else { 2966 Elements elems = new ElementsFinder(var_info_name).elems(); // vin ok 2967 return ppt.find_var_by_name(elems.name()); 2968 } 2969 } 2970 2971 /** 2972 * Returns the VarInfo that represents the hashcode of the base array of this array. For example, 2973 * if the array is a[].b.c, returns a. Returns null if there is no such variable. 2974 */ 2975 @Pure 2976 public @Nullable VarInfo get_base_array_hashcode() { 2977 if (FileIO.new_decl_format) { 2978 return get_base_array().enclosing_var; 2979 } else { 2980 Elements elems = new ElementsFinder(var_info_name).elems(); // vin ok 2981 // System.out.printf("term.name() = %s%n", elems.term.name()); 2982 return ppt.find_var_by_name(elems.term.name()); 2983 } 2984 } 2985 2986 /** Returns the lower bound of the array or slice. */ 2987 public Quantify.Term get_lower_bound() { 2988 assert file_rep_type.isArray() : "var " + name() + " rep " + file_rep_type; 2989 if (isDerived()) { 2990 return derived.get_lower_bound(); 2991 } else { 2992 return new Quantify.Constant(0); 2993 } 2994 } 2995 2996 /** Returns the upper bound of the array or slice. */ 2997 public Quantify.Term get_upper_bound() { 2998 assert file_rep_type.isArray(); 2999 if (isDerived()) { 3000 return derived.get_upper_bound(); 3001 } else { 3002 return new Quantify.Length(this, -1); 3003 } 3004 } 3005 3006 /** 3007 * Returns the length of this array. The array can be an array or a list. It cannot be a slice. 3008 */ 3009 public Quantify.Term get_length() { 3010 assert file_rep_type.isArray() && !isDerived() : this; 3011 return new Quantify.Length(this, 0); 3012 } 3013 3014 /** 3015 * Updates any references to other variables that should be within this ppt by looking them up 3016 * within the ppt. Necessary if a variable is moved to a different program point or if cloned 3017 * variable is placed in a new program point (such as is done when combined exits are created). 3018 */ 3019 public void update_after_moving_to_new_ppt() { 3020 if (enclosing_var != null) { 3021 // enclosing_var exists but is in the wrong ppt; update it 3022 enclosing_var = ppt.find_var_by_name(enclosing_var.name()); 3023 assert enclosing_var != null; 3024 } 3025 } 3026 3027 /** 3028 * Temporary to let things compile now that name is private. Eventually this should be removed. 3029 */ 3030 public VarInfoName get_VarInfoName() { 3031 return var_info_name; // vin ok 3032 } 3033 3034 private static boolean isStatic(String variable, String enclosing) { 3035 return !variable.startsWith(enclosing) || variable.charAt(enclosing.length()) != '.'; 3036 } 3037 3038 // Map java objects to C# objects. 3039 private static final Map<String, String> csharp_types = new HashMap<>(); 3040 3041 static { 3042 csharp_types.put("java.lang.String", "string"); 3043 csharp_types.put("java.lang.String[]", "string[]"); 3044 csharp_types.put("java.lang.Object", "object"); 3045 csharp_types.put("java.lang.Object[]", "object[]"); 3046 csharp_types.put("boolean", "bool"); 3047 } 3048 3049 /** Transforms a Daikon type representation into a valid C# type. */ 3050 public static String fix_csharp_type_name(String type) { 3051 if (csharp_types.containsKey(type)) { 3052 return csharp_types.get(type); 3053 } else { 3054 return type; 3055 } 3056 } 3057 3058 /** 3059 * If the variable is an array, returns a valid C# 'Select' statement representing the array. For 3060 * example, this.Array[].field would become this.Array.Select(x ⇒ x.field) 3061 * 3062 * <p>If the variable is not an array, csharp_name() is returned. 3063 */ 3064 public String csharp_collection_string() { 3065 String[] split = csharp_array_split(); 3066 if (split[1].equals("")) { 3067 return split[0]; 3068 } else { 3069 return split[0] + ".Select(x => x" + split[1] + ")"; 3070 } 3071 } 3072 3073 /** 3074 * Splits an array variable into the array and field portions. For example, if the variable 3075 * this.Array[].field then 3076 * 3077 * <pre> 3078 * result[0] = this.Array[] 3079 * result[1] = field 3080 * </pre> 3081 * 3082 * If the variable is not an array then 3083 * 3084 * <pre> 3085 * result[0] = csharp_name() 3086 * result[1] = "" 3087 * </pre> 3088 * 3089 * (there is no splitting). 3090 */ 3091 public String[] csharp_array_split() { 3092 String[] results = new String[2]; 3093 3094 if (!is_array()) { 3095 results[0] = csharp_name(); 3096 results[1] = ""; 3097 return results; 3098 } 3099 3100 String fields = ""; 3101 VarInfo v = this; 3102 // Go backwards from v until we reach the array portion. 3103 while (v.var_kind != VarInfo.VarKind.ARRAY && v.enclosing_var != null) { 3104 if (v.relative_name != null) { 3105 if (v.relative_name.equals("GetType()")) { 3106 fields = "." + v.relative_name; 3107 } else { 3108 fields = "." + v.relative_name + fields; 3109 } 3110 } 3111 v = v.enclosing_var; 3112 } 3113 3114 results[0] = v.csharp_name(); 3115 results[1] = fields; 3116 return results; 3117 } 3118 3119 /** Returns the name of this variable in the specified format. */ 3120 public String name_using(OutputFormat format) { 3121 if (format == OutputFormat.DAIKON) { 3122 return name(); 3123 } 3124 if (format == OutputFormat.SIMPLIFY) { 3125 return simplify_name(); 3126 } 3127 if (format == OutputFormat.ESCJAVA) { 3128 return esc_name(); 3129 } 3130 if (format == OutputFormat.JAVA) { 3131 return java_name(); 3132 } 3133 if (format == OutputFormat.JML) { 3134 return jml_name(); 3135 } 3136 if (format == OutputFormat.DBCJAVA) { 3137 return dbc_name(); 3138 } 3139 if (format == OutputFormat.CSHARPCONTRACT) { 3140 return csharp_name(); 3141 } 3142 throw new UnsupportedOperationException("Unknown format requested: " + format); 3143 } 3144 3145 /** Returns the name of this variable as a valid C# Code Contract. */ 3146 @SideEffectFree 3147 public String csharp_name() { 3148 return csharp_name(null); 3149 } 3150 3151 /** 3152 * Returns the name of this variable as a valid C# Code Contract. 3153 * 3154 * @param index an an array index. Must be null for a non-array variable. 3155 * @return the name of this variable as a valid C# Code Contract 3156 */ 3157 @SideEffectFree 3158 public String csharp_name(@Nullable String index) { 3159 if (index != null) { 3160 assert file_rep_type.isArray(); 3161 } 3162 3163 if (postState != null) { 3164 return "Contract.OldValue(" + postState.csharp_name(index) + ")"; 3165 } 3166 3167 if (derived != null) { 3168 return derived.csharp_name(index); 3169 } 3170 3171 switch (var_kind) { 3172 case FIELD: 3173 assert relative_name != null : this; 3174 3175 if (enclosing_var != null) { 3176 if (isStatic(str_name, enclosing_var.name())) { 3177 return str_name; 3178 } 3179 return enclosing_var.csharp_name(index) + "." + relative_name; 3180 } 3181 3182 return str_name; 3183 3184 case FUNCTION: 3185 if (var_flags.contains(VarFlags.TO_STRING)) { 3186 return enclosing_var.csharp_name(index); 3187 } 3188 3189 if (var_flags.contains(VarFlags.CLASSNAME)) { 3190 if (arr_dims > 0) { 3191 return csharp_collection_string(); 3192 } else { 3193 return enclosing_var.csharp_name(index) + ".GetType()"; 3194 } 3195 } 3196 3197 if (enclosing_var != null) { 3198 3199 if (isStatic(str_name, enclosing_var.name())) { 3200 String qualifiedName = str_name.substring(0, str_name.indexOf("(")); 3201 return qualifiedName + "(" + enclosing_var.csharp_name(index) + ")"; 3202 } else if (var_flags.contains(VarFlags.IS_PROPERTY)) { 3203 return enclosing_var.csharp_name(index) + "." + relative_name; 3204 } else { 3205 return enclosing_var.csharp_name(index) + "." + relative_name + "()"; 3206 } 3207 } else { 3208 return str_name; 3209 } 3210 3211 case ARRAY: 3212 if (index == null) { 3213 return enclosing_var.csharp_name(null); 3214 } 3215 return enclosing_var.csharp_name(null) + "[" + index + "]"; 3216 3217 case VARIABLE: 3218 assert enclosing_var == null; 3219 return str_name; 3220 3221 case RETURN: 3222 return "Contract.Result<" + fix_csharp_type_name(type.toString()) + ">()"; 3223 3224 default: 3225 throw new Error("can't drop through switch statement."); 3226 } 3227 } 3228 3229 /** Returns the name in Java format. This is the same as JML. */ 3230 public String java_name() { 3231 if (!FileIO.new_decl_format) { 3232 return var_info_name.java_name(this); // vin ok 3233 } 3234 3235 return jml_name(); 3236 } 3237 3238 /** Returns the name in DBC format. This is the same as JML. */ 3239 public String dbc_name() { 3240 if (!FileIO.new_decl_format) { 3241 return var_info_name.dbc_name(this); // vin ok 3242 } 3243 3244 return jml_name(); 3245 } 3246 3247 /** Returns the name of this variable in ESC format. */ 3248 @SideEffectFree 3249 public String esc_name() { 3250 if (!FileIO.new_decl_format) { 3251 return var_info_name.esc_name(); // vin ok 3252 } 3253 3254 return esc_name(null); 3255 } 3256 3257 /** 3258 * Returns the name of this variable in ESC format. If an index is specified, it is used as an 3259 * array index. It is an error to specify an index on a non-array variable. 3260 */ 3261 @SideEffectFree 3262 public String esc_name(@Nullable String index) { 3263 3264 // System.out.printf("esc_name for %s, flags %s, enclosing-var %s " 3265 // + " poststate %s index %s rname %s ppt %s%n", str_name, 3266 // var_flags, enclosing_var, postState, index, 3267 // relative_name, ppt.name()); 3268 if (index != null) { 3269 assert file_rep_type.isArray(); 3270 } 3271 3272 // If this is an orig variable, use the post version to generate the name 3273 if (postState != null) { 3274 return "\\old(" + postState.esc_name(index) + ")"; 3275 } 3276 3277 // If this is a derived variable, the derivations builds the name 3278 if (derived != null) { 3279 return derived.esc_name(index); 3280 } 3281 3282 // Build the name by processing back through all of the enclosing variables 3283 switch (var_kind) { 3284 case FIELD: 3285 assert relative_name != null : this; 3286 if (enclosing_var != null) { 3287 return enclosing_var.esc_name(index) + "." + relative_name; 3288 } 3289 return str_name; 3290 case FUNCTION: 3291 // function_args assert function_args == null : "function args not implemented"; 3292 if (var_flags.contains(VarFlags.CLASSNAME)) { 3293 return ("\\typeof(" + enclosing_var.esc_name(index) + ")"); 3294 } 3295 if (var_flags.contains(VarFlags.TO_STRING)) { 3296 return enclosing_var.esc_name(index) + ".toString"; 3297 } 3298 if (enclosing_var != null) { 3299 return enclosing_var.esc_name(index) + "." + relative_name + "()"; 3300 } 3301 return str_name; 3302 case ARRAY: 3303 if (index == null) { 3304 return enclosing_var.esc_name(null) + "[]"; 3305 } 3306 return enclosing_var.esc_name(null) + "[" + index + "]"; 3307 case VARIABLE: 3308 assert enclosing_var == null; 3309 return str_name; 3310 case RETURN: 3311 return "\\result"; 3312 default: 3313 throw new Error("can't drop through switch statement"); 3314 } 3315 } 3316 3317 /** Returns the name of this variable in JML format. */ 3318 @SideEffectFree 3319 public String jml_name() { 3320 if (!FileIO.new_decl_format) { 3321 return var_info_name.jml_name(this); // vin ok 3322 } 3323 3324 return jml_name(null); 3325 } 3326 3327 /** 3328 * Returns the name of this variable in JML format. 3329 * 3330 * @param index an array index. Must be null for a non-array variable. 3331 * @return the name of this variable in JML format 3332 */ 3333 public String jml_name(@Nullable String index) { 3334 3335 if (index != null) { 3336 assert file_rep_type.isArray(); 3337 } 3338 3339 // If this is an orig variable, use the post version to generate the name 3340 if (postState != null) { 3341 return "\\old(" + postState.jml_name(index) + ")"; 3342 } 3343 3344 // If this is a derived variable, the derivations builds the name 3345 if (derived != null) { 3346 return derived.jml_name(index); 3347 } 3348 3349 // If this is an array of fields, collect the fields into a collection 3350 if ((arr_dims > 0) && (var_kind != VarKind.ARRAY) && !var_flags.contains(VarFlags.CLASSNAME)) { 3351 String field_name = relative_name; 3352 ; 3353 VarInfo vi = this.enclosing_var; 3354 for (; vi.var_kind != VarKind.ARRAY; vi = vi.enclosing_var) { 3355 field_name = vi.relative_name + "." + field_name; 3356 } 3357 return String.format("daikon.Quant.collectObject(%s, \"%s\")", vi.jml_name(), field_name); 3358 } 3359 3360 // Build the name by processing back through all of the enclosing variables 3361 switch (var_kind) { 3362 case FIELD: 3363 assert relative_name != null : this; 3364 if (enclosing_var != null) { 3365 return enclosing_var.jml_name(index) + "." + relative_name; 3366 } 3367 return str_name; 3368 case FUNCTION: 3369 // function_args assert function_args == null : "function args not implemented"; 3370 if (var_flags.contains(VarFlags.CLASSNAME)) { 3371 if (arr_dims > 0) { 3372 return String.format("daikon.Quant.typeArray(%s)", enclosing_var.jml_name(index)); 3373 } else { 3374 return enclosing_var.jml_name(index) + DaikonVariableInfo.class_suffix; 3375 } 3376 } 3377 if (var_flags.contains(VarFlags.TO_STRING)) { 3378 return enclosing_var.jml_name(index) + ".toString()"; 3379 } 3380 if (enclosing_var != null) { 3381 return enclosing_var.jml_name(index) + "." + relative_name + "()"; 3382 } 3383 return str_name; 3384 case ARRAY: 3385 if (index == null) { 3386 return enclosing_var.jml_name(null); 3387 } 3388 return enclosing_var.jml_name(null) + "[" + index + "]"; 3389 case VARIABLE: 3390 assert enclosing_var == null; 3391 return str_name; 3392 case RETURN: 3393 return "\\result"; 3394 default: 3395 throw new Error("can't drop through switch statement"); 3396 } 3397 } 3398 3399 /** Returns the name of this variable in simplify format. */ 3400 @SideEffectFree 3401 public String simplify_name() { 3402 return simplify_name(null); 3403 } 3404 3405 /** 3406 * Returns the name of this variable in simplify format. If an index is specified, it is used as 3407 * an array index. It is an error to specify an index on a non-array variable. 3408 */ 3409 public String simplify_name(@Nullable String index) { 3410 if (!FileIO.new_decl_format) { 3411 return var_info_name.simplify_name(); // vin ok 3412 } 3413 3414 assert (index == null) || file_rep_type.isArray() : index + " " + name(); 3415 3416 // If this is a derived variable, the derivations builds the name 3417 if (derived != null) { 3418 return derived.simplify_name(); 3419 } 3420 3421 // Build the name by processing back through all of the enclosing variables 3422 switch (var_kind) { 3423 case FIELD: 3424 assert relative_name != null : this; 3425 return String.format("(select |%s| %s)", relative_name, enclosing_var.simplify_name(index)); 3426 case FUNCTION: 3427 // function_args assert function_args == null : "function args not implemented"; 3428 if (var_flags.contains(VarFlags.CLASSNAME)) { 3429 return ("(typeof " + enclosing_var.simplify_name(index) + ")"); 3430 } 3431 if (var_flags.contains(VarFlags.TO_STRING)) { 3432 return String.format("(select |toString| %s)", enclosing_var.simplify_name(index)); 3433 } 3434 if (enclosing_var != null) { 3435 return enclosing_var.simplify_name(index) + "." + relative_name + "()"; 3436 } 3437 return str_name; 3438 case ARRAY: 3439 if (index == null) { 3440 return String.format("(select elems %s)", enclosing_var.simplify_name()); 3441 } 3442 // if (index.equals("|0|")) { 3443 // System.err.printf("index = %s%n", index); 3444 // Throwable t = new Throwable(); 3445 // t.printStackTrace(); 3446 // } 3447 return String.format("(select (select elems %s) %s)", enclosing_var.simplify_name(), index); 3448 case VARIABLE: 3449 if (dkconfig_constant_fields_simplify && str_name.contains(".")) { 3450 String sel; 3451 String[] fields; 3452 if (postState != null) { 3453 fields = postState.name().split("\\."); 3454 sel = String.format("(select |%s| |__orig__%s|)", fields[1], fields[0]); 3455 } else { // not orig variable 3456 fields = str_name.split("\\."); 3457 sel = String.format("(select |%s| |%s|)", fields[1], fields[0]); 3458 } 3459 for (int ii = 2; ii < fields.length; ii++) { 3460 sel = String.format("(select |%s| %s)", fields[ii], sel); 3461 } 3462 return sel; 3463 } 3464 3465 assert enclosing_var == null; 3466 if (postState != null) { 3467 return "|__orig__" + postState.name() + "|"; 3468 } 3469 return "|" + str_name + "|"; 3470 case RETURN: 3471 return "|return|"; 3472 default: 3473 throw new Error("can't drop through switch statement"); 3474 } 3475 } 3476 3477 /** Return the name of this variable in its prestate (orig). */ 3478 @SideEffectFree 3479 public @Interned String prestate_name() { 3480 return ("orig(" + name() + ")").intern(); 3481 } 3482 3483 /** 3484 * Returns the name of the size variable that correponds to this array variable in simplify 3485 * format. Returns null if this variable is not an array or the size name can't be constructed for 3486 * other reasons. Note that isArray seems to distinguish between actual arrays and other sequences 3487 * (such as java.util.list). Simplify uses (it seems) the same length approach for both, so we 3488 * don't check isArray(). 3489 */ 3490 public @Nullable String get_simplify_size_name() { 3491 // Implement the method in two ways, to double-check results. 3492 3493 @Interned String result; 3494 if (!file_rep_type.isArray() || isDerived()) { 3495 result = null; 3496 } else { 3497 // System.out.printf("Getting size name for %s [%s]%n", name(), 3498 // get_length()); 3499 result = get_length().simplify_name().intern(); 3500 } 3501 3502 @Interned String old_result; 3503 if (!var_info_name.isApplySizeSafe()) { // vin ok 3504 old_result = null; 3505 } else { 3506 old_result = var_info_name.applySize().simplify_name().intern(); // vin ok 3507 } 3508 if (FileIO.new_decl_format && (old_result != result)) { 3509 throw new Error( 3510 String.format( 3511 "%s: '%s' '%s'%n basehashcode = %s%n", 3512 this, result, old_result, get_base_array_hashcode())); 3513 } 3514 3515 return old_result; 3516 } 3517 3518 /** Returns true if this variable contains a simple variable whose name is varname. */ 3519 public boolean includes_simple_name(String varname) { 3520 if (!FileIO.new_decl_format) { 3521 return var_info_name.includesSimpleName(varname); // vin ok 3522 } 3523 3524 if (isDerived()) { 3525 for (VarInfo base : derived.getBases()) { 3526 if (base.includes_simple_name(varname)) { 3527 return true; 3528 } 3529 } 3530 } else { 3531 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 3532 if ((vi.var_kind == VarKind.VARIABLE) && vi.name().equals(varname)) { 3533 return true; 3534 } 3535 } 3536 } 3537 return false; 3538 } 3539 3540 /** 3541 * Quantifies over the specified array variables in ESC format. Returns an array with 2 more 3542 * elements than the argument. Element 0 is the quantification, Element 1 is the indexed form of 3543 * variable 1, Element 2 is the indexed form of variable 3, and Element 4 is syntax such as close 3544 * parentheses. 3545 */ 3546 public static String[] esc_quantify(VarInfo... vars) { 3547 return esc_quantify(true, vars); 3548 } 3549 3550 /** 3551 * Quantifies over the specified array variables in ESC format. Returns an array with 2 more 3552 * elements than the argument. Element 0 is the quantification, Element 1 is the indexed form of 3553 * variable 1, Element 2 is the indexed form of variable 3, and Element 4 is syntax such as close 3554 * parentheses. 3555 */ 3556 public static String[] esc_quantify(boolean elementwise, VarInfo... vars) { 3557 3558 if (FileIO.new_decl_format) { 3559 Quantify.ESCQuantification quant = 3560 new Quantify.ESCQuantification(Quantify.get_flags(elementwise), vars); 3561 if (vars.length == 1) { 3562 return new String[] {quant.get_quantification(), quant.get_arr_vars_indexed(0), ")"}; 3563 } else if ((vars.length == 2) && vars[1].file_rep_type.isArray()) { 3564 return new String[] { 3565 quant.get_quantification(), 3566 quant.get_arr_vars_indexed(0), 3567 quant.get_arr_vars_indexed(1), 3568 ")" 3569 }; 3570 } else { 3571 return new String[] { 3572 quant.get_quantification(), quant.get_arr_vars_indexed(0), vars[1].esc_name(), ")" 3573 }; 3574 } 3575 } else { 3576 VarInfoName vin[] = new VarInfoName[vars.length]; 3577 for (int ii = 0; ii < vars.length; ii++) { 3578 vin[ii] = vars[ii].var_info_name; // vin ok 3579 } 3580 return VarInfoName.QuantHelper.format_esc(vin, elementwise); 3581 } 3582 } 3583 3584 /** 3585 * Returns a string array with 3 elements. The first element is the sequence, the second element 3586 * is the lower bound, and the third element is the upper bound. Returns null if this is not a 3587 * direct array or slice. 3588 */ 3589 public String @Nullable [] simplifyNameAndBounds() { 3590 if (!FileIO.new_decl_format) { 3591 return VarInfoName.QuantHelper.simplifyNameAndBounds(var_info_name); // vin ok 3592 } 3593 3594 String[] results = new String[3]; 3595 if (is_direct_non_slice_array() || (derived instanceof SequenceSubsequence)) { 3596 results[0] = get_base_array_hashcode().simplify_name(); 3597 results[1] = get_lower_bound().simplify_name(); 3598 results[2] = get_upper_bound().simplify_name(); 3599 return results; 3600 } 3601 3602 return null; 3603 } 3604 3605 /** 3606 * Returns the upper and lower bounds of the slice in simplify format. The implementation is 3607 * somewhat different that simplifyNameAndBounds (I don't know why). 3608 */ 3609 public String @Nullable [] get_simplify_slice_bounds() { 3610 if (!FileIO.new_decl_format) { 3611 @Interned VarInfoName[] bounds = var_info_name.getSliceBounds(); // vin ok 3612 if (bounds == null) { 3613 return null; 3614 } 3615 String[] str_bounds = new String[2]; 3616 str_bounds[0] = bounds[0].simplify_name(); 3617 str_bounds[1] = bounds[1].simplify_name(); 3618 return str_bounds; 3619 } 3620 3621 String[] results; 3622 if (derived instanceof SequenceSubsequence) { 3623 results = new String[2]; 3624 results[0] = get_lower_bound().simplify_name().intern(); 3625 results[1] = get_upper_bound().simplify_name().intern(); 3626 } else { 3627 results = null; 3628 } 3629 3630 return results; 3631 } 3632 3633 /** 3634 * Return a string in simplify format that will seclect the (index_base + index_off)-th element of 3635 * the sequence specified by this variable. 3636 * 3637 * @param simplify_index_name name of the index. If free is false, this must be a number or null 3638 * (null implies an index of 0). 3639 * @param free true of simplify_index_name is variable name 3640 * @param index_off offset from the index 3641 */ 3642 public String get_simplify_selectNth(String simplify_index_name, boolean free, int index_off) { 3643 3644 // Remove the simplify bars if present from the index name 3645 if ((simplify_index_name != null) 3646 && simplify_index_name.startsWith("|") 3647 && simplify_index_name.endsWith("|")) 3648 simplify_index_name = simplify_index_name.substring(1, simplify_index_name.length() - 1); 3649 3650 // Use VarInfoName to handle the old format 3651 if (!FileIO.new_decl_format) { 3652 VarInfoName select = 3653 VarInfoName.QuantHelper.selectNth( 3654 this.var_info_name, // vin ok 3655 simplify_index_name, 3656 free, 3657 index_off); 3658 // System.out.printf("sNth: index %s, free %b, off %d, result '%s'%n", 3659 // simplify_index_name, free, index_off, 3660 // select.simplify_name()); 3661 return select.simplify_name(); 3662 } 3663 3664 // Calculate the index (including the offset if non-zero) 3665 String complete_index; 3666 if (!free) { 3667 int index = 0; 3668 if (simplify_index_name != null) { 3669 index = Integer.decode(simplify_index_name); 3670 } 3671 index += index_off; 3672 complete_index = String.format("%d", index); 3673 } else { 3674 if (index_off != 0) { 3675 complete_index = String.format("(+ |%s| %d)", simplify_index_name, index_off); 3676 } else { 3677 complete_index = String.format("|%s|", simplify_index_name); 3678 } 3679 } 3680 3681 // Return the array properly indexed 3682 return simplify_name(complete_index); 3683 } 3684 3685 /** 3686 * Return a string in simplify format that will seclect the index_off element in a sequence that 3687 * has a lower bound. 3688 * 3689 * @param index_off offset from the index 3690 */ 3691 public String get_simplify_selectNth_lower(int index_off) { 3692 3693 // Use VarInfoName to handle the old format 3694 if (!FileIO.new_decl_format) { 3695 @Interned VarInfoName[] bounds = var_info_name.getSliceBounds(); 3696 VarInfoName lower = null; 3697 if (bounds != null) { 3698 lower = bounds[0]; 3699 } 3700 VarInfoName select = 3701 VarInfoName.QuantHelper.selectNth( 3702 var_info_name, // vin ok 3703 lower, 3704 index_off); 3705 return select.simplify_name(); 3706 } 3707 3708 // Calculate the index (including the offset if non-zero) 3709 String complete_index; 3710 Quantify.Term lower = get_lower_bound(); 3711 String lower_name = lower.simplify_name(); 3712 if (!(lower instanceof Quantify.Constant)) { 3713 lower_name = String.format("|%s|", lower_name); 3714 } 3715 if (index_off != 0) { 3716 if (lower instanceof Quantify.Constant) { 3717 complete_index = String.format("%d", ((Quantify.Constant) lower).get_value() + index_off); 3718 } else { 3719 complete_index = String.format("(+ %s %d)", lower_name, index_off); 3720 } 3721 } else { 3722 complete_index = String.format("%s", lower_name); 3723 } 3724 3725 // Return the array properly indexed 3726 // System.err.printf("lower bound type = %s [%s] %s%n", lower, 3727 // lower.getClass(), complete_index); 3728 return simplify_name(complete_index); 3729 } 3730 3731 /** Get a fresh variable name that doesn't appear in the given variable in simplify format. */ 3732 public static String get_simplify_free_index(VarInfo... vars) { 3733 if (!FileIO.new_decl_format) { 3734 VarInfoName[] vins = new VarInfoName[vars.length]; 3735 for (int ii = 0; ii < vars.length; ii++) { 3736 vins[ii] = vars[ii].var_info_name; // vin ok 3737 } 3738 return VarInfoName.QuantHelper.getFreeIndex(vins).simplify_name(); 3739 } 3740 3741 // Get a free variable for each variable and return the first one 3742 QuantifyReturn[] qret = Quantify.quantify(vars); 3743 return qret[0].index.simplify_name(); 3744 } 3745 3746 /** Get a 2 fresh variable names that doesn't appear in the given variable in simplify format. */ 3747 public static String[] get_simplify_free_indices(VarInfo... vars) { 3748 if (!FileIO.new_decl_format) { 3749 if (vars.length == 1) { 3750 VarInfoName index1_vin = 3751 VarInfoName.QuantHelper.getFreeIndex(vars[0].var_info_name); // vin ok 3752 String index2 = 3753 VarInfoName.QuantHelper.getFreeIndex(vars[0].var_info_name, index1_vin) 3754 .simplify_name(); // vin ok 3755 return new String[] {index1_vin.name(), index2}; 3756 } else if (vars.length == 2) { 3757 VarInfoName index1_vin = 3758 VarInfoName.QuantHelper.getFreeIndex( 3759 vars[0].var_info_name, vars[1].var_info_name); // vin ok 3760 String index2 = 3761 VarInfoName.QuantHelper.getFreeIndex( 3762 vars[0].var_info_name, vars[1].var_info_name, index1_vin) // vin ok 3763 .simplify_name(); 3764 return new String[] {index1_vin.name(), index2}; 3765 } else { 3766 throw new Error("unexpected length " + vars.length); 3767 } 3768 } 3769 3770 // Get a free variable for each variable 3771 if (vars.length == 1) { 3772 vars = new VarInfo[] {vars[0], vars[0]}; 3773 } 3774 QuantifyReturn qret[] = Quantify.quantify(vars); 3775 return new String[] {qret[0].index.simplify_name(), qret[1].index.simplify_name()}; 3776 } 3777 3778 /** 3779 * Quantifies over the specified array variables in Simplify format. Returns a string array that 3780 * contains the quantification, indexed form of each variable, optionally the index itself, and 3781 * the closer. 3782 * 3783 * <p>If elementwise is true, include the additional contraint that the indices (there must be 3784 * exactly two in this case) refer to corresponding positions. If adjacent is true, include the 3785 * additional constraint that the second index be one more than the first. If distinct is true, 3786 * include the constraint that the two indices are different. If includeIndex is true, return 3787 * additional strings, after the roots but before the closer, with the names of the index 3788 * variables. 3789 */ 3790 public static String[] simplify_quantify(EnumSet<QuantFlags> flags, VarInfo... vars) { 3791 3792 if (!FileIO.new_decl_format) { 3793 // Get the names for each variable. 3794 VarInfoName vin[] = new VarInfoName[vars.length]; 3795 for (int ii = 0; ii < vars.length; ii++) { 3796 vin[ii] = vars[ii].var_info_name; // vin ok 3797 } 3798 3799 return VarInfoName.QuantHelper.format_simplify( 3800 vin, 3801 flags.contains(QuantFlags.ELEMENT_WISE), 3802 flags.contains(QuantFlags.ADJACENT), 3803 flags.contains(QuantFlags.DISTINCT), 3804 flags.contains(QuantFlags.INCLUDE_INDEX)); 3805 } 3806 3807 Quantify.SimplifyQuantification quant = new Quantify.SimplifyQuantification(flags, vars); 3808 boolean include_index = flags.contains(QuantFlags.INCLUDE_INDEX); 3809 if ((vars.length == 1) && include_index) { 3810 return new String[] { 3811 quant.get_quantification(), 3812 quant.get_arr_vars_indexed(0), 3813 quant.get_index(0), 3814 quant.get_closer() 3815 }; 3816 } else if (vars.length == 1) { 3817 return new String[] { 3818 quant.get_quantification(), quant.get_arr_vars_indexed(0), quant.get_closer() 3819 }; 3820 } else if ((vars.length == 2) && include_index) { 3821 return new String[] { 3822 quant.get_quantification(), 3823 quant.get_arr_vars_indexed(0), 3824 quant.get_arr_vars_indexed(1), 3825 quant.get_index(0), 3826 quant.get_index(1), 3827 quant.get_closer() 3828 }; 3829 } else { // must be length 2 and no index 3830 return new String[] { 3831 quant.get_quantification(), 3832 quant.get_arr_vars_indexed(0), 3833 quant.get_arr_vars_indexed(1), 3834 quant.get_closer() 3835 }; 3836 } 3837 } 3838 3839 /** 3840 * See {@link #simplify_quantify(EnumSet, VarInfo[])}. 3841 * 3842 * @see #simplify_quantify(EnumSet, VarInfo[]) 3843 */ 3844 public static String[] simplify_quantify(VarInfo... vars) { 3845 return simplify_quantify(EnumSet.noneOf(QuantFlags.class), vars); 3846 } 3847 3848 /** 3849 * Returns a rough indication of the complexity of the variable. Higher numbers indicate more 3850 * complexity. 3851 */ 3852 public int complexity() { 3853 if (!FileIO.new_decl_format) { 3854 // System.out.printf("%s - %s%n", this, var_info_name.repr()); 3855 return var_info_name.inOrderTraversal().size(); // vin ok 3856 } 3857 3858 int cnt = 0; 3859 if (isDerived()) { 3860 cnt += derived.complexity(); 3861 VarInfo[] bases = derived.getBases(); 3862 for (VarInfo vi : bases) { 3863 cnt += vi.complexity(); 3864 } 3865 // Adjust for the complexity change when a prestate is nested in 3866 // another prestate. This is just done to match the old version 3867 if ((bases.length == 2) && bases[0].isPrestate()) { 3868 if (bases[1].isPrestate()) { 3869 cnt--; 3870 } else { 3871 cnt++; 3872 } 3873 } 3874 } else { 3875 if (isPrestate()) { 3876 cnt++; 3877 } 3878 for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) { 3879 cnt++; 3880 } 3881 } 3882 3883 // int old_cnt = var_info_name.inOrderTraversal().size(); 3884 // if (cnt != old_cnt) 3885 // System.out.printf("var %s, new cnt = %d, old cnt = %d [%s]%n", 3886 // name(), cnt, old_cnt, var_info_name.inOrderTraversal()); 3887 return cnt; 3888 } 3889 3890 /** 3891 * Returns true if this variable can be assigned to. Currently this is presumed true of all 3892 * variable except the special variable for the type of a variable and the size of a sequence. It 3893 * should include pure functions as well. 3894 */ 3895 @Pure 3896 public boolean is_assignable_var() { 3897 if (!FileIO.new_decl_format) { 3898 return !((var_info_name instanceof VarInfoName.TypeOf) // vin ok 3899 || (var_info_name instanceof VarInfoName.SizeOf)); // vin ok 3900 } 3901 3902 return !(is_typeof() || is_size()); 3903 } 3904 3905 /** 3906 * Returns whether or not this variable represents the type of a variable (eg, 3907 * a.getClass().getName()). Note that this will miss prestate variables such as 3908 * 'orig(a.getClass().getName())'. 3909 */ 3910 @Pure 3911 public boolean is_typeof() { 3912 if (!FileIO.new_decl_format) { 3913 return (var_info_name instanceof VarInfoName.TypeOf); // vin ok 3914 } 3915 3916 // The isPrestate check doesn't seem necessary, but is required to 3917 // match old behavior. 3918 return !isPrestate() && var_flags.contains(VarFlags.CLASSNAME); 3919 } 3920 3921 /** 3922 * Returns whether or not this variable represents the type of a variable (eg, 3923 * a.getClass().getName()). This version finds prestate variables such as 3924 * 'org(a.getClass().getName())'. 3925 */ 3926 public boolean has_typeof() { 3927 if (!FileIO.new_decl_format) { 3928 return var_info_name.hasTypeOf(); // vin ok 3929 } 3930 3931 if (isPrestate()) { 3932 return postState.has_typeof(); 3933 } 3934 return is_typeof(); 3935 } 3936 3937 /** Returns whether or not this variable is the 'this' variable. */ 3938 @Pure 3939 public boolean is_this() { 3940 return name().equals("this"); 3941 // return get_VarInfoName().equals(VarInfoName.THIS); 3942 } 3943 3944 /** 3945 * Returns whether or not this variable is the 'this' variable. True for both normal and prestate 3946 * versions of the variable. 3947 */ 3948 @Pure 3949 public boolean isThis() { 3950 return var_info_name.isThis(); 3951 } 3952 3953 /** Returns whether this is a size of an array or a prestate thereof. */ 3954 @Pure 3955 public boolean is_size() { 3956 return (derived instanceof SequenceLength); 3957 } 3958 3959 /** Returns wehther or not this variable is a field. */ 3960 @Pure 3961 public boolean is_field() { 3962 return (var_info_name instanceof VarInfoName.Field); 3963 } 3964 3965 /** Returns whether or not this variable has an integer offset (eg, a+2) */ 3966 @Pure 3967 public boolean is_add() { 3968 return (var_info_name instanceof VarInfoName.Add); 3969 } 3970 3971 /** 3972 * Returns the integer offset if this variable is an addition such as a+2. Throws an exception of 3973 * this variable is not an addition. 3974 * 3975 * @see #is_add() 3976 */ 3977 public int get_add_amount() { 3978 return ((VarInfoName.Add) var_info_name).amount; 3979 } 3980 3981 /** 3982 * Returns whether or not this variable is an actual array as opposed to an array that is created 3983 * over fields/methods of an array. For example, 'a[]' is a direct array, but 'a[].b' is not. 3984 */ 3985 @Pure 3986 public boolean is_direct_array() { 3987 // Must be an array to be a direct array 3988 if (!rep_type.isArray()) { 3989 return false; 3990 } 3991 3992 // If $Field or $Type appears before $Elements, false. 3993 // System.out.printf("%s flatten %s%n", name(), name); 3994 for (VarInfoName node : new VarInfoName.InorderFlattener(var_info_name).nodes()) { 3995 if (node instanceof VarInfoName.Field) { 3996 return false; 3997 } 3998 if (node instanceof VarInfoName.TypeOf) { 3999 return false; 4000 } 4001 if (node instanceof VarInfoName.Elements) { 4002 break; 4003 } 4004 } 4005 4006 return true; 4007 } 4008 4009 /** 4010 * Returns whether or not this variable is an actual array as opposed to an array that is created 4011 * over fields/methods of an array or a slice. For example, 'a[]' is a direct array, but 'a[].b' 4012 * and 'a[i..]' are not. 4013 */ 4014 @Pure 4015 public boolean is_direct_non_slice_array() { 4016 return (var_info_name instanceof VarInfoName.Elements); 4017 } 4018 4019 /** 4020 * Returns whether or not two variables have the same enclosing variable. If either variable is 4021 * not a field, returns false. 4022 */ 4023 public boolean has_same_parent(VarInfo other) { 4024 if (!is_field() || !other.is_field()) { 4025 return false; 4026 } 4027 4028 VarInfoName.Field name1 = (VarInfoName.Field) var_info_name; 4029 VarInfoName.Field name2 = (VarInfoName.Field) other.var_info_name; 4030 4031 return name1.term.equals(name2.term); 4032 } 4033 4034 /** 4035 * Returns the variable that encloses this one. For example if this variable is 'x.a.b', the 4036 * enclosing variable is 'x.a'. 4037 */ 4038 public @Nullable VarInfo get_enclosing_var() { 4039 if (FileIO.new_decl_format) { 4040 return enclosing_var; 4041 } else { 4042 List<VarInfoName> traversal = new VarInfoName.InorderFlattener(var_info_name).nodes(); 4043 if (traversal.size() <= 1) { 4044 // System.out.printf("size <= 1, traversal = %s%n", traversal); 4045 return null; 4046 } else { 4047 VarInfo enclosing_vi = ppt.find_var_by_name(traversal.get(1).name()); 4048 // if (enclosing_vi == null) 4049 // System.out.printf("Can't find '%s' in %s%n", 4050 // traversal.get(1).name(), ppt.varNames()); 4051 return enclosing_vi; 4052 } 4053 } 4054 } 4055 4056 /** 4057 * Replaces all instances of 'this' in the variable with the name of arg. Used to match up 4058 * enter/exit variables with object variables. 4059 */ 4060 public String replace_this(VarInfo arg) { 4061 VarInfoName parent_name = var_info_name.replaceAll(VarInfoName.THIS, arg.var_info_name); 4062 return parent_name.name(); 4063 } 4064 4065 /** 4066 * Creates a VarInfo that is a subsequence that begins at begin and ends at end with the specified 4067 * shifts. The begin or the end can be null, but a non-zero shift is only allowed with non-null 4068 * variables. 4069 */ 4070 public static VarInfo make_subsequence( 4071 VarInfo seq, @Nullable VarInfo begin, int begin_shift, @Nullable VarInfo end, int end_shift) { 4072 4073 String begin_str = inside_name(begin, seq.isPrestate(), begin_shift); 4074 if (begin_str.equals("")) { // interned if the null string, not interned otherwise 4075 begin_str = "0"; 4076 } 4077 String end_str = inside_name(end, seq.isPrestate(), end_shift); 4078 4079 VarInfoName begin_name; 4080 String parent_format = "%s.."; 4081 if (begin == null) { 4082 begin_name = null; 4083 } else { 4084 begin_name = (begin != null) ? begin.var_info_name : null; 4085 if (begin_shift == -1) { 4086 begin_name = begin_name.applyDecrement(); 4087 parent_format = "%s-1.."; 4088 } else if (begin_shift == 1) { 4089 begin_name = begin_name.applyIncrement(); 4090 parent_format = "%s+1.."; 4091 } else { 4092 assert begin_shift == 0; 4093 } 4094 } 4095 4096 VarInfoName end_name; 4097 if (end == null) { 4098 end_name = null; 4099 parent_format += "%s"; 4100 } else { 4101 end_name = end.var_info_name; 4102 if (end_shift == -1) { 4103 end_name = end_name.applyDecrement(); 4104 parent_format += "%s-1"; 4105 } else if (end_shift == 1) { 4106 end_name = end_name.applyIncrement(); 4107 parent_format += "%s+1"; 4108 } else { 4109 assert end_shift == 0; 4110 parent_format += "%s"; 4111 } 4112 } 4113 4114 VarInfoName new_name = seq.var_info_name.applySlice(begin_name, end_name); 4115 4116 VarInfo vi = new VarInfo(new_name, seq.type, seq.file_rep_type, seq.comparability, seq.aux); 4117 vi.setup_derived_base(seq, begin, end); 4118 vi.str_name = 4119 seq.apply_subscript(String.format("%s..%s", begin_str, end_str)) 4120 .intern(); // interning bugfix 4121 4122 // If there is a parent ppt (set in setup_derived_base), set the 4123 // parent variable accordingly. If all of the original variables used 4124 // the default name, this can as well. Otherwise, build the parent 4125 // name. 4126 for (VarParent parent : vi.parents) { 4127 int rid = parent.parent_relation_id; 4128 4129 if ((seq.get_parent(rid) == null) 4130 && ((begin == null) || !begin.has_parent(rid) || (begin.parent_var(rid) == null)) 4131 && ((end == null) || !end.has_parent(rid) || (end.parent_var(rid) == null))) { 4132 4133 parent.parent_variable = null; 4134 } else { 4135 String begin_pname = 4136 (begin == null || !begin.has_parent(rid)) ? "0" : begin.parent_var_name(rid); 4137 String end_pname = (end == null || !end.has_parent(rid)) ? "" : end.parent_var_name(rid); 4138 @SuppressWarnings( 4139 "formatter") // format string is constructed above using make_subsequence's arguments 4140 String res = 4141 apply_subscript( 4142 seq.parent_var_name(rid), String.format(parent_format, begin_pname, end_pname)); 4143 parent.parent_variable = res; 4144 // System.out.printf("-- set parent var from '%s' '%s' '%s' '%s'%n", 4145 // seq.parent_var_name(), parent_format, begin_pname, end_pname); 4146 } 4147 // System.out.printf("Parent for %s:%s is %s:%s%n", 4148 // ((seq.ppt != null)? seq.ppt.name() : "none"), vi.name(), 4149 // vi.parent_ppt, vi.parent_variable); 4150 } 4151 4152 return vi; 4153 } 4154 4155 /** 4156 * Returns the name to use for vi inside of a array reference. If the array reference is orig, 4157 * then orig is implied. This removes orig from orig variales and adds post to post variables. 4158 */ 4159 private static String inside_name(@Nullable VarInfo vi, boolean in_orig, int shift) { 4160 if (vi == null) { 4161 return ""; 4162 } 4163 4164 String shift_str = ""; 4165 if (shift != 0) { 4166 shift_str = String.format("%+d", shift); 4167 } 4168 4169 if (in_orig) { 4170 if (vi.isPrestate()) { 4171 return vi.postState.name() + shift_str; 4172 } else { 4173 return String.format("post(%s)%s", vi.name(), shift_str); 4174 } 4175 } else { 4176 return vi.name() + shift_str; 4177 } 4178 } 4179 4180 /** 4181 * Creates a VarInfo that is an index into a sequence. The type, file_rep_type, etc are taken from 4182 * the element type of the sequence. 4183 */ 4184 public static VarInfo make_subscript(VarInfo seq, @Nullable VarInfo index, int index_shift) { 4185 4186 String index_str = inside_name(index, seq.isPrestate(), index_shift); 4187 4188 VarInfoName index_name; 4189 if (index == null) { 4190 index_name = VarInfoName.parse(String.valueOf(index_shift)); 4191 } else { 4192 index_name = index.var_info_name; 4193 if (index_shift == -1) { 4194 index_name = index_name.applyDecrement(); 4195 } else { 4196 assert index_shift == 0 : "bad shift " + index_shift + " for " + index; 4197 } 4198 } 4199 4200 VarInfoName new_name = seq.var_info_name.applySubscript(index_name); 4201 VarInfo vi = 4202 new VarInfo( 4203 new_name, 4204 seq.type.elementType(), 4205 seq.file_rep_type.elementType(), 4206 seq.comparability.elementType(), 4207 VarInfoAux.getDefault()); 4208 vi.setup_derived_base(seq, index); 4209 vi.var_kind = VarInfo.VarKind.FIELD; 4210 vi.str_name = seq.apply_subscript(index_str).intern(); // interning bugfix 4211 for (VarParent parent : vi.parents) { 4212 int rid = parent.parent_relation_id; 4213 4214 if ((seq.parent_var(rid) == null) 4215 && ((index == null) || !index.has_parent(rid) || (index.parent_var(rid) == null))) { 4216 parent.parent_variable = null; 4217 } else { // one of the two bases has a different parent variable name 4218 String subscript_parent = String.valueOf(index_shift); 4219 if (index != null && index.has_parent(rid)) { 4220 subscript_parent = index.parent_var_name(rid); 4221 4222 if (seq.isPrestate() && !index.isPrestate()) { 4223 // Wrap the index in POST if the sequence is original 4224 subscript_parent = VarInfoName.parse(subscript_parent).applyPoststate().name_impl(); 4225 } else if (seq.isPrestate() && index.isPrestate()) { 4226 // Remove redundant ORIG 4227 subscript_parent = ((Prestate) VarInfoName.parse(subscript_parent)).term.name_impl(); 4228 } 4229 4230 if (index_shift == -1) { 4231 subscript_parent = subscript_parent + "-1"; 4232 } 4233 } 4234 parent.parent_variable = apply_subscript(seq.parent_var_name(rid), subscript_parent); 4235 } 4236 } 4237 return vi; 4238 } 4239 4240 /** 4241 * Create a VarInfo that is a function over one or more other variables. The type, rep_type, etc. 4242 * of the new function are taken from the first variable. 4243 */ 4244 public static VarInfo make_function(String function_name, VarInfo... vars) { 4245 4246 VarInfoName[] vin = new VarInfoName[vars.length]; 4247 for (int ii = 0; ii < vars.length; ii++) { 4248 vin[ii] = vars[ii].var_info_name; 4249 } 4250 4251 VarInfo vi = 4252 new VarInfo( 4253 VarInfoName.applyFunctionOfN(function_name, vin), 4254 vars[0].type, 4255 vars[0].file_rep_type, 4256 vars[0].comparability, 4257 vars[0].aux); 4258 vi.setup_derived_function(function_name, vars); 4259 return vi; 4260 } 4261 4262 /* 4263 * Creates the derived variable func(seq) from seq. 4264 * 4265 * @param func_name name of the function 4266 * @param type return type of the function. If null, the return type is 4267 * the element type of the sequence. 4268 * @param seq sequence variable 4269 * @param shift value to add or subtract from the function. Legal values 4270 * are -1, 0, and 1. 4271 */ 4272 public static VarInfo make_scalar_seq_func( 4273 String func_name, @Nullable ProglangType type, VarInfo seq, int shift) { 4274 4275 VarInfoName viname = seq.var_info_name.applyFunction(func_name); 4276 if (func_name.equals("size")) { 4277 viname = seq.var_info_name.applySize(); 4278 } 4279 String shift_name = ""; 4280 if (shift == -1) { 4281 viname = viname.applyDecrement(); 4282 shift_name = "_minus1"; 4283 } else if (shift == 1) { 4284 viname = viname.applyIncrement(); 4285 shift_name = "_plus1"; 4286 } else { 4287 assert shift == 0; 4288 } 4289 4290 @NonNull ProglangType ptype = type; 4291 @NonNull ProglangType frtype = type; 4292 VarComparability comp = seq.comparability.indexType(0); 4293 VarInfoAux aux = VarInfoAux.getDefault(); 4294 if (type == null) { 4295 ptype = seq.type.elementType(); 4296 frtype = seq.file_rep_type.elementType(); 4297 comp = seq.comparability.elementType(); 4298 aux = seq.aux; 4299 } 4300 VarInfo vi = new VarInfo(viname, ptype, frtype, comp, aux); 4301 vi.setup_derived_base(seq); 4302 vi.var_kind = VarInfo.VarKind.FUNCTION; 4303 vi.enclosing_var = seq; 4304 vi.arr_dims = 0; 4305 // null is initial value: vi.function_args = null; 4306 vi.relative_name = func_name + shift_name; 4307 4308 // Calculate the string to add for the shift. 4309 String shift_str = ""; 4310 if (shift != 0) { 4311 shift_str = String.format("%+d", shift); 4312 } 4313 4314 // Determine whether orig should be swapped with the function. 4315 // The original VarInfoName code did this only for the size 4316 // function (though it makes the same sense for all functions over 4317 // sequences). 4318 boolean swap_orig = 4319 func_name.equals("size") && seq.isPrestate() && !VarInfoName.dkconfig_direct_orig; 4320 4321 // Force orig to the outside if specified. 4322 if (swap_orig) { 4323 vi.str_name = 4324 String.format("orig(%s(%s))%s", func_name, seq.postState.name(), shift_str) 4325 .intern(); // interning bugfix 4326 } else { 4327 vi.str_name = 4328 String.format("%s(%s)%s", func_name, seq.name(), shift_str).intern(); // interning bugfix 4329 } 4330 4331 for (VarParent parent : vi.parents) { 4332 int rid = parent.parent_relation_id; 4333 if (!seq.has_parent(rid) || seq.parent_var(rid) == null) { 4334 parent.parent_variable = null; 4335 } else { 4336 if (func_name.equals("size")) { 4337 // Special handling for the case where the parent var name is orig(array[...]). 4338 // With swapping, the parent should be orig(size(array[..])), however it's stored 4339 // as a string so the swap can't be done textually. 4340 VarInfoName parentName = VarInfoName.parse(seq.parent_var_name(rid)); 4341 4342 // Can't use the more general applyFunction method here because it doesn't take into 4343 // account prestate values as the applySize method explicitly does 4344 parentName = parentName.applySize(); 4345 4346 parent.parent_variable = String.format("%s%s", parentName.name(), shift_str); 4347 } else { 4348 assert !swap_orig : "swap orig with parent " + vi; 4349 parent.parent_variable = 4350 String.format("%s(%s)%s", func_name, seq.parent_var_name(rid), shift_str); 4351 } 4352 } 4353 } 4354 return vi; 4355 } 4356 4357 /* 4358 * Creates the derived variable func(str) from string. 4359 * 4360 * @param func_name name of the function 4361 * @param type return type of the function 4362 * @param str sequence variable 4363 */ 4364 public static VarInfo make_scalar_str_func(String func_name, ProglangType type, VarInfo str) { 4365 4366 VarInfoName viname = str.var_info_name.applyFunction(func_name); 4367 4368 ProglangType ptype = type; 4369 ProglangType frtype = type; 4370 VarComparability comp = str.comparability.string_length_type(); 4371 VarInfoAux aux = VarInfoAux.getDefault(); 4372 VarInfo vi = new VarInfo(viname, ptype, frtype, comp, aux); 4373 vi.setup_derived_base(str); 4374 vi.var_kind = VarInfo.VarKind.FUNCTION; 4375 vi.enclosing_var = str; 4376 vi.arr_dims = 0; 4377 // null is initial value: vi.function_args = null; 4378 vi.relative_name = func_name; 4379 4380 vi.str_name = String.format("%s.%s()", str.name(), func_name).intern(); // interning bugfix 4381 4382 for (VarParent parent : vi.parents) { 4383 int rid = parent.parent_relation_id; 4384 if (str.get_parent(rid).parent_variable == null) { 4385 parent.parent_variable = null; 4386 } else { 4387 parent.parent_variable = 4388 String.format("%s.%s()", str.get_parent(rid).parent_variable, func_name); 4389 } 4390 } 4391 return vi; 4392 } 4393 4394 /** 4395 * Returns true if vi is the prestate version of this. If this is a derived variable, vi must be 4396 * the same derivation using prestate versions of each base variable. 4397 */ 4398 @Pure 4399 public boolean is_prestate_version(VarInfo vi) { 4400 4401 // If both variables are not derived 4402 if ((derived == null) && (vi.derived == null)) { 4403 4404 // true if vi is the prestate version of this 4405 return !isPrestate() && vi.isPrestate() && name().equals(vi.postState.name()); 4406 4407 // else if both variables are derived 4408 } else if ((derived != null) && (vi.derived != null)) { 4409 4410 return derived.is_prestate_version(vi.derived); 4411 4412 // one is derived and the other isn't 4413 } else { 4414 return false; 4415 } 4416 } 4417 4418 /** Returns true if this is an array or a slice. */ 4419 @Pure 4420 public boolean isArray() { 4421 return type.isArray(); 4422 } 4423 4424 /** Returns true if this is a slice. */ 4425 @Pure 4426 public boolean isSlice() { 4427 return isArray() && isDerived(); 4428 } 4429 4430 /** Converts a variable name or expression to the old style of names. */ 4431 public static String old_var_names(String name) { 4432 if (PrintInvariants.dkconfig_old_array_names && FileIO.new_decl_format) { 4433 return name.replace("[..]", "[]"); 4434 } else { 4435 return name; 4436 } 4437 } 4438 4439 /** Returns the old style variable name for this name. */ 4440 public String old_var_name() { 4441 return old_var_names(name()); 4442 } 4443 4444 /** Rough check to ensure that the variable name and derivation match up. */ 4445 public void var_check() { 4446 4447 if (false) { 4448 if (derived instanceof SequenceSubsequence) { 4449 if (name().contains("-1")) { 4450 SequenceSubsequence ss = (SequenceSubsequence) derived; 4451 // System.out.printf("checking %s [%s] with derived %s[%s]%n", 4452 // this, System.identityHashCode(this), derived, 4453 // System.identityHashCode(derived)); 4454 assert ss.index_shift == -1 4455 : "bad var " 4456 + this 4457 + " derived " 4458 + derived 4459 + " shift " 4460 + ss.index_shift 4461 + " in ppt " 4462 + ppt.name(); 4463 } 4464 } 4465 } 4466 } 4467}