Title: JPure: a Modular Purity System for Java
1JPure a Modular Purity System for Java
- David J. Pearce
- Victoria University of Wellington
- New Zealand
2Introduction
Definition A method is considered pure if it
does not assign (directly of indirectly) to any
field or array cell that existed before it was
called.
int sum(int items) int r 0 for(int
v items) r v return r
boolean isSorted(ListltIntegergt items) int
last Integer.MIN_VALUE for(Integer v
items) if(last gt v) return false
last v return true
3Typical Previous Purity Systems
Pointer Analysis
Purity Inference
Annotated Source
Java Source
?
Annotated Bytecode
- Pointer Analysis feeds Purity Inference
- Pointer Analysis typically whole-program
- Inferred annotations cannot be checked easily
- i.e. without regeneration pointer information
4Modular Purity System
Purity Inference
Java Compiler
Annotated Source
Java Source
Purity Checker
Annotated Bytecode
- Purity Inference
- Generates annotations via interprocedural
analysis - Generated annotations are modularly checkable
- Purity Checker
- Verifies annotations via intraprocedural analysis
- Integrates easily with Java Bytecode Verification
5Simple (Modular) Approach
- Pure Methods
- Cannot contain field assignments
- Can only call methods marked _at_Pure
- Only pure methods can override pure methods
6Simple (Modular) Approach
- Pure Methods
- Cannot contain field assignments
- Can only call methods marked _at_Pure
- Only pure methods can override pure methods
7Simple (Modular) Approach
Parent private int f _at_Pure void method()f1
- Pure Methods
- Cannot contain field assignments
- Can only call methods marked _at_Pure
- Only pure methods can override pure methods
8Simple (Modular) Approach
- Pure Methods
- Cannot contain field assignments
- Can only call methods marked _at_Pure
- Only pure methods can override pure methods
9Simple (Modular) Approach
- Pure Methods
- Cannot contain field assignments
- Can only call methods marked _at_Pure
- Only pure methods can override pure methods
10Problems
public class Test private ListltStringgt
items _at_Pure boolean has(String s)
for(String i items) if(s i)
return true return false
public class AbstractStringBuilder private
char data private int count // number of
items used public AbstractStringBuilder
append(String s) s.getChars(0,
s.length(), data, count) _at_Pure String
f(String x) return x hello
11Problems
public class Test private ListltStringgt
items _at_Pure boolean has(String s)
for(String i items) if(s i)
return true return false
public class AbstractStringBuilder private
char data private int count // number of
items used public AbstractStringBuilder
append(String s) s.getChars(0,
s.length(), data, count) _at_Pure String
f(String x) return x hello
12Problems
public class Test private ListltStringgt
items _at_Pure boolean has(String s)
for(String i items) if(s i)
return true return false
public class AbstractStringBuilder private
char data private int count // number of
items used public AbstractStringBuilder
append(String s) s.getChars(0,
s.length(), data, count) _at_Pure String
f(String x) return x hello
13Problems
public class Test private ListltStringgt
items _at_Pure boolean has(String s)
for(String i items) if(s i)
return true return false
public class AbstractStringBuilder private
char data private int count // number of
items used public AbstractStringBuilder
append(String s) s.getChars(0,
s.length(), data, count) _at_Pure String
f(String x) return x hello
14Introducing JPure!
interface Collection _at_Fresh Object
iterator() interface Iterator _at_Pure
boolean hasNext() _at_Local Object
next() class Test ListltStringgt items
_at_Pure boolean has(String s) for(String i
items) if(s i) return true
return false
15Introducing JPure!
Indicates iterator() returns fresh object
interface Collection _at_Fresh Object
iterator() interface Iterator _at_Pure
boolean hasNext() _at_Local Object
next() class Test ListltStringgt items
_at_Pure boolean has(String s) for(String i
items) if(s i) return true
return false
16Introducing JPure!
Indicates iterator() returns fresh object
interface Collection _at_Fresh Object
iterator() interface Iterator _at_Pure
boolean hasNext() _at_Local Object
next() class Test ListltStringgt items
_at_Pure boolean has(String s) for(String i
items) if(s i) return true
return false
Indicates next() only modifies local state
17Introducing JPure!
Indicates iterator() returns fresh object
interface Collection _at_Fresh Object
iterator() interface Iterator _at_Pure
boolean hasNext() _at_Local Object
next() class Test ListltStringgt items
_at_Pure boolean has(String s) for(String i
items) if(s i) return true
return false
Indicates next() only modifies local state
18Introducing JPure!
Indicates iterator() returns fresh object
interface Collection _at_Fresh Object
iterator() interface Iterator _at_Pure
boolean hasNext() _at_Local Object
next() class Test ListltStringgt items
_at_Pure boolean has(String s) for(String i
items) if(s i) return true
return false
Indicates next() only modifies local state
_at_Pure boolean has(String s) Iterator tmp
tmp items.iterator() while(tmp.hasNext())
i tmp.next() if(s i) return true
return false
19Introducing JPure!
Indicates iterator() returns fresh object
interface Collection _at_Fresh Object
iterator() interface Iterator _at_Pure
boolean hasNext() _at_Local Object
next() class Test ListltStringgt items
_at_Pure boolean has(String s) for(String i
items) if(s i) return true
return false
Indicates next() only modifies local state
_at_Pure boolean has(String s) Iterator tmp
tmp items.iterator() while(tmp.hasNext())
i tmp.next() if(s i) return true
return false
20class ArrayList implements Collection
_at_Fresh Object iterator() return new
Iterator(data) static class Iterator
Object data int idx 0 _at_Pure boolean
hasNext() return idx lt data.size() _at_Local
Object next() return dataidx
- Methods annotated _at_Fresh
- Must return new objects
- Or, values returned by methods marked _at_Fresh
- Methods annotated _at_Local
- May update local state
- But otherwise must remain pure
21Iterator Implementation
class ArrayList implements Collection
_at_Fresh Object iterator() return new
Iterator(data) static class Iterator
Object data int idx 0 _at_Pure boolean
hasNext() return idx lt data.size() _at_Local
Object next() return dataidx
- Methods annotated _at_Fresh
- Must return new objects
- Or, values returned by methods marked _at_Fresh
- Methods annotated _at_Local
- May update local state
- But otherwise must remain pure
22class TList private int length private
_at_Local Object data private Type type
_at_Local public TList(Type t, int m) length
0 data new Objectm type t
_at_Local public void copy(TList dst) length
dst.length type dst.type data new
Objectdst.length for(int i0i!lengthi)
datai dst.datai
Locality Invariant 1 (Construction). When a new
object is constructed its locality is always
fresh.
Locality Invariant 2 (Preservation). When the
locality of a fresh object is modified, its
locality must remain fresh.
23class TList private int length private
_at_Local Object data private Type type
_at_Local public TList(Type t, int m) length
0 data new Objectm type t
_at_Local public void copy(TList dst) length
dst.length type dst.type data new
Objectdst.length for(int i0i!lengthi)
datai dst.datai
Required for Invariant 1
Locality Invariant 1 (Construction). When a new
object is constructed its locality is always
fresh.
Locality Invariant 2 (Preservation). When the
locality of a fresh object is modified, its
locality must remain fresh.
24class TList private int length private
_at_Local Object data private Type type
_at_Local public TList(Type t, int m) length
0 data new Objectm type t
_at_Local public void copy(TList dst) length
dst.length type dst.type data new
Objectdst.length for(int i0i!lengthi)
datai dst.datai
Required for Invariant 1
Safe under Invariant 2
Locality Invariant 1 (Construction). When a new
object is constructed its locality is always
fresh.
Locality Invariant 2 (Preservation). When the
locality of a fresh object is modified, its
locality must remain fresh.
25Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
26Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
27Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
28Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
29Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
30Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
31Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
32Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
33Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
LDST
34Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
LDST
LTHIS
LDST
LDST
35Detailed Example
tmp
dst
this
_at_Local public void copy(TList dst) var tmp
dst.length this.length tmp tmp
dst.type this.type tmp tmp new
Objectdst.length this.data tmp
for(int i0i!lengthi) tmp
dst.datai this.datai tmp
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
?
LTHIS
LDST
LDST
LTHIS
LDST
LDST
LTHIS
LDST
LDST
36Checking vs Inference
- Purity Checker
- Intraprocedural dataflow analysis
- Uses static information about called methods
- Checks fresh objects flow to _at_Fresh returns
- Checks assignments to _at_Local fields are fresh
- Checks assignments to other fields are in
locality - Checks annotations overridden correctly
- Purity Inference
- Interprocedural dataflow analysis
- Uses static call graph
37(No Transcript)
38Limitations
class Test private int hashCode public
boolean equals(Object o) if(hashCode()
o.hashCode()) return false public
int hashCode() if(hashCode -1) hashCode
return hashCode
- Disappointment!
- Object.equals() not inferred _at_Pure
- Object.hashCode() not inferred _at_Pure
39Conclusion
- The JPure System
- Built around Modularly Checkable Annotations
- Interprocedural analysis infers annotations
- Intraprocedural analysis checks annotations
- Could be incorporated in Java Bytecode Verifier
- Locality freshness help uncover more purity
- 41 on average for benchmarks (vs 25 for simple)
- See http//www.ecs.vuw.ac.nz/djp/jpure
40Law of Locality
Law of Locality. When checking _at_Local
annotations, one can safely assume parameters are
not aliased (!)
- Example
- What if other aliased with this?
- Applying Law of Locality seems counter-intuitive
class Test private int field _at_Local void
f(Test other) this.field 1