Java Annotations for
Invariant Specification
RICE UNIVERSITY
Mathias Ricken
COMPUTER
SCIENCE
September 22, 2008
COMP 617 Seminar
2
Comments are dumb
class HashMap {
// returns null if no mapping for key
Object get(Object key) { … }
}
HashMap m = new HashMap();
Object o = m.get("foo");
String s = o.toString();
NullPointerException at runtime:
o is null
3
Types are smart
class HashMap {
// returns null if no mapping for key
Object|null get(Object key) { … }
} Compiler Error:
Return type Object|null
HashMap m = new HashMap(); incompatible with type
Object
Object o = m.get("foo");
String s = o.toString();
This is not Java!
4
Annotations can make Java smarter
class HashMap {
// returns null if no mapping for key
@Nullable Object get(Object key) { … }
}
Compiler Warning:
HashMap m = new HashMap(); Return value may be null,
assigned to non-null variable.
Object o = m.get("foo");
String s = o.toString();
Pluggable type systems in Java 5 7?
5
Annotation Targets in Java 5
@A package some.package.name;
@B class MyClass {
@NonNull Object field;
@C MyClass(@NonNull Object param) {
field = param;
}
@NonNull Object method() {
@NonNull Object localVar = field;
return localVar;
}
} Note: Local variable annotations are completely ignored.
6
Concurrency Invariants
interface TableModel {
// may only be called from event thread
void setValueAt(…);
}
TableModel m;
// from outside event thread…
m.setValueAt(…);
Possible race condition.
7
Invariant Specification
interface TableModel {
@OnlyEventThread
void setValueAt(…);
}
TableModel m;
// from outside event thread…
m.setValueAt(…);
Invariant Violation Warning at Runtime
(but still possible race condition)
8
Comparison to assert
void setValueAt(…) {
assert (EventQueue.isDispatchThread());
…
Similarity
Debug mode – disabled in production code
@OnlyEventThread
void setValueAt(…) { … }
9
Annotations are Easier to Find
Javadoc produces invariant index
10
Inherited Invariants
TableModel
Object getValueAt(…)
@OnlyEventThread void setValueAt(…)
Implied AbstractTableModel
@OnlyEventThread
Object getValueAt(…)
void setValueAt(…)
DefaultTableModel MySpecialTableModel
Object getValueAt(…) Object getValueAt(…)
void setValueAt(…) void setValueAt(…)
Implied Implied
@OnlyEventThread @OnlyEventThread
11
Inherited Invariants
@OnlyEventThread TableModel
Object getValueAt(…)
void setValueAt(…)
Implied
@OnlyEventThread AbstractTableModel
Object getValueAt(…) Implied
void setValueAt(…) @OnlyEventThread
DefaultTableModel MySpecialTableModel
Object getValueAt(…) Object getValueAt(…)
void setValueAt(…) void setValueAt(…)
Implied Implied
@OnlyEventThread @OnlyEventThread
12
Limited Universality
A few supplied invariant annotations
@OnlyEventThread @NotEventThread
@OnlyThreadWithName @NotThreadWithName
@OnlySynchronizedThis @NotSynchronizedThis
assert can test an arbitrary predicate
assert (someComplexPredicate());
13
Predicate Invariant Annotations
@PredicateLink(value=Predicates.class, method="eval")
public @interface OnlyThreadWithName {
String value;
e t hod
}
at em
r edic
1. in dp
F
@OnlyThreadWithName("main")
void myMethod() { … } 2.
Call predicate method and
pass as arguments:
Return true or false • this (nor null if static)
to indicate violation 3. • data in invariant annotation
public class Predicates {
public static boolean eval(Object this0, String name) {
return Thread.currentThread().getName().
equals(name);
}
}
14
Further Limitation of Annotations
One occurrence of an annotation class per
target
@OnlyThreadWithName("main") // illegal; and is
@OnlyThreadWithName("other") // this "and" or "or"?
void myMethod() { … }
Suggestion
@Or({
@OnlyThreadWithName("main"),
@OnlyThreadWithName("other")
})
void myMethod() { … }
15
Annotation Members extends OtherAnnotation
not allowed no subtyping
@interface MyAnnotation {
int intMember; // primitives
String stringMember; // strings
Class classMember; // class literals
SomeEnum enumMember; // enums
// annotions
OnlyThreadWithName annotMember;
// arrays of the above
OnlyThreadWithName[] arrayMember;
}
16
No Annotation Subtyping in Java
@interface Or { OnlyThreadWithName[] value; }
@Or({@OnlyThreadWithName("main"),
@OnlyThreadWithName("other")})
void myMethod() { … } // legal
@Or({@OnlyThreadWithName("main"),
@NotThreadWithName("other")})
void otherMethod() { … } // illegal
No common supertype for annotations
17
xajavac Modified Compiler
@interface Base {}
@interface OnlyThreadWithName extends Base {
String value;
}
@interface NotThreadWithName extends Base {
String value;
}
@interface Or extends Base {
Base[] value;
}
18
Results
Annotations with Subtyping
Minimalchanges to the compiler
No changes to class file format
Reduced invariant checker by ~1500 lines
Improved code reuse
19
Results
Invariant Annotations
Annotated part of Swing and DrJava
Discovered and fixed some bugs in DrJava
Hard to do retroactively and without inside
knowledge
20
Future Work
Reflection library for annotations with
subtyping
Annotation getAnnotation(Class c)
currently returns the annotation of class c
Annotation[] getAnnotations(Class c)
should be added to return all annotations of
class c and its subclasses
Modify JSR 308 prototype compiler to
support subtyping
21
More Information and Download
Invariant Specification
http://www.concutest.org/tc/
Annotations with Subtyping
http://www.cs.rice.edu/~mgricken/research/xajavac/