400 likes | 544 Views
Javari: Adding Reference Immutability to Java. Matthew Tschantz & Michael Ernst (MIT) OOPSLA’05 Presented by Tali Shragai. Extension to Javari2004 [Birka]. Javari: Java + (C++ const) + more… Two new keywords: readonly , mutable OOPSLA’04: Initial Javari implementation
E N D
Javari: Adding Reference Immutability to Java Matthew Tschantz & Michael Ernst (MIT) OOPSLA’05 Presented by Tali Shragai
Extension to Javari2004 [Birka] • Javari: Java + (C++ const) + more… • Two new keywords: readonly, mutable • OOPSLA’04: Initial Javari implementation • Experience: 160,000 code lines • Better documentation • Error detection and prevention • Compatible with Java & existing JVMs • But: • No Generics, serialization, reflection • Confuses mutability & assignability • Javari 05: • More keywords: assignable, romaybe • Genericity, reflection, serialization • Most important (?): mutability vs. assignability
Protecting method arguments • A library routine does not modify its 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; Object[] getSigners() { return signers; // JDK 1.1 security hole } } myClass.getSigners()[0] = “Sun”;
Protecting abstract state: observer methods (revised) class Class { private Object[] signers; readonly Object[] getSigners() { return signers; // Fixes JDK 1.1 bug } } myClass.getSigners()[0] = “Sun”; // Error
Reference vs. Object immutability • Reference immutability • Object modification: • Readonly reference: no! • Other reference: yes. • Deep: cannot modify transitively reachable state • Static type checking! • Dynamic checking for mutability downcasts • Object immutability: write protect from any reference Graph temp = new Graph(); // construct the graph readonly Graph g = temp; temp = null; // further usage through read only reference… • Javari: object immutability emulated by reference immutability
Related work • Old trick: pass/return by value • pain for large objects • C++: const, const_cast • Unsound (unchecked casts) • Non-transitive! • No immutability-based parameterization • Leads to code duplication (const methods) • No support for multidimensional arrays? • JAC [Kniesel 2001]: • Implementation requires rewriting the code • Unsound (subtyping, arrays) • Return type mutability equals receiver's
Readonly readonly Date rd = new Date(); rd.year = 2005; // Compile-time error rd.incrementDay(); // Compile-time error rd = new Foo(); // OK
Mutability vs. Assignability 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
Immutable classes • A class/interface can be declared immutable using readonly in its class declaration. • Non-static fields & methods readonly (default) • Reference to its objects implicitly read-only • Subclasses must be immutable too! readonly class String{…} /*readonly*/ String s1 = new String(); readonly String s2 = new String(); S1 = s2; // OK S2 = s1; // OK
Assignability • May a reference 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 (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 rb.gear = 5; // Error
Mutability • May the object’s transitive abstract state 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 excludes requests from the abstract state of the object mutable 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:
rs Readonly assignable Readonly?? grades Non-readonly s Downcast this-mutable fields reached through a readonly 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 • this-mutable fields through readonly reference are: • readonlyas rvalue (may only be assigned to a readonly reference) • mutableas lvalue (may only be assigned withmutable reference) • Notation: <? readonly GradeReport>
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
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 Device { /*this-mut*/ List</*this-mut*/Driver> drivers; } /*mutable*/ Device d; readonly Device rd; d.drivers has type /*mut*/ List</*mut*/ Driver> /*mut*/ List</*mut*/Driver> dl = d.drivers; // OK
this-mutable type arguments class Device { /*this-mut*/ List</*this-mut*/Driver> drivers; } /*mutable*/ Device d; readonly Device rd; rd.drivers has type ? readonly List<? readonly Driver> readonly List<?readonly Driver> x = rd. drivers; //OK readonly List<readonlyDriver> y = rd. drivers; //Error readonly List</*mutable*/Driver> z = rd. drivers; //Error
Serialization • Add a readonly-ness bit to the serialized form. • De-serialization done using 2 versions of ObjectInputStream.readObject: • readObjectReadonly – returns a read-only object, without checking. • readObject – returns a mutable objects, but throws an exception if the readonly-ness bit is set.
Reducing code duplication • In Java, each class definition defines exactly one type. In Javari, by contrast, a class C actually denotes 2 type: C and readonly C. • 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;} }
Downcasts • Javari guarantees that a readonly reference cannot be used, directly or indirectly, to modify its referent. • Down-casting a reference from readonly to mutable triggers run-time checks, which disallow any modifications done through the casted reference.
Downcast example class Foo{ Date d; void setD() /*mutable*/{ d = new Date(); } } Foo f1 = new Foo(); readonly Foo rf = f1; Foo f2 = (mutable)rf; f1.d = new Date(); // OK f2.d = new Date(); // run-time error f1.setD(); // OK F2.setD(); // run-time error
Contributions • Transitive reference immutability • Distinguishes assignability and mutability • Formal model (not shown here) • Type system for full Java 5.0 including parametric polymorphism, reflection, and serialization • Templates to reduce code duplication • Interoperable with Java • Still to come: type inference…
The End! Thank you for listening…