Javari: Java Reference Immutability Language and Inference Tool

Preview:

DESCRIPTION

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 - PowerPoint PPT Presentation

Citation preview

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

Recommended