001package daikon.inv.binary;
002
003import static daikon.inv.Invariant.asInvClass;
004
005import daikon.PptSlice;
006import daikon.VarInfo;
007import daikon.inv.Invariant;
008import daikon.inv.InvariantStatus;
009import java.lang.reflect.Method;
010import org.checkerframework.checker.interning.qual.Interned;
011import org.checkerframework.checker.nullness.qual.Nullable;
012import org.checkerframework.dataflow.qual.Pure;
013import typequals.prototype.qual.Prototype;
014
015/** Provides a class that defines the functions that must exist for each two variable invariant. */
016public abstract class BinaryInvariant extends Invariant {
017  static final long serialVersionUID = 20130808L;
018
019  protected BinaryInvariant(PptSlice ppt) {
020    super(ppt);
021  }
022
023  protected @Prototype BinaryInvariant() {
024    super();
025  }
026
027  public abstract InvariantStatus check(
028      @Interned Object val1, @Interned Object val2, int mod_index, int count);
029
030  public abstract InvariantStatus add(
031      @Interned Object val1, @Interned Object val2, int mod_index, int count);
032
033  /**
034   * Applies the variables in the correct order. If the second variable is an array and the first
035   * variable is not, the order of the values is reversed (so that the array is always the first
036   * argument).
037   */
038  public InvariantStatus add_unordered(
039      @Interned Object val1, @Interned Object val2, int mod_index, int count) {
040
041    VarInfo v1 = ppt.var_infos[0];
042    VarInfo v2 = ppt.var_infos[1];
043
044    // If one argument is scalar and the other an array, put the scalar first.
045    if (v2.rep_type.isArray() && !v1.rep_type.isArray()) {
046      return add(val2, val1, mod_index, count);
047    } else {
048      return add(val1, val2, mod_index, count);
049    }
050  }
051
052  /**
053   * Checks the specified values in the correct order. If the second value is an array and the first
054   * value is not, the order of the values is reversed (so that the array is always the first
055   * argument).
056   *
057   * <p>The values are checked rather than the variables because this is sometimes called on
058   * prototype invariants.
059   */
060  public InvariantStatus check_unordered(
061      @Prototype BinaryInvariant this,
062      @Interned Object val1,
063      @Interned Object val2,
064      int mod_index,
065      int count) {
066
067    // If one argument is scalar and the other an array, put the scalar first.
068    if (((val2 instanceof long[]) || (val2 instanceof double[]) || (val2 instanceof String[]))
069        && !((val1 instanceof long[])
070            || (val1 instanceof String[])
071            || (val1 instanceof double[]))) {
072      return check(val2, val1, mod_index, count);
073    } else {
074      return check(val1, val2, mod_index, count);
075    }
076  }
077
078  /**
079   * Returns true if the binary function is symmetric (x,y &rArr; y,x). Subclasses that are
080   * symmetric should override.
081   */
082  @Pure
083  public boolean is_symmetric() {
084    return false;
085  }
086
087  /**
088   * Returns the swap setting for invariants that support a swap boolean to handle different
089   * permutations. This version should never be called.
090   */
091  public boolean get_swap() {
092    throw new Error("swap called in BinaryInvariant");
093  }
094
095  /**
096   * Searches for the specified binary invariant (by class) in the specified slice. Returns null if
097   * the invariant is not found.
098   */
099  protected @Nullable Invariant find(Class<? extends Invariant> cls, VarInfo v1, VarInfo v2) {
100
101    // find the slice containing v1 and v2
102    boolean fswap = false;
103    PptSlice ppt;
104    if (v1.varinfo_index > v2.varinfo_index) {
105      fswap = true;
106      ppt = this.ppt.parent.findSlice(v2, v1);
107    } else {
108      ppt = this.ppt.parent.findSlice(v1, v2);
109    }
110    if (ppt == null) {
111      return null;
112    }
113
114    // The following is complicated because we are inconsistent in
115    // how we handle permutations in binary invariants.  Some
116    // invariants (notably the comparison invariants <=, >=, >, etc)
117    // use only one permutation, but have two different invariants (eg,
118    // < and >) to account for both orders.  Other invariants (notably
119    // most of those in Numeric.java.jpp) keep a swap boolean that indicates
120    // the order of their arguments.  Still others (such as == and
121    // BitwiseComplement) are symmetric and need only track one invariant
122    // for each argument pair.
123    //
124    // The classes with multiple invariants, must provide a static
125    // method named swap_class that provides the converse invariant.
126    // Symmetric invariants return true from is_symmetric().  Others
127    // must support the get_swap() method that returns the current
128    // swap setting.
129
130    // If the specified invariant has a different class when swapped
131    // find that class.
132    boolean swap_class = true;
133    try {
134      Method swap_method = cls.getMethod("swap_class", (Class<?>[]) null);
135      if (fswap) {
136        @SuppressWarnings("nullness") // static method, so null first arg is OK: swap_class()
137        Class<? extends Invariant> tmp_cls =
138            asInvClass(swap_method.invoke(null, (Object @Nullable []) null));
139        cls = tmp_cls;
140      }
141    } catch (Exception e) {
142      swap_class = false;
143    }
144
145    // Loop through each invariant, looking for the matching class
146    for (Invariant inv : ppt.invs) {
147      BinaryInvariant bi = (BinaryInvariant) inv;
148      if (bi.getClass() == cls) {
149        if (bi.is_symmetric() || swap_class) {
150          return bi;
151        } else {
152          if (bi.get_swap() == fswap) {
153            return bi;
154          }
155        }
156      }
157    }
158
159    return null;
160  }
161}