001package daikon.inv; 002 003import daikon.Debug; 004import daikon.Global; 005import daikon.PptSlice; 006import daikon.PptTopLevel; 007import daikon.Quantify.QuantFlags; 008import daikon.ValueTuple; 009import daikon.VarComparability; 010import daikon.VarInfo; 011import java.util.ArrayList; 012import java.util.Collection; 013import java.util.Collections; 014import java.util.Iterator; 015import java.util.List; 016import java.util.Set; 017import java.util.TreeSet; 018import java.util.logging.Level; 019import java.util.logging.Logger; 020import org.checkerframework.checker.initialization.qual.UnknownInitialization; 021import org.checkerframework.checker.lock.qual.GuardSatisfied; 022import org.checkerframework.checker.nullness.qual.Nullable; 023import org.checkerframework.dataflow.qual.Pure; 024import org.checkerframework.dataflow.qual.SideEffectFree; 025import typequals.prototype.qual.NonPrototype; 026import typequals.prototype.qual.Prototype; 027 028// Note that this Invariant is used in a *very* different way from 029// the same-named one in V2. In V2, this is just for printing. In V3, 030// this does all the canonicalizing, etc. 031 032// This is a Java (not Javadoc) comment because the Daikon user manual 033// reproduces the Javadoc but doesn't need all these implementation 034// details. 035// 036// During checking, Equality keeps track of variables that are 037// comparable and equal, so we only need to instantiate (other) 038// invariants for one member of each Equal set, the leader. 039// 040// During postProcessing, each instance of Equality instantiates into 041// displaying several EqualityComparison invariants ("x == y", "x == 042// z"). Equality invariants have leaders, which are the canonical 043// forms of their variables. In the previous example, x is the 044// leader. Equality invariants sort their variables by index ordering 045// during checking. During printing, however, equality invariants may 046// "pivot" -- that is, switch leaders if the current leader wouldn't 047// be printed because it was not an interesting variable. Notice that 048// when pivoting, all the other invariants based on this.leader also 049// need to be pivoted. 050 051/** 052 * Keeps track of sets of variables that are equal. Other invariants are instantiated for only one 053 * member of the Equality set, the leader. If variables {@code x}, {@code y}, and {@code z} are 054 * members of the Equality set and {@code x} is chosen as the leader, then the Equality will 055 * internally convert into binary comparison invariants that print as {@code x == y} and {@code x == 056 * z}. 057 */ 058public final /*(at)Interned*/ class Equality extends Invariant { 059 static final long serialVersionUID = 20021231L; 060 061 public static final Logger debug = Logger.getLogger("daikon.inv.Equality"); 062 063 public static final Logger debugPostProcess = Logger.getLogger("daikon.inv.Equality.postProcess"); 064 065 /** How many samples this has seen. */ 066 private int numSamples; 067 068 public void setSamples(int sample_cnt) { 069 numSamples = sample_cnt; 070 } 071 072 public int numSamples(@GuardSatisfied Equality this) { 073 return numSamples; 074 } 075 076 /** 077 * The Set of VarInfos that this represents equality for. Can change over time as this invariant 078 * weakens. Sorted by index until pivoting. 079 */ 080 private TreeSet<VarInfo> vars; 081 082 /** Returns the number of variables in the set. */ 083 @Pure 084 public int size(@GuardSatisfied Equality this) { 085 return vars.size(); 086 } 087 088 /** Returns the variables in their index order. Unmodifiable. */ 089 public Set<VarInfo> getVars() { 090 return Collections.unmodifiableSet(vars); 091 } 092 093 /** 094 * Creates a new Equality invariant. 095 * 096 * @param variables variables that are equivalent, with the canonical one first 097 * @param ppt the program point 098 */ 099 @SuppressWarnings("initialization.field.write.initialized") // weakness of FBC type system 100 public Equality(Collection<VarInfo> variables, PptSlice ppt) { 101 super(ppt); 102 if (debug.isLoggable(Level.FINE)) { 103 debug.fine("Creating at " + ppt.parent.name() + " vars: "); 104 } 105 106 numSamples = 0; 107 vars = new TreeSet<VarInfo>(VarInfo.IndexComparator.theInstance); 108 vars.addAll(variables); 109 VarInfo leader = leader(); 110 111 // ensure well-formedness and set equality slots 112 assert variables.size() > 0; 113 assert vars.size() == variables.size(); 114 115 for (VarInfo vi : variables) { 116 if (debug.isLoggable(Level.FINE)) { 117 debug.fine(" " + vi.name() + " [" + vi.comparability + "]"); 118 } 119 assert vi.ppt == leader.ppt; 120 assert vi.comparableNWay(leader); 121 assert VarComparability.comparable(leader, vi) 122 : "not comparable " + leader.name() + " " + vi.name() + " at ppt " + ppt.parent.name(); 123 assert vi.rep_type.isArray() == leader.rep_type.isArray(); 124 vi.equalitySet = this; 125 } 126 } 127 128 // //////////////////////// 129 // Accessors 130 131 private @Nullable VarInfo leaderCache = null; 132 133 /** 134 * Return the canonical VarInfo of this. Note that the leader never changes. 135 * 136 * @return the canonical VarInfo of this 137 */ 138 @SuppressWarnings("all:purity") // set cache field 139 @Pure 140 public VarInfo leader(@GuardSatisfied @UnknownInitialization(Equality.class) Equality this) { 141 if (leaderCache == null) { 142 leaderCache = vars.iterator().next(); 143 } 144 return leaderCache; 145 } 146 147 public boolean hasNonCanonicalVariable() { 148 throw new Error("Illegal operation on Equality invariant"); 149 } 150 151 /** 152 * Always return JUSTIFIED because we aggregate EqualityComparison invariants that are all 153 * justified to the confidence_limit threshold. 154 */ 155 @Override 156 public double computeConfidence() { 157 return Invariant.CONFIDENCE_JUSTIFIED; 158 } 159 160 // //////////////////////// 161 // Printing 162 163 // The format methods aren't called, because for output, we 164 // convert to normal two-way IntEqual type invariants. However, 165 // they can be called if desired. 166 167 @Override 168 public String repr(@GuardSatisfied Equality this) { 169 return "Equality: size=" 170 + size() 171 + " leader: " 172 + leader().name() 173 + " with " 174 + format_daikon() 175 + " samples: " 176 + numSamples(); 177 } 178 179 @SideEffectFree 180 @Override 181 public String format_using(@GuardSatisfied Equality this, OutputFormat format) { 182 183 if (format.isJavaFamily()) { 184 return format_java_family(format); 185 } 186 187 if (format == OutputFormat.DAIKON) { 188 return format_daikon(); 189 } 190 if (format == OutputFormat.ESCJAVA) { 191 return format_esc(); 192 } 193 // Commented out by MDE 7/27/2003. I can't figure out whether 194 // to just change JAVA_IDENTIFIER to IDENTIFIER, or whether other 195 // changes are also necessary. 196 // if (format == OutputFormat.JAVA_IDENTIFIER) { 197 // return format_java(); 198 // } 199 if (format == OutputFormat.SIMPLIFY) { 200 return format_simplify(); 201 } 202 return format_unimplemented(format); 203 } 204 205 public String format_daikon(@GuardSatisfied Equality this) { 206 StringBuilder result = new StringBuilder(); 207 boolean start = true; 208 for (VarInfo var : vars) { 209 if (!start) { 210 result.append(" == "); 211 } else { 212 start = false; 213 } 214 result.append(var.name()); 215 result.append("[" + var.varinfo_index + "]"); 216 // result.append("[" + var.comparability + "]"); 217 if (var == leader()) { 218 result.append("L"); 219 } 220 } 221 return result.toString(); 222 } 223 224 public String format_java() { 225 VarInfo leader = leader(); 226 String leaderName = leader.name(); 227 List<String> clauses = new ArrayList<>(); 228 for (VarInfo var : vars) { 229 if (leader == var) { 230 continue; 231 } 232 clauses.add(String.format("(%s == %s)", leaderName, var.name())); 233 } 234 return String.join(" && ", clauses); 235 } 236 237 public String format_esc(@GuardSatisfied Equality this) { 238 String result = ""; 239 240 List<VarInfo> valid_equiv = new ArrayList<>(); 241 List<VarInfo> invalid_equiv = new ArrayList<>(); 242 243 List<VarInfo> equal_vars = new ArrayList<>(); 244 245 for (VarInfo other : vars) { 246 if (other.isDerivedSequenceMinMaxSum()) { 247 break; 248 } 249 if (other.isValidEscExpression()) { 250 valid_equiv.add(other); 251 } else { 252 invalid_equiv.add(other); 253 } 254 } 255 // Choose a leader, preferring the valid variables. 256 VarInfo leader; 257 if (valid_equiv.size() > 0) { 258 leader = valid_equiv.get(0); 259 } else { 260 assert invalid_equiv.size() > 0; 261 leader = invalid_equiv.get(0); 262 } 263 // Print the equality statements, stating expressible ones first. 264 equal_vars.clear(); 265 equal_vars.addAll(valid_equiv); 266 equal_vars.addAll(invalid_equiv); 267 int numprinted = 0; 268 for (int j = 0; j < equal_vars.size(); j++) { 269 VarInfo other = equal_vars.get(j); 270 if (other == leader) { 271 continue; 272 } 273 if (leader.prestate_name().equals(other.name())) { 274 continue; 275 } 276 if (other.prestate_name().equals(leader.name())) { 277 continue; 278 } 279 if (numprinted > 0) { 280 result += Global.lineSep; 281 } 282 numprinted++; 283 if (j >= valid_equiv.size()) { 284 result = 285 result + "warning: method Equality.format_esc() needs to be implemented: " + format(); 286 } 287 if (leader.rep_type.isArray()) { 288 String[] form = VarInfo.esc_quantify(leader, other); 289 result = result + form[0] + "( " + form[1] + " == " + form[2] + " )" + form[3]; 290 } else { 291 result = result + leader.esc_name() + " == " + other.esc_name(); 292 } 293 } 294 return result; 295 } 296 297 // When A and B are pointers, don't say (EQ A B); instead say (EQ 298 // (hash A) (hash B)). If we said the former, Simplify would 299 // presume that A and B were always interchangeable, which is not 300 // the case when your programming language involves mutation. 301 private String format_elt(@GuardSatisfied Equality this, String simname) { 302 String result = simname; 303 if (leader().is_reference()) { 304 result = "(hash " + result + ")"; 305 } 306 return result; 307 } 308 309 public String format_simplify(@GuardSatisfied Equality this) { 310 StringBuilder result = new StringBuilder("(AND"); 311 VarInfo leader = leader(); 312 String leaderName = leader.simplify_name(); 313 if (leader.rep_type.isArray()) { 314 for (VarInfo var : vars) { 315 if (var == leader) { 316 continue; 317 } 318 String[] form = VarInfo.simplify_quantify(QuantFlags.element_wise(), leader, var); 319 String a = format_elt(form[1]); 320 String b = format_elt(form[2]); 321 result.append(" " + form[0] + "(EQ " + a + " " + b + ")" + form[3]); 322 } 323 } else { 324 for (VarInfo var : vars) { 325 if (var == leader) { 326 continue; 327 } 328 String a = format_elt(leaderName); 329 String b = format_elt(var.simplify_name()); 330 result.append(" (EQ "); 331 result.append(a); 332 result.append(" "); 333 result.append(b); 334 result.append(")"); 335 } 336 } 337 result.append(")"); 338 return result.toString(); 339 } 340 341 public String shortString() { 342 return format_daikon(); 343 } 344 345 public String format_java_family(@GuardSatisfied Equality this, OutputFormat format) { 346 VarInfo leader = leader(); 347 String leaderName = leader.name_using(format); 348 List<String> clauses = new ArrayList<>(); 349 for (VarInfo var : vars) { 350 if (leader == var) { 351 continue; 352 } 353 if (leader.rep_type.isArray()) { 354 clauses.add( 355 String.format( 356 "(daikon.Quant.pairwiseEqual(%s, %s)", leaderName, var.name_using(format))); 357 } else { 358 if (leader.type.isFloat()) { 359 clauses.add("(" + Invariant.formatFuzzy("eq", leader, var, format) + ")"); 360 } else { 361 if ((leaderName.indexOf("daikon.Quant.collectObject") != -1) 362 || (var.name_using(format).indexOf("daikon.Quant.collectObject") != -1)) { 363 clauses.add( 364 "(warning: it is meaningless to compare hashcodes for values " 365 + "obtained through daikon.Quant.collect... methods."); 366 } else { 367 clauses.add(String.format("(%s == %s)", leaderName, var.name_using(format))); 368 } 369 } 370 } 371 } 372 return String.join(" && ", clauses); 373 } 374 375 @SideEffectFree 376 @Override 377 public String toString(@GuardSatisfied Equality this) { 378 return repr(); 379 } 380 381 // ////////////////////////////////////////////////////////////////////// 382 // Processing of data 383 384 /** 385 * Return a List of VarInfos that do not fit into this set anymore. 386 * 387 * <p>Originally (8/14/2003), this did not check for the modified bits. It seems however, quite 388 * wrong to leave variables in the same equality set when one is missing and the other is not. 389 * It's possible we should go farther and break out of the equality set any variable that is 390 * missingOutOfBounds (JHP). 391 * 392 * @param vt the newly-observed sample 393 * @param count the number of times the sample is seen 394 * @return a List of VarInfos that do not fit into this set anymore 395 */ 396 public List<VarInfo> add(ValueTuple vt, int count) { 397 // Need to handle specially if leader is missing. 398 VarInfo leader = leader(); 399 Object leaderValue = leader.getValueOrNull(vt); 400 int leaderMod = leader.getModified(vt); 401 boolean leaderOutOfBounds = leader.missingOutOfBounds(); 402 if (leader.isMissing(vt)) { 403 } else { 404 numSamples += count; 405 } 406 407 List<VarInfo> result = new ArrayList<>(); 408 if (debug.isLoggable(Level.FINE)) { 409 debug.fine("Doing add at " + this.ppt.parent.name() + " for " + this); 410 } 411 for (Iterator<VarInfo> i = vars.iterator(); i.hasNext(); ) { 412 VarInfo vi = i.next(); 413 if (vi == leader) { 414 continue; 415 } 416 assert vi.comparableNWay(leader); 417 Object viValue = vi.getValueOrNull(vt); 418 int viMod = vi.getModified(vt); 419 // The following is possible because values are interned. The 420 // test also takes into account missing values, since they are 421 // null. 422 if ((leaderValue == viValue) 423 && (leaderMod == viMod) 424 && !leaderOutOfBounds 425 && !vi.missingOutOfBounds() 426 // If the values are NaN, treat them as different. 427 && !((leaderValue instanceof Double) && ((Double) leaderValue).isNaN())) { 428 // The values are the same. 429 continue; 430 } 431 // The values differ. Remove this from the equality set. 432 433 // if (debug.isLoggable(Level.FINE)) { 434 // debug.fine (" vi name: " + vi.name.name()); 435 // debug.fine (" vi value: " + viValue); 436 // debug.fine (" le value: " + leaderValue); 437 // } 438 if (Debug.logOn()) { 439 Debug.log( 440 getClass(), 441 ppt.parent, 442 Debug.vis(vi), 443 "Var " 444 + vi.name() 445 + " [" 446 + viValue 447 + "," 448 + viMod 449 + "] split from leader " 450 + leader.name() 451 + " [" 452 + Debug.toString(leaderValue) 453 + "," 454 + leaderMod 455 + "]"); 456 } 457 458 result.add(vi); 459 i.remove(); 460 } 461 462 return result; 463 } 464 465 // This method isn't going to be called, but it's declared abstract in Invariant. 466 @Override 467 protected Invariant resurrect_done(int[] permutation) { 468 throw new UnsupportedOperationException(); 469 } 470 471 // This method isn't going to be called, but it's declared abstract in Invariant. 472 @Pure 473 @Override 474 public boolean isSameFormula(Invariant other) { 475 throw new UnsupportedOperationException( 476 "Equality.isSameFormula(): this method should not be called"); 477 } 478 479 /** 480 * Convert Equality invariants into normal IntEqual type for filtering, printing, etc. Add these 481 * to parent. 482 * 483 * <p>If the leader was changed to not be the first member of the group adds leader == leader 484 * invariant as well since that invariant is used in suppressions and obvious tests. 485 */ 486 public void postProcess() { 487 if (this.numSamples() == 0) { 488 // All were missing or not present 489 return; 490 } 491 PptTopLevel parent = this.ppt.parent; 492 VarInfo[] varArray = this.vars.toArray(new VarInfo[0]); 493 if (debugPostProcess.isLoggable(Level.FINE)) { 494 debugPostProcess.fine("Doing postProcess: " + this.format_daikon()); 495 debugPostProcess.fine(" at: " + this.ppt.parent.name()); 496 } 497 VarInfo leader = leader(); 498 499 if (debugPostProcess.isLoggable(Level.FINE)) { 500 debugPostProcess.fine(" var1: " + leader.name()); 501 } 502 for (int i = 0; i < varArray.length; i++) { 503 if (varArray[i] == leader) { 504 continue; 505 } 506 if (debugPostProcess.isLoggable(Level.FINE)) { 507 debugPostProcess.fine(" var2: " + varArray[i].name()); 508 } 509 510 // Guard to prevent creating unnecessary Equality invariants related to 511 // purity method black boxing 512 if (leader.function_args != null 513 && varArray[i].function_args != null 514 && leader.function_args.size() > 1 515 && leader.function_args.size() == varArray[i].function_args.size()) { 516 boolean allEqual = true; 517 for (int j = 0; j < leader.function_args.size(); j++) { 518 if (!leader.function_args.get(j).isEqualTo(varArray[i].function_args.get(j))) { 519 allEqual = false; 520 break; 521 } 522 } 523 if (allEqual) { 524 continue; 525 } 526 } 527 528 parent.create_equality_inv(leader, varArray[i], numSamples()); 529 } 530 } 531 532 /** 533 * Switch the leader of this invariant, if possible, to a more canonical VarInfo: a VarInfo that 534 * is not isDerived() is better than one that is; one that is not isDerivedParamAndUninteresting() 535 * is better than one that is; and other things being equal, choose the least complex name. Call 536 * this only after postProcess has been called. We do a pivot so that anything that's interesting 537 * to be printed gets printed and not filtered out. For example, if a == b and a is the leader, 538 * but not interesting, we still want to print f(b) as an invariant. Thus we pivot b to be the 539 * leader. Later on, each relevant PptSlice gets pivoted. But not here. 540 */ 541 public void pivot() { 542 VarInfo newLeader = null; 543 for (VarInfo var : vars) { 544 // System.out.printf(" processing %s%n", var); 545 if (newLeader == null) { 546 newLeader = var; 547 } else if (newLeader.isDerivedParamAndUninteresting() 548 && !var.isDerivedParamAndUninteresting()) { 549 // System.out.printf("%s derived and uninteresting, %s is leader%n", 550 // newLeader, var); 551 newLeader = var; 552 } else if (var.isDerivedParamAndUninteresting() 553 && !newLeader.isDerivedParamAndUninteresting()) { 554 // do nothing 555 } else if (var.derivedDepth() < newLeader.derivedDepth()) { 556 // System.out.printf("%s greater depth, %s is leader%n", 557 // newLeader, var); 558 newLeader = var; 559 } else if (var.derivedDepth() > newLeader.derivedDepth()) { 560 // do nothing 561 } 562 // if we got here, this is the "all other things being equal" case 563 else if (var.complexity() < newLeader.complexity()) { 564 // System.out.printf("%s greater comlexity, %s is leader%n", 565 // newLeader, var); 566 newLeader = var; 567 } 568 } 569 // System.out.printf("%s complexity = %d, %s complexity = %d%n", leaderCache, 570 // leaderCache.complexity(), newLeader, 571 // newLeader.complexity()); 572 leaderCache = newLeader; 573 } 574 575 @Override 576 public void repCheck() { 577 super.repCheck(); 578 VarInfo leader = leader(); 579 for (VarInfo var : vars) { 580 assert VarComparability.comparable(leader, var) 581 : "not comparable: " + leader.name() + " " + var.name() + " at ppt " + ppt.parent.name(); 582 } 583 } 584 585 @Override 586 public boolean enabled(@Prototype Equality this) { 587 throw new Error("do not invoke " + getClass() + ".enabled()"); 588 } 589 590 @Override 591 public boolean valid_types(@Prototype Equality this, VarInfo[] vis) { 592 throw new Error("do not invoke " + getClass() + ".valid_types()"); 593 } 594 595 @Override 596 protected @NonPrototype Equality instantiate_dyn(@Prototype Equality this, PptSlice slice) { 597 throw new Error("do not invoke " + getClass() + ".instantiate_dyn()"); 598 } 599 600 @Override 601 public @Nullable @NonPrototype Equality merge( 602 @Prototype Equality this, List<@NonPrototype Invariant> invs, PptSlice parent_ppt) { 603 throw new Error("Don't merge Equality invariants"); 604 } 605}