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}