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}