Difficult Specifications in Javari Immutability Inference
Jaime QuinonezProgram Analysis Group
April 20, 2007
2
Overview
Overview of Javari Java with reference immutability
Overview of Javarifier Immutability reference algorithm
Cases with specifications that are hard to interpret Closed-world assumption Subtyping Conflicting, legal specifications
Correct specification depends on use
3
Reference Immutability in Java
For convenience, use annotations as type qualifiers
@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 @Mutable T is equivalent to T T can be used anywhere @ReadOnly T is expected @ReadOnly T cannot be used where T is expected
4
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;}
}
5
@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;}
}
6
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;
}}
7
@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;
}}
8
@ThisMutable gives fields mutability of containing reference
@ThisMutable is the default for all fields @ReadOnly Cell’s provide @ReadOnly Date references @Mutable Cell’s provide @Mutable Date references
public class Cell {public @ThisMutable Date d;
}
@ReadOnly Cell roCell;@Mutable Cell muCell;
roCell.d; // type: @ReadOnly DatemuCell.d; // type: @Mutable Date
9
@RoMaybe templates methods over mutability
All @RoMaybe’s can be replaced with all @ReadOnly or @Mutable
public class Cell {public @ThisMutable Date d;
public @RoMaybe Date get() @RoMaybe {return d;
}}@ReadOnly Cell roCell;@Mutable Cell muCell;
roCell.get(); // type: @ReadOnly Date, no rep exposuremuCell.get(); // type: @Mutable Date, have rep exposure
10
Javarifier : Type Inference Algorithm
Flow-insensitive and context-insensitive Generate a set of mutability constraints
Unguarded constraint states unconditional mutability
• “x is mutable” Guarded constraint states conditional mutability
• “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
11
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;}
}
12
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
13
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);
}}
14
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);
}}
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)
}}
Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.get().this mutable
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);
}}
Date.getTime().this mutable -> Cell.d mutable Date.getTime().this mutable -> Cell.get().this mutable
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 Date.setTime().this mutable -> Cell.d mutable Date.setTime().this mutable -> Cell.reset().this mutable
18
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
19
Closed-world assumption causes ambiguity
Javarifier assumes that all classes in the system are known All classes are implemented All interfaces and abstract classes have some implementation If any part of the system changes, all annotations become
invalid If an implementation is missing, nothing can be done Whatever is inferred, programmer can claim the opposite
should be true
public class C {public void foo(Object o) {
throw new RuntimeException();}
}
public interface A {Object void bar();
}
20
Subclasses must inherit mutability exactly Mutability is part of the method signature A defines method foo with no body, B and C extend A and provide
implementation C.foo(List) modifies its argument, B.foo(List) does not
public abstract class A {abstract void foo(List arg);
}
public class B extends A {void foo(List arg) { }
}
public class C extends A {void foo(List arg) {
arg.clear();}
}
21
Subclasses must inherit mutability exactly C.foo(List) must take a mutable parameter, so all definitions of
foo must take a mutable parameter If you only examined output on class A and B, mutability wouldn’t
make sense
public abstract class A {abstract void foo(@Mutable List arg);
}
public class B extends A {void foo(@Mutable List arg) { }
}
public class C extends A {void foo(@Mutable List arg) {
arg.clear();}
}
22
Alternative annotations on a class
Cell wraps around a Date object, which makes up its abstract state
set() clearly modifies the abstract state of the object get() does not modify the abstract state, but does expose it
public class Cell {private Date d;
public void set(Date d) {this.d = d;
}
public Date get() {return d;
}}
23
Alternative annotations on a class Field d is this-mutable set() has a mutable ‘this’, so this.d is mutable and thus
parameter is mutable get() returns a Date of the same mutability as what is passed
in Can only insert mutable Dates
public class Cell {private @ThisMutable Date d;
public void set(@Mutable Date d) @Mutable {this.d = d;
}
public @RoMaybe Date get() @RoMaybe {return d;
}}
24
Alternative annotations on a class Field d is readonly set() still mutates its receiver, but now accepts a
readonly Date get() can only return a readonly Date Can only get back readonly Dates
public class Cell {private @ReadOnly Date d;
public void set(@ReadOnly Date d) @Mutable {this.d = d;
}
public @ReadOnly Date get() @ReadOnly {return d;
}}
25
Alternative annotations on a class Field d is mutable set() still mutates its receiver, must take a mutable Date get() can return a mutable Date Can only insert mutable Dates
public class Cell {private @Mutable Date d;
public void set(@Mutable Date d) @Mutable {this.d = d;
}
public @Mutable Date get() @ReadOnly {return d;
}}
26
Field can be excluded from abstract state Field d is assignable and this-mutable set() does not change the abstract state of the object get() can only return mutable Date Can only return mutable Dates
public class Cell {private @Assignable @ThisMutable Date d;
public void set(@ReadOnly Date d) @ReadOnly {this.d = d;
}
public @ReadOnly Date get() @ReadOnly {return d;
}}
27
Field can be excluded from abstract state Field d is assignable and mutable, completely excluded from
abstract state set() does not change the abstract state of the object get() can return a mutable Date Can only insert mutable Dates
public class Cell {private @Assignable @Mutable Date d;
public void set(@Mutable Date d) @ReadOnly {this.d = d;
}
public @Mutable Date get() @ReadOnly {return d;
}}
28
‘this’ is mutable inside constructor If field d is this-mutable, then constructor must accept mutable
parameter Can’t even construct a Cell without a mutable Date
public class Cell {private @ThisMutable Date d;
public Cell(@Mutable Date d) {this.d = d;
}
public void set(@Mutable Date d) @Mutable {this.d = d;
}
public @RoMaybe Date get() @RoMaybe {return d;
}}
29
Many possibilities can be correct
All of the previous annotated classes are valid Javari
Will type-check when compiling Cell.java The ‘correct’ set of annotations is that which
satisfy the contract that other code requires Closed-world assumption: Know all uses of class If you don’t need to mutate the result of get(), can
have every Date in Cell be @ReadOnly If you will only be inserting mutable Dates, can have
every Date in Cell be @Mutable
30
Can excluding fields from state be inferred?
Not likely
31
Excluding a field from state can break equals() Field d is assignable and mutable, completely excluded from
abstract state Calling a readonly method changes which objects this is
equal to
public class Cell {private @Assignable @Mutable Date d;
public void set(@Mutable Date d) @ReadOnly {this.d = d;
}
public boolean equals(Object o) {// true iff o is a Cell and this.d.equals(o.d)
}}
32
Can excluding fields from state be inferred?
Not likely A feasible heuristic is to exclude fields not used
in equals() Debug/log information can be read/modified in equals() Debug/log fields are the ones you can reasonably exclude Might also have variables not read in equals() that can
change abstract state Counters decremented every method call that once they
timeout, will change part of state A shallow equals() could omit information read in
toString() User doesn’t want toString() to return different Strings
between calls to readonly methods
33
Customizing Javarifier results
User could annotate certain fields individually as not being part of the state of the class, and Javarifier can trust those annotations
Presently, user can only annotate whole classes (unannotated elements take their default mutability)
Can annotate all the methods in a Logger class to be readonly
A general framework for specifying user versus inferred annotations is being investigated