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