Post on 12-Jan-2016
description
Ownership and Immutability in Generic Java (OIGJ)
Yoav Zibin+, Alex Potanin*
Paley Li*, Mahmood Ali^, and Michael Ernst$
Presenter: Yossi Gil+
+IBM *Victoria,NZ ^MIT $Washington
2/22
Ownership + Immutability
Our previous work OGJ: added Ownership to Java IGJ: added Immutability to Java
This work OIGJ: combine Ownership + Immutability The sum is greater than its parts
IGJ could not type-check existing code for creating immutable cyclic data-structures (e.g., lists, trees)
We found a non-trivial connection between ownership and immutability
3/22
Contributions
No refactoring of existing code Prototype implementation
No syntax changes (uses type-annotations in Java 7) No runtime overhead Backward compatible
Verified that Java’s collection classes are properly encapsulated (using few annotations)
Flexibility OIGJ can type-check more code than previous work:
cyclic structures, the factory and visitor design patterns Formalization
Formalized the concepts of raw/cooked immutable objects and wildcards as owner parameters
Proved soundness
4/22
Problem 1: Representation exposure
Internal representation leaks to the outside private doesn’t offer real protection!Real life example!class Class { private List signers; public List getSigners() { return this.signers; }}
http://java.sun.com/security/getSigners.htmlBug: the system thinks that code signed by one identity is signed by a different identity
Forgot to copy signers!
5/22
Solution for Representation Exposure
Ownership! Class should own the list signers No outside alias can exist Ownership can be nested: note the tree structure
Classsigners
entry1 entry2 entryN…
elem1 elem2 elemN…
X
X
X
6/22
Ownership: Owner-as-dominator
Dominators in graph theory Given: a directed rooted graph X dominates Y if any path from the root to Y
passes X Owner-as-dominator
Object graph; roots are the static variables An object cannot leak outside its owner, i.e., Any path from a root to an object passes its
owner Conclusion: No aliases to internal state
7/22
Problem 2: Unintended Modification
Modification is not explicit in the language can Map.get() modify the map? for (Object key : map.keySet()) {
map.get(key); }throws ConcurrentModificationExceptionfor the following mapnew LinkedHashMap(100, 1, true)
Reorders elements according to last-accessed (like a cache)
8/22
Solution: Immutability
Varieties of Immutability Class immutability (like String or Integer in
Java) Object immutability
The same class may have both mutable and immutable instances
Reference immutability A particular reference cannot be used to mutate its
referent (but other aliases might cause mutations)
class Student { @Immutable Date dateOfBirth; … void setTutor(@ReadOnly Student tutor) @Mutable { … }}
Method may modify the this object
Example in IGJ syntax
9/23
Objects vs. References
Objects mutable or immutable Creation of an immutable object
Raw state: Fields can be assigned Cooked state: Fields cannot be assigned
References mutable, immutable, or readonly
Challenge: Cyclic Immutability
Cooking a cyclic data-structure is complicatedMany objects must be raw simultaneously to
manipulate backward pointersThen everything must become immutable
simultaneously
OIGJ’s novel idea:Prolong the cooking phase by using
ownership informationEnables creation of immutable cyclic
structures
11/22
Cooking immutable objects
Previous work An object becomes cooked when its
constructor finishes OIGJ’s observation
An object becomes cooked when its owner’s constructor finishes
The outside world will not see this cooking phase
The complex object with its representation becomes immutable simultenously
12/22
Cooking LinkedList (1 of 2)
No refactoring – the original code must compile in OIGJ
1 : LinkedList(Collection<E> c) {2 : this();3 : Entry<E> succ = this.header, pred = succ.prev;4 : for (E e : c) {5 : Entry<E> entry = 6 : new Entry<E>(e,succ,pred);7 : // An entry is modified after it’s constructor finished8 : pred.next = entry; pred = entry;9 : }10: succ.prev = pred;11: }
1 : LinkedList(Collection<E> c) {2 : this();3 : Entry<E> succ = this.header, pred = succ.prev;4 : for (E e : c) {5 : Entry<E> entry = 6 : new Entry<E>(e,succ,pred);7 : // An entry is modified after it’s constructor finished8 : pred.next = entry; pred = entry;9 : }10: succ.prev = pred;11: }
Sun’s code is similar
13/22
Cooking LinkedList (2 of 2)
The list owns its entries Therefore, it can mutate them, even
after their constructor finished
1 : LinkedList(@ReadOnly Collection<E> c) @Raw {2 : this();3 : @This @I Entry<E> succ = this.header, pred = succ.prev;4 : for (E e : c) {5 : @This @I Entry<E> entry = 6 : new @This @I Entry<E>(e,succ,pred);7 : // An entry is modified after it’s constructor finished8 : pred.next = entry; pred = entry;9 : }10: succ.prev = pred;11: }
1 : LinkedList(@ReadOnly Collection<E> c) @Raw {2 : this();3 : @This @I Entry<E> succ = this.header, pred = succ.prev;4 : for (E e : c) {5 : @This @I Entry<E> entry = 6 : new @This @I Entry<E>(e,succ,pred);7 : // An entry is modified after it’s constructor finished8 : pred.next = entry; pred = entry;9 : }10: succ.prev = pred;11: }
Sun’s code is similar
Code in OIGJ; Annotations next slide.
14/22
Hierarchies in OIGJ
Immutability hierarchy
ReadOnly – no modification
Raw – object under construction
Ownership hierarchyWorld – anyone can
accessThis – this owns the
object
World
This
ReadOnly
Raw
Mutable
Immut
15/22
OIGJ syntax: fields (1 of 2)
Two annotations per type
1:class Foo {2: // An immutable reference to an immutable date.
@O @Immut Date imD = new @O @Immut Date ();3: // A mutable reference to a mutable date.
@O @Mutable Date mutD = new @O @Mutable Date();4: // A readonly reference to any date. Both roD and imD cannot mutate // their referent, however the referent of roD might be mutated by an // alias, whereas the referent of imD is immutable.
@O @ReadOnly Date roD = ... ? imD : mutD;5: // A date with the same owner and immutability as this
@O @I Date sameD;6: // A date owned by this; it cannot leak.
@This @I Date ownedD;7: // Anyone can access this date.
@World @I Date publicD;
1:class Foo {2: // An immutable reference to an immutable date.
@O @Immut Date imD = new @O @Immut Date ();3: // A mutable reference to a mutable date.
@O @Mutable Date mutD = new @O @Mutable Date();4: // A readonly reference to any date. Both roD and imD cannot mutate // their referent, however the referent of roD might be mutated by an // alias, whereas the referent of imD is immutable.
@O @ReadOnly Date roD = ... ? imD : mutD;5: // A date with the same owner and immutability as this
@O @I Date sameD;6: // A date owned by this; it cannot leak.
@This @I Date ownedD;7: // Anyone can access this date.
@World @I Date publicD;
16/22
OIGJ syntax: methods (2 of 2)
Method receiver’s annotation has a dual purpose: Determines if the method is applicable. Inside the method, the bound of @I is the annotation.
8 : // Can be called on any receiver; cannot mutate this. int readonlyMethod() @ReadOnly {...}9 : // Can be called only on mutable receivers; can mutate this. void mutatingMethod() @Mutable {...}10: // Constructor that can create (im)mutable objects. Foo(@O @I Date d) @Raw {11: this.sameD = d;12: this.ownedD = new @This @I Date ();13: // Illegal, because sameD came from the outside. // this.sameD.setTime(...);14: // OK, because Raw is transitive for owned fields. this.ownedD.setTime(...);15: }
8 : // Can be called on any receiver; cannot mutate this. int readonlyMethod() @ReadOnly {...}9 : // Can be called only on mutable receivers; can mutate this. void mutatingMethod() @Mutable {...}10: // Constructor that can create (im)mutable objects. Foo(@O @I Date d) @Raw {11: this.sameD = d;12: this.ownedD = new @This @I Date ();13: // Illegal, because sameD came from the outside. // this.sameD.setTime(...);14: // OK, because Raw is transitive for owned fields. this.ownedD.setTime(...);15: }
17/22
Formalization: Featherweight OIGJ
Novel idea: CookersEvery location l in the heap is of the form:
l' is the owner of ll” is the cooker of l, i.e., when the
constructor of l” finishes then l becomes cooked
We keep track of the set of ongoing constructors
Subtyping rules connect cookers and owners
lFoo<l’,Immut >lFoo<l’,Mutable> or l"
Proved soundness and type preservation
18/22
Case studies
Implementation uses the checkers frameworkOnly 1600 lines of code (but still a
prototype)Requires type annotations available in
Java 7 Java’s Collections case study
77 classes, 33K lines of code85 ownership-related annotations46 immutability-related annotations
19/22
Case studies conclusions
Verified that collections own their representation
Method clone is problematic Clone makes a shallow copy that breaks ownership Our suggestion: compiler-generated clone that
nullifies fields, and then calls a copy-constructor
20/22
Previous Work
Universes Relaxed owner-as-dominator to owner-as-
modifier ReadOnly references can be freely shared Constrains modification instead of aliasing,
i.e., only the owner can modify an object
Reference immutability: C++’s const Javari
21/22
Future work
Inferring ownership and immutability annotations
Bigger case study Extending OIGJ
owner-as-modifier uniqueness or external uniqueness
22/22
Conclusions
Ownership Immutability Generic Java (OIGJ)Simple, intuitive, smallStatic – no runtime penalties (like
generics)Backward compatible, no JVM changes
Case study proving usefulnessFormal proof of soundnessPaper submitted to OOPSLA. Links:
http://ecs.victoria.ac.nz/twiki/pub/Main/TechnicalReportSeries/
http://code.google.com/p/checker-framework/ http://code.google.com/p/ownership-immutability/
24/22
OIGJ typing rules
Ownership nesting Field access Field assignment Method invocation Method guards Raw parameter
can be used only in method guards (see the paper for all rules, such as
inner classes, covariant subtyping)
25/22
Ownership example in OGJ
This-owned fields/methods can be accessed only via this
class Class { @This List signers; // This-owned field public @This List getSigners1() { return this.signers; } public @World List getSigners2() { return new @World LinkedList(this.signers); } public void example(Class other) { this.signers = …; // Ok other.signers = …; // Illegal this.getSigners1(); // Ok other.getSigners1(); // Illegal other.getSigners2(); // Ok}
26/22
OIGJ syntax: fields (1 of 2)
Two new generic parameters were added
1:class Foo<O extends World, I extends ReadOnly> {2: // An immutable reference to an immutable date.
Date<O,Immut> imD = new Date<O,Immut>();3: // A mutable reference to a mutable date.
Date<O,Mutable> mutD = new Date<O,Mutable>();4: // A readonly reference to any date. Both roD and imD cannot mutate // their referent, however the referent of roD might be mutated by an // alias, whereas the referent of imD is immutable.
Date<O,ReadOnly> roD = ... ? imD : mutD;5: // A date with the same owner and immutability as this
Date<O,I> sameD;6: // A date owned by this; it cannot leak.
Date<This,I> ownedD;7: // Anyone can access this date.
Date<World,I> publicD;
1:class Foo<O extends World, I extends ReadOnly> {2: // An immutable reference to an immutable date.
Date<O,Immut> imD = new Date<O,Immut>();3: // A mutable reference to a mutable date.
Date<O,Mutable> mutD = new Date<O,Mutable>();4: // A readonly reference to any date. Both roD and imD cannot mutate // their referent, however the referent of roD might be mutated by an // alias, whereas the referent of imD is immutable.
Date<O,ReadOnly> roD = ... ? imD : mutD;5: // A date with the same owner and immutability as this
Date<O,I> sameD;6: // A date owned by this; it cannot leak.
Date<This,I> ownedD;7: // Anyone can access this date.
Date<World,I> publicD;
27/22
OIGJ syntax: methods (2 of 2)
Method guard <T extends U>? has a dual purpose: The method is included only if T extends U Inside the method, the bound of T is U.
8 : // Can be called on any receiver; cannot mutate this. <I extends ReadOnly>? int readonlyMethod(){...}9 : // Can be called only on mutable receivers; can mutate this. <I extends Mutable>? void mutatingMethod(){...}10: // Constructor that can create (im)mutable objects. <I extends Raw>? Foo(Date<O,I> d) {11: this.sameD = d;12: this.ownedD = new Date<This,I>();13: // Illegal, because sameD came from the outside. // this.sameD.setTime(...);14: // OK, because Raw is transitive for owned fields. this.ownedD.setTime(...);15: }
8 : // Can be called on any receiver; cannot mutate this. <I extends ReadOnly>? int readonlyMethod(){...}9 : // Can be called only on mutable receivers; can mutate this. <I extends Mutable>? void mutatingMethod(){...}10: // Constructor that can create (im)mutable objects. <I extends Raw>? Foo(Date<O,I> d) {11: this.sameD = d;12: this.ownedD = new Date<This,I>();13: // Illegal, because sameD came from the outside. // this.sameD.setTime(...);14: // OK, because Raw is transitive for owned fields. this.ownedD.setTime(...);15: }
28/22
LinkedList Example
1 : class Entry<O,I,E> {2 : E element;3 : Entry<O,I,E> next, prev; …4 : }5 : class LinkedList<O,I,E> {6 : Entry<This,I,E> header; …7 : <I extends Raw>? LinkedList(8 : Collection<?,ReadOnly,E> c) {9 : this(); this.addAll(c);10: }11: <I extends Raw>? void addAll(12: Collection<?,ReadOnly,E> c) {13: Entry<This,I,E> succ = this.header,14: pred = succ.prev;15: for (E e : c) {16: Entry<This,I,E> en =17: new Entry<This,I,E>(e,succ,pred);18: pred.next = en; pred = en; }19: succ.prev = pred;20: }
1 : class Entry<O,I,E> {2 : E element;3 : Entry<O,I,E> next, prev; …4 : }5 : class LinkedList<O,I,E> {6 : Entry<This,I,E> header; …7 : <I extends Raw>? LinkedList(8 : Collection<?,ReadOnly,E> c) {9 : this(); this.addAll(c);10: }11: <I extends Raw>? void addAll(12: Collection<?,ReadOnly,E> c) {13: Entry<This,I,E> succ = this.header,14: pred = succ.prev;15: for (E e : c) {16: Entry<This,I,E> en =17: new Entry<This,I,E>(e,succ,pred);18: pred.next = en; pred = en; }19: succ.prev = pred;20: }
29/22
Ownership nesting
List<This, I,Date<World,I>> l1; // Legal nestingList<World,I,Date<This, I>> l2; // Illegal!
List<This, I,Date<World,I>> l1; // Legal nestingList<World,I,Date<This, I>> l2; // Illegal!
The main owner parameter must be inside any other owner parameter.
public static Object<World,ReadOnly> alias_l2;public static Object<World,ReadOnly> alias_l2;
It’s illegal because we can store l2 in this variable:
30/22
Field access/assignment
1: class Foo<O extends World, I extends ReadOnly> {2: Date<This,I> ownedD; // this-owned field3: Date<O,I> sameD;4: <I extends Mutable>? void bar(Foo<This,I> other) {5: this.ownedD = …; // Legal: assign via this6: other.ownedD = …; // Illegal: not via this7: other.sameD = …; // Legal: not this-owned8: }
1: class Foo<O extends World, I extends ReadOnly> {2: Date<This,I> ownedD; // this-owned field3: Date<O,I> sameD;4: <I extends Mutable>? void bar(Foo<This,I> other) {5: this.ownedD = …; // Legal: assign via this6: other.ownedD = …; // Illegal: not via this7: other.sameD = …; // Legal: not this-owned8: }
this-owned fields can be accessed/assigned only via this
31/22
Field assignment
1 : class Foo<O extends World, I extends ReadOnly> {2 : Date<O,I> sameD;3 : <I extends Raw>? void bar(4 : Foo<?,Mutable> mutableFoo,5 : Foo<?,ReadOnly> readonlyFoo,6 : Foo<?,I> rawFoo1,7 : Foo<This,I> rawFoo2) {8 : mutableFoo.sameD = …; // Legal: object is Mutable9 : readonlyFoo.sameD = …; // Illegal: object is not Raw nor Mutable10: rawFoo1.sameD = …; // Illegal: object is not this nor this-owned11: rawFoo2.sameD = …; // Legal: object is Raw and this-owned12: this.sameD = …; // Legal: object is Raw and this13: }
1 : class Foo<O extends World, I extends ReadOnly> {2 : Date<O,I> sameD;3 : <I extends Raw>? void bar(4 : Foo<?,Mutable> mutableFoo,5 : Foo<?,ReadOnly> readonlyFoo,6 : Foo<?,I> rawFoo1,7 : Foo<This,I> rawFoo2) {8 : mutableFoo.sameD = …; // Legal: object is Mutable9 : readonlyFoo.sameD = …; // Illegal: object is not Raw nor Mutable10: rawFoo1.sameD = …; // Illegal: object is not this nor this-owned11: rawFoo2.sameD = …; // Legal: object is Raw and this-owned12: this.sameD = …; // Legal: object is Raw and this13: }
1) A field can be assigned only if the object is Raw or Mutable.
2) If it is Raw, then the object must be this or this-owned.
32/22
Method invocation
1 : class Foo<O extends World, I extends ReadOnly> {2 : Date<This,I> m1() { … } // Parameter is this-owned 3 : <I extends Raw>? void m2() { … }4 : <I extends Raw>? void bar(5 : Foo<?,I> rawFoo1,6 : Foo<This,I> rawFoo2) {7 : this.m1(); // Legal: object is this 8 : rawFoo2.m1(); // Illegal: object is not this9 : rawFoo1.m2(); // Illegal: object is not this nor this-owned10: rawFoo2.m2(); // Legal: both Raw and object is this-owned11: this.m2(); // Legal: both Raw and object is this12: }
1 : class Foo<O extends World, I extends ReadOnly> {2 : Date<This,I> m1() { … } // Parameter is this-owned 3 : <I extends Raw>? void m2() { … }4 : <I extends Raw>? void bar(5 : Foo<?,I> rawFoo1,6 : Foo<This,I> rawFoo2) {7 : this.m1(); // Legal: object is this 8 : rawFoo2.m1(); // Illegal: object is not this9 : rawFoo1.m2(); // Illegal: object is not this nor this-owned10: rawFoo2.m2(); // Legal: both Raw and object is this-owned11: this.m2(); // Legal: both Raw and object is this12: }
Method invocation is the same as field access/assignment:1)If any parameter is this-owned, then the receiver must be this.2)If the guard is Raw and the object is Raw, then the receiver must be this or this-owned.
33/22
Method guards
1: class Foo<O extends World, I extends ReadOnly> {2: <I extends Raw>? void rawM() { … }3: <I extends Mutable>? void bar(4: Foo<?,ReadOnly> readonlyFoo,5: Foo<?,I> mutableFoo) {6: readonlyFoo.rawM(); // Illegal: ReadOnly is not a subtype of Raw // The bound of I in this method is Mutable7: mutableFoo.rawM(); // Legal: Mutable is a subtype of Raw8: this.rawM(); // Legal: Mutable is a subtype of Raw9: }
1: class Foo<O extends World, I extends ReadOnly> {2: <I extends Raw>? void rawM() { … }3: <I extends Mutable>? void bar(4: Foo<?,ReadOnly> readonlyFoo,5: Foo<?,I> mutableFoo) {6: readonlyFoo.rawM(); // Illegal: ReadOnly is not a subtype of Raw // The bound of I in this method is Mutable7: mutableFoo.rawM(); // Legal: Mutable is a subtype of Raw8: this.rawM(); // Legal: Mutable is a subtype of Raw9: }
Guard “<T extends U>?” has a dual purpose:1)The receiver’s T must be a subtype of U.2)Inside the method, the bound of T is U.
Conditional Java (cJ) proposed method guards for Java