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}