001package daikon.tools.runtimechecker;
002
003import java.io.ObjectStreamException;
004import java.io.Serializable;
005import java.util.Collections;
006import java.util.HashMap;
007import java.util.HashSet;
008import java.util.List;
009import java.util.Set;
010import org.checkerframework.checker.interning.qual.UsesObjectEquals;
011import org.checkerframework.checker.lock.qual.GuardSatisfied;
012import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
013import org.checkerframework.checker.nullness.qual.Nullable;
014import org.checkerframework.checker.signedness.qual.UnknownSignedness;
015import org.checkerframework.dataflow.qual.Pure;
016import org.checkerframework.dataflow.qual.SideEffectFree;
017
018/** A program property (currently, derived by Daikon). */
019public class Property implements Serializable {
020
021  private static final long serialVersionUID = 1L;
022
023  // Maps into all the Property objects created.
024  private static HashMap<Integer, Property> propertiesMap = new HashMap<>();
025
026  // The name of the method that this property describes.
027  private final String method;
028
029  /** The name of the method that this property describes. ("null" for object invariants.) */
030  public String method(@GuardSatisfied Property this) {
031    return method;
032  }
033
034  // The kind of proerty (enter, exit or objectInvariant).
035  private final Kind kind;
036
037  /** The kind of property (enter, exit or objectInvariant). */
038  public Kind kind(@GuardSatisfied Property this) {
039    return kind;
040  }
041
042  /** Daikon representation (as output by Daikon's default output format). */
043  private final String daikonRep;
044
045  /** Daikon representation (as output by Daikon's default output format). */
046  public String daikonRep(@GuardSatisfied Property this) {
047    return daikonRep;
048  }
049
050  /** JML representation of this property. */
051  public String jmlRep;
052
053  /**
054   * A measure of a property's universality: whether it captures the general behavior of the
055   * program. The measure ranges from 0 (no confidence) to 1 (high confidence).
056   */
057  public double confidence;
058
059  /** The Daikon class name that this property represents. */
060  public String daikonClass;
061
062  // Creates a new property with the given attributes.
063  private Property(
064      Kind kind,
065      String daikonRep,
066      String method,
067      String jmlRep,
068      String daikonClass,
069      double confidence) {
070    this.kind = kind;
071    this.daikonRep = daikonRep;
072    this.method = method;
073    this.jmlRep = jmlRep;
074    this.daikonClass = daikonClass;
075    this.confidence = confidence;
076  }
077
078  /** Easy-on-the-eye string representation. */
079  @SideEffectFree
080  @Override
081  public String toString(@GuardSatisfied Property this) {
082    return kind.toString() + " : " + daikonRep();
083  }
084
085  /**
086   * A class representing the kind of an property. An invariant is either {@code Kind.enter}, {@code
087   * Kind.exit}, or {@code Kind.objectInvariant}.
088   */
089  // This should be an enum.
090  @UsesObjectEquals
091  public static class Kind implements Serializable {
092    private static final long serialVersionUID = 1L;
093
094    public final String name;
095
096    public final String xmlname;
097
098    private Kind(String name, String xmlname) {
099      this.name = name;
100      this.xmlname = xmlname;
101    }
102
103    @Pure
104    @Override
105    public int hashCode(@GuardSatisfied @UnknownSignedness Kind this) {
106      return name.hashCode();
107    }
108
109    @SideEffectFree
110    @Override
111    public String toString(@GuardSatisfied Kind this) {
112      return name;
113    }
114
115    public String xmlString() {
116      return xmlname;
117    }
118
119    public static final Kind enter = new Kind("precondition ", "<ENTER>");
120
121    public static final Kind exit = new Kind("postcondition", "<EXIT>");
122
123    public static final Kind objectInvariant = new Kind("obj invariant", "<OBJECT>");
124
125    public static final Kind classInvariant = new Kind("class invariant", "<CLASS>");
126
127    // See documentation for Serializable.
128    private Object readResolve() throws ObjectStreamException {
129      if (name.equals("precondition ")) {
130        return enter;
131      } else if (name.equals("postcondition")) {
132        return exit;
133      } else if (name.equals("class invariant")) {
134        return classInvariant;
135      } else {
136        assert name.equals("obj invariant");
137        return objectInvariant;
138      }
139    }
140  }
141
142  /**
143   * Two properties are equal if their fields {@code daikonRep}, {@code method} and {@code kind} are
144   * equal. The other fields may differ.
145   */
146  @EnsuresNonNullIf(result = true, expression = "#1")
147  @Pure
148  @Override
149  public boolean equals(@GuardSatisfied Property this, @GuardSatisfied @Nullable Object o) {
150    if (o == null) {
151      return false;
152    }
153    if (!(o instanceof Property)) {
154      return false;
155    }
156    Property anno = (Property) o;
157    return (this.daikonRep().equals(anno.daikonRep())
158        && this.method().equals(anno.method())
159        && this.kind().equals(anno.kind()));
160  }
161
162  @Pure
163  @Override
164  public int hashCode(@GuardSatisfied @UnknownSignedness Property this) {
165    return daikonRep.hashCode() + kind.hashCode() + (method == null ? 0 : method.hashCode());
166  }
167
168  /**
169   * Parse a String and return the property that it represents. An example of the String
170   * representation of an property is:
171   *
172   * <pre>{@code
173   * <INVINFO>
174   * <INV> this.topOfStack <= this.theArray.length-1 </INV>
175   * <ENTER>
176   * <DAIKON>  this.topOfStack <= size(this.theArray[])-1  </DAIKON>
177   * <DAIKONCLASS>class daikon.inv.binary.twoScalar.IntLessEqual</DAIKONCLASS>
178   * <METHOD>  isEmpty()  </METHOD>
179   * </INVINFO>
180   * }</pre>
181   *
182   * <p>The above string should actually span only one line.
183   *
184   * <p>To be well-formed, a property should be enclosed in {@code <INVINFO>} tags, contain {@code
185   * <DAIKON>} and {@code <METHOD>} tags, and exactly one of {@code <ENTER>}, {@code <EXIT>}, {@code
186   * <OBJECT>}, or {@code <CLASS>}.
187   */
188  // [[ Using an XML parser seems like too strong a hammer here.
189  //    But should do some profiling to see if all the string
190  //    matching slows things sufficiently for us to want to optimize. ]]
191  public static Property get(String annoString) throws MalformedPropertyException {
192
193    // check well-formedness
194    if (!(annoString.matches(".*<INVINFO>.*</INVINFO>.*")
195        && annoString.matches(".*<DAIKON>(.*)</DAIKON>.*")
196        && annoString.matches(".*<METHOD>(.*)</METHOD>.*"))) {
197      throw new MalformedPropertyException(annoString);
198    }
199
200    // figure out what kind of property it is
201    Kind k;
202    if (annoString.matches(".*<ENTER>.*")) {
203      k = Kind.enter;
204    } else if (annoString.matches(".*<EXIT>.*")) {
205      k = Kind.exit;
206    } else if (annoString.matches(".*<OBJECT>.*")) {
207      k = Kind.objectInvariant;
208    } else if (annoString.matches(".*<CLASS>.*")) {
209      k = Kind.classInvariant;
210    } else {
211      throw new MalformedPropertyException(annoString);
212    }
213
214    String theDaikonRep = annoString.replaceFirst(".*<DAIKON>(.*)</DAIKON>.*", "$1").trim();
215    String theMethod = annoString.replaceFirst(".*<METHOD>(.*)</METHOD>.*", "$1").trim();
216
217    String theInvRep = annoString.replaceFirst(".*<INV>(.*)</INV>.*", "$1").trim();
218    String theDaikonClass =
219        annoString.replaceFirst(".*<DAIKONCLASS>(.*)</DAIKONCLASS>.*", "$1").trim();
220    double theConfidence = -1;
221    if (annoString.matches(".*<CONFIDENCE>(.*)</CONFIDENCE>.*")) {
222      theConfidence =
223          Double.parseDouble(
224              annoString.replaceFirst(".*<CONFIDENCE>(.*)</CONFIDENCE>.*", "$1").trim());
225    }
226    return Property.get(k, theDaikonRep, theMethod, theInvRep, theDaikonClass, theConfidence);
227  }
228
229  /**
230   * XML representation. May be diferent from the String used to parse the property; only those tags
231   * that were parsed by the get() method will be output here.
232   */
233  public String xmlString() {
234    return "<INVINFO> "
235        + kind.xmlString()
236        + "<DAIKON>"
237        + daikonRep
238        + " </DAIKON> "
239        + "<METHOD> "
240        + method
241        + " </METHOD>"
242        + "<INV>"
243        + jmlRep
244        + "</INV>"
245        + " <CONFIDENCE>"
246        + confidence
247        + " </CONFIDENCE>"
248        + " <DAIKONCLASS>"
249        + daikonClass
250        + " </DAIKONCLASS>"
251        + "</INVINFO>";
252  }
253
254  /**
255   * Similar to {@link #xmlString()}, but without a {@code <INV>...</INV>} tag (the JML
256   * representation).
257   *
258   * <p>Invariant: {@code this.equals(Property.get(this.xmlStringNoJml())}
259   */
260  public String xmlStringNoJml() {
261    return "<INVINFO> "
262        + kind.xmlString()
263        + "<DAIKON>"
264        + daikonRep
265        + " </DAIKON> "
266        + "<METHOD> "
267        + method
268        + " </METHOD>"
269        + " <CONFIDENCE>"
270        + confidence
271        + " </CONFIDENCE>"
272        + " <DAIKONCLASS>"
273        + daikonClass
274        + " </DAIKONCLASS>"
275        + "</INVINFO>";
276  }
277
278  /**
279   * Find, parse and return all well-formed XML String representing properties. String. The string
280   * {@code annoString} may contain none, one, or several properties. Ignores malformed properties.
281   */
282  public static Property[] findProperties(String annoString) {
283    List<String> l = Collections.singletonList(annoString);
284    return findProperties(l);
285  }
286
287  /**
288   * Find, parse and return all distinct properties found in a list of strings. Each string in
289   * {@code annoStrings} may contain none, one, or several properties. Malformed properties are
290   * ignored.
291   */
292  public static Property[] findProperties(List<String> annoStrings) {
293
294    if (annoStrings == null) {
295      return new Property[] {};
296    }
297    // Pattern p = Pattern.compile("(<INVINFO>.*</INVINFO>)");
298    Set<Property> annos = new HashSet<>();
299    for (String location : annoStrings) {
300      if (location == null || location.equals("")) {
301        continue;
302      }
303      String[] cutUp = location.split("<INVINFO>");
304      // Matcher m = p.matcher(location);
305      for (int splits = 0; splits < cutUp.length; splits++) {
306        // while (m.find()) {
307        try {
308          // String s = m.group(1);
309          String s = cutUp[splits];
310          Property anno = Property.get("<INVINFO>" + s);
311          // [[[ explain this! ]]]
312          annos.add(anno);
313        } catch (Exception e) {
314          // malformed property; just go to next iteration
315        }
316      }
317    }
318    return annos.toArray(new Property[] {});
319  }
320
321  /** Get the property with the given attributes. */
322  private static Property get(
323      Kind kind,
324      String daikonRep,
325      String method,
326      String jmlRep,
327      String daikonClass,
328      double confidence)
329      throws MalformedPropertyException {
330
331    Property anno = new Property(kind, daikonRep, method, jmlRep, daikonClass, confidence);
332    Integer key = anno.hashCode();
333    if (propertiesMap.containsKey(key)) {
334      return propertiesMap.get(key);
335    } else {
336      if (confidence == -1) {
337        anno.confidence = anno.calculateConfidence();
338      }
339      propertiesMap.put(key, anno);
340      return anno;
341    }
342  }
343
344  // This is never used, and the "break" clause seems to be buggy, so
345  // that this returns at most one property.
346  // /**
347  //  * The properties in {@code annas} with the given kind.
348  //  */
349  // public static Property[] getKind(Property[] annas, Kind kind) {
350  //     List<Property> retval = new ArrayList<>();
351  //     for (int i = 0; i < annas.length; i++) {
352  //         if (kind == annas[i].kind) {
353  //             retval.add(annas[i]);
354  //         }
355  //         break;
356  //     }
357  //     return retval.toArray(new Property[] {});
358  // }
359
360  /**
361   * A heuristic technique that takes into account several factors when calculating the confidence
362   * of an property, among them:
363   *
364   * <ul>
365   *   <li>The values of {@code property.kind()}.
366   *   <li>The "Daikon class" of the property.
367   *   <li>Whether the property relates old and new state.
368   *   <li>The total number of properties in the method containing this property.
369   * </ul>
370   */
371  // [[ A work in progress ]]
372  public double calculateConfidence() {
373
374    double ret = 0;
375
376    // [[[ why the weird numbers like 0,89 and 0.91? Purely for
377    // debugging purposes -- upon seeing a confidence number
378    // assigned, I can tell why it was assigned. And as long as it's
379    // above the confidence threshold (by default 0.5), 0.89 is treated
380    // the same as 0.91. ]]]
381
382    // Order is important in the following statements.
383
384    // These types of invariants often seem to capture true properties
385    if ((daikonClass != null)
386        && ((this.daikonClass.indexOf("daikon.inv.unary.scalar.NonZero") != -1)
387            || (this.daikonClass.indexOf("daikon.inv.unary.scalar.LowerBound") != -1))) {
388      ret = 0.91;
389
390      // These types of invariants often seem NOT to capture generally
391      // true properties
392    } else if ((daikonClass != null)
393        && ((this.daikonClass.indexOf("SeqIndex") != -1)
394            || (this.daikonClass.indexOf("EltOneOf") != -1)
395            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.NoDuplicates") != -1)
396            || (this.daikonClass.indexOf("daikon.inv.unary.scalar.OneOf") != -1)
397            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence") != -1)
398            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.OneOfFloatSequence") != -1)
399            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.OneOfSequence") != -1)
400            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatNonEqual") != -1)
401            || (this.daikonClass.indexOf("daikon.inv.binary.twoScalar.NumericFloat") != -1)
402            || (this.daikonClass.indexOf("daikon.inv.binary.twoScalar.NumericInt") != -1)
403
404            // Ignore invariants over two sequences.
405            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseFloatEqual") != -1)
406            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseFloatGreaterEqual")
407                != -1)
408            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseFloatGreaterThan")
409                != -1)
410            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseFloatLessEqual")
411                != -1)
412            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseFloatLessThan")
413                != -1)
414            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseIntEqual") != -1)
415            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseIntGreaterEqual")
416                != -1)
417            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseIntGreaterThan")
418                != -1)
419            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseIntLessEqual")
420                != -1)
421            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseIntLessThan") != -1)
422            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseLinearBinary")
423                != -1)
424            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseLinearBinaryFloat")
425                != -1)
426            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseNumericFloat")
427                != -1)
428            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.PairwiseNumericInt") != -1)
429            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.Reverse") != -1)
430            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.ReverseFloat") != -1)
431            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqFloatEqual") != -1)
432            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqFloatGreaterEqual")
433                != -1)
434            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqFloatGreaterThan")
435                != -1)
436            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqFloatLessEqual")
437                != -1)
438            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqFloatLessThan") != -1)
439            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqIntEqual") != -1)
440            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqIntGreaterEqual")
441                != -1)
442            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqIntGreaterThan")
443                != -1)
444            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqIntLessEqual") != -1)
445            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqIntLessThan") != -1)
446            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqStringEqual") != -1)
447            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqStringGreaterEqual")
448                != -1)
449            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqStringGreaterThan")
450                != -1)
451            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqStringLessEqual")
452                != -1)
453            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SeqSeqStringLessThan")
454                != -1)
455            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SubSequence") != -1)
456            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SubSequenceFloat") != -1)
457            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SubSet") != -1)
458            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SubSetFloat") != -1)
459            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SuperSet") != -1)
460            || (this.daikonClass.indexOf("daikon.inv.binary.twoSequence.SuperSetFloat") != -1)
461
462            // Ignore invariants having to do with a sequence and its indices
463            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatEqual") != -1)
464            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatGreaterEqual")
465                != -1)
466            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatGreaterThan")
467                != -1)
468            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatLessEqual") != -1)
469            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatLessThan") != -1)
470            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexFloatNonEqual") != -1)
471            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexIntEqual") != -1)
472            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexIntGreaterEqual") != -1)
473            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexIntGreaterThan") != -1)
474            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexIntLessEqual") != -1)
475            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexIntLessThan") != -1)
476            || (this.daikonClass.indexOf("daikon.inv.unary.sequence.SeqIndexIntNonEqual") != -1)
477
478            // These often lead to bogus linear equations.
479            || (this.daikonClass.indexOf("daikon.inv.ternary.threeScalar.FunctionBinary") != -1)
480            || (this.daikonClass.indexOf("daikon.inv.ternary.threeScalar.FunctionBinaryFloat")
481                != -1)
482            || (this.daikonClass.indexOf("daikon.inv.ternary.threeScalar.LinearTernary") != -1)
483            || (this.daikonClass.indexOf("daikon.inv.ternary.threeScalar.LinearTernaryFloat") != -1)
484
485            // Ignore invariants that compare all elements in a sequences against some value.
486            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqFloatEqual") != -1)
487            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqFloatGreaterEqual")
488                != -1)
489            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqFloatGreaterThan")
490                != -1)
491            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqFloatLessEqual")
492                != -1)
493            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqFloatLessThan") != -1)
494            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqIntEqual") != -1)
495            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqIntGreaterEqual")
496                != -1)
497            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqIntGreaterThan")
498                != -1)
499            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqIntLessEqual") != -1)
500            || (this.daikonClass.indexOf("daikon.inv.binary.sequenceScalar.SeqIntLessThan") != -1)
501            || (this.daikonClass.indexOf("(lexically)") != -1))) {
502      ret = 0.19;
503
504    } else if (kind == Kind.objectInvariant || kind == Kind.classInvariant) {
505      ret = 1.0;
506      //         } else if (properties != null
507      //                 && (properties.methodAnnos(this.method()).length
508      //                     < ANNOS_PER_METHOD_FOR_GOOD_QUALITY)) {
509      //             ret = 0.9;
510    } else {
511      ret = 0.2;
512    }
513
514    return ret;
515  }
516
517  /** The maximum confidence value calculated among all Properties given. */
518  public static double maxConf(Property[] annos) {
519    if (annos.length == 0) {
520      return -1;
521    }
522    double max = 0.0;
523    for (int i = 0; i < annos.length; i++) {
524      if (annos[i].confidence > max) {
525        max = annos[i].confidence;
526      }
527    }
528    return max;
529  }
530
531  /** The confidence of a set of properties. Currently it's just the maximum confidence. */
532  public static double confidence(Property[] annos) {
533    return maxConf(annos);
534  }
535
536  // [[ TODO: first, you need to check that immutability and
537  // uniqueness even hold. Then, you need to figoure out a better
538  // way to do the stuff below (it seems to me like jmlRep,
539  // daikonClass and confidence should be given to the constructor
540  // of the object). ]]
541  private Object readResolve() throws ObjectStreamException {
542    try {
543
544      Property anno = get(kind(), daikonRep(), method(), jmlRep, daikonClass, confidence);
545      assert anno.jmlRep == null || anno.jmlRep.equals(this.jmlRep)
546          : "anno.jmlRep==" + anno.jmlRep + " this.jmlRep==" + this.jmlRep;
547      assert anno.daikonClass == null || anno.daikonClass.equals(this.daikonClass)
548          : "anno.daikonClass==" + anno.daikonClass + " this.daikonClass==" + this.daikonClass;
549      assert anno.confidence == 0 || anno.confidence == this.confidence
550          : "anno.confidence==" + anno.confidence + " this.confidence==" + this.confidence;
551      return anno;
552
553    } catch (MalformedPropertyException e) {
554      throw new Error(e);
555    }
556  }
557}