Upload
roland-blake
View
218
Download
0
Embed Size (px)
Citation preview
User-defined type checkers for error detection and prevention
in JavaMichael D. Ernst
MIT Computer Science & AI Lab
http://pag.csail.mit.edu/jsr308/
Code without type qualifiers
class DAG { Set<Edge> edges;
// ...
List<Vertex> getNeighbors( Vertex v) { List<Vertex> neighbors = new LinkedList<Vertex>(); for (Edge e : edges) if (e.from() == v) neighbors.add(e.to()); return neighbors; }}
@NonNull type qualifier@NonNullDefaultclass DAG { Set<Edge> edges;
// ...
List<Vertex> getNeighbors( Vertex v) { List<Vertex> neighbors = new LinkedList<Vertex>(); for (Edge e : edges) if (e.from() == v) neighbors.add(e.to()); return neighbors; }}
@NonNull type qualifier@NonNullDefaultclass DAG { @NonNull Set<@NonNull Edge> edges;
// ...
@NonNull List<@NonNull Vertex> getNeighbors( @NonNull Vertex v) { List<Vertex> neighbors = new LinkedList<Vertex>(); for (Edge e : edges) if (e.from() == v) // OK neighbors.add(e.to()); // OK return neighbors; }}
@Interned type qualifier@NonNullDefaultclass DAG { Set<Edge> edges;
// ...
List<Vertex> getNeighbors(@Interned Vertex v) { List<Vertex> neighbors = new LinkedList<Vertex>(); for (Edge e : edges) if (e.from() == v) // OK neighbors.add(e.to()); return neighbors; }}
@ReadOnly type qualifier@NonNullDefaultclass DAG { Set<Edge> edges;
// ...
List<Vertex> getNeighbors(@ReadOnly Vertex v) @ReadOnly { List<Vertex> neighbors = new LinkedList<Vertex>(); for (Edge e : edges) if (e.from() == v) neighbors.add(e.to()); return neighbors; }}
Using a checker
• Write annotations on the code– Or use an inference tool
• Compiler plugin for javac javac -processor NonnullChecker MyFile.java
• Produces additional errors and warnings– All existing Java checks are performed
• Type-system-specific checks– For NonNull: dereferences– For Interned: equality
• Error to assign supertype to subtype: myObject = myNonNullObject; // OK myInternedObject = myObject; // error myObject = myReadOnlyObject; // error
– Similarly for arguments, returns, overriding, …
Outline
• NonNull checker
• Interned checker
• Javari checker
• IGJ checker
• Creating your own checker
NonNull checker
• Problem: NullPointerExceptions• Example:
Object obj; // might be null
@NonNull Object nnobj; // never null
nnobj.toString(); // OK
obj.toString(); // possible NPE
obj = ...; // OK
nnobj = obj; // nnobj may become null
• Type system:
NonNull case study
• Programs:– Annotation file utilities (5 KLOC)– Lookup (4 KLOC, twice)– NonNull checker (1 KLOC)– Checkers framework (5 KLOC)
• Two different defaults: NonNull and Nullable
• Annotations with & without checker available
NonNull results
• 1 annotation per 50 lines of code– Inference tools exist (for NonNull and other qualifiers)
• 44 errors– if (x.y().z()) { somethingOptional(); }
where no work need be done if x.y() is null
• 83 application invariants– @NonNull indicates whether a variable may be null– @NonNull cannot indicate conditions that hold when a
variable is null• entry_re == null iff exit_re == null
• 43 tool weaknesses (many already fixed)
Comparison with other tools
Program to be checked: Lookup (4KLOC)
Other tools also find non-NPE bugs
To reduce false positives, they discard many warnings
Our checker 7 errors (every error!) 7 false pos.
FindBugs 1 warning: dead code 1 false pos.
JLint 0 8
PMD 0 0
Good defaults reduce user effort
• Nullable default:– Dictated by backward compatibility– Many instances of @NonNull – too verbose– Flow-sensitive analysis inferred many NonNull types
for local variables, reducing annotation burden• NonNull default
– Less verbose in signatures• Draws attention to exceptions rather than the rule
– More annotations in method bodies• Our system: Nullable only for local variables
– Not for generics on local variables– An alternative to type inference; effect may be similar
Interned checker
• Interning (aka hash-consing, canonicalization) chooses a canonical representative– Saves memory– Permits use of == for comparisons
• Problem: Equality errors• Example:
String s;@Interned String is, is2;... is = myString.intern() ... // OKif (is == is2) { ... } // OKif (s == is) { ... } // unsafe equalityis = s; // unsafe assignment
Interned case study
• Program: Daikon (250 KLOC)– Daikon’s key scalability problem is memory– 1170 lines of code/comment refer to interning
• 72% of files have none, 87% have 0, 1, or 2
– 200 run-time assertions– Emacs plug-in for String equality checking
• 127 annotations in 11 files (12KLOC)
Interned results
• 9 errors– Missing calls to intern
• 2 performance bugs– Unnecessary interning in inner loop of file reading
• 1 design flaw– VarInfoName is not interned within its implementation,
but all escaping objects are– Too hard to understand complex interning
• 14 false positives (used @SuppressWarnings)
Javari checker
• Problem: Unintended side effects (mutations)• Example:
<E> List<E> sort(@ReadOnly List<E> arg) { ... // swap two elements E tmp = arg.get(i); arg.put(i, arg.get(j)); // illegal mutation arg.put(j, tmp); // illegal mutation}
• Type system:
Javari case study
• Programs (8KLOC)– JOlden benchmarks– Javari checker– Some JDK classes
Javari results
• Mutability bug in Javari checker– Global variable indicating checker state was
set on entering an inner class, not reset on exiting
– The bug-fix allocates a new object instead
• 3 false positives due to varargs– void format(Object...) { … }
IGJ checker
• Problem: Unintended side effects (mutations)• Type system:
– ReadOnly: Reference immutability (aliases may modify)– Immutable: Object immutability (object cannot change)
• Syntax uses annotations, not generics
IGJ case study
• Programs (128KLOC)– JOlden benchmarks– htmlparser library– tinySQL library– SVNKit subversion client– IGJ checker
• 4760 annotations (62133 possible locations)
IGJ results
• Representation exposure errors• Constructors that left object in inconsistent state• 26 false positives (arrays, varargs, casting)• In SVNKit:
– some getters have side effects– some setters have no side effects
• Used both reference and object immutability• Preferred Annotation IGJ to original dialect: arrays, and
– List<ReadOnly, Date<Mutable>>– @ReadOnly List<@Mutable Date>
• More opportunities for immutability in new code than old
Writing your own checker
• Base checker performs all standard checks (assignment, method arguments)
• Override only a couple methods, for special rules– For NonNull: dereferences– For Interned: equality
• Check what you want:– General properties (tainting)– Project-specific (string formatting)
JSR 308: Annotation on Java types
• Permits annotations in new locationsList<@NonNull String> myList;
class Properties extends @Immutable Map {…}
• Planned for inclusion in Java 7• Implementation is backwards compatible:
– Write annotations in comments:
List</*@NonNull*/ String> myList;
/*@NonNull*/ String s;– Compiles with any Java compiler
Pluggable type system benefits
• Easy to use• Scalable• Reveals errors• Prevents even more errors
• Download the checkers from http://pag.csail.mit.edu/jsr308/– Prerelease of annotation extensions in Java 7
• Backward-compatible implementation– Includes inference tools for some type checkers– Includes a framework for writing new checkers