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}