Download presentation
Presentation is loading. Please wait.
Published byAja Dingley Modified over 10 years ago
1
1 Practical Pluggable types for Java Chris Andreae James Noble Victoria University of Wellington Shane Arkus Todd Millstein University of California Presented by Lina Zarivach
2
2 Existing type systems are not sufficient Non-null types Declaring and Checking Non-null Types by M. Fahndrich, OOPSLA’03 Non-Null Safety in Eiffel by B. Meyer, ECOOP’05 Readonly types Javari by M. Tschantz, OOPSLA’05 Other type systems Confined Types by J. Vitek, OOPSLA’99 …
3
3 Implementation Ad-hoc: New keywords and compiler reimplementation. Pluggable type systems: Idea by Gilad Bracha, ’04. Implementation by Andreae, Nobel et.al. ’06: JavaCOP Define new types with Java Annotations Express new type requirements via JavaCOP Rule Language
4
4 NonNull types example – Pseudo-Declarative syntax & semantics class Person { @NonNull String firstName = "Chris"; void setFirstName(String newName) { firstName = newName;} } A JavaCOP rule to discover this case: rule checkNonNull(Assign a) { where(nonnull(a.lhs)) { require(defNonNull(a.rhs)): error(a,“Assigning possible null to @NonNull variable”); } newName may be NULL!!!
5
5 javac compiler JavaCOP Architecture Java program with new types AST of the program JavaCOP rules
6
6 JavaCOP Rule Language Types A subset of data types generated in the javac compiler. In particular: AST nodes types Type type Symbol type String and generic List types Env Globals
7
7 AST Node types Method call, like meth(args); Instance creation, like new World(“Hello”); Field or method selection, i.e., o.field or o.method();
8
8 Symbols and Types Symbol Table … …
9
9 Rules for AST nodes rule aTreeRule(Tree arg) { } rule finalClass(ClassDef c){ require(!c.supertype().getSym().hasAnnotation("Final")); } Only one argument of type Tree or subclasses of Tree. The rule will be applied to each node of the specified type. Huge API
10
10 Primitive Constraints require( ); rule UnconditionalIf (If i){ require(i.cond.getSymbol != globals.trueConst ): warning(i.cond,"The body of this if will unconditionally be executed"); require(i.cond.getSymbol != globals.falseConst ): warning(i.cond,"The body of this if will never be executed"); } Instance of Globals
11
11 Conditional Constraints where( ) { } rule MicroPatternsExample (ClassDef c){ where(c.isInterface && c.getSym.members.length == 0){ require(c.interfaces.length != 1): warning(globals.NOPOS, "Taxonomy:"+c.flatName ); } Primitive constraint
12
12 Pattern matching where( ; tree => [match using vars]) { } rule checkNonNullFieldAccess(Assign a){ where(nonnull(a.lhs)) { where(String n; a.rhs => [%.n]){ require(n.equals("class")): error(a, "Assigning possibly null field access " + " to @NonNull variable"); }}} % is a wildcard for Tree.
13
13 Universal quantifier Similar to the “enhanced for” in Java 1.5. rule finalMethod(MethodDef m){ forall(Type st: m.enclClass.transitiveSupertypes){ forall(Symbol other: st.getSymbol.memberLookup(m.name)){ where(other.isFinal){ require(!m.overrides(other, st.getSymbol)) :error(m, "You may not override final method "+other ); } } } }
14
14 Existential quantifier Implicit depth-first traversal from a given AST node. Only nodes that match the declared type of the quantifier variable are considered. rule allLocalVarsMustBeAssigned (MethodDef m){ forall(VarDef v : m.varDefs()){ where(v.init == null){ exists(Assign a : m){ require(a.lhs.getSymbol == v.getSymbol ); }: error(v,"The variable " + v + "is never assigned"); }}}
15
15 Predicates (package several conditions into boolean function) declare ( [, ]) { } Invoked by the bodies of rules and other predicates. declare nonnull(Tree t){ require(t.holdsSymbol && t.getSymbol.hasAnnotation("NonNull")); }
16
16 Predicates - achieving disjunctive constraints Predicate can have multiple definitions. An invocation of the predicate succeeds if at least one of the definitions’ bodies is satisfied. declare defNotNull(Tree t){ require(nonnull(t)); } declare defNotNull(Tree t){ require(t.type.isPrimitive()); } declare defNotNull(NewClass t){ require(true); } t is non-null if it has @NonNull annotation. Primitives can’t be null. New instance can’t be null.
17
17 Error reporting : (, ) is either error or warning. When a constraint fails to be satisfied, JavaCOP searches for the nearest enclosing failure clause and executes it. require(a){ require(b); require(c): error(pos1, "error - c has failed"); }: warning(globals.NOPOS, "warning - a or b have failed")
18
18 Rules for custs – syntactic sugar rule aCustRule(origType } Applied to every Tree node that performs explicit or implicit cust rule checkNonNullCust (a <: b @ e){ where(!nonnull(a)){ require(!nonnull(b)): error(e, “A possibly null type "+a +" may not be cast to Non-Null type "+b); }
19
19 Applications A checker for non-null types. A checker for Confined Types. Rules supplied by PMD Java Checker. Rules to identify some micro-patterns. Rules that gather information for software metrics. An EJB3.0 verifier.
20
20 NonNull assignment rule checkNonNullAssignment (Assign a){ where(nonnull(a.lhs )){ require(defNotNull(a.rhs)): error(a,"Assigning possibly null value to @NonNull” "+"variable."); } if(x != null){ @NonNull Object nonnull_x = x;...} The above rule will issue an error for this case!!!
21
21 NonNull assignment – flow-sensitive check rule checkNonNull (Assign a){ where(nonnull(a.lhs )){ require(defNotNull(a.rhs) || safeNullableAssign (a)): error(a,"Assigning possibly null value to @NonNull "+"variable."); }} declare safeNullableAssign (Assign a){ require(localVariable (a.rhs )); require(safeNullableAssign (env.tree, env.next, a)); } declare safeNullableAssign (Block b, Env e, Assign a){ require(safeNullableAssign (e.tree, e.next, a)); } declare safeNullableAssign (If i, Env e, Assign a){ require(nonNullTest(i.cond, a.rhs)&& firstExpression (i.thenpart, a)); } Env represents traversal information
22
22 Confined types Confined type is a type whose instances may not be referenced or accessed from outside a certain protection domain. Confined types protect objects from use by untrusted code.
23
23 Confined types – example 1 A confined type must not be declared public and must not belong to the unnamed global package. rule ConfinedExample1 (ClassDef c){ where(confined(c)){ require(!c.isPublic ()): error(c, "Confined class may not be public"); require(c.packge() != globals.emptyPackage): error(c, "Confined class may not be in the default package"); }
24
24 Confined Types – example 2 Subtypes of a confined type must be confined and belong to the same package as their supertype. rule ConfinedExample2 (ClassDef c){ where(confined(c.supertype)){ require(confined(c)): error(c, "An unconfined class may not extend " + "a confined superclass"); require(c.packge == c.supertype.packge): error(c, “A confined subclass must belong to the same package as its superclass”); }
25
25 JavaCOP Architecture - details Performance: ~0.02 sec/class
26
26 Conclusions The first practical framework for pluggable types. Contributions: Design of JavaCOP Rule Language Implementation of JavaCOP Validation on various kinds of application Future extensions: More support for flow sensitivity Error clause, that count the number of times a constraint failed. Increasing the efficiency of checking the constraints.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.