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}