Javari: Java Reference Immutability Language and Inference Tool
-
Upload
tanya-oconnor -
Category
Documents
-
view
44 -
download
2
description
Transcript of Javari: Java Reference Immutability Language and Inference Tool
Javari: Java Reference Immutability Language and Inference Tool
Jaime QuinonezNovember 14, 2006
2
Reference Immutability
@ReadOnly on a type specifies a reference that cannot be used to modify an object
@ReadOnly can annotate any use of a type
For a type T, @ReadOnly T is a supertype of T
T can be used anywhere @ReadOnly T is expected
@ReadOnly T cannot be used where T is expected
3
Example mutable class
setTime() mutates object getTime() does not mutate object
public class Date {private long time;
public Date(long t) {this.time = time;
}public long getTime() {
return time;}public void setTime(long time) {
this.time = time;}
}
4
@ReadOnly receiver annotates method
getTime() does not mutate object receiver of getTime() is @ReadOnly
public class Date {private long time;
public Date(long t) {this.time = time;
}public long getTime() @ReadOnly {
return time;}public void setTime(long time) {
this.time = time;}
}
5
Some fields not part of state
hashCode() does not modify abstract state hashCode() modifies a field
public class Date {private int hc;
public int hashCode() @ReadOnly {if(hc == 0) {
hc = … ;}return hc;
}}
6
@Assignable excludes fields from abstract state
hc is not part of abstract state
public class Date {private @Assignable int hc;
public int hashCode() @ReadOnly {if(hc == 0) {
hc = … ;}return hc;
}}
7
@ReadOnly on generic types
ArrayList<@ReadOnly Date> list; list is a mutable ArrayList
Contains immutable references to Dates The list can be mutated
list.get(0) - legal list.clear() - legal
Elements in the list cannot be mutated Return type of list.get() is @ReadOnly Date list.get(0).getTime() - legal list.get(0).setTime(2) - illegal @ReadOnly Date d = list.get(0) - legal Date d = list.get(0) - illegal
8
@ReadOnly doesn’t propagate to generics
@ReadOnly ArrayList<Date> list; list is an immutable ArrayList
Contains mutable references to Dates The list cannot be mutated
list.get(0) - legal list.clear() - illegal
Elements in the list can be mutated Return type of list.get() is Date list.get(0).getTime() - legal list.get(0).setTime(2) - legal @ReadOnly Date d = list.get(0) - legal Date d = list.get(0) - legal
9
Need to add @ReadOnly annotations
Date is an existing class Date.getTime() and Date.setTime() are not
annotated Javari defaults method receiver to mutable
unless @ReadOnly is specified It is tedious and error-prone for humans to
infer these annotations Need a tool to infer reference immutability
in existing code
10
@ReadOnly methods do not mutate fields
Given unannotated class file:
public class Date {private long time;
public Date(long time) {this.time = time
}public void setTime(long time) {
this.time = time;}public long getTime() {
return time;}
}
11
@ReadOnly methods do not mutate fields
Modify class file to contain appropriate @ReadOnly annotations:
public class Date {private long time;
public Date(long time) {this.time = time
}public void setTime(long time) {
this.time = time;}public long getTime() @ReadOnly {
return time;}
}
12
Type Inference Algorithm
Flow-insensitive and context-insensitive Generate a set of mutability constraints
Unguarded constraint states unconditional mutability
x = new Object(); “x is mutable”
Guarded constraint states conditional mutability y.x(); “if x is mutable, then y is mutable”
Solve set of constraints to see what types have to be mutable
All other types can safely be declared immutable
13
Field reassignment mutates object
Create unguarded constraint
public class Date {private long time;public Date(long time) {
this.time = time}public void setTime(long time) {
this.time = time;}public long getTime() {
return time;}
}
14
Field reassignment mutates object
Create unguarded constraint
public class Date {private long time;public Date(long time) {
this.time = time}public void setTime(long time) {
this.time = time;}public long getTime() {
return time;}
}
Date.setTime().this is mutable
15
Calling methods on fields
Create guarded constraint based on field’s typepublic class Cell {
private Date d;
public long get() {return d.getTime();
}
public void reset() {d.setTime(0);
}}
16
Calling methods on fields
Create guarded constraint based on field’s typepublic class Cell {
private Date d;
public long get() {return d.getTime();
}
public void reset() {d.setTime(0);
}}
17
Calling methods on fields
Create guarded constraint based on field’s typepublic class Cell {
private Date d;
public long get() {return d.getTime();
}
public void reset() {d.setTime(0)
}} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.get().this mutable
18
Calling methods on fields
Create guarded constraint based on field’s typepublic class Cell {
private Date d;
public long get() {return d.getTime();
}
public void reset() {d.setTime(0);
}} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.get().this mutable
19
Calling methods on fields
Create guarded constraint based on field’s typepublic class Cell {
private Date d;
public long get() {return d.getTime();
}
public void reset() {d.setTime(0);
}} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.get().this mutable Date.setTime().this mutable -> Cell.d mutable Date.setTime().this mutable -> Cell.reset().this mutable
20
Javarifier propagates unguarded constraints
Constraints1. Date.setTime().this is mutable2. Date.getTime().this mutable -> Cell.d mutable3. Date.getTime().this mutable -> Cell.get().this mutable4. Date.setTime().this mutable -> Cell.d mutable5. Date.setTime().this mutable -> Cell.reset().this
mutable
21
Javarifier propagates unguarded constraints Constraints
1. Date.setTime().this is mutable2. Date.getTime().this mutable -> Cell.d mutable3. Date.getTime().this mutable -> Cell.get().this mutable4. Date.setTime().this mutable -> Cell.d mutable5. Date.setTime().this mutable -> Cell.reset().this
mutable1. New Constraints
1. Cell.d is mutable
22
Javarifier propagates unguarded constraints Constraints
1. Date.setTime().this is mutable2. Date.getTime().this mutable -> Cell.d mutable3. Date.getTime().this mutable -> Cell.get().this mutable4. Date.setTime().this mutable -> Cell.d mutable5. Date.setTime().this mutable -> Cell.reset().this
mutable1. New Constraints
1. Cell.d is mutable2. Cell.reset().this is mutable
23
Javarifier propagates unguarded constraints Constraints
1. Date.setTime().this is mutable2. Date.getTime().this mutable -> Cell.d mutable3. Date.getTime().this mutable -> Cell.get().this mutable4. Date.setTime().this mutable -> Cell.d mutable5. Date.setTime().this mutable -> Cell.reset().this
mutable1. New Constraints
1. Cell.reset().this is mutable2. Cell.d is mutable
Final unguarded constraints1. Date.setTime().this is mutable2. Cell.reset().this is mutable3. Cell.d is mutable4. All other types are @ReadOnly
24
Parameters assigned to fields
Field mutability guards parameter mutability
public class Cell {private Date d;
public long getTime() {return this.d.getTime();
}
public void setDate(Date newDate) {this.d = newDate;
}}
25
Parameters assigned to fields
Field mutability guards parameter mutability
public class Cell {private Date d;
public long getTime() {return this.d.getTime();
}
public void setDate(Date newDate) {this.d = newDate;
}}
26
Parameters assigned to fields
Field mutability guards parameter mutability
public class Cell {private Date d;
public long getTime() {return this.d.getTime();
}
public void setDate(Date newDate) {this.d = newDate;
}} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.getTime().this mutable
27
Parameters assigned to fields
Field mutability guards parameter mutability
public class Cell {private Date d;
public long getTime() {return this.d.getTime();
}
public void setDate(Date newDate) {this.d = newDate;
}} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.getTime().this mutable
28
Parameters assigned to fields
Field mutability guards parameter mutability
public class Cell {private Date d;
public long getTime() {return this.d.getTime();}
public void setDate(Date newDate) {this.d = newDate;}
} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.getTime().this mutable Cell.setDate().this is mutable Cell.d mutable -> Cell.setDate().param:newDate mutable
29
Parameters assigned to fields
Field mutability guards parameter mutability
public class Cell {private Date d;
public long getTime() {return this.d.getTime();}
public void setDate(Date newDate) {this.d = newDate;}
} Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.getTime().this mutable Cell.setDate().this is mutable Cell.d mutable -> Cell.setDate().param:newDate mutable
30
Javarifier propagates unguarded constraints
Constraintsn Date.setTime().this is mutablen Date.getTime().this mutable -> Cell.d mutablen Date.getTime().this mutable -> Cell.getTime().this
mutablen Cell.setDate().this is mutablen Cell.d mutable -> Cell.setDate().param:newDate is
mutable
31
Javarifier propagates unguarded constraints
Constraintsn Date.setTime().this is mutablen Date.getTime().this mutable -> Cell.d mutablen Date.getTime().this mutable -> Cell.getTime().this
mutablen Cell.setDate().this is mutablen Cell.d mutable -> Cell.setDate().param:newDate is
mutable1. No guards can be satisfied2. Finished with two unguarded constraints:
1. Date.setTime().this is mutable2. Cell.setDate().this is mutable3. All other types are @ReadOnly
32
Generic types have same inference
Place constraints on upper and lower boundpublic class Cell<T extends Date super SubDate> {
T t;
public void set(T t) { this.t = t; }
public void reset() { this.t.setTime(0); }
}
public class SubDate extends Date {
public void setTime(long time) { this.time = time; }
}
33
Generic types have same inference
Place constraints on upper and lower boundpublic class Cell<T extends Date super SubDate> {
T t;public void set(T t) { this.t = t; }public void reset() { this.t.setTime(0); }
}public class SubDate extends Date {
public void setTime(long time) { this.time = time; }} Cell.set().this is mutable
34
Generic types have same inference
Place constraints on upper and lower boundpublic class Cell<T extends Date super SubDate> {
T t;public void set(T t) { this.t = t; }public void reset() { this.t.setTime(0); }
}public class SubDate extends Date {
public void setTime(long time) { this.time = time; }} Cell.set().this is mutable Cell.t mutable -> Cell.set().param:t mutable
35
Generic types have same inference
Place constraints on upper and lower boundpublic class Cell<T extends Date super SubDate> {
T t;public void set(T t) { this.t = t; }public void reset() { this.t.setTime(0); }
}public class SubDate extends Date {
public void setTime(long time) { this.time = time; }} Cell.set().this is mutable Cell.t mutable -> Cell.set().param:t mutable Date.setTime().this mutable -> Cell.t mutable
36
Generic types have same inference
Place constraints on upper and lower boundpublic class Cell<T extends Date super SubDate> {
T t;public void set(T t) { this.t = t; }public void reset() { this.t.setTime(0); }
}public class SubDate extends Date {
public void setTime(long time) { this.time = time; }} Cell.set().this is mutable Cell.t mutable -> Cell.set().param:t mutable Date.setTime().this mutable -> Cell.t mutable Date.setTime().this mutable -> Cell.reset().this mutable
37
Generic types have same inference
Place constraints on upper and lower boundpublic class Cell<T extends Date super SubDate> {
T t;public void set(T t) { this.t = t; }public void reset() { this.t.setTime(0); }
}public class SubDate extends Date {
public void setTime(long time) { this.time = time; }} Cell.set().this is mutable Cell.t mutable -> Cell.set().param:t mutable Date.setTime().this mutable -> Cell.t mutable Date.setTime().this mutable -> Cell.reset().this mutable SubDate.setTime().this mutable -> Date.setTime().this
mutable Subtype can only be mutable if supertype is mutable
38
Javarifier propagates unguarded constraints
Constraints1. Date.setTime().this is mutable2. SubDate.setTime().this is mutable3. Cell.set().this is mutable4. Cell.t mutable -> Cell.set().param:t mutable5. Date.setTime().this mutable -> Cell.t mutable6. Date.setTime().this mutable -> Cell.reset().this
mutable7. SubDate.setTime().this mutable -> Date.setTime().this
mutable
39
Javarifier propagates unguarded constraints
Constraints1. Date.setTime().this is mutable2. SubDate.setTime().this is mutable3. Cell.set().this is mutable4. Cell.t mutable -> Cell.set().param:t mutable5. Date.setTime().this mutable -> Cell.t mutable6. Date.setTime().this mutable -> Cell.reset() mutable7. SubDate.setTime().this mutable -> Date.setTime().this
mutable1. New Constraints
1. Cell.t is mutable
40
Javarifier propagates unguarded constraints
Constraints1. Date.setTime().this is mutable2. SubDate.setTime().this is mutable3. Cell.set().this is mutable4. Cell.t mutable -> Cell.set().param:t mutable5. Date.setTime().this mutable -> Cell.t mutable6. Date.setTime().this mutable -> Cell.reset().this
mutable7. SubDate.setTime().this mutable -> Date.setTime().this
mutable1. New Constraints
1. Cell.t is mutable2. Cell.reset() is mutable
41
Javarifier propagates unguarded constraints
Constraints1. Date.setTime().this is mutable2. SubDate.setTime().this is mutable3. Cell.set().this is mutable4. Cell.t mutable -> Cell.set().param:t mutable5. Date.setTime().this mutable -> Cell.t mutable6. Date.setTime().this mutable -> Cell.reset().this
mutable7. SubDate.setTime().this mutable -> Date.setTime().this
mutable1. New Constraints
n Cell.t is mutablen Cell.reset() is mutablen Cell.set().param:t is mutable
42
Javarifier propagates unguarded constraints
Constraints1. Date.setTime().this is mutable2. SubDate.setTime().this is mutable3. Cell.set().this is mutable4. Cell.t mutable -> Cell.set().param:t mutable5. Date.setTime().this mutable -> Cell.t mutable6. Date.setTime().this mutable -> Cell.reset().this
mutable7. SubDate.setTime().this mutable -> Date.setTime().this
mutable1. New Constraints
1. Cell.t is mutable2. Cell.reset() is mutable3. Cell.set().param:t is mutable
2. No references can be marked @ReadOnly
43
Case study of Javarifier on scene library
6935 lines of code 1008 annotatable slots Hand-annotated by original author Matt McCutchen 126 annotations disagree with Javarifier results
5 differences due to bugs in Javarifier code 121 differences due to programmer error Type-checker would have caught most of the programmer's
errors
44
Javarifier inserts @ReadOnly wherever legal
Mutable is more restricting than @ReadOnly on a receiver
If code that uses a method is type-safe when the method has a @Mutable receiver, it is type-safe with a @ReadOnly receiver
Two viewpoints Programmers interpretation of Javari:
@Mutable is legal Javarifier: both @ReadOnly and @Mutable
satisfy constraints, so use @ReadOnly Javarifier finds mutations, propagates
mutable restriction, then assigns everything else @ReadOnly
45
Some specifications cannot be inferred
VivifyingMap<K,V> implements Map<K,V>
get(K k) returns the value that k is mapped to If k isn’t mapped to a value, adds mapping from k to a new “empty” value, and returns that value
prune() removes all mappings from keys to “empty” values
Abstract state of a VivifyingMap is the set of mappings (possibly to empty values)
The result of keySet() is in the abstract state Both get() and prune() modify abstract state
46
Abstract methods have no body to analyze
public abstract class VivifyingMap<K,V> extends HashMap<K,V> {
// Removes all mappings to empty values// Modifies receiver
public void prune() {// for each key in map:if(checkEmpty(super.getValue(key))) {
super.remove(key)}
}// Returns whether the parameter is empty// Modifies receiver
public abstract boolean checkEmpty(V v);}
47
Javarifier infers @ReadOnly on abstract methods
public abstract class VivifyingMap<@ReadOnly K, @ReadOnly V>extends HashMap<@ReadOnly K, @ReadOnly V> {
// prune() not @ReadOnly
public void prune() { … }
public abstract boolean checkEmpty(@ReadOnly V v) @ReadOnly;
}
checkEmpty() annotated as @ReadOnly No evidence that method modifies v Violates programmer specification
48
Valid subtype can cause Javarifier conflict
public class NamedMaps<K, V> extends VivifyingMap<String,VivifyingMap<K,V>> {
public boolean checkEmpty(VivifyingMap<K,V> vm) {
vm.prune();
return vm.keySet().isEmpty();
}
} checkEmpty() modifies parameter
Calls prune() on parameter Meets programmer specification of
VivifyingMap.checkEmpty() Javarifier does not annotate parameter as @ReadOnly
49
Javarifier can violate programmer’s specification
(abstract) VivifyingMap.checkEmpty(V v) Programmer: v modifiable Javari: @ReadOnly v
NamedMaps.checkEmpty(V v) Programmer: v modifiable Javari: v modifiable
Programmer of NamedMaps met programmer specification of VivifyingMap
In Javarifier’s specification, NamedMaps not a true subtype of VivifyingMap
Javarifier can’t infer programmer’s specification of abstract method without some implementation
50
Javarifier infers legal annotations
Javarifier is able to analyze code and infer reference immutability
Javarifier does not ensure consistency when classes are analyzed separately
Javarifier cannot truly capture programmer’s intent when this intent does not manifest itself in code
Sometimes formal specification comes from programmer intent and not necessarily code
Most errors in annotating a specification are simple errors that would be caught by a type-checker