001package daikon; 002 003import daikon.inv.DiscardInfo; 004import daikon.inv.Invariant; 005import daikon.suppress.NIS; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.Comparator; 009import java.util.Iterator; 010import java.util.List; 011import java.util.logging.Level; 012import java.util.logging.Logger; 013import org.checkerframework.checker.initialization.qual.UnknownInitialization; 014import org.checkerframework.checker.lock.qual.GuardSatisfied; 015import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 016import org.checkerframework.checker.nullness.qual.Nullable; 017import org.checkerframework.checker.nullness.qual.RequiresNonNull; 018import org.checkerframework.dataflow.qual.Pure; 019import org.checkerframework.dataflow.qual.SideEffectFree; 020import org.plumelib.util.ArraysPlume; 021 022/** 023 * A Slice is a view of some of the variables for a program point. A program point (that is, 024 * PptTopLevel) does not directly contain invariants. Instead, slices contain the invariants that 025 * involve (all) the Slice's variables. 026 * 027 * <p>Suppose a program point has variables A, B, C, and D.<br> 028 * There would be 4 unary slices -- one each for variables A, B, C, and D.<br> 029 * There would be 6 binary slices -- for {A,B}, {A,C}, {A,D}, {B,C}, {B,D}, and {C,D}.<br> 030 * There would be 4 ternary slices -- for {A,B,C}, {A,B,D}, {A,C,D}, and {B,C,D}. 031 */ 032public abstract class PptSlice extends Ppt { 033 static final long serialVersionUID = 20040921L; 034 035 // Permit subclasses to use it. 036 protected static final String lineSep = Global.lineSep; 037 038 /** Debug tracer. */ 039 public static final Logger debug = Logger.getLogger("daikon.PptSlice"); 040 041 /** Debug tracer for debugging both this and PptSlices. */ 042 public static final Logger debugGeneral = Logger.getLogger("daikon.PptSlice.general"); 043 044 public static final Logger debugFlow = Logger.getLogger("daikon.flow.flow"); 045 046 public static final Logger debugGuarding = Logger.getLogger("daikon.guard"); 047 048 // A better name would perhaps be "container", as this has nothing to do 049 // with the program point hierarchy. 050 /** This is a slice of the 'parent' ppt. */ 051 public PptTopLevel parent; 052 053 public abstract int arity(@UnknownInitialization(PptSlice.class) PptSlice this); 054 055 /** 056 * The invariants contained in this slice. This should not be used directly, in general. In 057 * particular, subclasses such as PptSlice0 need to synchronize it with other values. Therefore, 058 * it should be manipulated via {@link #addInvariant} and {@link #removeInvariant}. 059 */ 060 @SuppressWarnings("serial") 061 public List<Invariant> invs; 062 063 PptSlice(PptTopLevel parent, VarInfo[] var_infos) { 064 super(var_infos); 065 this.parent = parent; 066 invs = new ArrayList<Invariant>(); 067 // Ensure that the VarInfo objects are in order (and not duplicated). 068 for (int i = 0; i < var_infos.length - 1; i++) { 069 assert var_infos[i].varinfo_index <= var_infos[i + 1].varinfo_index; 070 } 071 assert this instanceof PptSliceEquality || arity() == var_infos.length; 072 073 if (debugGeneral.isLoggable(Level.FINE)) { 074 debugGeneral.fine(Arrays.toString(var_infos)); 075 } 076 } 077 078 @SideEffectFree 079 @Override 080 @SuppressWarnings("nullness:override.receiver") // see comment on overridden definition in Ppt 081 public final String name(@GuardSatisfied @UnknownInitialization(PptSlice.class) PptSlice this) { 082 return parent.name + varNames(var_infos); 083 } 084 085 public boolean usesVar(VarInfo vi) { 086 return (ArraysPlume.indexOfEq(var_infos, vi) != -1); 087 } 088 089 // This is only called from inv.filter.VariableFilter. 090 public boolean usesVar(String name) { 091 for (int i = 0; i < var_infos.length; i++) { 092 // mistere: I'm not sure is this is the right thing for 093 // the gui, but it's probably close 094 if (var_infos[i].name().equals(name)) { 095 return true; 096 } 097 } 098 return false; 099 } 100 101 /** 102 * Returns true if any of our variables is named NAME, or is derived from a variable named NAME. 103 * 104 * @return true if any of our variables is named NAME, or is derived from a variable named NAME 105 */ 106 // Only called right now from tools/ExtractConsequent 107 public boolean usesVarDerived(String name) { 108 for (VarInfo vi : var_infos) { 109 if (vi.includes_simple_name(name)) { 110 return true; 111 } 112 } 113 return false; 114 } 115 116 /** 117 * Returns true if all of this slice's variables are orig() variables. 118 * 119 * @return true if all of this slice's variables are orig() variables 120 */ 121 public boolean allPrestate() { 122 for (VarInfo vi : var_infos) { 123 if (!vi.isPrestateDerived()) { 124 return false; 125 } 126 } 127 return true; 128 } 129 130 public abstract void addInvariant(Invariant inv); 131 132 /** This method actually removes the invariant from its PptSlice. */ 133 // I don't just use ppt.invs.remove because I want to be able to defer 134 // and to take action if the vector becomes void. 135 public void removeInvariant(Invariant inv) { 136 137 if (Debug.logDetail()) { 138 log("Removing invariant '" + inv.format() + "'"); 139 } 140 if (Debug.logOn()) { 141 inv.log("Removed from slice: %s", inv.format()); 142 } 143 boolean removed = invs.remove(inv); 144 assert removed : "inv " + inv + " not in ppt " + name(); 145 Global.falsified_invariants++; 146 if (invs.size() == 0) { 147 if (Debug.logDetail()) { 148 log("last invariant removed"); 149 } 150 } 151 } 152 153 // This can be called with very long lists by the conditionals code. 154 // At least until that's fixed, it's important for it not to be 155 // quadratic. 156 public void removeInvariants(List<Invariant> to_remove) { 157 if (to_remove.size() < 10) { 158 for (Invariant trinv : to_remove) { 159 removeInvariant(trinv); 160 } 161 } else { 162 int old_invs_size = invs.size(); 163 invs.removeAll(to_remove); 164 assert old_invs_size - invs.size() == to_remove.size(); 165 Global.falsified_invariants += to_remove.size(); 166 if (invs.size() == 0) { 167 if (Debug.logDetail()) { 168 log("last invariant removed"); 169 } 170 } 171 } 172 } 173 174 /** 175 * This procedure accepts a sample (a ValueTuple), extracts the values from it, casts them to the 176 * proper types, and passes them along to the invariants proper. (The invariants accept typed 177 * values rather than a ValueTuple that encapsulates objects of any type whatever.) 178 * 179 * @return a List of Invariants that weakened due to the processing 180 */ 181 abstract List<Invariant> add(ValueTuple full_vt, int count); 182 183 /** Removes any falsified invariants from our list. */ 184 @RequiresNonNull("daikon.suppress.NIS.suppressor_map") 185 protected void remove_falsified() { 186 187 // Remove the dead invariants 188 for (Iterator<Invariant> iFalsified = invs.iterator(); iFalsified.hasNext(); ) { 189 Invariant inv = iFalsified.next(); 190 if (inv.is_false()) { 191 iFalsified.remove(); 192 NIS.falsified(inv); 193 } 194 } 195 } 196 197 /** Return an approximation of the number of samples seen on this slice. */ 198 public abstract int num_samples(@UnknownInitialization @GuardSatisfied PptSlice this); 199 200 /** Return an approximation of the number of distinct values seen on this slice. */ 201 public abstract int num_values(); 202 203 /** Instantiate invariants on the VarInfos this slice contains. */ 204 abstract void instantiate_invariants(); 205 206 /** 207 * This class is used for comparing PptSlice objects. It orders by arity, then by variable names. 208 * It's somewhat less efficient than ArityPptnameComparator. 209 */ 210 public static final class ArityVarnameComparator implements Comparator<PptSlice> { 211 @Pure 212 @Override 213 public int compare(PptSlice slice1, PptSlice slice2) { 214 if (slice1 == slice2) { 215 return 0; 216 } 217 // Don't do this assert, which prevents comparison across different Ppts. 218 // (The assert check may be useful in some situations, though.) 219 // assert slice1.parent == slice2.parent; 220 if (slice1.arity() != slice2.arity()) { 221 return slice2.arity() - slice1.arity(); 222 } 223 return Ppt.varNames(slice1.var_infos).compareTo(Ppt.varNames(slice2.var_infos)); 224 } 225 } 226 227 /** 228 * This class is used for comparing PptSlice objects. It orders by arity, then by name. Because of 229 * the dependence on name, it should be used only for slices on the same Ppt. 230 */ 231 public static final class ArityPptnameComparator implements Comparator<PptSlice> { 232 @Pure 233 @Override 234 public int compare(PptSlice slice1, PptSlice slice2) { 235 if (slice1 == slice2) { 236 return 0; 237 } 238 // Don't do this, to permit comparison across different Ppts. 239 // (The check may be useful in some situations, though.) 240 // assert slice1.parent == slice2.parent; 241 if (slice1.arity() != slice2.arity()) { 242 return slice2.arity() - slice1.arity(); 243 } 244 return slice1.name().compareTo(slice2.name()); 245 } 246 } 247 248 // /////////////////////////////////////////////////////////////////////////// 249 // Invariant guarding 250 251 /** 252 * Returns true if every invariant is a guarding predicate. 253 * 254 * @return true if every invariant is a guarding predicate 255 */ 256 public boolean containsOnlyGuardingPredicates() { 257 for (Invariant inv : invs) { 258 if (!inv.isGuardingPredicate) { 259 return false; 260 } 261 } 262 return true; 263 } 264 265 // /////////////////////////////////////////////////////////////////////////// 266 // Miscellaneous 267 268 /** Remove the invariants noted in omitTypes. */ 269 public void processOmissions(boolean[] omitTypes) { 270 if (invs.size() == 0) { 271 return; 272 } 273 List<Invariant> toRemove = new ArrayList<>(); 274 for (Invariant inv : invs) { 275 if (omitTypes['r'] && inv.isReflexive()) { 276 toRemove.add(inv); 277 } 278 } 279 removeInvariants(toRemove); 280 } 281 282 /** 283 * Check the internals of this slice. Each invariant in the slice is checked for consistency and 284 * each inv.ppt must equal this. 285 */ 286 public void repCheck() { 287 288 // System.out.printf("Checking slice %s%n", this); 289 290 // Make sure that each variable is a leader. There is one exception to this 291 // rule. Post processing of equality sets creates equality invariants between the 292 // various members of the equality set. Thus one non-leader is acceptable 293 // in binary (two variable) slices if it is in the same equality set as the 294 // other variable. 295 for (VarInfo vi : var_infos) { 296 // System.out.printf("equality set for vi %s = %s%n", vi, vi.equalitySet); 297 if (!vi.isCanonical()) { 298 assert var_infos.length == 2 : this + " - " + vi; 299 assert var_infos[0].canonicalRep() == var_infos[1].canonicalRep() : this + " - " + vi; 300 } 301 } 302 303 for (Invariant inv : invs) { 304 inv.repCheck(); 305 if (inv.ppt != this) { 306 throw new Error( 307 String.format( 308 "inv.ppt != this. inv.ppt=%s; this=%s; for inv=%s [%s] in invs=%s", 309 inv.ppt, this, inv, inv.getClass(), invs)); 310 } 311 } 312 } 313 314 /** 315 * Clone self and replace this.var_infos with newVis. Do the same in all invariants that this 316 * holds. Return a new PptSlice that's like this except with the above replacement, along with 317 * correct flow pointers for varInfos. Invariants are also pivoted so that any VarInfo index order 318 * swapping is handled correctly. 319 * 320 * @param newVis to replace this.var_infos 321 * @return a new PptSlice that satisfies the characteristics above 322 */ 323 PptSlice cloneAndPivot(VarInfo[] newVis) { 324 throw new Error("Shouldn't get called"); 325 } 326 327 public PptSlice copy_new_invs(PptTopLevel ppt, VarInfo[] vis) { 328 throw new Error("Shouldn't get called"); 329 } 330 331 /** For debugging only. */ 332 @Override 333 @SuppressWarnings("all:purity") // string creation 334 @SideEffectFree 335 public String toString(@GuardSatisfied PptSlice this) { 336 StringBuilder sb = new StringBuilder(); 337 for (VarInfo vi : var_infos) { 338 sb.append(" " + vi.name()); 339 } 340 return this.getClass().getName() 341 + ": " 342 + parent.ppt_name 343 // sb starts with a space 344 + sb 345 + " samples: " 346 + num_samples(); 347 } 348 349 /** 350 * Returns whether or not this slice already contains the specified invariant. Whether not 351 * invariants match is determine by Invariant.match() This will return true for invariants of the 352 * same kind with different formulas (eg, one_of, bound, linearbinary). 353 */ 354 public boolean contains_inv(Invariant inv) { 355 356 for (Invariant mine : invs) { 357 if (mine.match(inv)) { 358 return true; 359 } 360 } 361 return false; 362 } 363 364 /** 365 * Returns whether or not this slice contains an exact match for the specified invariant. An exact 366 * match requires that the invariants be of the same class and have the same formula. 367 */ 368 @EnsuresNonNullIf(result = true, expression = "find_inv_exact(#1)") 369 public boolean contains_inv_exact(Invariant inv) { 370 371 return (find_inv_exact(inv) != null); 372 } 373 374 /** 375 * Returns the invariant that matches the specified invariant if it exists. Otherwise returns 376 * null. An exact match requires that the invariants be of the same class and have the same 377 * formula. 378 */ 379 @Pure 380 public @Nullable Invariant find_inv_exact(Invariant inv) { 381 382 for (Invariant mine : invs) { 383 if ((mine.getClass() == inv.getClass()) && mine.isSameFormula(inv)) { 384 return mine; 385 } 386 } 387 return null; 388 } 389 390 /** 391 * Returns the invariant that matches the specified class if it exists. Otherwise returns null. 392 */ 393 public @Nullable Invariant find_inv_by_class(Class<? extends Invariant> cls) { 394 395 for (Invariant inv : invs) { 396 if ((inv.getClass() == cls)) { 397 return inv; 398 } 399 } 400 return null; 401 } 402 403 /** 404 * Returns true if the invariant is true in this slice. This can occur if the invariant exists in 405 * this slice, is suppressed, or is obvious statically. 406 */ 407 @Pure 408 public boolean is_inv_true(Invariant inv) { 409 410 if (contains_inv_exact(inv)) { 411 if (Debug.logOn() && (Daikon.current_inv != null)) { 412 Daikon.current_inv.log("inv %s exists", inv.format()); 413 } 414 return true; 415 } 416 417 // Check to see if the invariant is obvious statically over the leaders. 418 // This check should be sufficient since if it isn't obvious statically 419 // over the leaders, it should have been created. 420 DiscardInfo di = inv.isObviousStatically(var_infos); 421 if (di != null) { 422 if (Debug.logOn() && (Daikon.current_inv != null)) { 423 Daikon.current_inv.log("inv %s is obv statically", inv.format()); 424 } 425 return true; 426 } 427 428 boolean suppressed = inv.is_ni_suppressed(); 429 if (suppressed && Debug.logOn() && (Daikon.current_inv != null)) { 430 Daikon.current_inv.log("inv %s is ni suppressed", inv.format()); 431 } 432 return suppressed; 433 } 434 435 /** 436 * Output specified log information if the PtpSlice class, and this ppt and variables are enabled 437 * for logging. 438 */ 439 public void log(String msg) { 440 Debug.log(getClass(), this, msg); 441 } 442}