001package daikon; 002 003import static daikon.tools.nullness.NullnessUtil.castNonNullDeep; 004 005import daikon.inv.Equality; 006import daikon.inv.Invariant; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Collections; 010import java.util.Comparator; 011import java.util.HashMap; 012import java.util.Iterator; 013import java.util.LinkedHashMap; 014import java.util.List; 015import java.util.Map; 016import java.util.Set; 017import java.util.logging.Level; 018import java.util.logging.Logger; 019import org.checkerframework.checker.initialization.qual.UnknownInitialization; 020import org.checkerframework.checker.lock.qual.GuardSatisfied; 021import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 022import org.checkerframework.checker.nullness.qual.KeyFor; 023import org.checkerframework.checker.nullness.qual.Nullable; 024import org.checkerframework.dataflow.qual.Pure; 025import org.checkerframework.dataflow.qual.SideEffectFree; 026 027/** Holds Equality invariants. */ 028public class PptSliceEquality extends PptSlice { 029 static final long serialVersionUID = 20021231L; 030 031 // Variables starting with dkconfig_ should only be set via the 032 // daikon.config.Configuration interface. 033 034 /** 035 * If true, create one equality set for each variable. This has the effect of turning the equality 036 * optimization off, without actually removing the sets themselves (which are presumed to exist in 037 * many parts of the code). 038 */ 039 public static boolean dkconfig_set_per_var = false; 040 041 public static final Logger debug = Logger.getLogger("daikon.PptSliceEquality"); 042 043 public static final Logger debugGlobal = Logger.getLogger("daikon.PptSliceEquality.Global"); 044 045 PptSliceEquality(PptTopLevel parent) { 046 super(parent, parent.var_infos); 047 } 048 049 @Override 050 public final int arity(@UnknownInitialization(PptSlice.class) PptSliceEquality this) { 051 throw new Error("Don't call arity on PptSliceEquality"); 052 } 053 054 void init_po() { 055 throw new Error("Shouldn't get called"); 056 } 057 058 @Override 059 public void addInvariant(Invariant inv) { 060 assert inv instanceof Equality; 061 invs.add(inv); 062 } 063 064 // Not valid for this type of slice. Always pretend there are enough. 065 @Override 066 public int num_samples(@UnknownInitialization @GuardSatisfied PptSliceEquality this) { 067 if (true) { 068 throw new Error(); 069 } 070 return Integer.MAX_VALUE; 071 } 072 073 public int num_mod_samples() { 074 if (true) { 075 throw new Error(); 076 } 077 return Integer.MAX_VALUE; 078 } 079 080 @Override 081 public int num_values() { 082 if (true) { 083 throw new Error(); 084 } 085 return Integer.MAX_VALUE; 086 } 087 088 /** 089 * Encapsulates a VarInfo and its Comparability so that the two can be used to create sets of 090 * VarInfos that are initially equal. Two VarInfoAndComparability's are true iff they are 091 * VarComparability.comparable() to each other. 092 */ 093 private static class VarInfoAndComparability { 094 final VarInfo vi; 095 096 @Pure 097 @Override 098 public int hashCode(@GuardSatisfied VarInfoAndComparability this) { 099 // This is very coarse but is about as good as we can do it. Can't do hashcode of 100 // the comparability because two comparabilities may be 101 // comparable and yet be not the same. 102 // (e.g. VarComparabilityExplicit). 103 return vi.file_rep_type.hashCode(); 104 } 105 106 @EnsuresNonNullIf(result = true, expression = "#1") 107 @Pure 108 @Override 109 public boolean equals( 110 @GuardSatisfied VarInfoAndComparability this, @GuardSatisfied @Nullable Object o) { 111 if (!(o instanceof VarInfoAndComparability)) { 112 return false; 113 } 114 return equalsVarInfoAndComparability((VarInfoAndComparability) o); 115 } 116 117 /** 118 * True if two VarInfos can be set to be equal to each other is whether they are comparableNWay. 119 * Since we do not yet handle inheritance, we require that the comparability go both ways. 120 */ 121 @EnsuresNonNullIf(result = true, expression = "#1") 122 @Pure 123 boolean equalsVarInfoAndComparability( 124 @GuardSatisfied VarInfoAndComparability this, @GuardSatisfied VarInfoAndComparability o) { 125 126 return (vi.comparableNWay(o.vi) 127 && vi.comparability.equality_set_ok(o.vi.comparability) 128 && vi.aux.equals_for_instantiation(o.vi.aux)); 129 } 130 131 VarInfoAndComparability(VarInfo vi) { 132 this.vi = vi; 133 } 134 } 135 136 /** Actually instantiate the equality sets. */ 137 @Override 138 void instantiate_invariants() { 139 140 // If each variable gets its own set, create those sets and return 141 if (dkconfig_set_per_var) { 142 // Debug.debugTrack.fine ("Vars for " + parent.name()); 143 for (VarInfo vi : var_infos) { 144 List<VarInfo> vi_list = Collections.singletonList(vi); 145 Equality eq = new Equality(vi_list, this); 146 invs.add(eq); 147 // System.out.println (" eq set = " + eq.shortString()); 148 } 149 return; 150 } 151 152 // Start with everything comparable being equal. 153 if (debug.isLoggable(Level.FINE)) { 154 debug.fine("PptSliceEquality.instantiate_invariants: " + parent.name() + " vars:"); 155 } 156 LinkedHashMap<VarInfoAndComparability, List<VarInfo>> multiMap = new LinkedHashMap<>(); 157 for (VarInfo vi : var_infos) { 158 VarInfoAndComparability viac = new VarInfoAndComparability(vi); 159 addToBindingList(multiMap, viac, vi); 160 if (debug.isLoggable(Level.FINE)) { 161 debug.fine(" " + vi.name() + ": " + vi.comparability); 162 } 163 } 164 if (debug.isLoggable(Level.FINE)) { 165 debug.fine( 166 "PptSliceEquality.instantiate_invariants " 167 + parent.name() 168 + ": " 169 + Integer.toString(multiMap.keySet().size()) 170 + " VarInfoAndComparability keys"); 171 } 172 Equality[] newInvs = new Equality[multiMap.keySet().size()]; 173 int varCount = 0; 174 int invCount = 0; 175 for (List<VarInfo> list : multiMap.values()) { 176 varCount += list.size(); 177 178 Equality eq = new Equality(list, this); 179 newInvs[invCount] = eq; 180 if (debug.isLoggable(Level.FINE)) { 181 debug.fine(" Created: " + eq); 182 for (VarInfo vi : list) { 183 debug.fine(" vi: " + vi + " aux : " + vi.aux); 184 } 185 } 186 if (Debug.logOn()) { 187 Debug.log(getClass(), parent, Debug.vis(eq.leader()), "Created"); 188 } 189 invCount++; 190 } 191 // Ensure determinism 192 Arrays.sort(newInvs, EqualityComparator.theInstance); 193 invs.addAll(Arrays.<Invariant>asList(newInvs)); 194 assert varCount == var_infos.length; // Check that we get all vis 195 } 196 197 /** 198 * Instantiate the full equality sets from a set of variable pairs where each member of a pair is 199 * equal to the other. 200 */ 201 public void instantiate_from_pairs(Set<VarInfo.Pair> eset) { 202 203 // Build a map from each variable to all those that are equal to it 204 Map<VarInfo, List<VarInfo>> varmap = new LinkedHashMap<>(); 205 Map<VarInfo, Integer> sample_cnt_map = new LinkedHashMap<>(); 206 for (VarInfo.Pair cp : eset) { 207 List<VarInfo> vlist = varmap.get(cp.v1); 208 if (vlist == null) { 209 vlist = new ArrayList<VarInfo>(); 210 vlist.add(cp.v1); 211 varmap.put(cp.v1, vlist); 212 sample_cnt_map.put(cp.v1, cp.samples); 213 } 214 vlist.add(cp.v2); 215 vlist = varmap.get(cp.v2); 216 if (vlist == null) { 217 vlist = new ArrayList<VarInfo>(); 218 vlist.add(cp.v2); 219 varmap.put(cp.v2, vlist); 220 sample_cnt_map.put(cp.v2, cp.samples); 221 } 222 vlist.add(cp.v1); 223 } 224 225 // Loop through each variable, building the appropriate equality set 226 // for each. Note that variables that are distinct still have an 227 // equality set (albeit with only the one variable) 228 ArrayList<Invariant> newInvs = new ArrayList<>(); 229 for (VarInfo v : var_infos) { 230 if (v.equalitySet != null) { 231 continue; 232 } 233 List<VarInfo> vlist = varmap.get(v); 234 if (vlist == null) { 235 vlist = Collections.singletonList(v); 236 } 237 Equality eq = new Equality(vlist, this); 238 Integer sample_cnt = sample_cnt_map.get(v); 239 if (sample_cnt != null) { 240 eq.setSamples(sample_cnt.intValue()); 241 } 242 v.equalitySet = eq; 243 newInvs.add(eq); 244 } 245 invs.addAll(newInvs); 246 } 247 248 /** 249 * Returns a List of Invariants that have been weakened/destroyed. However, this handles the 250 * creation of new Equality invariants and the instantiation of other invariants. 251 * 252 * @return a List of invariants that have been weakened 253 */ 254 // The basic approach is as follows: 255 // - Loop through each equality set 256 // - look for any variables that are no longer equal 257 // - Create new equality sets (call createEqualityInvs) 258 // - Get the new leaders 259 // - Create new slices and invariants (call CopyInvsFromLeader) 260 // 261 @Override 262 public List<Invariant> add(ValueTuple vt, int count) { 263 264 ArrayList<Equality> allNewInvs = new ArrayList<>(); 265 ArrayList<Invariant> weakenedInvs = new ArrayList<>(); 266 267 // Loop through each existing equality invariant 268 for (Invariant invar : invs) { 269 Equality inv = (Equality) invar; 270 271 // Add this sample to the invariant and track any vars that fall 272 // out of the set. 273 List<VarInfo> nonEqualVis = inv.add(vt, count); 274 275 // If some vars fell out 276 if (!nonEqualVis.isEmpty()) { 277 278 // Create new equality sets for all of the non-equal vars 279 List<Equality> newInvs = createEqualityInvs(nonEqualVis, vt, inv, count); 280 281 // Get a list of all of the new non-missing leaders 282 List<VarInfo> newInvsLeaders = new ArrayList<>(newInvs.size()); 283 for (Equality eq : newInvs) { 284 if ((parent.constants == null) || !parent.constants.is_missing(eq.leader())) { 285 newInvsLeaders.add(eq.leader()); 286 } 287 } 288 289 // Debug print the new leaders 290 if (Debug.logOn()) { 291 for (VarInfo nileader : newInvsLeaders) { 292 Debug.log( 293 getClass(), 294 parent, 295 Debug.vis(nileader), 296 "Split off from previous leader " 297 + inv.leader().name() 298 + ": new set = " 299 + nileader.equalitySet 300 + ": old set = " 301 + inv); 302 } 303 } 304 305 // Create new slices and invariants for each new leader 306 weakenedInvs.addAll(copyInvsFromLeader(inv.leader(), newInvsLeaders)); 307 308 // Keep track of all of the new invariants created. 309 allNewInvs.addAll(newInvs); 310 } 311 } 312 313 // Add all of the new equality sets to our list 314 invs.addAll(allNewInvs); 315 316 return weakenedInvs; 317 } 318 319 /** 320 * Dummy value that's incomparable to everything else to indicate missing in createEqualityInvs. 321 */ 322 private static final Object dummyMissing = new Object(); 323 324 /** 325 * Create a List of Equality invariants based on the values given by vt for the VarInfos in vis. 326 * Any variables that are out of bounds are forced into a separate equality set (since they no 327 * longer make sense and certainly shouldn't be equal to anything else) 328 * 329 * <p>Requires: vis.size() > 0 330 * 331 * <p>Ensures: result.size() > 0 332 * 333 * @param vis the VarInfos that were different from leader 334 * @param vt the ValueTuple associated with the VarInfos now 335 * @param leader the original leader of VarInfos 336 * @param count the number of samples seen (needed to set the number of samples for the new 337 * Equality invariants) 338 * @return a List of Equality invariants bundling together same values from vis, and if needed, 339 * another representing all the missing values 340 */ 341 private List<Equality> createEqualityInvs( 342 List<VarInfo> vis, ValueTuple vt, Equality leader, int count) { 343 assert !vis.isEmpty(); 344 HashMap<Object, List<VarInfo>> multiMap = new HashMap<>(); /* key is a value */ 345 List<VarInfo> out_of_bounds = new ArrayList<>(); 346 for (VarInfo vi : vis) { 347 if (vi.missingOutOfBounds()) { 348 out_of_bounds.add(vi); 349 } else if (vt.isMissing(vi)) { 350 addToBindingList(multiMap, dummyMissing, vi); 351 } else { 352 if (vi.getValue(vt) == null) { 353 System.out.printf( 354 "null value for variable %s, mod=%d at ppt %s%n", 355 vi.name(), vt.getModified(vi), parent.name()); 356 VarInfo rv = parent.find_var_by_name("return"); 357 assert rv != null : "@AssumeAssertion(nullness)"; 358 System.out.println("return value = " + Debug.toString(rv.getValue(vt))); 359 System.out.println("At line number " + FileIO.get_linenum()); 360 } 361 addToBindingList(multiMap, vi.getValue(vt), vi); 362 } 363 } 364 // Why use an array? Because we'll be sorting shortly 365 /*NNC:@MonotonicNonNull*/ Equality[] resultArray = 366 new Equality[multiMap.values().size() + out_of_bounds.size()]; 367 int resultCount = 0; 368 for (Map.Entry<@KeyFor("multiMap") Object, List<VarInfo>> entry : multiMap.entrySet()) { 369 Object key = entry.getKey(); 370 List<VarInfo> list = entry.getValue(); 371 assert !list.isEmpty(); 372 Equality eq = new Equality(list, this); 373 @SuppressWarnings("interning") // special value 374 boolean isMissing = (key == dummyMissing); 375 if (isMissing) { 376 eq.setSamples(leader.numSamples() - count); 377 } else { 378 eq.setSamples(leader.numSamples()); 379 } 380 if (debug.isLoggable(Level.FINE)) { 381 debug.fine(" created new inv: " + eq + " samples: " + eq.numSamples()); 382 } 383 resultArray[resultCount] = eq; 384 resultCount++; 385 } 386 for (VarInfo oob : out_of_bounds) { 387 List<VarInfo> list = Collections.singletonList(oob); 388 resultArray[resultCount] = new Equality(list, this); 389 resultCount++; 390 } 391 resultArray = castNonNullDeep(resultArray); // https://tinyurl.com/cfissue/986 392 393 // Sort for determinism 394 Arrays.sort(resultArray, EqualityComparator.theInstance); 395 List<Equality> result = Arrays.<Equality>asList(resultArray); 396 assert !result.isEmpty(); 397 return result; 398 } 399 400 /** 401 * Create a List of Equality invariants based on the VarInfos in vis. Assumes that the VarInfos in 402 * vis are not missing. The method is used exclusively for reversing optimizations in Daikon. 403 * 404 * <p>Requires: vis.size() > 0 405 * 406 * <p>Ensures: result.size() > 0 407 * 408 * @param vis the VarInfos that were different from leader 409 * @param leader the original leader of VarInfos 410 * @return a List of Equality invariants bundling together same values from vis 411 */ 412 public List<Equality> createEqualityInvs(List<VarInfo> vis, Equality leader) { 413 assert !vis.isEmpty(); 414 415 // Why use an array? Because we'll be sorting shortly 416 /*NNC:@MonotonicNonNull*/ Equality[] resultArray = new Equality[vis.size()]; 417 for (int i = 0; i < vis.size(); i++) { 418 VarInfo vi = vis.get(i); 419 List<VarInfo> list = new ArrayList<>(); 420 list.add(vi); 421 Equality eq = new Equality(list, this); 422 eq.setSamples(leader.numSamples()); 423 resultArray[i] = eq; 424 } 425 resultArray = castNonNullDeep(resultArray); // https://tinyurl.com/cfissue/986 426 427 // Sort for determinism 428 Arrays.sort(resultArray, PptSliceEquality.EqualityComparator.theInstance); 429 List<Equality> result = Arrays.<Equality>asList(resultArray); 430 assert !result.isEmpty(); 431 return result; 432 } 433 434 /** 435 * Map maps keys to non-empty lists of elements. This method adds var to the list mapped by key, 436 * creating a new list for key if one doesn't already exist. 437 * 438 * <p>Requires: Each value in map is a list of size 1 or greater 439 * 440 * <p>Ensures: Each value in map is a list of size 1 or greater 441 * 442 * @param map the map to add the bindings to 443 * @param key if there is already a List associated with key, then add value to key. Otherwise 444 * create a new List associated with key and insert value. 445 * @param value the value to insert into the List mapped to key 446 */ 447 private <T> void addToBindingList(Map<T, List<VarInfo>> map, T key, VarInfo value) { 448 if (key == null) { 449 throw new IllegalArgumentException(); 450 } 451 List<VarInfo> elements = map.computeIfAbsent(key, __ -> new ArrayList<VarInfo>()); 452 elements.add(value); 453 } 454 455 /** 456 * Instantiate invariants from each inv's leader. This is like instantiate_invariants at the start 457 * of reading the trace file, where we create new PptSliceNs. This is called when newVis have just 458 * split off from leader, and we want the leaders of newVis to have the same invariants as leader. 459 * 460 * <p>post: Adds the newly instantiated invariants and slices to this.parent. 461 * 462 * @param leader the old leader 463 * @param newVis a List of new VarInfos that used to be equal to leader. Actually, it's the list 464 * of canonical that were equal to leader, representing their own newly-created equality sets. 465 */ 466 public List<Invariant> copyInvsFromLeader(VarInfo leader, List<VarInfo> newVis) { 467 468 List<Invariant> falsified_invs = new ArrayList<>(); 469 List<PptSlice> newSlices = new ArrayList<>(); 470 if (debug.isLoggable(Level.FINE)) { 471 debug.fine( 472 "copyInvsFromLeader: " 473 + parent.name() 474 + ": leader " 475 + leader.name() 476 + ": new leaders = " 477 + newVis); 478 debug.fine(" orig slices count:" + parent.numViews()); 479 } 480 481 // Copy all possible combinations from the current ppt (with repetition) 482 // of replacing leader with different members of newVis. 483 484 // Loop through each slice. 485 // Uses old-style for loop and Iterator because it will be side-effected. 486 for (Iterator<PptSlice> i = parent.views_iterator(); i.hasNext(); ) { 487 PptSlice slice = i.next(); 488 489 if (debug.isLoggable(Level.FINE)) { 490 debug.fine(" Slice is: " + slice.toString()); 491 debug.fine(" With invs: " + slice.invs); 492 } 493 494 // If this slice contains the old leader 495 if (slice.containsVar(leader)) { 496 497 // Substitute new leader for old leader and create new slices/invs 498 VarInfo[] toFill = new VarInfo[slice.var_infos.length]; 499 copyInvsFromLeaderHelper(leader, newVis, slice, newSlices, 0, -1, toFill); 500 501 // Remove any statically obvious invariants in the old slice. 502 // This is called here because breaking up the equality set may 503 // cause some invariants to become statically obvious (because 504 // they will now be the only item in their set) 505 for (Invariant inv : slice.invs) { 506 if (!Daikon.dkconfig_undo_opts) { 507 if (inv.isObviousStatically_AllInEquality()) { 508 inv.falsify(); 509 falsified_invs.add(inv); 510 } 511 } 512 } 513 if (slice.invs.isEmpty()) { 514 i.remove(); 515 } 516 } 517 } 518 519 // Add each new slice with invariants 520 for (PptSlice slice : newSlices) { 521 if (slice.invs.isEmpty()) { 522 continue; 523 } 524 assert (parent.findSlice(slice.var_infos) == null) : parent.findSlice(slice.var_infos); 525 slice.repCheck(); 526 parent.addSlice(slice); 527 } 528 529 parent.repCheck(); 530 531 if (debug.isLoggable(Level.FINE)) { 532 debug.fine(" new slices count:" + parent.numViews()); 533 } 534 return falsified_invs; 535 } 536 537 /** 538 * Clones slice (zero or more times) such that instances of leader are replaced by members of 539 * newVis; places new slices in newSlices. The replacement is such that we get all combinations, 540 * with repetition of newVis and leader in every slot in slice where there used to be leader. For 541 * example, if slice contained (A1, A1, B) and A1 is leader and newVis contains A2 and A3, then 542 * the slices we produce would be: (A1, A2, B), (A1, A3, B), (A2, A2, B) (A2, A3, B), (A3, A3, B). 543 * We do not produce (A1, A1, B) because it is already there. We do not produce (A2, A1, B) 544 * because it is the same as (A1, A2, B) wrt combinations. This method does the main work of 545 * copyInvsFromLeader so that each new equality set that spawned off leader has the correct 546 * slices. It works as a nested series of for loops, whose depth is equal to the length of 547 * slice.var_infos. The position and loop arguments along with the call stack keep track of the 548 * loop nesting. When position reaches the end of slice.var_infos, this method attempts to 549 * instantiate the slice that has been produced. The standard start for position is 0, and for 550 * loop is -1. 551 * 552 * @param leader the variable to replace in slice 553 * @param newVis of VarInfos that will replace leader in combination in slice 554 * @param slice the slice to clone 555 * @param newSlices where to put the cloned slices 556 * @param position the position currently being replaced in source. Starts at 0. 557 * @param loop the iteration of the loop for this position. If -1, means the previous replacement 558 * is leader. 559 * @param soFar buffer to which assignments temporarily go before becoming instantiated. Has to 560 * equal slice.var_infos in length. 561 */ 562 private void copyInvsFromLeaderHelper( 563 VarInfo leader, 564 List<VarInfo> newVis, 565 PptSlice slice, 566 List<PptSlice> newSlices, 567 int position, 568 int loop, 569 VarInfo[] soFar) { 570 571 // Track debug if any variables are in newVis 572 Debug dlog = null; 573 if (Debug.logOn()) { 574 dlog = new Debug(getClass(), parent, newVis); 575 } 576 577 if (position >= slice.var_infos.length) { 578 // Done with assigning positions and recursion 579 if (parent.findSlice_unordered(soFar) == null) { 580 // If slice is already there, no need to clone. 581 582 if (parent.is_slice_ok(soFar, slice.arity())) { 583 PptSlice newSlice = slice.cloneAndPivot(soFar); 584 // Debug.debugTrack.fine ("LeaderHelper: Created Slice " + newSlice); 585 if (Debug.logOn()) { 586 assert dlog != null : "@AssumeAssertion(nullness): dependent: set if Debug.logOn()"; 587 dlog.log( 588 "Created slice " + newSlice + " Leader equality set = " + soFar[0].equalitySet); 589 Debug.log(getClass(), newSlice, "Created this slice"); 590 } 591 List<Invariant> invs = newSlice.invs; 592 for (Iterator<Invariant> iInvs = invs.iterator(); iInvs.hasNext(); ) { 593 Invariant inv = iInvs.next(); 594 if (!Daikon.dkconfig_undo_opts) { 595 if (inv.isObviousStatically_AllInEquality()) { 596 iInvs.remove(); 597 } 598 } 599 } 600 if (newSlice.invs.isEmpty()) { 601 Debug.log(debug, getClass(), newSlice, soFar, "slice not added because 0 invs"); 602 } else { 603 newSlices.add(newSlice); 604 } 605 } 606 } else { 607 if (Debug.logOn()) { 608 assert dlog != null : "@AssumeAssertion(nullness): dependent: set if Debug.logOn()"; 609 dlog.log("Slice already existed " + parent.findSlice_unordered(soFar)); 610 } 611 } 612 return; 613 } else { 614 // Not yet done with recursion, keep assigning to soFar 615 if (slice.var_infos[position] == leader) { 616 // If leader does need replacing 617 // newLoop starts at loop so that we don't have repeats 618 for (int newLoop = loop; newLoop < newVis.size(); newLoop++) { 619 VarInfo vi = newLoop == -1 ? leader : newVis.get(newLoop); 620 soFar[position] = vi; 621 // Advance position to next step, let next loop variable be 622 // this loop's counter. 623 copyInvsFromLeaderHelper(leader, newVis, slice, newSlices, position + 1, newLoop, soFar); 624 } 625 } else { 626 // Non leader position, just keep going after assigning soFar 627 soFar[position] = slice.var_infos[position]; 628 copyInvsFromLeaderHelper(leader, newVis, slice, newSlices, position + 1, loop, soFar); 629 } 630 } 631 } 632 633 @Override 634 public void repCheck() { 635 for (Invariant inv : invs) { 636 inv.repCheck(); 637 assert inv.ppt == this; 638 } 639 } 640 641 @SideEffectFree 642 @Override 643 public String toString(@GuardSatisfied PptSliceEquality this) { 644 StringBuilder result = new StringBuilder("PptSliceEquality: ["); 645 for (Invariant inv : invs) { 646 result.append(inv.repr()); 647 result.append(lineSep); 648 } 649 result.append(" ]"); 650 return result.toString(); 651 } 652 653 /** Order Equality invariants by the indices of leaders. */ 654 public static final class EqualityComparator implements Comparator<Equality> { 655 public static final EqualityComparator theInstance = new EqualityComparator(); 656 657 private EqualityComparator() {} 658 659 @Pure 660 @Override 661 public int compare(Equality eq1, Equality eq2) { 662 return VarInfo.IndexComparator.theInstance.compare(eq1.leader(), eq2.leader()); 663 } 664 } 665 666 /** Returns an array of all of the leaders sorted by varinfo_index for this equality view. */ 667 public VarInfo[] get_leaders_sorted() { 668 List<VarInfo> leaders = new ArrayList<>(invs.size()); 669 for (Invariant inv : invs) { 670 VarInfo leader = ((Equality) inv).leader(); 671 assert leader != null; 672 leaders.add(leader); 673 } 674 Collections.sort(leaders, VarInfo.IndexComparator.getInstance()); 675 return leaders.toArray(new VarInfo[0]); 676 } 677}