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 ⇒ 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}