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}