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