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