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