object and reference immutability using java generics yoav zibin(1), alex potanin(2), shay artzi(1),...
Post on 20-Dec-2015
251 views
TRANSCRIPT
Object and Reference Immutability using Java Generics
Yoav Zibin(1), Alex Potanin(2), Shay Artzi(1), Adam Kiezun(1), and Michael D. Ernst(1)
1) MIT Computer Science and Artificial Intelligence Lab, USA2) Victoria University of Wellington, New-Zealand
Presenting: Ori Arad ([email protected]) – 14/2/07
2/23
Immutability – What for?...
Program comprehension
Verification Compile & Run-time
optimizations Invariant detection
Refactoring Test input
generation Regression oracle
creation Specification
mining Modeling
3/23
Immutability varieties
Class immutability No instance of an immutable class may be
change after creation (e.g. String, Integer etc.) Object immutability
Immutable instances may not be changed, while other instances (of the same class) may…
Reference immutability a given reference cannot be used to modify its
referent.
4/23
IGJ - Immutability Generic Java
Object immutability: An object: mutable or immutable.
Reference immutability: A reference: Immutable, Mutable, or
ReadOnly Class immutability
Also support Class immutability
5/23
IGJ - Immutability Generic Java
one new generic parameter (at the beginning of the list of generic parameters) was added
1: // An immutable reference to an immutable date; side effects are prohibited
2: Date<Immutable> immutD = new Date<Immutable>();3: // A mutable reference to a mutable date; side effects are permitted
4: Date<Mutable> mutD = new Date<Mutable>();5: // A readonly reference to any date; side effects are prohibited
6: Date<ReadOnly> roD = ... ? immutD : mutD;
1: // An immutable reference to an immutable date; side effects are prohibited
2: Date<Immutable> immutD = new Date<Immutable>();3: // A mutable reference to a mutable date; side effects are permitted
4: Date<Mutable> mutD = new Date<Mutable>();5: // A readonly reference to any date; side effects are prohibited
6: Date<ReadOnly> roD = ... ? immutD : mutD;
6/23
IGJ main design principles
Transitivity: The design must provide transitive (deep) immutability
Purely static: There should be no runtime representation for immutability
Polymorphism: It must be possible to abstract over immutability without code duplication
Simplicity: The design should not change Java's syntax and have a small set of typing rules
7/23
Type Hierarchies for IGJ
The type hierarchy for immutability parameters
The top of the type hierarchy for all other classes
8/23
IGJ Subtype hierarchy
// Demonstrates how to copy value of readonly roObj to mutable mutableObjObject<ReadOnly> roObj = ...;
List<Mutable,Object<Mutable>> l = new List<Mutable,Object<Mutable>>();
((List<Mutable,Object<ReadOnly>>) l).add(roObj);
Object<Mutable> mutableObj = l.get(0); // mutableObj equals roObj
// Demonstrates how to copy value of readonly roObj to mutable mutableObjObject<ReadOnly> roObj = ...;
List<Mutable,Object<Mutable>> l = new List<Mutable,Object<Mutable>>();
((List<Mutable,Object<ReadOnly>>) l).add(roObj);
Object<Mutable> mutableObj = l.get(0); // mutableObj equals roObj
Legend:L List, O object, R ReadOnlyIM Immutable, M Mutable
For example:L< R,O<M> > meansList<ReadOnly, Object<Mutable>>
9/23
Java Array Co-Variance Problem
Read from Array: OK!Write to Array: Problem!
// A is a single-element array of String.String[] a = new String[1];
// B is an array of ObjectObject[] b = a;
// Assign an Integer to b. This would be possible if b really were// an array of Object, but since it really is an array of String,// we will get a java.lang.ArrayStoreException.b[0] = new Integer (1);
// A is a single-element array of String.String[] a = new String[1];
// B is an array of ObjectObject[] b = a;
// Assign an Integer to b. This would be possible if b really were// an array of Object, but since it really is an array of String,// we will get a java.lang.ArrayStoreException.b[0] = new Integer (1);
Solution with IGJ – immutability promise us
only reading no Co-Variance problem
10/23
The Field Rule
legal iff I(o) = Mutable.Example:
Employee<ReadOnly> roE = ...;
roE.address = ...; // Compilation error!
Employee<ReadOnly> roE = ...;
roE.address = ...; // Compilation error!
o.someField = exp;
Typing is guaranteed by several rules.Let I(x) denote the immutability of x
Rule I: Field Assignment Rule
11/23
"Immutability" of Methods
4 new annotations: @ReadOnly, @Mutable, @Immutable and @AssignFields
Example:@Mutable void m()
{...this...} Here: I(this) is Mutable In general: in any method m, I(this)
is the same as I(m) “this” immutability depends on the
context
12/23
Reference-Immutability Rules
Method Invocation Rule:
(not necessarily I(o) = I(m) )
Employee<Mutable> o = ...;o.setAddress(...); // OK since I(o) = Mutable and I(setAddress) = Mutable
o.getAddress(); // OK since I(o) = Mutable and I(getAddress) = ReadOnly
((Employee<ReadOnly>) o).setAddress(...); // Compilation error!
Employee<Mutable> o = ...;o.setAddress(...); // OK since I(o) = Mutable and I(setAddress) = Mutable
o.getAddress(); // OK since I(o) = Mutable and I(getAddress) = ReadOnly
((Employee<ReadOnly>) o).setAddress(...); // Compilation error!
o.m(...) is legal if I(o) is subtype of I(m).
13/23
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @Mutable Edge(long id) { this.setId(id); }
4: public @Mutable synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id; }
7: public @ReadOnly long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
10: class Graph<I extends ReadOnly> {
11: public Edge<I> lastN;
12: public List<I,Edge<I>> l;
13: public @Mutable Graph(List<I,Edge<I>> l) { this.l = l; }
14: public @Mutable void addEdge(Edge<I> n) {
15: this.l.add(n); this.lastN = n; }
16: public @ReadOnly Edge<I> getLast() { return this.lastN; }
17: public static <T extends ReadOnly>
18: Edge<T> findEdge(Graph<T> nl, long id) { ... }
19: }
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @Mutable Edge(long id) { this.setId(id); }
4: public @Mutable synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id; }
7: public @ReadOnly long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
10: class Graph<I extends ReadOnly> {
11: public Edge<I> lastN;
12: public List<I,Edge<I>> l;
13: public @Mutable Graph(List<I,Edge<I>> l) { this.l = l; }
14: public @Mutable void addEdge(Edge<I> n) {
15: this.l.add(n); this.lastN = n; }
16: public @ReadOnly Edge<I> getLast() { return this.lastN; }
17: public static <T extends ReadOnly>
18: Edge<T> findEdge(Graph<T> nl, long id) { ... }
19: }
ExampleIGJ classes Edge<I> and Graph<I>, with the immutability parameters (and annotations, for this) underlined.
For now:Assume @Immutable is like @ReadOnlyAnd @AssignFields is like @Mutable
14/23
Example:
Line 5: the assignment into this.id is OK If this.id = …; was on line 6 – illegal… Line 3: OK due to Method Rule Line 8: static no annotation, Edge of any
immutability could be pass here…
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @AssignFields Edge(long id) { this.setId(id); }
4: public @AssignFields synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id; }
7: public @Immutable long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @AssignFields Edge(long id) { this.setId(id); }
4: public @AssignFields synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id; }
7: public @Immutable long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
15/23
Example:
Line 11: a class can pass its immutability parameter to its fields
Line 16: Also return type – no need for overloading Line 12: Transitivity - in an immutable Graph the
field l will contain an immutable list of immutable edges
10: class Graph<I extends ReadOnly> {
11: public Edge<I> lastN;
12: public List<I,Edge<I>> l;
13: public @AssignFields Graph(List<I,Edge<I>> l) { this.l = l; }
14: public @Mutable void addEdge(Edge<I> n) {
15: this.l.add(n); this.lastN = n; }
16: public @ReadOnly Edge<I> getLast() { return this.lastN; }
17: public static <T extends ReadOnly>
18: Edge<T> findEdge(Graph<T> nl, long id) { ... }
19: }
10: class Graph<I extends ReadOnly> {
11: public Edge<I> lastN;
12: public List<I,Edge<I>> l;
13: public @AssignFields Graph(List<I,Edge<I>> l) { this.l = l; }
14: public @Mutable void addEdge(Edge<I> n) {
15: this.l.add(n); this.lastN = n; }
16: public @ReadOnly Edge<I> getLast() { return this.lastN; }
17: public static <T extends ReadOnly>
18: Edge<T> findEdge(Graph<T> nl, long id) { ... }
19: }
16/23
Object Immutability & constructors
What annotation should CTOR be?... @Mutable? problem:
public @Mutable Graph(List<I,Edge<I>> l) {
this.l = l;
this.addEdge( new Edge<Mutable>(0) ); // }
…
List<Immutable,Edge<Immutable>> imList = ...;
new Graph<Immutable>(imList); // An element was added to imList
public @Mutable Graph(List<I,Edge<I>> l) {
this.l = l;
this.addEdge( new Edge<Mutable>(0) ); // }
…
List<Immutable,Edge<Immutable>> imList = ...;
new Graph<Immutable>(imList); // An element was added to imList
@Immutable? Guess not…
17/23
Object Immutability & constructors
Solution: Forth kind of reference immutability: AssignFields
Permit to perform limited side-effects without permitting modification of immutable objects @Mutable method can assign & mutate @AssignFields method can only assign
18/23
Immutability & Assignability
MyClass myObject = new MyClass();
myObject = anotherObject; Assignability
myObject.setField(4); immutability
19/23
Revised rules
Field Rule revised (relaxed): AssignField is not transitive…
Method Rule revised (restricted):
e.g. this.l.get(0).setId(42); // will be illegal
o.someField = ...; is legal if I(o) = Mutableor (I(o) = AssignFields and o=this)
o.m(...) is legal if I(o) is subtype of I(m)and (if I(m) is AssignFields o must be this)
20/23
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @AssignFields Edge(long id) { this.setId(id); }
4: public @AssignFields synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id; }
7: public @Immutable long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
10: class Graph<I extends ReadOnly> {
11: public Edge<I> lastN;
12: public List<I,Edge<I>> l;
13: public @AssignFields Graph(List<I,Edge<I>> l) { this.l = l; }
14: public @Mutable void addEdge(Edge<I> n) {
15: this.l.add(n); this.lastN = n; }
16: public @ReadOnly Edge<I> getLast() { return this.lastN; }
17: public static <T extends ReadOnly>
18: Edge<T> findEdge(Graph<T> nl, long id) { ... }
19: }
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @AssignFields Edge(long id) { this.setId(id); }
4: public @AssignFields synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id; }
7: public @Immutable long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
10: class Graph<I extends ReadOnly> {
11: public Edge<I> lastN;
12: public List<I,Edge<I>> l;
13: public @AssignFields Graph(List<I,Edge<I>> l) { this.l = l; }
14: public @Mutable void addEdge(Edge<I> n) {
15: this.l.add(n); this.lastN = n; }
16: public @ReadOnly Edge<I> getLast() { return this.lastN; }
17: public static <T extends ReadOnly>
18: Edge<T> findEdge(Graph<T> nl, long id) { ... }
19: }
ExampleThe original code – with @Immutable and @AssignFields
21/23
Example:
Line 5: the assignment into this.id is still OK setId is annotated with @AssignFields I(this)=AssignFields
Line 3: I(this) = AssignFields & I(setId) = AssignFields
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @AssignFields Edge(long id) { this.setId(id); }
4: public @AssignFields synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id;}
7: public @Immutable long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
1: class Edge<I extends ReadOnly> {
2: private long id;
3: public @AssignFields Edge(long id) { this.setId(id); }
4: public @AssignFields synchronized void setId(long id) {
5: this.id = id; }
6: public @ReadOnly synchronized long getId() { return id;}
7: public @Immutable long getIdImmutable() { return id; }
8: public static void print(Edge<ReadOnly> n) {... }
9: }
22/23
Example:
Line 13: If we will add the following code – it will be illegal in the revised rule (and legal in the old one)
public @mutable Graph(List<I,Edge<I>> l) {
this.l = l;
this.l.get(0).setId(42); // }
public @mutable Graph(List<I,Edge<I>> l) {
this.l = l;
this.l.get(0).setId(42); // }
New Rule:new SomeClass<X,…>(…) is legal only if
X = Mutable and this CTOR is not marked as @Mutable, or X = Imuutable and this CTOR is not marked as @Immutable (X = readOnly & X=AssignFileds are illegal)
23/23
Future Work
Plug-in for IDE (e.g. Eclipse) A WriteOnly immutability parameter @readable notation for field
class Vector<I extends None, T> {
@readable int size; ...
@None int size() { return size; }
@WriteOnly void add(T t) { ... }
@WriteOnly void removeLast() { ... }
@Mutable void remove(T t) { ... }
@ReadOnly void get(int index) { ... }
}
class Vector<I extends None, T> {
@readable int size; ...
@None int size() { return size; }
@WriteOnly void add(T t) { ... }
@WriteOnly void removeLast() { ... }
@Mutable void remove(T t) { ... }
@ReadOnly void get(int index) { ... }
}
24/23
Future Work (cont.)
Add default immutability
class Graph<I extends ReadOnly default Mutable>class Graph<I extends ReadOnly default Mutable>
An alternative syntax (Java 7?...)
Runtime support (e.g. down-cast)
@immutable Document[@readonly]
new @mutable ArrayList<@immutable Edge>(...)
@immutable Document[@readonly]
new @mutable ArrayList<@immutable Edge>(...)