001package daikon; 002 003import static daikon.tools.nullness.NullnessUtil.castNonNullDeep; 004 005import daikon.inv.Implication; 006import daikon.inv.Invariant; 007import java.util.HashSet; 008import java.util.List; 009import org.checkerframework.checker.initialization.qual.UnknownInitialization; 010import org.checkerframework.checker.lock.qual.GuardSatisfied; 011import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 012import org.checkerframework.checker.nullness.qual.Nullable; 013import org.checkerframework.dataflow.qual.Pure; 014 015// This is a fake PptSlice for use with Implication invariants. 016 017// - The implication invariants at a program point are grouped into a 018// single PptSlice0 with no variables 019 020// - In order to output pre-state invariants as if they were 021// post-state, or OBJECT invariants as if they applied to a particular 022// parameter, we construct a PptSlice0 whose VarInfos have had their 023// names tweaked, and temporarily use that as the invariant's ppt. 024 025public class PptSlice0 extends PptSlice { 026 static final long serialVersionUID = 20020122L; 027 028 PptSlice0(PptTopLevel parent) { 029 super(parent, new VarInfo[0]); 030 } 031 032 @Override 033 public final int arity(@UnknownInitialization(PptSlice.class) PptSlice0 this) { 034 return 0; 035 } 036 037 // Make a fake slice whose variables are the same as the ones in 038 // sliceTemplate and at the same PptTopLevel, but marked as prestate 039 // (i.e., orig(x) rather than x) and transplanted to origSlice. 040 public static PptSlice makeFakePrestate(PptSlice sliceTemplate) { 041 PptSlice0 fake = new PptSlice0(sliceTemplate.parent); 042 fake.var_infos = new /*NNC:@MonotonicNonNull*/ VarInfo[sliceTemplate.var_infos.length]; 043 for (int i = 0; i < fake.var_infos.length; i++) { 044 fake.var_infos[i] = VarInfo.origVarInfo(sliceTemplate.var_infos[i]); 045 } 046 fake.var_infos = castNonNullDeep(fake.var_infos); // https://tinyurl.com/cfissue/986 047 return fake; 048 } 049 050 // We trade space for time by keeping a hash table of all the 051 // implications (they're also stored as a vector in invs) so we can 052 // efficiently avoid adding implications more than once. 053 054 // This should not be transient: more implications can be created during 055 // printing, for instance due to guarding. 056 private transient HashSet<ImplicationWrapper> invariantsSeen = new HashSet<>(); 057 058 // In lieu of a readResolve method. 059 private void initInvariantsSeen() { 060 if (invariantsSeen == null) { 061 invariantsSeen = new HashSet<ImplicationWrapper>(); 062 for (Invariant inv : invs) { 063 invariantsSeen.add(new ImplicationWrapper((Implication) inv)); 064 } 065 } 066 } 067 068 @SuppressWarnings("nullness") // reinitialization 069 private void reinitInvariantsSeen() { 070 invariantsSeen = null; 071 initInvariantsSeen(); 072 } 073 074 public void checkRep() { 075 if (invariantsSeen != null && invs.size() != invariantsSeen.size()) { 076 assert invs.size() == invariantsSeen.size() 077 : "invs.size()=" + invs.size() + ", invariantsSeen.size()=" + invariantsSeen.size(); 078 } 079 assert invariantsSeen == null || invs.size() == invariantsSeen.size(); 080 } 081 082 /** 083 * The invariant is typically an Implication; but PptSlice0 can contain other joiners than 084 * implications, such as "and" or "or". That feature isn't used as of November 2003. 085 */ 086 @Override 087 public void addInvariant(Invariant inv) { 088 assert inv != null; 089 assert inv instanceof Implication; 090 // checkRep(); 091 // assert ! hasImplication((Implication) inv); 092 initInvariantsSeen(); 093 invs.add(inv); 094 invariantsSeen.add(new ImplicationWrapper((Implication) inv)); 095 // checkRep(); 096 } 097 098 @Override 099 public void removeInvariant(Invariant inv) { 100 assert inv != null; 101 assert inv instanceof Implication; 102 // checkRep(); 103 // assert hasImplication((Implication) inv); 104 initInvariantsSeen(); 105 invs.remove(inv); 106 invariantsSeen.remove(new ImplicationWrapper((Implication) inv)); 107 // checkRep(); 108 } 109 110 // This can be called with very long lists by the conditionals code. 111 // At least until that's fixed, it's important for it not to be 112 // quadratic. 113 @Override 114 public void removeInvariants(List<Invariant> to_remove) { 115 if (to_remove.size() < 10) { 116 for (Invariant trinv : to_remove) { 117 removeInvariant(trinv); 118 } 119 } else { 120 invs.removeAll(to_remove); 121 if (to_remove.size() > invariantsSeen.size() / 2) { 122 // Faster to throw away and recreate 123 reinitInvariantsSeen(); 124 } else { 125 // Faster to update 126 for (Invariant trinv : to_remove) { 127 invariantsSeen.remove(new ImplicationWrapper((Implication) trinv)); 128 } 129 } 130 } 131 } 132 133 public boolean hasImplication(Implication imp) { 134 initInvariantsSeen(); 135 return invariantsSeen.contains(new ImplicationWrapper(imp)); 136 } 137 138 // // For debugging only 139 // public Implication getImplication(Implication imp) { 140 // initInvariantsSeen(); 141 // ImplicationWrapper resultWrapper 142 // = (ImplicationWrapper) UtilPlume.getFromSet( 143 // invariantsSeen, new ImplicationWrapper(imp)); 144 // if (resultWrapper == null) { 145 // return null; 146 // } 147 // return (Implication) resultWrapper.theImp; 148 // } 149 150 // We'd like to use a more sophisticated equality check and hashCode 151 // for implications when they appear in the invariantsSeen HashSet, 152 // but not anywhere else, so we make wrapper objects with the 153 // desired methods to go directly in the set. 154 155 // Not "implements serializable": If this is serializable, then the hash 156 // set tries to get the hash codes of all the invariants when it 157 // reads them in, but their format methods croak when they couldn't 158 // get their varInfos. 159 160 private static final class ImplicationWrapper { 161 162 public Implication theImp; 163 // hashCode is cached to make equality checks faster. 164 private int hashCode; 165 166 public ImplicationWrapper(Implication theImp) { 167 this.theImp = theImp; 168 // this.format = theImp.format(); 169 this.hashCode = 0; 170 } 171 172 // Abstracted out to permit use of a cached value 173 /** 174 * Returns a string representation of this. 175 * 176 * @return a string representation of this 177 */ 178 private String format(@GuardSatisfied ImplicationWrapper this) { 179 // return format; 180 return theImp.format(); 181 // return theImp.repr(); 182 } 183 184 @Pure 185 @Override 186 public int hashCode(@GuardSatisfied ImplicationWrapper this) { 187 if (hashCode == 0) { 188 hashCode = format().hashCode(); 189 // hashCode = (theImp.iff ? 1 : 0); 190 // hashCode = 37 * hashCode + theImp.predicate().getClass().hashCode(); 191 // hashCode = 37 * hashCode + theImp.consequent().getClass().hashCode(); 192 } 193 return hashCode; 194 } 195 196 // Returns the value of "isSameInvariant()". 197 @SuppressWarnings("AlreadyChecked") // debugging code 198 @EnsuresNonNullIf(result = true, expression = "#1") 199 @Pure 200 @Override 201 public boolean equals( 202 @GuardSatisfied ImplicationWrapper this, @GuardSatisfied @Nullable Object o) { 203 if (o == null) { 204 return false; 205 } 206 assert o instanceof ImplicationWrapper; 207 ImplicationWrapper other = (ImplicationWrapper) o; 208 if (hashCode() != other.hashCode()) { 209 return false; 210 } 211 boolean same_eq = theImp.isSameInvariant(other.theImp); 212 213 // For debugging, look for differences between the format based check 214 // and the isSameInvariant check. Note that there are certain 215 // invariants that print identically but are internally different: 216 // "this.theArray[this.topOfStack..] == 217 // this.theArray[this.topOfStack..]" can be either SeqSeqIntEqual or 218 // PairwiseLinearBinary, and "(return != null) ==> (return.getClass() 219 // != this.theArray.getClass())" can be either an Implication or a 220 // guarded invariant. 221 if (false) { 222 boolean fmt_eq = format().equals(other.format()); 223 if (!(!same_eq || fmt_eq)) { 224 System.out.println("imp1 = " + theImp.format()); 225 System.out.println("imp2 = " + other.theImp.format()); 226 System.out.println("fmt_eq = " + fmt_eq + " same_eq = " + same_eq); 227 System.out.println("lefteq = " + theImp.left.isSameInvariant(other.theImp.left)); 228 System.out.println("righteq = " + theImp.right.isSameInvariant(other.theImp.right)); 229 System.out.println( 230 "right class = " + theImp.right.getClass() + "/" + other.theImp.right.getClass()); 231 // throw new Error(); 232 } 233 assert !same_eq || fmt_eq; 234 } 235 return same_eq; 236 } 237 } 238 239 // I need to figure out how to set these. 240 @Override 241 public int num_samples(@UnknownInitialization @GuardSatisfied PptSlice0 this) { 242 return 2222; 243 } 244 245 public int num_mod_samples() { 246 return 2222; 247 } 248 249 @Override 250 public int num_values() { 251 return 2222; 252 } 253 254 @Override 255 void instantiate_invariants() { 256 throw new Error("Shouldn't get called"); 257 } 258 259 @Override 260 public List<Invariant> add(ValueTuple vt, int count) { 261 throw new Error("Shouldn't get called"); 262 } 263}