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