1 / 50

Javari: Java Reference Immutability Language and Inference Tool

Javari: Java Reference Immutability Language and Inference Tool. Jaime Quinonez November 14, 2006. 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

Download Presentation

Javari: Java Reference Immutability Language and Inference Tool

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Javari: Java Reference Immutability Language and Inference Tool Jaime Quinonez November 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; • listis 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; • listis 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 • Dateis an existing class • Date.getTime() andDate.setTime()are not annotated • Javari defaults method receiver to mutable unless@ReadOnlyis 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 type public 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 type public 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 type public 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 type public 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 type public 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 • Constraints • Date.setTime().thisis mutable • Date.getTime().thismutable -> Cell.dmutable • Date.getTime().thismutable -> Cell.get().thismutable • Date.setTime().thismutable -> Cell.dmutable • Date.setTime().thismutable -> Cell.reset().thismutable

  21. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • Date.getTime().thismutable -> Cell.dmutable • Date.getTime().thismutable -> Cell.get().thismutable • Date.setTime().thismutable -> Cell.dmutable • Date.setTime().thismutable -> Cell.reset().thismutable • New Constraints • Cell.dis mutable

  22. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • Date.getTime().thismutable -> Cell.dmutable • Date.getTime().thismutable -> Cell.get().thismutable • Date.setTime().thismutable -> Cell.dmutable • Date.setTime().thismutable -> Cell.reset().thismutable • New Constraints • Cell.dis mutable • Cell.reset().thisis mutable

  23. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • Date.getTime().thismutable -> Cell.dmutable • Date.getTime().thismutable -> Cell.get().thismutable • Date.setTime().thismutable -> Cell.dmutable • Date.setTime().thismutable -> Cell.reset().thismutable • New Constraints • Cell.reset().thisis mutable • Cell.dis mutable • Final unguarded constraints • Date.setTime().thisis mutable • Cell.reset().thisis mutable • Cell.dis mutable • 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().thismutable -> Cell.dmutable • 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().thismutable -> Cell.dmutable • 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().thismutable -> Cell.dmutable • Date.getTime().this mutable -> Cell.getTime().this mutable • Cell.setDate().this is mutable • Cell.d mutable -> Cell.setDate().param:newDatemutable

  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().thismutable -> Cell.dmutable • Date.getTime().this mutable -> Cell.getTime().this mutable • Cell.setDate().this is mutable • Cell.d mutable -> Cell.setDate().param:newDatemutable

  30. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • Date.getTime().thismutable -> Cell.dmutable • Date.getTime().thismutable -> Cell.getTime().thismutable • Cell.setDate().thisis mutable • Cell.dmutable -> Cell.setDate().param:newDateis mutable

  31. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • Date.getTime().thismutable -> Cell.dmutable • Date.getTime().thismutable -> Cell.getTime().thismutable • Cell.setDate().thisis mutable • Cell.dmutable -> Cell.setDate().param:newDateis mutable • No guards can be satisfied • Finished with two unguarded constraints: • Date.setTime().this is mutable • Cell.setDate().this is mutable • All other types are@ReadOnly

  32. Generic types have same inference • Place constraints on upper and lower bound public 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 bound public 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 bound public 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 bound public 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 bound public 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 bound public 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 • Constraints • Date.setTime().thisis mutable • SubDate.setTime().thisis mutable • 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

  39. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • SubDate.setTime().thisis mutable • 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() mutable • SubDate.setTime().this mutable -> Date.setTime().this mutable • New Constraints • Cell.t is mutable

  40. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • SubDate.setTime().thisis mutable • 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 • New Constraints • Cell.t is mutable • Cell.reset() is mutable

  41. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • SubDate.setTime().thisis mutable • 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 • New Constraints • Cell.t is mutable • Cell.reset() is mutable • Cell.set().param:t is mutable

  42. Javarifier propagates unguarded constraints • Constraints • Date.setTime().thisis mutable • SubDate.setTime().thisis mutable • 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 • New Constraints • Cell.t is mutable • Cell.reset() is mutable • Cell.set().param:t is mutable • 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() modifiesparameter • 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

More Related