001package daikon.suppress; 002 003import daikon.Daikon; 004import daikon.Debug; 005import daikon.PptSlice; 006import daikon.PptTopLevel; 007import daikon.ValueTuple; 008import daikon.VarComparability; 009import daikon.VarInfo; 010import daikon.inv.Invariant; 011import daikon.inv.InvariantStatus; 012import daikon.inv.ValueSet; 013import daikon.inv.binary.BinaryInvariant; 014import daikon.inv.binary.twoScalar.IntEqual; 015import java.util.ArrayList; 016import java.util.Iterator; 017import java.util.LinkedHashMap; 018import java.util.LinkedHashSet; 019import java.util.List; 020import java.util.ListIterator; 021import java.util.Map; 022import java.util.Set; 023import java.util.concurrent.TimeUnit; 024import java.util.logging.Level; 025import java.util.logging.Logger; 026import org.checkerframework.checker.initialization.qual.UnknownInitialization; 027import org.checkerframework.checker.lock.qual.GuardSatisfied; 028import org.checkerframework.checker.mustcall.qual.MustCallUnknown; 029import org.checkerframework.checker.nullness.qual.EnsuresNonNull; 030import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 031import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 032import org.checkerframework.checker.nullness.qual.Nullable; 033import org.checkerframework.checker.nullness.qual.RequiresNonNull; 034import org.checkerframework.dataflow.qual.Pure; 035import org.checkerframework.dataflow.qual.SideEffectFree; 036import typequals.prototype.qual.Prototype; 037 038// Outstanding NIS todo list 039// 040// - Merging is slow when there are multiple children. 041// 042// - Move the missingOutOfBounds check to is_slice_ok() 043// 044 045/** Main class for non-instantiating suppression. Handles setup and other overall functions. */ 046public class NIS { 047 048 @SuppressWarnings("initialization.fields.uninitialized") // never instantiated 049 public NIS() { 050 throw new Error("Do not instantiate"); 051 } 052 053 /** Debug tracer. */ 054 public static final Logger debug = Logger.getLogger("daikon.suppress.NIS"); 055 056 /** Debug Tracer for antecedent method. */ 057 public static final Logger debugAnt = Logger.getLogger("daikon.suppress.NIS.Ant"); 058 059 /** Boolean. If true, enable non-instantiating suppressions. */ 060 public static boolean dkconfig_enabled = true; 061 062 /** Signifies which algorithm is used by NIS to process suppressions. */ 063 public enum SuppressionProcessor { 064 HYBRID, 065 ANTECEDENT, 066 FALSIFIED 067 } 068 069 /** 070 * Specifies the algorithm that NIS uses to process suppressions. Possible selections are 071 * 'HYBRID', 'ANTECEDENT', and 'FALSIFIED'. The default is the hybrid algorithm which uses the 072 * falsified algorithm when only a small number of suppressions need to be processed and the 073 * antecedent algorithm when a large number of suppressions are processed. 074 */ 075 public static SuppressionProcessor dkconfig_suppression_processor = SuppressionProcessor.HYBRID; 076 077 /** 078 * Boolean. If true, use antecedent method for NIS processing. If false, use falsified method for 079 * processing falsified invariants for NISuppressions. Note this flag is for internal use only and 080 * is controlled by NIS.dkconfig_suppression_processor. 081 */ 082 public static boolean antecedent_method = true; 083 084 /** 085 * Boolean. If true, use a combination of the falsified method for a small number of suppressions 086 * to be processed and the antecedent method for a large number. Number is determined by 087 * NIS.dkconfig_hybrid_threshhold. Note this flag is for internal use only and is controlled by 088 * NIS.dkconfig_suppression_processor. 089 */ 090 public static boolean hybrid_method = true; 091 092 /** 093 * Int. Less and equal to this number means use the falsified method in the hybrid method of 094 * processing falsified invariants, while greater than this number means use the antecedent 095 * method. Empirical data shows that number should not be more than 10000. 096 */ 097 public static int dkconfig_hybrid_threshhold = 2500; 098 099 /** 100 * Boolean. If true, use the specific list of suppressor related invariant prototypes when 101 * creating constant invariants in the antecedent method. 102 */ 103 public static boolean dkconfig_suppressor_list = true; 104 105 /** 106 * Boolean. If true, skip variables of file rep type {@code hashcode} when creating invariants 107 * over constants in the antecedent method. 108 */ 109 public static boolean dkconfig_skip_hashcode_type = true; 110 111 /** 112 * Possible states for suppressors and suppressions. When a suppression is checked, it sets one of 113 * these states on each suppressor. 114 */ 115 public enum SuppressState { 116 /** initial state -- suppressor has not been checked yet */ 117 NONE, 118 /** suppressor matches the falsified invariant */ 119 MATCH, 120 /** suppressor is true */ 121 VALID, 122 /** suppressor is not true */ 123 INVALID, 124 /** suppressor contains a variable that has always been nonsensical */ 125 NONSENSICAL 126 } 127 128 /** 129 * Map from invariant class to a list of all of the suppression sets that contain a suppressor of 130 * that class. 131 */ 132 public static @MonotonicNonNull Map<Class<? extends Invariant>, List<NISuppressionSet>> 133 suppressor_map; 134 135 /** 136 * Map from invariant class to the number of suppressions that contain a suppressor of that class. 137 */ 138 public static @MonotonicNonNull Map<Class<? extends Invariant>, Integer> 139 suppressor_map_suppression_count; 140 141 /** List of all suppressions. Is set by {@link #init_ni_suppression}. */ 142 static @MonotonicNonNull List<NISuppressionSet> all_suppressions; 143 144 /** List of suppressor invariant prototypes. */ 145 public static @MonotonicNonNull List<@Prototype Invariant> suppressor_proto_invs; 146 147 /** 148 * List of invariants that are unsuppressed by the current sample. The {@link #falsified} and 149 * {@link #process_falsified_invs} methods add created invariants to this list. This list is 150 * cleared by {@link #apply_samples}. 151 */ 152 public static List<Invariant> new_invs = new ArrayList<>(); 153 154 /** 155 * List of invariants that are unsuppressed and then falsified by the current sample. This list is 156 * cleared at the beginning of apply_samples() and falsified invariants are added as the current 157 * sample is applied to invariants in new_invs. The list is only used when the falsified method is 158 * used for processing suppressions. 159 */ 160 public static List<Invariant> newly_falsified = new ArrayList<>(); 161 162 // Statistics that are kept during processing. Some of these are kept 163 // and/or make sense for some approaches and not for others 164 165 /** Whether or not to keep statistics. */ 166 public static boolean keep_stats = false; 167 168 /** Number of falsified invariants in the program point. */ 169 public static int false_cnts = 0; 170 171 /** Number of falsified invariants in the program point that are potential suppressors. */ 172 public static int false_invs = 0; 173 174 /** Number of suppressions processed. */ 175 public static int suppressions_processed = 0; 176 177 /** Number of suppressions processed by the falsified method. */ 178 public static int suppressions_processed_falsified = 0; 179 180 /** Number of invariants that are no longer suppressed by a suppression. */ 181 static int new_invs_cnt = 0; 182 183 /** Number of new_invs_cnt that are falsified by the sample. */ 184 public static int false_invs_cnt = 0; 185 186 /** Number of invariants actually created. */ 187 public static int created_invs_cnt = 0; 188 189 /** Number of invariants that are still suppressed. */ 190 static int still_suppressed_cnt = 0; 191 192 /** Total time spent in NIS processing. */ 193 public static long duration = 0; 194 195 /** First execution of dump_stats(). Used to dump a header. */ 196 static boolean first_time = true; 197 198 /** 199 * Sets up non-instantiation suppression. Primarily this includes setting up the map from 200 * suppressor classes to all of the suppression sets associated with that suppressor invariant. 201 */ 202 @EnsuresNonNull({ 203 "suppressor_map", 204 "suppressor_map_suppression_count", 205 "all_suppressions", 206 "suppressor_proto_invs" 207 }) 208 public static void init_ni_suppression() { 209 210 // Creating these here, rather than where they are declared, allows 211 // this method to be called multiple times without a problem. 212 suppressor_map = new LinkedHashMap<>(256); 213 suppressor_map_suppression_count = new LinkedHashMap<>(256); 214 all_suppressions = new ArrayList<NISuppressionSet>(); 215 suppressor_proto_invs = new ArrayList<@Prototype Invariant>(); 216 217 // This should be the first statement in the method, but put it after the 218 // field initalizations so that the Initialization Checker doesn't complain. 219 if (!dkconfig_enabled) { 220 return; 221 } 222 223 // Get all defined suppressions. 224 for (Invariant inv : Daikon.proto_invs) { 225 NISuppressionSet ss = inv.get_ni_suppressions(); 226 if (ss != null) { 227 for (int j = 0; j < ss.suppression_set.length; j++) { 228 NISuppression sup = ss.suppression_set[j]; 229 if (true) { 230 assert inv.getClass() == sup.suppressee.sup_class 231 : "class " 232 + inv.getClass() 233 + " doesn't match " 234 + sup 235 + "/" 236 + sup.suppressee.sup_class; 237 assert inv.getClass() == ss.suppression_set[j].suppressee.sup_class 238 : "class " + inv.getClass() + " doesn't match " + ss.suppression_set[j]; 239 } 240 } 241 all_suppressions.add(ss); 242 } 243 } 244 245 // map suppressor classes to suppression sets 246 for (NISuppressionSet suppression_set : all_suppressions) { 247 suppression_set.add_to_suppressor_map(suppressor_map); 248 } 249 250 // If any suppressor is itself suppressed, augment the suppressions 251 // where the suppressor is used with the suppressor's suppressions. 252 for (List<NISuppressionSet> ss_list : suppressor_map.values()) { 253 for (NISuppressionSet ss : ss_list) { 254 NISuppressee suppressee = ss.get_suppressee(); 255 List<NISuppressionSet> suppressor_ss_list = suppressor_map.get(suppressee.sup_class); 256 if (suppressor_ss_list == null) { 257 continue; 258 } 259 for (NISuppressionSet suppressor_ss : suppressor_ss_list) { 260 suppressor_ss.recurse_definitions(ss); 261 } 262 } 263 } 264 265 // map suppressor classes to suppression sets 266 // (now that recursive definitions are in place) 267 suppressor_map.clear(); 268 for (NISuppressionSet suppression_set : all_suppressions) { 269 suppression_set.add_to_suppressor_map(suppressor_map); 270 } 271 272 if (NIS.dkconfig_suppressor_list) { 273 // Set up the list of suppressor invariant prototypes 274 for (@Prototype Invariant i : Daikon.proto_invs) { 275 if (suppressor_map.containsKey(i.getClass())) { 276 suppressor_proto_invs.add(i); 277 } 278 } 279 } 280 281 // count the number of suppressions associated with each 282 // suppressor 283 // if (NIS.hybrid_method) { 284 for (Class<? extends Invariant> a : suppressor_map.keySet()) { 285 286 int x = 0; 287 List<NISuppressionSet> ss_list = suppressor_map.get(a); 288 for (NISuppressionSet ss : ss_list) { 289 x += ss.suppression_set.length; 290 } 291 292 suppressor_map_suppression_count.put(a, x); 293 } 294 // } 295 296 if (Debug.logDetail() && debug.isLoggable(Level.FINE)) { 297 dump(debug); 298 } 299 } 300 301 /** 302 * Instantiates any invariants that are no longer suppressed because inv has been falsified. 303 * 304 * <p>Note: this method is should NOT be used with the antecedent approach. 305 * 306 * @see #process_falsified_invs 307 */ 308 @RequiresNonNull("suppressor_map") 309 public static void falsified(Invariant inv) { 310 311 if (!dkconfig_enabled || antecedent_method) { 312 return; 313 } 314 315 if (NIS.dkconfig_skip_hashcode_type) { 316 317 boolean hashFound = false; 318 319 for (VarInfo vi : inv.ppt.var_infos) { 320 if (vi.file_rep_type.isScalar() 321 && !vi.file_rep_type.isIntegral() 322 && !vi.file_rep_type.isArray()) { 323 hashFound = true; 324 } 325 } 326 327 if (hashFound) { 328 return; 329 } 330 } 331 332 // Get the suppression sets (if any) associated with this invariant 333 List<NISuppressionSet> ss_list = suppressor_map.get(inv.getClass()); 334 if (ss_list == null) { 335 return; 336 } 337 338 long startTime = 0; 339 // Count the number of falsified invariants that are antecedents 340 if (keep_stats) { 341 startTime = System.nanoTime(); 342 if (PptTopLevel.first_pass_with_sample && suppressor_map.containsKey(inv.getClass())) { 343 false_invs++; 344 } 345 } 346 347 // Process each suppression set 348 for (NISuppressionSet ss : ss_list) { 349 ss.clear_state(); 350 if (debug.isLoggable(Level.FINE)) { 351 debug.fine("processing suppression set " + ss + " over falsified inv " + inv.format()); 352 } 353 ss.falsified(inv, new_invs); 354 suppressions_processed += ss.suppression_set.length; 355 } 356 357 if (keep_stats) { 358 duration += (System.nanoTime() - startTime); 359 } 360 } 361 362 /** 363 * Applies sample values to all of the newly created invariants (kept in new_invs). The sample 364 * should never falsify the invariant (since we don't create an invariant if the sample would 365 * invalidate it). The sample still needs to be applied, however, for sample-dependent invariants. 366 * 367 * <p>Clears the new_invs list after processing. Currently this routine checks to insure that the 368 * newly falsified invariant is not itself a possible NI suppressor. 369 */ 370 public static void apply_samples(ValueTuple vt, int count) { 371 newly_falsified.clear(); 372 373 if (NIS.debug.isLoggable(Level.FINE)) { 374 NIS.debug.fine("Applying samples to " + new_invs.size() + " new invariants"); 375 } 376 377 // Loop through each invariant 378 for (Invariant inv : new_invs) { 379 if (inv.is_false()) { 380 assert !inv.is_false() 381 : String.format( 382 "inv %s in ppt %s is false before sample is applied ", inv.format(), inv.ppt); 383 } 384 385 // Looks to see if any variables are missing. This can happen 386 // when a variable not involved in the suppressor is missing on 387 // this sample. 388 boolean missing = false; 389 for (int j = 0; j < inv.ppt.var_infos.length; j++) { 390 if (inv.ppt.var_infos[j].isMissing(vt)) { 391 missing = true; 392 break; 393 } 394 } 395 396 // If no variables are missing, apply the sample 397 if (!missing) { 398 InvariantStatus result = inv.add_sample(vt, count); 399 if (result == InvariantStatus.FALSIFIED) { 400 if (NIS.antecedent_method) { 401 throw new Error( 402 "inv " 403 + inv.format() 404 + " falsified by sample " 405 + Debug.toString(inv.ppt.var_infos, vt) 406 + " at ppt " 407 + inv.ppt); 408 } else { 409 inv.falsify(); 410 newly_falsified.add(inv); 411 } 412 } 413 } 414 415 // Add the invariant to its slice 416 if (Debug.dkconfig_internal_check) { 417 assert inv.ppt.parent.findSlice(inv.ppt.var_infos) == inv.ppt; 418 } 419 inv.ppt.addInvariant(inv); 420 if (Debug.logOn()) { 421 inv.log("%s added to slice", inv.format()); 422 } 423 424 if (NIS.antecedent_method) { 425 created_invs_cnt++; 426 } 427 } 428 429 // Make a second pass through the new invariants and make sure that 430 // they are still not suppressed. They can become suppressed when 431 // there are recursive suppressions and the new suppressor wasn't 432 // yet created above when the invariant was first checked to see 433 // if it was suppressed. 434 for (Iterator<Invariant> i = new_invs.iterator(); i.hasNext(); ) { 435 Invariant inv = i.next(); 436 // inv.log ("Considering whether still suppressed in second pass"); 437 if (inv.is_ni_suppressed()) { 438 still_suppressed_cnt++; 439 inv.log("removed, still suppressed in second pass"); 440 inv.ppt.invs.remove(inv); 441 i.remove(); 442 } 443 } 444 445 new_invs.clear(); 446 } 447 448 /** Clears the current NIS statistics and enables the keeping of statistics. */ 449 public static void clear_stats() { 450 451 keep_stats = true; 452 duration = 0; 453 false_invs = 0; 454 false_cnts = 0; 455 suppressions_processed = 0; 456 suppressions_processed_falsified = 0; 457 new_invs_cnt = 0; 458 false_invs_cnt = 0; 459 created_invs_cnt = 0; 460 still_suppressed_cnt = 0; 461 } 462 463 public static void clear_sample_stats() { 464 keep_stats = true; 465 false_invs = 0; 466 false_cnts = 0; 467 suppressions_processed = 0; 468 suppressions_processed_falsified = 0; 469 new_invs_cnt = 0; 470 false_invs_cnt = 0; 471 created_invs_cnt = 0; 472 still_suppressed_cnt = 0; 473 } 474 475 public static void stats_header(Logger log) { 476 477 log.fine( 478 "false invs : " 479 + "suppressions processed : " 480 + "new invs cnt : " 481 + "false invs cnt : " 482 + "created invs cnt : " 483 + "still suppressed cnt : " 484 + "elapsed time msecs : " 485 + "ppt name"); 486 } 487 488 /** dump statistics on NIS to the specified logger. */ 489 public static void dump_stats(Logger log, PptTopLevel ppt) { 490 491 if (first_time) { 492 stats_header(log); 493 first_time = false; 494 } 495 496 if (false_invs > 0) { 497 log.fine( 498 false_invs 499 + " : " 500 + suppressions_processed 501 + " : " 502 + new_invs_cnt 503 + " : " 504 + false_invs_cnt 505 + " : " 506 + created_invs_cnt 507 + " : " 508 + still_suppressed_cnt 509 + " : " 510 + TimeUnit.NANOSECONDS.toMillis(duration) 511 + " msecs " 512 // + build_ants_msecs + " " + process_ants_msecs + " : " 513 + ppt.name); 514 } 515 } 516 517 /** 518 * Creates any invariants that were previously suppressed, but are no longer suppressed. Must be 519 * called after the sample has been processed and any invariants falsified by the sample are 520 * marked as such, but before they have been removed. 521 */ 522 @RequiresNonNull({ 523 "suppressor_map", 524 "suppressor_map_suppression_count", 525 "all_suppressions", 526 "NIS.suppressor_proto_invs" 527 }) 528 public static void process_falsified_invs(PptTopLevel ppt, ValueTuple vt) { 529 530 // if using the hybrid method, need to know the number of falsified suppressor 531 // invariants before deciding which method to use 532 if (NIS.hybrid_method) { 533 int count = 0; 534 for (Iterator<Invariant> i = ppt.invariants_iterator(); i.hasNext(); ) { 535 Invariant inv = i.next(); 536 537 if (NIS.dkconfig_skip_hashcode_type) { 538 539 boolean hashFound = false; 540 541 for (VarInfo vi : inv.ppt.var_infos) { 542 if (vi.file_rep_type.isHashcode()) { 543 hashFound = true; 544 } 545 } 546 547 if (hashFound) { 548 continue; 549 } 550 } 551 552 if (inv.is_false()) { 553 false_cnts++; 554 555 if (suppressor_map.containsKey(inv.getClass())) { 556 557 // use the following count update when splitting the hybrid method by the 558 // number of total suppressions associated with the falsified invariants 559 @SuppressWarnings("nullness") // map: same keys in suppressor_map and 560 // suppressor_map_suppression_count 561 int map_count = suppressor_map_suppression_count.get(inv.getClass()); 562 count += map_count; 563 suppressions_processed_falsified += map_count; 564 } 565 } 566 } 567 568 if (count > NIS.dkconfig_hybrid_threshhold) { 569 antecedent_method = true; 570 } else { 571 antecedent_method = false; 572 } 573 } 574 575 if (!dkconfig_enabled || !antecedent_method) { 576 return; 577 } 578 579 if (false) { 580 System.out.println("Variables for ppt " + ppt.name()); 581 for (int i = 0; i < ppt.var_infos.length; i++) { 582 VarInfo v = ppt.var_infos[i]; 583 ValueSet vs = v.get_value_set(); 584 System.out.printf( 585 " %s %s %s %b %s%n", 586 v.comparability, v.name(), v.file_rep_type, ppt.is_constant(v), vs.repr_short()); 587 } 588 } 589 590 // If there are no falsified invariants that are suppressors, there is nothing to do 591 int false_cnt = 0; 592 for (Iterator<Invariant> i = ppt.invariants_iterator(); i.hasNext(); ) { 593 Invariant inv = i.next(); 594 if (inv.is_false() && suppressor_map.containsKey(inv.getClass())) { 595 false_cnt++; 596 } 597 } 598 599 // System.out.printf("Invariants for ppt %s: %d%n", ppt, inv_cnt); 600 if (false_cnt == 0) { 601 return; 602 } 603 604 if (debugAnt.isLoggable(Level.FINE)) { 605 debugAnt.fine("at ppt " + ppt.name + " false_cnt = " + false_cnt); 606 } 607 // false_invs = false_cnt; 608 609 if (debugAnt.isLoggable(Level.FINE)) { 610 ppt.debug_invs(debugAnt); 611 } 612 613 // Find all antecedents and organize them by their variables comparability 614 Map<VarComparability, Antecedents> comp_ants = new LinkedHashMap<>(); 615 store_antecedents_by_comparability(ppt.views_iterator(), comp_ants); 616 617 if (ppt.constants != null) { 618 store_antecedents_by_comparability( 619 ppt.constants.create_constant_invs().iterator(), comp_ants); 620 } 621 622 if (debugAnt.isLoggable(Level.FINE)) { 623 for (Antecedents ants : comp_ants.values()) { 624 debugAnt.fine(ants.toString()); 625 } 626 } 627 628 // Add always-comparable antecedents to each of the other maps. 629 merge_always_comparable(comp_ants); 630 631 if (false) { 632 for (Antecedents ants : comp_ants.values()) { 633 List<Invariant> eq_invs = ants.get(IntEqual.class); 634 if ((eq_invs != null) && (eq_invs.size() > 1000)) { 635 Map<VarInfo, Count> var_map = new LinkedHashMap<>(); 636 System.out.printf( 637 "ppt %s, comparability %s has %s equality invs%n", 638 ppt.name, ants.comparability, eq_invs.size()); 639 for (Invariant inv : eq_invs) { 640 IntEqual ie = (IntEqual) inv; 641 VarInfo v1 = ie.ppt.var_infos[0]; 642 VarInfo v2 = ie.ppt.var_infos[1]; 643 if (ppt.is_constant(v1) && ppt.is_constant(v2)) { 644 System.out.printf("inv %s has two constant variables%n", ie.format()); 645 } 646 if (!v1.compatible(v2)) { 647 System.out.printf("inv %s has incompatible variables%n", ie.format()); 648 } 649 Count cnt = var_map.computeIfAbsent(v1, __ -> new Count(0)); 650 cnt.val++; 651 cnt = var_map.computeIfAbsent(v2, __ -> new Count(0)); 652 cnt.val++; 653 } 654 System.out.printf("%d distinct variables%n", var_map.size()); 655 for (VarInfo key : var_map.keySet()) { 656 Count cnt = var_map.get(key); 657 System.out.printf(" %s %s %d %n", key.comparability, key.name(), cnt.val); 658 } 659 } 660 } 661 } 662 663 // Remove any Antecedents without any falsified invariants. They can't 664 // possibly create any newly unsuppressed invariants 665 for (Iterator<Antecedents> i = comp_ants.values().iterator(); i.hasNext(); ) { 666 Antecedents ants = i.next(); 667 if (ants.false_cnt == 0) { 668 i.remove(); 669 } 670 } 671 if (debugAnt.isLoggable(Level.FINE)) { 672 for (Antecedents ants : comp_ants.values()) { 673 debugAnt.fine(ants.toString()); 674 } 675 } 676 677 // Loop through each suppression creating each invariant that 678 // is suppressed by that suppression. Each set of comparable antecedents 679 // is processed separately 680 Set<SupInv> unsuppressed_invs = new LinkedHashSet<>(); 681 for (NISuppressionSet ss : all_suppressions) { 682 for (NISuppression sup : ss) { 683 suppressions_processed++; 684 for (Antecedents ants : comp_ants.values()) { 685 sup.find_unsuppressed_invs(unsuppressed_invs, ants); 686 } 687 } 688 } 689 690 if (debugAnt.isLoggable(Level.FINE)) { 691 debugAnt.fine( 692 "Found " + unsuppressed_invs.size() + " unsuppressed invariants: " + unsuppressed_invs); 693 } 694 695 // Create each new unsuppressed invariant that is not still suppressed 696 // by a different suppression. Skip any that will be falsified by 697 // the sample. Checking the sample is faster than checking suppression 698 // and removes the invariant more often, so it is checked first 699 for (SupInv supinv : unsuppressed_invs) { 700 new_invs_cnt++; 701 if (supinv.check(vt) == InvariantStatus.FALSIFIED) { 702 supinv.log("unsuppressed inv falsified by sample"); 703 false_invs_cnt++; 704 continue; 705 } 706 if (supinv.is_ni_suppressed()) { 707 supinv.log("unsuppresed inv still suppressed"); 708 still_suppressed_cnt++; 709 continue; 710 } 711 Invariant inv = supinv.instantiate(ppt); 712 if (inv != null) { 713 if (Debug.dkconfig_internal_check) { 714 assert !inv.is_ni_suppressed() : "Still suppressed: " + inv.format(); 715 if (inv.ppt.find_inv_exact(inv) != null) { 716 throw new Error("inv " + inv.format() + " already exists in ppt " + ppt.name); 717 } 718 } 719 new_invs.add(inv); 720 } 721 } 722 } 723 724 /** 725 * Merges the always-comparable antecedents (if any) into each of the other sets of antecedents. 726 * Also removes the always-comparable set of antecedents as a separate set (since it is now merged 727 * into each of the other sets). Updates comp_ants accordingly. 728 * 729 * <p>In general, in implicit comparability, the variables at a program point are partioned into 730 * disjoint sets of comparable variables. However, implicit comparability also allows some 731 * variables to be comparable to all others (always-comparable). An invariant is always-comparable 732 * if all of its variables are always-comparable. Since always-comparable invariants can form 733 * suppressions with all other invariants, they must be added to each of set of comparable 734 * antecedents. 735 */ 736 @RequiresNonNull("NIS.suppressor_map") 737 static void merge_always_comparable(Map<VarComparability, Antecedents> comp_ants) { 738 739 // Find the antecedents that are always comparable (if any) 740 Antecedents compare_all = null; 741 for (VarComparability vc : comp_ants.keySet()) { 742 if (vc.alwaysComparable()) { 743 compare_all = comp_ants.get(vc); 744 break; 745 } 746 } 747 748 // Add always comparable antecedents to each of the other maps. 749 if ((compare_all != null) && (comp_ants.size() > 1)) { 750 for (Antecedents ants : comp_ants.values()) { 751 if (ants.alwaysComparable()) { 752 continue; 753 } 754 ants.add(compare_all); 755 } 756 comp_ants.remove(compare_all.comparability); 757 } 758 } 759 760 /** 761 * Creates all suppressed invariants for the specified ppt and places them in their associated 762 * slices. 763 * 764 * @return a list of created invariants 765 */ 766 @RequiresNonNull({"all_suppressions", "suppressor_map"}) 767 public static List<Invariant> create_suppressed_invs(PptTopLevel ppt) { 768 769 // Find all antecedents and organize them by their variables comparability 770 Map<VarComparability, Antecedents> comp_ants = new LinkedHashMap<>(); 771 store_antecedents_by_comparability(ppt.views_iterator(), comp_ants); 772 773 // Add always-comparable antecedents to each of the other maps. 774 merge_always_comparable(comp_ants); 775 776 // Loop through each suppression creating each invariant that 777 // is suppressed by that suppression. Each set of comparable antecedents 778 // is processed separately. 779 Set<SupInv> suppressed_invs = new LinkedHashSet<>(); 780 for (NISuppressionSet ss : all_suppressions) { 781 for (NISuppression sup : ss) { 782 for (Antecedents ants : comp_ants.values()) { 783 sup.find_suppressed_invs(suppressed_invs, ants); 784 } 785 } 786 } 787 788 // Create each invariant and add it to its slice. 789 List<Invariant> created_invs = new ArrayList<>(suppressed_invs.size()); 790 for (SupInv supinv : suppressed_invs) { 791 Invariant inv = supinv.instantiate(ppt); 792 if (inv != null) { 793 if (Debug.dkconfig_internal_check) { 794 assert inv.ppt.find_inv_exact(inv) == null; 795 } 796 inv.ppt.addInvariant(inv); 797 created_invs.add(inv); 798 } 799 } 800 801 return created_invs; 802 } 803 804 /** 805 * Adds each antecedent invariant in the specified slices to the Antecedents object in comp_ants 806 * with the corresponding VarComparability. 807 */ 808 @RequiresNonNull("suppressor_map") 809 static void store_antecedents_by_comparability( 810 Iterator<PptSlice> slice_iterator, Map<VarComparability, Antecedents> comp_ants) { 811 812 for (Iterator<PptSlice> i = slice_iterator; i.hasNext(); ) { 813 PptSlice slice = i.next(); 814 815 if (NIS.dkconfig_skip_hashcode_type) { 816 817 boolean hashFound = false; 818 819 for (VarInfo vi : slice.var_infos) { 820 if (vi.file_rep_type.isHashcode()) { 821 hashFound = true; 822 } 823 } 824 825 if (hashFound) { 826 continue; 827 } 828 } 829 830 for (Invariant inv : slice.invs) { 831 if (!is_suppressor(inv.getClass())) { 832 continue; 833 } 834 835 if (inv.is_false()) { 836 false_invs++; 837 } 838 839 VarComparability vc = inv.get_comparability(); 840 Antecedents ants = comp_ants.computeIfAbsent(vc, Antecedents::new); 841 ants.add(inv); 842 // if (Debug.logOn()) 843 // inv.log ("Added to antecedent map " + inv.format() + " compare = " 844 // + vc); 845 } 846 } 847 } 848 849 /** 850 * Processes each slice in slice_iterator and fills the specified map with a list of all of the 851 * antecedent invariants for each class. @return the number of false antecedents found 852 */ 853 @RequiresNonNull("suppressor_map") 854 static int find_antecedents( 855 Iterator<PptSlice> slice_iterator, 856 Map<Class<? extends Invariant>, List<Invariant>> antecedent_map) { 857 858 int false_cnt = 0; 859 860 while (slice_iterator.hasNext()) { 861 PptSlice slice = slice_iterator.next(); 862 for (Invariant inv : slice.invs) { 863 if (!is_suppressor(inv.getClass())) { 864 continue; 865 } 866 if (inv.is_false()) { 867 false_cnt++; 868 } 869 List<Invariant> antecedents = 870 antecedent_map.computeIfAbsent(inv.getClass(), __ -> new ArrayList<Invariant>()); 871 antecedents.add(inv); 872 } 873 } 874 875 return false_cnt; 876 } 877 878 /** Removes any invariants in the specified ppt that are suppressed. */ 879 public static void remove_suppressed_invs(PptTopLevel ppt) { 880 881 for (PptSlice slice : ppt.views_iterable()) { 882 // Old-style for loop with Iterator because it will be side-effected 883 for (Iterator<Invariant> j = slice.invs.iterator(); j.hasNext(); ) { 884 Invariant inv = j.next(); 885 if (inv.is_ni_suppressed()) { 886 inv.log("Removed because suppressed %s", inv.format()); 887 j.remove(); 888 } 889 } 890 } 891 } 892 893 /** 894 * Returns true if the specified class is an antecedent in any NI suppression. 895 * 896 * @param cls the class for some subtype of Invariant 897 * @return true if the specified class is an antecedent in any NI suppression 898 */ 899 @RequiresNonNull("NIS.suppressor_map") 900 @Pure 901 public static boolean is_suppressor(Class<? extends @MustCallUnknown Invariant> cls) { 902 return suppressor_map.containsKey(cls); 903 } 904 905 /** Dump out the suppressor map. */ 906 @RequiresNonNull("suppressor_map") 907 public static void dump(Logger log) { 908 909 if (!log.isLoggable(Level.FINE)) { 910 return; 911 } 912 913 for (Class<? extends Invariant> sclass : suppressor_map.keySet()) { 914 List<NISuppressionSet> suppression_set_list = suppressor_map.get(sclass); 915 for (ListIterator<NISuppressionSet> j = suppression_set_list.listIterator(); j.hasNext(); ) { 916 NISuppressionSet ss = j.next(); 917 if (j.previousIndex() > 0) { 918 log.fine(String.format(" : %s", ss)); 919 } else { 920 log.fine(String.format("%s: %s", sclass, ss)); 921 } 922 } 923 } 924 } 925 926 /** 927 * Class used to describe invariants without instantiating the invariant. The invariant is defined 928 * by its NISuppressee and variables (Its ppt is also stored, but not used in comparisions, its 929 * presumed that only SupInvs from the same ppt will every be compared.) 930 */ 931 static class SupInv { 932 NISuppressee suppressee; 933 VarInfo[] vis; 934 PptTopLevel ppt; 935 936 /** Create an invariant definition for a suppressed invariant. */ 937 public SupInv(NISuppressee suppressee, VarInfo[] vis, PptTopLevel ppt) { 938 this.suppressee = suppressee; 939 this.vis = vis; 940 this.ppt = ppt; 941 if (Debug.logOn()) { 942 log("Created " + suppressee); 943 } 944 } 945 946 /** Track Log the specified message. */ 947 public void log(@UnknownInitialization(SupInv.class) SupInv this, String message) { 948 if (Debug.logOn()) { 949 Debug.log(suppressee.sup_class, ppt, vis, message); 950 } 951 } 952 953 /** Equal iff classes / swap variable / and variables match exactly. */ 954 @EnsuresNonNullIf(result = true, expression = "#1") 955 @Pure 956 @Override 957 public boolean equals(@GuardSatisfied SupInv this, @GuardSatisfied @Nullable Object obj) { 958 if (!(obj instanceof SupInv)) { 959 return false; 960 } 961 962 // Class and variables must match 963 SupInv sinv = (SupInv) obj; 964 if (sinv.suppressee.sup_class != suppressee.sup_class) { 965 return false; 966 } 967 if (vis.length != sinv.vis.length) { 968 return false; 969 } 970 for (int i = 0; i < vis.length; i++) { 971 if (vis[i] != sinv.vis[i]) { 972 return false; 973 } 974 } 975 976 // Binary invariants must match swap var as well 977 if (suppressee.var_count == 2) { 978 if (sinv.suppressee.get_swap() != suppressee.get_swap()) { 979 return false; 980 } 981 } 982 983 return true; 984 } 985 986 /** Hash on class and variables. */ 987 @Pure 988 @Override 989 public int hashCode(@GuardSatisfied SupInv this) { 990 int code = suppressee.sup_class.hashCode(); 991 for (int i = 0; i < vis.length; i++) { 992 code += vis[i].hashCode(); 993 } 994 return code; 995 } 996 997 /** Check this invariant against the sample and return the result. */ 998 public InvariantStatus check(ValueTuple vt) { 999 return suppressee.check(vt, vis); 1000 } 1001 1002 /** Returns true if the invariant is still suppressed. */ 1003 @SuppressWarnings("all:purity") // new object is not returned 1004 @Pure 1005 public boolean is_ni_suppressed() { 1006 1007 NISuppressionSet ss = suppressee.sample_inv.get_ni_suppressions(); 1008 assert ss != null 1009 : "@AssumeAssertion(nullness): dependent: this invariant's class can be suppressed, so" 1010 + " ss != null"; 1011 return ss.suppressed(ppt, vis); 1012 } 1013 1014 /** Instantiate this invariant on the specified ppt. */ 1015 public @Nullable Invariant instantiate(PptTopLevel ppt) { 1016 return suppressee.instantiate(vis, ppt); 1017 } 1018 1019 /** 1020 * Returns the invariant if it already exists, or null otherwise. Unary and and ternary 1021 * invariant must match by class (there are no permutations for unary invariants and ternary 1022 * invariants handle permutations as different classes). Binary invariants must match the class 1023 * and if there is an internal swap variable for variable order, that must match as well. 1024 */ 1025 public @Nullable Invariant already_exists() { 1026 Invariant cinv = ppt.find_inv_by_class(vis, suppressee.sup_class); 1027 if (cinv == null) { 1028 return null; 1029 } 1030 if (suppressee.var_count != 2) { 1031 return cinv; 1032 } 1033 BinaryInvariant binv = (BinaryInvariant) cinv; 1034 if (binv.is_symmetric()) { 1035 return cinv; 1036 } 1037 if (binv.get_swap() != suppressee.get_swap()) { 1038 return null; 1039 } 1040 return cinv; 1041 } 1042 1043 /** Return string representation of the suppressed invariant. */ 1044 @SideEffectFree 1045 @Override 1046 public String toString(@GuardSatisfied SupInv this) { 1047 String[] names = new String[vis.length]; 1048 for (int i = 0; i < vis.length; i++) { 1049 names[i] = vis[i].name(); 1050 } 1051 return suppressee + "[" + String.join(", ", names) + "]"; 1052 } 1053 } 1054 1055 /** Class that organizes all of the antecedent invariants with the same comparability by class. */ 1056 static class Antecedents { 1057 1058 /** 1059 * Comparability of the variables in the antecedents. Only variables that are comparable should 1060 * be stored here. 1061 */ 1062 VarComparability comparability; 1063 1064 /** 1065 * Map from the antecedent invariants class to a list of the antecedent invariants of that 1066 * class. Allows fast access to invariants by type. 1067 */ 1068 Map<Class<? extends Invariant>, List<Invariant>> antecedent_map; 1069 1070 /** Number of antecedents that are false. */ 1071 int false_cnt = 0; 1072 1073 /** Create with specified comparability. */ 1074 public Antecedents(VarComparability comparability) { 1075 1076 antecedent_map = new LinkedHashMap<>(); 1077 this.comparability = comparability; 1078 } 1079 1080 /** Returns true if this contains antecedents that are always comparable. */ 1081 public boolean alwaysComparable() { 1082 return comparability.alwaysComparable(); 1083 } 1084 1085 /** 1086 * Adds the specified invariant to the list for its class. Falsified invariants are added to the 1087 * beginning of the list, non-falsified ones to the end. 1088 */ 1089 @RequiresNonNull("NIS.suppressor_map") 1090 public void add(Invariant inv) { 1091 1092 // Only possible antecedents need to be added 1093 if (!is_suppressor(inv.getClass())) { 1094 return; 1095 } 1096 1097 // Only antecedents comparable to this one should be added 1098 assert VarComparability.comparable(inv.get_comparability(), comparability); 1099 1100 // Ignore antecedents that are missing out of bounds. They can't 1101 // create any valid invariants (since the suppressee is always over 1102 // the same variables 1103 for (int i = 0; i < inv.ppt.var_infos.length; i++) { 1104 VarInfo v = inv.ppt.var_infos[i]; 1105 if (v.missingOutOfBounds()) { 1106 return; 1107 } 1108 } 1109 1110 if (inv.is_false()) { 1111 false_cnt++; 1112 } 1113 1114 // Add the invariant to the map for its class 1115 List<Invariant> antecedents = get(inv.getClass()); 1116 if (antecedents == null) { 1117 antecedents = new ArrayList<Invariant>(); 1118 antecedent_map.put(inv.getClass(), antecedents); 1119 } 1120 if (inv.is_false()) { 1121 antecedents.add(0, inv); 1122 } else { 1123 antecedents.add(inv); 1124 } 1125 } 1126 1127 /** Adds all of the antecedents specified to the lists for their class. */ 1128 @RequiresNonNull("NIS.suppressor_map") 1129 public void add(Antecedents ants) { 1130 1131 for (List<Invariant> invs : ants.antecedent_map.values()) { 1132 for (Invariant inv : invs) { 1133 add(inv); 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Returns a list of all of the antecedent invariants of the specified class. Returns null if 1140 * there are none of that class. 1141 */ 1142 public @Nullable List<Invariant> get(Class<? extends Invariant> cls) { 1143 1144 return antecedent_map.get(cls); 1145 } 1146 1147 /** Returns a string representation of all of the antecedents by class. */ 1148 @SideEffectFree 1149 @Override 1150 public String toString(@GuardSatisfied Antecedents this) { 1151 1152 String out = "Comparability " + comparability + " : "; 1153 1154 for (Class<? extends Invariant> iclass : antecedent_map.keySet()) { 1155 out += iclass.getSimpleName() + " : "; 1156 List<Invariant> ilist = antecedent_map.get(iclass); 1157 for (Invariant inv : ilist) { 1158 if (inv.is_false()) { 1159 out += inv.format() + "[FALSE] "; 1160 } else { 1161 out += inv.format() + " "; 1162 } 1163 } 1164 out += " : "; 1165 } 1166 1167 return out; 1168 } 1169 } 1170 1171 static class Count { 1172 public int val; 1173 1174 Count(int val) { 1175 this.val = val; 1176 } 1177 } 1178}