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}