480 likes | 629 Views
Javari: Adding Reference Immutability to Java. Matthew Tschantz and Michael Ernst MIT CSAIL. Protecting method arguments. static void print( readonly Date d) { ... // Cannot modify d } String toString() readonly { ... // Cannot modify the receiver }.
E N D
Javari: Adding Reference Immutability to Java Matthew Tschantz and Michael Ernst MIT CSAIL
Protecting method arguments static void print(readonly Date d) { ... // Cannot modify d } String toString() readonly { ... // Cannot modify the receiver }
Protecting abstract state: observer methods class Class { private Object[] signers; readonly Object[] getSigners() { return signers; // JDK 1.1 bug } } myClass.getSigners()[0] = “Sun”;
Protecting abstract state: observer methods class Class { private Object[] signers; readonly Object[] getSigners() { return signers; // Fixes JDK 1.1 bug } } myClass.getSigners()[0] = “Sun”; // Error
Reference immutability • A given reference cannot be used to modify its referent • other references to the object may modify it • Is deep: the transitively reachable state (the abstract state) is protected • Statically type checkable
Reference immutability versus object immutability Object immutability: an object cannot be modified through any reference Graph temp = new Graph(); // construct the graph readonly Graph g = temp; temp = null;
Motivation • Provide compiler-enforced guarantees that a method will not change a given parameter • Protect an object’s abstract state • Prevent/detect errors • Documentation • Enable analyses and transformations
Other work • C++: • unsound (unchecked casts) • non-transitive • no parametric polymorphism • JAC [Kniesel 2001]: • unsound (subtyping, arrays) • return type mutability equals receiver's • Javari2004 [Birka 2004]: • no parametric polymorphism • conflates assignability and mutability • Object immutability • restricts programmer
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Language overview readonly Immutable reference assignable Field may be reassigned even through a read-only reference mutable Field may be mutated even through a read-only reference
Readonly readonly Date rd = new Date(); rd.year = 2005; // Compile-time error rd.incrementDay(); // Compile-time error rd = new Foo(); // OK
Mutability versus assignability • Assignability • not part of the type • final • Mutability • part of the type • readonly final Date fd = null; readonly Date rd = null; fd = new Date(); // Error: final rd = null; // OK Date d1 = fd; // OK Date d2 = rd; // Error: wrong type
Type system Every type (mutable) T has readonly T as a supertype readonly Object /*mutable*/ Object readonly Date /*mutable*/ Date
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Assignability Whether a reference may be assigned to. class Appointment { final Date time; // Same as in Java assignable int room; } Appointment appoint; appoint.time = new Date(); //Error appoint.room = 250; //OK
Assignability modifiers final: May never be assigned assignable: May always be assigned; the default for locals this-assignable: • May be assigned through a mutable reference • May not be assigned through a readonly reference • Only applicable to fields; the default • The assignability depends on the mutability of the enclosing object: “this”
Assignability example class Bicycle { final int id; // Never changes assignable int hashCode; // A cache /*this-assign*/ int gear; // Abstract state } /*mutable*/ Bicycle b; readonly Bicycle rb;
final class Bicycle { final int id; // Never changes assignable int hashCode; // A cache /*this-assign*/ int gear; // Abstract state } /*mutable*/ Bicycle b; readonly Bicycle rb; b.id = 5; // Error rb.id = 5; // Error
assignable class Bicycle { final int id; // Never changes assignable int hashCode; // A cache /*this-assign*/ int gear; // Abstract state } /*mutable*/ Bicycle b; readonly Bicycle rb; b.hashCode = 5; // OK rb.hashCode = 5; // OK
this-assignable class Bicycle { final int id; // Never changes assignable int hashCode; // A cache /*this-assign*/ int gear; // Abstract state } /*mutable*/ Bicycle b; readonly Bicycle rb; b.gear = 5; // OK
this-assignable class Bicycle { final int id; // Never changes assignable int hashCode; // A cache /*this-assign*/ int gear; // Abstract state } /*mutable*/ Bicycle b; readonly Bicycle rb; b.gear = 5; // OK rb.gear = 5; // Error
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Mutability Whether an object’s transitive abstract state may be modified class Date { /*this-assignable*/ int year; } /*mutable*/ Date d; readonly Date rd; d.year = 2005; //OK rd.year = 2005; //Error
Mutability modifiers readonly: May never be mutated mutable: May always be mutated; the default for locals this-mutable: • May be mutated through mutable reference • May not be mutated through readonly references • Only applicable to fields; the default • The mutability depends on the mutability of the enclosing class: “this”
Mutability example class Account { readonly Customer owner; mutable RequestLog requests; /*this-mut*/ Balance bal; } /*mutable*/ Account a; readonly Account ra;
readonly class Account { readonly Customer owner; mutable RequestLog requests; /*this-mut*/ Balance bal; } /*mutable*/ Account a; readonly Account ra; a.owner.setName(“Bob”); // Error ra.owner.setName(“Bob”); // Error
mutable mutable excludes requests from the abstract state of the object class Account { readonly Customer owner; mutable RequestLog requests; /*this-mut*/ Balance bal; } /*mutable*/ Account a; readonly Account ra; a.requests.add(“checkBalance”); // OK ra.requests.add(“checkBalance”); // OK
this-mutable class Account { readonly Customer owner; mutable RequestLog requests; /*this-mut*/ Balance bal; } /*mutable*/ Account a; readonly Account ra; a.balance.withdraw(1000); // OK
this-mutable class Account { readonly Customer owner; mutable RequestLog requests; /*this-mut*/ Balance bal; } /*mutable*/ Account a; readonly Account ra; a.balance.withdraw(1000); // OK ra.balance.withdraw(1000); // Error
this-mutable fields reached through a readonly reference readonly, right?
this-mutable fields reached through a readonly reference readonly, right? NO, would result in type loophole allowing one to convert a readonly reference to a mutable reference:
this-mutable fields reached through a readonly reference readonly, right? NO, would result in type loophole allowing one to convert a readonly reference to a mutable reference: class Student { assignable /*this-mut*/ GradeReport grades; } /*mutable*/ Student s = new Student(); readonly Student rs = s; readonly GradeReport rg; /*mutable*/ GradeReport g; rs.grades = rg; //readonly assigned to this-mutable g = s.grades; //this-mutable assigned to mutable
this-mutable fields reached through a readonly reference Solution: Disallow readonly references to be assigned to a this-mutable field class Student { assignable /*this-mut*/ GradeReport grades; } this-mutable fields are • taken out at readonly GradeReport • written to as mutable GradeReport
this-mutable fields reached through a readonly reference Solution: Disallow readonly references to be assigned to a this-mutable field class Student { assignable /*this-mut*/ GradeReport grades; } this-mutable fields are • taken out at readonly GradeReport • written to as mutable GradeReport Using wildcards and bounds, the type of grades can be written as: <? extends readonly GradeReport super mutable GradeReport> Notation: <? readonly GradeReport>
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Uses of parametric classes Local variable declarations: /*mut*/ List</*mut*/Date> a; // add, mutate /*mut*/ List<readonlyDate> b; // add readonly List</*mut*/ Date> c; // mutate readonly List<readonlyDate> d; // neither (Arrays are similar)
Inside parametric classes Type arguments include mutability Library cannot write: class C<T> { mutable T x; } C<readonly Date> y; // Conflicting types
Inside parametric classes Type arguments include mutability Library cannot write: class C<T> { mutable T x; } C<readonly Date> y; // Conflicting types Library can force a mutable type using a bound: ; class C<T extends mutable Object> { T x; }
this-mutable type arguments class Wheel { /*this-mut*/ List</*this-mut*/Spoke> spokes; } /*mutable*/ Wheel w; readonly Wheel rw; w.spokes has type /*mut*/ List</*mut*/ Spoke> /*mut*/ List</*mut*/ Spoke> w = w.spokes; // OK
this-mutable type arguments class Wheel { /*this-mut*/ List</*this-mut*/Spoke> spokes; } /*mutable*/ Wheel w; readonly Wheel rw; rw.spokes has type ? readonly List<? readonly Spoke> readonly List<?readonly Spoke> x = rw.spokes; // OK readonly List<readonlySpoke> y = rw.spokes; // Error readonly List</*mutable*/ Spoke> z = rw.spokes; // Error
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Formal model • Core language • Extension to Featherweight Generic Java • Type soundness proof underway • Subject reduction and progress theorems • Proof that an object is never modified if only a readonly reference exists
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Reflection • Method.invoke continues to return mutable references • dynamically checks type signature: return type must be mutable • New method Method.invokeReadonly returns a readonly reference • no dynamic checks • Type, including mutability, of arguments (including receiver) is checked at compile time.
Serialization • Add a readonly-ness bit to the serialized form. • Check readonly-ness bit if one attempts to deserialization an object to a mutable reference.
Reducing code duplication romaybe keyword templates over methods class Conference { /*this-mutable*/ Date d; We wish to write two (overloaded) methods: readonly Date getDate() readonly { return d; } /*mutable*/ Date getDate() /*mutable*/ { return d; } Syntactic sugar: romaybe Date getDate() romaybe { return d; } }
Outline • Language overview • assignability • mutability • Parametric polymorphism • Formal model • Other language features • Conclusion
Contributions • Transitive reference immutability • Distinguishes assignability and mutability • Formal model • Type system for full Java including parametric polymorphism, reflection, and serialization • Templates to reduce code duplication • Interoperable with Java