001package daikon.suppress;
002
003import daikon.Debug;
004import daikon.PptSlice;
005import daikon.PptTopLevel;
006import daikon.ValueTuple;
007import daikon.VarInfo;
008import daikon.inv.Invariant;
009import daikon.inv.InvariantStatus;
010import daikon.inv.binary.BinaryInvariant;
011import daikon.inv.ternary.TernaryInvariant;
012import daikon.inv.unary.UnaryInvariant;
013import java.lang.reflect.Method;
014import java.util.ArrayList;
015import java.util.Arrays;
016import java.util.List;
017import org.checkerframework.checker.lock.qual.GuardSatisfied;
018import org.checkerframework.checker.nullness.qual.NonNull;
019import org.checkerframework.checker.nullness.qual.Nullable;
020import org.checkerframework.checker.nullness.qual.RequiresNonNull;
021import org.checkerframework.dataflow.qual.SideEffectFree;
022import typequals.prototype.qual.Prototype;
023
024/**
025 * Defines a suppressee for non-instantiating suppression. A suppressee consists only of the class
026 * at this point since ternary invariants only require the class to define them fully (permutations
027 * are built into the class name). When binary invariants are suppressed additional information will
028 * need to be included.
029 */
030public class NISuppressee {
031
032  public Class<? extends Invariant> sup_class;
033  public int var_count;
034  public @Prototype Invariant sample_inv;
035
036  public NISuppressee(Class<? extends Invariant> cls, int var_count) {
037    sup_class = cls;
038    assert (var_count >= 1) && (var_count <= 3);
039    this.var_count = var_count;
040
041    try {
042      Method get_proto = cls.getMethod("get_proto", new Class<?>[] {});
043      @SuppressWarnings({"nullness", "prototype"}) // reflective invocation is nullness-correct
044      @NonNull @Prototype
045      Invariant sample_inv_local = (@Prototype Invariant) get_proto.invoke(null, new Object[] {});
046      sample_inv = sample_inv_local;
047      assert sample_inv != null : cls.getName();
048    } catch (Exception e) {
049      throw new RuntimeException("error instantiating invariant " + cls.getName() + ": " + e);
050    }
051  }
052
053  /** Define a binary suppressee on the specified class with the specified variable order. */
054  public NISuppressee(Class<? extends Invariant> cls, boolean swap) {
055    sup_class = cls;
056    this.var_count = 2;
057
058    try {
059      Method get_proto = cls.getMethod("get_proto", new Class<?>[] {boolean.class});
060      @SuppressWarnings({"nullness", "prototype"}) // reflective invocation is nullness-correct
061      @NonNull @Prototype
062      Invariant sample_inv_local =
063          (@Prototype Invariant) get_proto.invoke(null, new Object[] {Boolean.valueOf(swap)});
064      sample_inv = sample_inv_local;
065      assert sample_inv != null : cls.getName();
066    } catch (Exception e) {
067      throw new RuntimeException(
068          "error instantiating binary invariant " + cls.getName() + ": " + e);
069    }
070  }
071
072  /** Instantiates the suppressee invariant on the specified slice. */
073  public @Nullable Invariant instantiate(PptSlice slice) {
074
075    Invariant inv = sample_inv.instantiate(slice);
076    if (Debug.logOn()) {
077      if (inv != null) {
078        inv.log("Created %s", inv.format());
079      } else {
080        Debug.log(sup_class, slice, "Didn't create, instantiate returned null");
081      }
082    }
083    return inv;
084  }
085
086  /** Checks this invariant against the specified sample and returns the status. */
087  public InvariantStatus check(ValueTuple vt, VarInfo[] vis) {
088
089    // Nothing to check if any variable is missing
090    for (int i = 0; i < vis.length; i++) {
091      if (vis[i].isMissing(vt)) {
092        return InvariantStatus.NO_CHANGE;
093      }
094    }
095
096    if (var_count == 3) {
097      TernaryInvariant ternary_inv = (TernaryInvariant) sample_inv;
098      return ternary_inv.check(vt.getValue(vis[0]), vt.getValue(vis[1]), vt.getValue(vis[2]), 1, 1);
099    } else if (var_count == 2) {
100      if (!(sample_inv instanceof BinaryInvariant)) {
101        throw new Error("not binary: " + sample_inv.getClass());
102      }
103      BinaryInvariant binary_inv = (BinaryInvariant) sample_inv;
104      // System.out.printf("checking %s over %s=%s and %s=%s%n", sample_inv.getClass(),
105      //        vis[0].name(), vt.getValue(vis[0]),
106      //        vis[1].name(), vt.getValue(vis[1]));
107      return binary_inv.check_unordered(vt.getValue(vis[0]), vt.getValue(vis[1]), 1, 1);
108    } else /* must be unary */ {
109      UnaryInvariant unary_inv = (UnaryInvariant) sample_inv;
110      return unary_inv.check(vt.getValue(vis[0]), 1, 1);
111    }
112  }
113
114  /**
115   * Instantiates the suppressee invariant on the slice specified by vis in the specified ppt. If
116   * the slice is not currently there, it will be created.
117   */
118  public @Nullable Invariant instantiate(VarInfo[] vis, PptTopLevel ppt) {
119
120    PptSlice slice = ppt.get_or_instantiate_slice(vis);
121    return instantiate(slice);
122  }
123
124  //   /**
125  //    * Instantiates the suppressee invariant on all of the slices
126  //    * specified by vis in the specified ppt.  Multiple slices can
127  //    * be specified by vis if a slot in vis is null.  The slot will be
128  //    * filled by all leaders that can correctly fill the slot and an
129  //    * invariant created for each. @return a list of all of the created
130  //    * invariants.
131  //    */
132  //   public List<Invariant> instantiate_all (VarInfo[] vis, PptTopLevel ppt) {
133  //
134  //     List<Invariant> created_list = new ArrayList<>();
135  //
136  //     // Check for empty slots in vis, fail if there is more than one
137  //     int missing_index = -1;
138  //     for (int i = 0; i < vis.length; i++)
139  //       if (vis[i] == null) {
140  //         assert missing_index == -1 : "Multiple empty vars";
141  //         missing_index = i;
142  //       }
143  //
144  //     // If all of the slots were full, create the invariant
145  //     if (missing_index == -1) {
146  //       if (ppt.is_slice_ok (vis, vis.length)) {
147  //         Invariant inv = instantiate (vis, ppt);
148  //         if (inv != null)
149  //           created_list.add (inv);
150  //       }
151  //       return created_list;
152  //     }
153  //
154  //     // Fill in the missing slot with each possible matching leader and
155  //     // create an invariant for it.
156  //     VarInfo leaders[] = ppt.equality_view.get_leaders_sorted();
157  //     for (int i = 0; i < leaders.length; i++) {
158  //       VarInfo v = leaders[i];
159  //       vis[missing_index] = v;
160  //       if (!ppt.vis_order_ok (vis))
161  //         continue;
162  //       if (!ppt.is_slice_ok (vis, vis.length))
163  //         continue;
164  //       Invariant inv = instantiate (vis, ppt);
165  //       if (inv != null)
166  //         created_list.add (inv);
167  //     }
168  //
169  //     return created_list;
170  //   }
171
172  /**
173   * Finds the suppressee invariants on all of the slices specified by vis in the specified ppt.
174   * Multiple slices can be specified by vis if a slot in vis is null. The slot will be filled by
175   * all leaders that can correctly fill the slot and SupInv created for each.
176   *
177   * @param cinvs an array of the actual invariants that were found for each slot. It is used for
178   *     for debug printing only.
179   * @return a list describing all of the invariants
180   */
181  @RequiresNonNull("#2.equality_view")
182  public List<NIS.SupInv> find_all(
183      VarInfo[] vis, PptTopLevel ppt, @Nullable Invariant @Nullable [] cinvs) {
184
185    List<NIS.SupInv> created_list = new ArrayList<>();
186
187    // Check for empty slots in vis, fail if there is more than one
188    int missing_index = -1;
189    for (int i = 0; i < vis.length; i++) {
190      if (vis[i] == null) {
191        assert missing_index == -1 : "Multiple empty vars";
192        missing_index = i;
193      } else {
194        assert !vis[i].missingOutOfBounds();
195      }
196    }
197
198    // If all of the slots were full, specify the invariant
199    if (missing_index == -1) {
200      if (ppt.is_slice_ok(vis, vis.length)
201          && NISuppression.vis_compatible(vis)
202          && sample_inv.valid_types(vis)) {
203        NIS.SupInv sinv = new NIS.SupInv(this, vis, ppt);
204        sinv.log("Created for invariants: " + Arrays.toString(cinvs));
205        created_list.add(sinv);
206      }
207      return created_list;
208    }
209
210    // Fill in the missing slot with each possible matching leader and
211    // create an invariant for it.
212    VarInfo leaders[] = ppt.equality_view.get_leaders_sorted();
213    for (int i = 0; i < leaders.length; i++) {
214      VarInfo v = leaders[i];
215      vis[missing_index] = v;
216      if (v.missingOutOfBounds()) {
217        continue;
218      }
219      if (!ppt.vis_order_ok(vis)) {
220        continue;
221      }
222      if (!ppt.is_slice_ok(vis, vis.length)) {
223        continue;
224      }
225      if (!NISuppression.vis_compatible(vis)) {
226        continue;
227      }
228      if (!sample_inv.valid_types(vis)) {
229        continue;
230      }
231      NIS.SupInv sinv = new NIS.SupInv(this, vis.clone(), ppt);
232      sinv.log("Unspecified variable = " + v.name());
233      sinv.log("Created for invariants: " + Arrays.toString(cinvs));
234      created_list.add(sinv);
235    }
236    return created_list;
237  }
238
239  /**
240   * Returns the swap variable setting for the suppressee. Returns false if the suppressee is not a
241   * binary invariant, is symmetric, or permutes by changing classes.
242   */
243  public boolean get_swap() {
244
245    if (var_count != 2) {
246      return false;
247    }
248
249    BinaryInvariant binv = (BinaryInvariant) sample_inv;
250    if (binv.is_symmetric()) {
251      return false;
252    }
253    return binv.get_swap();
254  }
255
256  /**
257   * Returns a new suppressee that is the same as this one except that its variables are swapped.
258   */
259  public NISuppressee swap() {
260    assert var_count == 2;
261    BinaryInvariant binv = (BinaryInvariant) sample_inv;
262    if (binv != null) {
263      assert !binv.is_symmetric();
264    }
265    if ((binv == null) || binv.get_swap()) {
266      return new NISuppressee(sup_class, false);
267    } else {
268      return new NISuppressee(sup_class, true);
269    }
270  }
271
272  @SideEffectFree
273  @Override
274  public String toString(@GuardSatisfied NISuppressee this) {
275
276    String extra = "";
277    if (var_count == 2) {
278      BinaryInvariant binv = (BinaryInvariant) sample_inv;
279      if (binv == null) {
280        extra = " [null sample inv]";
281      } else if (binv.is_symmetric()) {
282        extra = " [sym]";
283      } else if (binv.get_swap()) {
284        extra = " [swap]";
285      }
286    }
287    return sup_class.getSimpleName() + extra;
288  }
289}