001package daikon.inv;
002
003import static daikon.inv.Invariant.asInvClass;
004
005import daikon.PptTopLevel;
006import daikon.VarInfo;
007import daikon.inv.binary.BinaryInvariant;
008import java.lang.reflect.Method;
009import java.util.logging.Logger;
010import org.checkerframework.checker.lock.qual.GuardSatisfied;
011import org.checkerframework.checker.nullness.qual.Nullable;
012import org.checkerframework.dataflow.qual.SideEffectFree;
013
014/** Class that defines an invariant so that it can be searched for as part of suppression. */
015public class InvDef {
016
017  /** Debug tracer. */
018  public static final Logger debug = Logger.getLogger("daikon.inv.InvDef");
019
020  /**
021   * Variables used by the invariant. If v2 is null, this is a unary invariant, if v2 is not null,
022   * then this is a binary invariant.
023   */
024  VarInfo v1;
025
026  @Nullable VarInfo v2;
027
028  /** Argument indices used by the invariant. */
029  int v1_index = -1;
030
031  int v2_index = -1;
032  int v3_index = -1;
033
034  /** invariant class. */
035  Class<? extends Invariant> inv_class;
036
037  /** State to check. Only for invariants with state. */
038  @Nullable Object state;
039
040  /** True if the order of the variables was swapped. */
041  boolean swap = false;
042
043  /** True if invariant permutes by changing its class. */
044  boolean swap_class = false;
045
046  /** The array {0}. */
047  public static final long[] elts_zero = {0};
048
049  /** The array {0.0}. */
050  public static final double[] elts_zero_float = {0.0};
051
052  /** The array {-1}. */
053  public static final long[] elts_minus_one = {-1};
054
055  /** The array {-1.0}. */
056  public static final double[] elts_minus_one_float = {-1.0};
057
058  /** The array {-1, 1}. */
059  public static final long[] elts_minus_one_and_plus_one = {-1, 1};
060
061  /** The array {-1.0, 1.0}. */
062  public static final double[] elts_minus_one_and_plus_one_float = {-1.0, 1.0};
063
064  /** The array {1}. */
065  public static final long[] elts_one = {1};
066
067  /** The array {1.0}. */
068  public static final double[] elts_one_float = {1.0};
069
070  /**
071   * Create a new InvDef with one variable.
072   *
073   * @param v1 the variable
074   * @param cls the class of the invariant to be defined
075   */
076  public InvDef(VarInfo v1, Class<? extends Invariant> cls) {
077    this(v1, cls, null);
078  }
079
080  /**
081   * Create a new InvDef with one variable and the given state.
082   *
083   * @param v1 the variable
084   * @param cls the class of the invariant to be defined
085   * @param state the state of the invariant
086   */
087  public InvDef(VarInfo v1, Class<? extends Invariant> cls, @Nullable Object state) {
088    this.v1 = v1;
089    this.v2 = null;
090    inv_class = cls;
091    this.state = state;
092  }
093
094  public InvDef(VarInfo v1, VarInfo v2, Class<? extends Invariant> cls) {
095
096    debug.fine("creating " + cls.getName() + " " + v1.name() + ", " + v2.name());
097    // put the variables in their standard order
098    if (v1.varinfo_index > v2.varinfo_index) {
099      this.v1 = v2;
100      this.v2 = v1;
101      swap = true;
102    } else {
103      this.v1 = v1;
104      this.v2 = v2;
105      swap = false;
106    }
107
108    // If the specified class handles swapping with a different class,
109    // get the class
110    swap_class = true;
111    try {
112      Method swap_method = cls.getMethod("swap_class", (Class<?>[]) null);
113      if (swap) {
114        @SuppressWarnings("nullness") // static method, so null first arg is OK: swap_class()
115        Class<? extends Invariant> tmp_cls =
116            asInvClass(swap_method.invoke(null, (Object @Nullable []) null));
117        cls = tmp_cls;
118      }
119    } catch (Exception e) {
120      swap_class = false;
121    }
122
123    this.inv_class = cls;
124
125    debug.fine("Created " + this);
126  }
127
128  //   /**
129  //    * Defines a ternary invariant independent of specific variables by
130  //    * using the var_info instead.  The class must be correctly permuted
131  //    * to match the variable order (i.e., the indices must be 0, 1, 2).
132  //    * This is ok for now, since we are only using these to define
133  //    * suppressees and we always know the correct permuation in that
134  //    * instance.
135  //    */
136  //   public InvDef (int v1_index, int v2_index, int v3_index, Class<? extends Invariant>
137  // inv_class) {
138  //
139  //     assert v1_index < v2_index;
140  //     assert v2_index < v3_index;
141  //     this.v1_index = v1_index;
142  //     this.v2_index = v2_index;
143  //     this.v3_index = v3_index;
144  //     this.inv_class = inv_class;
145  //   }
146
147  @SideEffectFree
148  @Override
149  public String toString(@GuardSatisfied InvDef this) {
150    String out = "v1=" + v1.name();
151    if (v2 != null) {
152      out += ", v2=" + v2.name();
153    }
154    return (out
155        + ", class="
156        + inv_class.getName()
157        + ", swap="
158        + swap
159        + ", swap_class="
160        + swap_class);
161  }
162
163  public boolean check(Invariant inv) {
164    assert inv.getClass() == inv_class;
165
166    debug.fine("checking " + this);
167
168    // If it's a binary invariant that is swapped, make sure it matches
169    if ((v2 != null) && !swap_class) {
170      BinaryInvariant binv = (BinaryInvariant) inv;
171      if (!binv.is_symmetric() && swap != binv.get_swap()) {
172        debug.fine(
173            "inv "
174                + inv.format()
175                + " doesn't match swap value, "
176                + "symmetric="
177                + binv.is_symmetric());
178        return false;
179      }
180    }
181
182    // If a state was specified make sure it matches
183    if (state != null) {
184      if (!inv.state_match(state)) {
185        debug.fine("inv doesn't match state");
186        return false;
187      }
188    }
189
190    debug.fine("inv " + inv.format() + " matches");
191    return true;
192  }
193
194  /**
195   * Looks for this invariant (in this ppt). Returns the invariant if it finds it, null otherwise.
196   */
197  public @Nullable Invariant find() {
198
199    PptTopLevel ppt = v1.ppt;
200
201    VarInfo[] vis;
202    if (v2 == null) {
203      vis = new VarInfo[] {v1};
204    } else {
205      vis = new VarInfo[] {v1, v2};
206    }
207
208    Invariant inv = ppt.find_inv_by_class(vis, inv_class);
209
210    if ((inv != null) && check(inv)) {
211      return inv;
212    } else {
213      return null;
214    }
215  }
216}