001package daikon.suppress; 002 003import static daikon.tools.nullness.NullnessUtil.castNonNullDeep; 004 005import daikon.Debug; 006import daikon.Global; 007import daikon.PptTopLevel; 008import daikon.VarInfo; 009import daikon.inv.Invariant; 010import java.util.ArrayList; 011import java.util.Arrays; 012import java.util.Iterator; 013import java.util.List; 014import java.util.Set; 015import org.checkerframework.checker.lock.qual.GuardSatisfied; 016import org.checkerframework.checker.nullness.qual.NonNull; 017import org.checkerframework.checker.nullness.qual.Nullable; 018import org.checkerframework.dataflow.qual.SideEffectFree; 019import org.plumelib.util.StringsPlume; 020 021/** 022 * Class that defines a single non-instantiating suppression. A suppression consists of one or more 023 * suppressors and a suppressee. If each of the suppressors is true they imply the suppressee. 024 */ 025public class NISuppression { 026 027 /** Set of suppressor invariants. */ 028 NISuppressor[] suppressors; 029 030 /** Suppressee invariant. */ 031 NISuppressee suppressee; 032 033 private boolean debug = false; 034 035 public NISuppression(NISuppressor[] suppressor_set, NISuppressee suppressee) { 036 037 suppressors = suppressor_set; 038 this.suppressee = suppressee; 039 } 040 041 /** 042 * Creates a NISuppression. 043 * 044 * @param suppressor_set the suppressor set 045 * @param suppressee the suppressee 046 */ 047 public NISuppression(List<NISuppressor> suppressor_set, NISuppressee suppressee) { 048 049 suppressors = suppressor_set.toArray(new NISuppressor[0]); 050 this.suppressee = suppressee; 051 } 052 053 public NISuppression(NISuppressor sup1, NISuppressee suppressee) { 054 055 this(new NISuppressor[] {sup1}, suppressee); 056 } 057 058 public NISuppression(NISuppressor sup1, NISuppressor sup2, NISuppressee suppressee) { 059 060 this(new NISuppressor[] {sup1, sup2}, suppressee); 061 } 062 063 public NISuppression( 064 NISuppressor sup1, NISuppressor sup2, NISuppressor sup3, NISuppressee suppressee) { 065 066 this(new NISuppressor[] {sup1, sup2, sup3}, suppressee); 067 } 068 069 public NISuppression( 070 NISuppressor sup1, 071 NISuppressor sup2, 072 NISuppressor sup3, 073 NISuppressor sup4, 074 NISuppressee suppressee) { 075 076 this(new NISuppressor[] {sup1, sup2, sup3, sup4}, suppressee); 077 } 078 079 public NISuppression( 080 NISuppressor sup1, 081 NISuppressor sup2, 082 NISuppressor sup3, 083 NISuppressor sup4, 084 NISuppressor sup5, 085 NISuppressee suppressee) { 086 087 this(new NISuppressor[] {sup1, sup2, sup3, sup4, sup5}, suppressee); 088 } 089 090 public Iterator<NISuppressor> suppressor_iterator() { 091 return Arrays.<NISuppressor>asList(suppressors).iterator(); 092 } 093 094 /** 095 * Checks this suppression. Each suppressor is checked to see if it matches inv and if not, 096 * whether or not it is valid (true). The results are saved in each suppressor. The suppressor 097 * results are used later by {@link #invalidated()}. 098 * 099 * @param ppt program point in which to check suppression 100 * @param vis variables over which to check suppression 101 * @param inv falsified invariant (if any). Any suppressor that matches inv will be marked as 102 * NIS.SuppressState.MATCH 103 * @return NIS.SuppressState.VALID if the suppression is valid, NIS.SuppressState.NONSENSICAL if 104 * one or more suppressors were nonsensical and the rest were valid, NIS.SuppressState.INVALID 105 * otherwise 106 */ 107 public NIS.SuppressState check(PptTopLevel ppt, VarInfo[] vis, @Nullable Invariant inv) { 108 109 NIS.SuppressState status = NIS.SuppressState.VALID; 110 boolean set = false; 111 for (int i = 0; i < suppressors.length; i++) { 112 NISuppressor ssor = suppressors[i]; 113 NIS.SuppressState st = ssor.check(ppt, vis, inv); 114 115 if (!set) { 116 if (st == NIS.SuppressState.NONSENSICAL) { 117 status = NIS.SuppressState.NONSENSICAL; 118 } else if (st != NIS.SuppressState.VALID) { 119 status = NIS.SuppressState.INVALID; 120 if (st == NIS.SuppressState.INVALID) { 121 return status; 122 } 123 // !valid in this case means invalid or match 124 // If invalid, then stop immediately 125 // otherwise, check state of the rest of the suppressors 126 // This check is needed because we are reviving the 127 // falsified method so match is now a valid status. 128 set = true; 129 } 130 } 131 } 132 return status; 133 } 134 135 /** 136 * Determines whether or not the falsified invariant previously passed to {@link 137 * #check(PptTopLevel,VarInfo[],Invariant)} was the first suppressor to be falsified in this 138 * suppression. If the falsified invariant is not involved in this suppression, then it can't have 139 * been invalidated. 140 */ 141 public boolean invalidated() { 142 143 // We return true when every suppressor except the falsified 144 // one is valid and at least one suppressor matches the falsified 145 // invariant. Note that match can be true on more than one 146 // suppressor due to reflexive (x, x, x) invariants. In this 147 // code, the suppressor should never be nonsensical, since we should 148 // have never looked at a slice with nonsensical variables. 149 boolean inv_match = false; 150 for (int i = 0; i < suppressors.length; i++) { 151 NISuppressor ssor = suppressors[i]; 152 assert ssor.state != NIS.SuppressState.NONSENSICAL; 153 if (ssor.state == NIS.SuppressState.MATCH) { 154 inv_match = true; 155 } else if (ssor.state != NIS.SuppressState.VALID) { 156 return false; 157 } 158 } 159 return inv_match; 160 } 161 162 /** 163 * Finds all of the invariants that are suppressed by this suppression. 164 * 165 * @param suppressed_invs any invariants that are suppressed by the antecedent invariants in 166 * {@code ants} using this suppression are added to this set 167 * @param ants antecedents organized by class 168 */ 169 public void find_suppressed_invs(Set<NIS.SupInv> suppressed_invs, NIS.Antecedents ants) { 170 171 // debug = suppressee.sup_class.getName().indexOf("NonZero") != -1; 172 if (debug) { 173 System.out.println("In find_suppressed_invs for " + this); 174 } 175 176 // Get the antecedents that match our suppressors. Return if there are 177 // no antecedents for a particular suppressor. 178 List<Invariant>[] antecedents = antecedents_for_suppressors(ants); 179 if (antecedents == null) { 180 return; 181 } 182 183 // Recursively check each combination of possible antecedents that 184 // match our suppressors for suppressions 185 VarInfo vis[] = new VarInfo[suppressee.var_count]; 186 find_suppressed_invs(suppressed_invs, antecedents, vis, 0); 187 188 if (debug) { 189 System.out.println(" suppressed invariants: " + suppressed_invs); 190 } 191 } 192 193 /** 194 * Finds invariants that have become unsuppressed (one or more of their antecedent invariants is 195 * falsified). The invariant may still be suppressed by a different suppression. 196 * 197 * @param unsuppressed_invs any invariants that are suppressed by the antecedent invariants in 198 * ants using this suppression are added to this set if one or more of the antecedents are 199 * falsified 200 * @param ants antecedents organized by class 201 */ 202 public void find_unsuppressed_invs(Set<NIS.SupInv> unsuppressed_invs, NIS.Antecedents ants) { 203 204 // debug = suppressee.sup_class.getName().indexOf("SeqIntLessEqual") != -1; 205 206 // Get the antecedents that match our suppressors. Return if there are 207 // no antecedents for a particular suppressor. 208 List<Invariant>[] antecedents = antecedents_for_suppressors(ants); 209 if (antecedents == null) { 210 return; 211 } 212 213 int total_false_cnt = 0; 214 for (int i = 0; i < antecedents.length; i++) { 215 List<Invariant> a = antecedents[i]; 216 int false_cnt = 0; 217 for (Invariant inv : a) { 218 if (inv.is_false()) { 219 false_cnt++; 220 } 221 } 222 223 total_false_cnt += false_cnt; 224 } 225 226 if (total_false_cnt == 0) { 227 return; 228 } 229 230 // Recursively check each combination of possible antecedents that 231 // match our suppressors for suppressions 232 VarInfo vis[] = new VarInfo[suppressee.var_count]; 233 // int old_size = unsuppressed_invs.size(); 234 Invariant[] cinvs = new Invariant[antecedents.length]; 235 find_unsuppressed_invs(unsuppressed_invs, antecedents, vis, 0, false, cinvs); 236 if (debug) { 237 System.out.println(" unsuppressed invariants: " + unsuppressed_invs); 238 } 239 } 240 241 /** 242 * Recursively finds suppressed invariants. The cross product of antecedents for each suppressor 243 * are examined and each valid combination will yield an entry in suppressed_invs. 244 * 245 * @param unsuppressed_invs this set is updated with any invariants that used to be suppressed, 246 * but no longer are 247 * @param antecedents array of antecedents per suppressor 248 * @param vis current variables for the suppressed invariant As antecedents are chosen, their 249 * variables are placed into vis 250 * @param idx current index into suppressors and antecedents 251 * @see #find_unsuppressed_invs (Set, List, VarInfo[], int, boolean) 252 * @see #consider_inv (Invariant, NISuppressor, VarInfo[]) 253 */ 254 private void find_suppressed_invs( 255 Set<NIS.SupInv> unsuppressed_invs, List<Invariant> antecedents[], VarInfo vis[], int idx) { 256 257 // Loop through each antecedent that matches the current suppressor 258 NISuppressor s = suppressors[idx]; 259 for (Invariant inv : antecedents[idx]) { 260 PptTopLevel ppt = inv.ppt.parent; 261 assert ppt.equality_view != null : "@AssumeAssertion(nullness): need to check justification"; 262 263 // See if this antecedent can be used with the ones we have found so far 264 VarInfo[] cvis = consider_inv(inv, s, vis); 265 if (cvis == null) { 266 continue; 267 } 268 269 // If this is the last suppressor 270 if ((idx + 1) == suppressors.length) { 271 272 // Create descriptions of the suppressed invariants 273 List<NIS.SupInv> new_invs = suppressee.find_all(cvis, ppt, null); 274 unsuppressed_invs.addAll(new_invs); 275 276 // Check to insure that none of the invariants already exists 277 if (Debug.dkconfig_internal_check) { 278 for (NIS.SupInv supinv : new_invs) { 279 Invariant cinv = supinv.already_exists(); 280 if (cinv != null) { 281 @SuppressWarnings("nullness") 282 @NonNull NISuppressionSet ss = cinv.get_ni_suppressions(); 283 // this is apparently called for side effect (debugging output) 284 ss.suppressed(cinv.ppt); 285 throw new Error( 286 "inv " 287 + cinv.repr() 288 + " of class " 289 + supinv.suppressee 290 + " already exists in ppt " 291 + ppt.name 292 + " suppressionset = " 293 + ss 294 + " suppression = " 295 + this 296 + " last antecedent = " 297 + inv.format()); 298 } 299 } 300 } 301 } else { 302 // Recursively process the next suppressor 303 find_suppressed_invs(unsuppressed_invs, antecedents, cvis, idx + 1); 304 } 305 } 306 } 307 308 /** 309 * Recursively finds unsuppressed invariants. The cross product of antecedents for each suppressor 310 * is examined and each valid combination with at least one falsified antecedent will yield an 311 * entry in unsuppressed_invs. 312 * 313 * @param unsuppressed_invs this set is updated with any invariants that were suppressed, but one 314 * of the suppressors is falsified (thus, the invariant is no longer suppressed) 315 * @param antecedents array of antecedents per suppressor 316 * @param vis current variables for the suppressed invariant As antecedents are chosen, their 317 * variables are placed into vis 318 * @param idx current index into suppressors and antecedents 319 * @param false_antecedents true if a false antecedent has been found 320 * @param cinvs the invariants associated with the current set of antecedents. Used only for debug 321 * printing. May be side-effected by having cinvs[idx] set to null. 322 * @see find_unsuppressed_invs (Set, List, VarInfo[], int) 323 * @see #consider_inv (Invariant, NISuppressor, VarInfo[]) 324 */ 325 private void find_unsuppressed_invs( 326 Set<NIS.SupInv> unsuppressed_invs, 327 List<Invariant> antecedents[], 328 VarInfo vis[], 329 int idx, 330 boolean false_antecedents, 331 @Nullable Invariant[] cinvs) { 332 333 boolean all_true_at_end = ((idx + 1) == suppressors.length) && !false_antecedents; 334 335 // Loop through each antecedent that matches the current suppressor 336 NISuppressor s = suppressors[idx]; 337 for (Invariant inv : antecedents[idx]) { 338 PptTopLevel ppt = inv.ppt.parent; 339 assert ppt.equality_view != null : "@AssumeAssertion(nullness): need to check justification"; 340 cinvs[idx] = inv; 341 342 // If this is the last suppressor, no previous antecedents were 343 // false, and this antecedent is not false either, we can stop 344 // checking. The antecedent lists are sorted so that the false 345 // ones are first. There is no need to look at antecedents that 346 // are all true. 347 if (all_true_at_end && !inv.is_false()) { 348 cinvs[idx] = null; 349 return; 350 } 351 // See if this antecedent can be used with the ones we have found so far 352 VarInfo[] cvis = consider_inv(inv, s, vis); 353 if (cvis == null) { 354 continue; 355 } 356 357 // If this is the last suppressor 358 if ((idx + 1) == suppressors.length) { 359 360 // JHP: this check can be removed if the earlier check for all 361 // true antecedents is included. 362 if (!false_antecedents && !inv.is_false()) { 363 if (debug) { 364 System.out.printf("Skipping %s, no false antecedents%n", Arrays.toString(cvis)); 365 } 366 continue; 367 } 368 369 // Create descriptions of the suppressed invariants 370 List<NIS.SupInv> new_invs = suppressee.find_all(cvis, ppt, cinvs); 371 if (debug) { 372 System.out.printf("created %s new invariants", new_invs); 373 } 374 unsuppressed_invs.addAll(new_invs); 375 376 // Check to insure that none of the invariants already exists 377 if (Debug.dkconfig_internal_check) { 378 for (NIS.SupInv supinv : new_invs) { 379 Invariant cinv = supinv.already_exists(); 380 if (cinv != null) { 381 throw new Error( 382 "inv " 383 + cinv.format() 384 + " of class " 385 + supinv.suppressee 386 + " already exists in ppt " 387 + ppt.name); 388 } 389 } 390 } 391 } else { 392 // Recursively process the next suppressor 393 find_unsuppressed_invs( 394 unsuppressed_invs, 395 antecedents, 396 cvis, 397 idx + 1, 398 false_antecedents || inv.is_false(), 399 cinvs); 400 } 401 } 402 cinvs[idx] = null; 403 } 404 405 /** 406 * Determine if the specified invariant can be used as part of this suppression. The invariant 407 * must match suppressor and its variables must match up with any antecedents that have been 408 * previously processed. As invariants are processed by this method, their variables are added to 409 * the slots in vis that correspond to their suppressor. 410 * 411 * <p>For example, consider the invariant 'result = arg1 * arg2', the suppression '(result=arg1) ^ 412 * (arg2=1)' and the invariants 'x = y' and 'q = 1'. If the varinfo_index of 'q' is less than 'x' 413 * then it can't be used (because it would form an invalid permutation. Note that this set of 414 * antecedents will match a different suppression for multiply that has a different argument 415 * permutation. More complex suppressions may refer to the same variable more than once. In those 416 * cases, the antecedent invariants must also be over the same variables. 417 * 418 * @param inv the invariant to attempt to add to the suppression 419 * @param supor the suppressor we are trying to match 420 * @param vis the current variables (if any) that have already been determined by previous 421 * antecedents 422 * @return a new VarInfo[] containing the variables of inv, or null if inv does not match in some 423 * way 424 */ 425 private VarInfo @Nullable [] consider_inv(Invariant inv, NISuppressor supor, VarInfo[] vis) { 426 427 // Make sure this invariant really matches this suppressor. We know 428 // the class already matches, but if the invariant has a swap variable 429 // it must match as well 430 if (!supor.match(inv)) { 431 return null; 432 } 433 434 // Assign the variables from this invariant into vis. If a variable 435 // is already there and doesn't match this variable, then this 436 // antecedent can't be used. 437 VarInfo v1 = inv.ppt.var_infos[0]; 438 if ((vis[supor.v1_index] != null) && (vis[supor.v1_index] != v1)) { 439 return null; 440 } 441 if ((supor.v2_index != -1) 442 && (vis[supor.v2_index] != null) 443 && (vis[supor.v2_index] != inv.ppt.var_infos[1])) { 444 return null; 445 } 446 VarInfo cvis[] = vis.clone(); 447 cvis[supor.v1_index] = v1; 448 if (supor.v2_index != -1) { 449 cvis[supor.v2_index] = inv.ppt.var_infos[1]; 450 } 451 if (debug) { 452 System.out.printf( 453 "Placed antecedent '%s' into cvis %s%n", inv.format(), Arrays.toString(cvis)); 454 } 455 456 // Make sure the resulting variables are in the proper order and are 457 // compatible 458 if (!vis_order_ok(cvis) || !vis_compatible(cvis)) { 459 if (debug) { 460 System.out.println("Skipping, cvis has bad order or is incompatible"); 461 } 462 return null; 463 } 464 465 return cvis; 466 } 467 468 /** 469 * Builds an array of lists of antecedents that corresponds to each suppressor in this 470 * suppression. Returns null if the list is empty for any suppressor (because that means there 471 * can't be any suppressions based on these antecedents). 472 */ 473 List<Invariant> @Nullable [] antecedents_for_suppressors(NIS.Antecedents ants) { 474 475 @SuppressWarnings({"unchecked", "rawtypes"}) 476 /*NNC:@MonotonicNonNull*/ List<Invariant> antecedents[] = 477 (List<Invariant>[]) new List[suppressors.length]; 478 479 // Find the list of antecedents that matches each suppressor. If any 480 // suppressor doesn't have any matching antecedents, there can't be 481 // any invariants that are suppressed by this suppression. 482 for (int i = 0; i < suppressors.length; i++) { 483 NISuppressor s = suppressors[i]; 484 List<Invariant> alist = ants.get(s.get_inv_class()); 485 if (alist == null) { 486 return null; 487 } 488 antecedents[i] = alist; 489 } 490 491 if (debug) { 492 System.out.println( 493 suppressee.sup_class.getName() + " " + antecedents_for_suppression(antecedents)); 494 } 495 496 antecedents = castNonNullDeep(antecedents); // https://tinyurl.com/cfissue/986 497 498 return antecedents; 499 } 500 501 /** 502 * Determines whether the order of the variables in vis a valid permutations (i.e., their 503 * varinfo_index's are ordered). Null elements are ignored (and an all-null list is OK). 504 */ 505 private boolean vis_order_ok(VarInfo[] vis) { 506 507 VarInfo prev = vis[0]; 508 for (int i = 1; i < vis.length; i++) { 509 if ((prev != null) && (vis[i] != null)) { 510 if (vis[i].varinfo_index < prev.varinfo_index) { 511 return false; 512 } 513 } 514 if (vis[i] != null) { 515 prev = vis[i]; 516 } 517 } 518 return true; 519 } 520 521 /** 522 * Determines if the non-null entries in vis are comparable. Returns true if they are, false if 523 * they are not. 524 * 525 * <p>JHP: This should really be part of is_slice_ok. 526 */ 527 public static boolean vis_compatible(VarInfo[] vis) { 528 529 // Unary vis are always compatble 530 if (vis.length == 1) { 531 return true; 532 } 533 534 // Check binary 535 if (vis.length == 2) { 536 if ((vis[0] == null) || (vis[1] == null)) { 537 return true; 538 } 539 540 if (vis[0].rep_type.isArray() == vis[1].rep_type.isArray()) { 541 return vis[0].compatible(vis[1]); 542 } else if (vis[0].rep_type.isArray()) { 543 return vis[0].eltsCompatible(vis[1]); 544 } else { 545 return vis[1].eltsCompatible(vis[0]); 546 } 547 } 548 549 // Check ternary 550 if ((vis[1] != null) && (vis[2] != null)) { 551 if (!vis[1].compatible(vis[2])) { 552 return false; 553 } 554 } 555 556 if ((vis[0] != null) && (vis[2] != null)) { 557 if (!vis[0].compatible(vis[2])) { 558 return false; 559 } 560 } 561 562 return true; 563 } 564 565 public List<NISuppression> recurse_definition(NISuppressionSet ss) { 566 567 NISuppressee sse = ss.get_suppressee(); 568 List<NISuppression> new_suppressions = new ArrayList<>(); 569 570 // Create a list of all of our suppressors that don't match the suppressee 571 // of ss 572 List<NISuppressor> old_sors = new ArrayList<>(); 573 NISuppressor match = null; 574 for (int i = 0; i < suppressors.length; i++) { 575 if (suppressors[i].match(sse)) { 576 match = suppressors[i]; 577 } else { 578 old_sors.add(suppressors[i]); 579 } 580 } 581 582 // If we didn't match any suppressor there is nothing to do 583 if (match == null) { 584 return new_suppressions; 585 } 586 587 // Right now this only works if we match exactly one suppressor 588 assert (old_sors.size() + 1) == suppressors.length; 589 590 // Create one new suppression for each suppression in ss. The suppressee 591 // of ss is replaced by one of the suppressions of ss. Each suppressor 592 // in ss have its variable indices modified to match the original 593 // suppressor. 594 for (int i = 0; i < ss.suppression_set.length; i++) { 595 NISuppression s = ss.suppression_set[i]; 596 List<NISuppressor> sors = new ArrayList<>(old_sors); 597 for (int j = 0; j < s.suppressors.length; j++) { 598 sors.add(s.suppressors[j].translate(match)); 599 } 600 new_suppressions.add(new NISuppression(sors, suppressee)); 601 } 602 603 return new_suppressions; 604 } 605 606 /** Clears the suppressor state in each suppressor. */ 607 public void clear_state() { 608 for (int i = 0; i < suppressors.length; i++) { 609 suppressors[i].clear_state(); 610 } 611 } 612 613 /** Returns {@code "suppressor && suppressor ... ==> suppressee"}. */ 614 @SideEffectFree 615 @Override 616 public String toString(@GuardSatisfied NISuppression this) { 617 String suppressorsString = 618 (suppressors.length == 1) 619 ? suppressors[0].toString() 620 : "(" + StringsPlume.join(" && ", suppressors) + ")"; 621 return suppressorsString + " ==> " + suppressee; 622 } 623 624 /** Returns a string describing each of the antecedents for each suppressor. */ 625 public String antecedents_for_suppression(List<Invariant> antecedents[]) { 626 627 String sep = Global.lineSep; 628 629 String out = "suppression " + this + sep; 630 for (int i = 0; i < antecedents.length; i++) { 631 out += "antecedents for suppressor " + i + sep; 632 for (Invariant inv : antecedents[i]) { 633 out += " " + inv.format() + (inv.is_false() ? " [false]" : " t") + sep; 634 } 635 } 636 return out; 637 } 638}