jpure: a modular purity system for java

40
JPure: a Modular Purity System for Java David J. Pearce Victoria University of Wellington New Zealand

Upload: penelope-duncan

Post on 02-Jan-2016

35 views

Category:

Documents


2 download

DESCRIPTION

JPure: a Modular Purity System for Java. David J. Pearce Victoria University of Wellington New Zealand. Introduction. 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. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: JPure: a Modular Purity System for Java

JPure: a Modular Purity System for Java

David J. PearceVictoria University of Wellington

New Zealand

Page 2: JPure: a Modular Purity System for Java

Introduction

int sum(int[] items) {

int r = 0;

for(int v : items) { r += v; }

return r;

}

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.

boolean isSorted(List<Integer> items) {

int last = Integer.MIN_VALUE;

for(Integer v : items) {

if(last > v) { return false; }

last = v;

}

return true;

}

Page 3: JPure: a Modular Purity System for Java

Typical Previous Purity Systems

• Pointer Analysis feeds Purity Inference

– Pointer Analysis typically whole-program

– Inferred annotations cannot be checked easily• i.e. without regeneration pointer information

PointerAnalysis

?

Java Source Annotated Source

Annotated Bytecode

PurityInference

Page 4: JPure: a Modular Purity System for Java

Modular Purity System

• 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

PurityInference

PurityChecker

Java Source Annotated Source

Annotated Bytecode

Java Compiler

Page 5: JPure: a Modular Purity System for Java

Simple (Modular) Approach

• Pure Methods:

– Cannot contain field assignments

– Can only call methods marked @Pure

– Only pure methods can override pure methods

Parent

private int afield;

@Pure void method() { }

Child

@Pure void method() { }

Client

@Pure void f(Parent p) {

p.method();

}

Page 6: JPure: a Modular Purity System for Java

Simple (Modular) Approach

• Pure Methods:

– Cannot contain field assignments

– Can only call methods marked @Pure

– Only pure methods can override pure methods

Parent

private int afield;

@Pure void method() { }

Child

@Pure void method() { }

Client

@Pure void f(Parent p) {

p.method();

}

Page 7: JPure: a Modular Purity System for Java

Simple (Modular) Approach

• Pure Methods:

– Cannot contain field assignments

– Can only call methods marked @Pure

– Only pure methods can override pure methods

Parent

private int f;

@Pure void method(){f=1;}

Child

@Pure void method() { }

Client

@Pure void f(Parent p) {

p.method();

}

Page 8: JPure: a Modular Purity System for Java

Simple (Modular) Approach

• Pure Methods:

– Cannot contain field assignments

– Can only call methods marked @Pure

– Only pure methods can override pure methods

Child

@Pure void method() { }

Client

@Pure void f(Parent p) {

p.method();

}

Page 9: JPure: a Modular Purity System for Java

Simple (Modular) Approach

• Pure Methods:

– Cannot contain field assignments

– Can only call methods marked @Pure

– Only pure methods can override pure methods

Parent

private int afield;

@Pure void method() { }

Client

@Pure void f(Parent p) {

p.method();

}

Page 10: JPure: a Modular Purity System for Java

Problems

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);

}}

@Pure String f(String x) { return x + “hello”; }

public class Test {

private List<String> items;

@Pure boolean has(String s) {

for(String i : items) {

if(s == i) { return true; }

}

return false;

} }

Page 11: JPure: a Modular Purity System for Java

Problems

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);

}}

@Pure String f(String x) { return x + “hello”; }

public class Test {

private List<String> items;

@Pure boolean has(String s) {

for(String i : items) {

if(s == i) { return true; }

}

return false;

} }

Page 12: JPure: a Modular Purity System for Java

Problems

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);

}}

@Pure String f(String x) { return x + “hello”; }

public class Test {

private List<String> items;

@Pure boolean has(String s) {

for(String i : items) {

if(s == i) { return true; }

}

return false;

} }

Page 13: JPure: a Modular Purity System for Java

Problems

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);

}}

@Pure String f(String x) { return x + “hello”; }

public class Test {

private List<String> items;

@Pure boolean has(String s) {

for(String i : items) {

if(s == i) { return true; }

}

return false;

} }

Page 14: JPure: a Modular Purity System for Java

interface Collection {

@Fresh Object iterator();

}

interface Iterator {

@Pure boolean hasNext();

@Local Object next();

}

class Test {

List<String> items;

@Pure boolean has(String s){

for(String i : items) {

if(s == i) return true;

}

return false;

} }

Introducing JPure!

Page 15: JPure: a Modular Purity System for Java

interface Collection {

@Fresh Object iterator();

}

interface Iterator {

@Pure boolean hasNext();

@Local Object next();

}

class Test {

List<String> items;

@Pure boolean has(String s){

for(String i : items) {

if(s == i) return true;

}

return false;

} }

Introducing JPure!Indicates iterator() returns “fresh” object

Page 16: JPure: a Modular Purity System for Java

interface Collection {

@Fresh Object iterator();

}

interface Iterator {

@Pure boolean hasNext();

@Local Object next();

}

class Test {

List<String> items;

@Pure boolean has(String s){

for(String i : items) {

if(s == i) return true;

}

return false;

} }

Introducing JPure!Indicates iterator() returns “fresh” object

Indicates next() only modifies “local” state

Page 17: JPure: a Modular Purity System for Java

interface Collection {

@Fresh Object iterator();

}

interface Iterator {

@Pure boolean hasNext();

@Local Object next();

}

class Test {

List<String> items;

@Pure boolean has(String s){

for(String i : items) {

if(s == i) return true;

}

return false;

} }

Introducing JPure!Indicates iterator() returns “fresh” object

Indicates next() only modifies “local” state

Page 18: JPure: a Modular Purity System for Java

interface Collection {

@Fresh Object iterator();

}

interface Iterator {

@Pure boolean hasNext();

@Local Object next();

}

class Test {

List<String> items;

@Pure boolean has(String s){

for(String i : items) {

if(s == i) return true;

}

return false;

} }

Introducing JPure!

@Pure boolean has(String s) {

Iterator tmp;

tmp = items.iterator();

while(tmp.hasNext()) {

i = tmp.next();

if(s == i) return true;

}

return false;

}

Indicates iterator() returns “fresh” object

Indicates next() only modifies “local” state

Page 19: JPure: a Modular Purity System for Java

interface Collection {

@Fresh Object iterator();

}

interface Iterator {

@Pure boolean hasNext();

@Local Object next();

}

class Test {

List<String> items;

@Pure boolean has(String s){

for(String i : items) {

if(s == i) return true;

}

return false;

} }

Introducing JPure!

@Pure boolean has(String s) {

Iterator tmp;

tmp = items.iterator();

while(tmp.hasNext()) {

i = tmp.next();

if(s == i) return true;

}

return false;

}

Indicates iterator() returns “fresh” object

Indicates next() only modifies “local” state

Page 20: JPure: a Modular Purity System for Java

• Methods annotated @Fresh– Must return new objects– Or, values returned by methods marked @Fresh

• Methods annotated @Local– May update “local” state … – But otherwise must remain pure

class ArrayList implements Collection{

@Fresh Object iterator() { return new Iterator(data); }

static class Iterator {

Object[] data; int idx = 0;

@Pure boolean hasNext() { return idx < data.size(); }

@Local Object next() { return data[idx++]; }

}}

Page 21: JPure: a Modular Purity System for Java

Iterator Implementation

• Methods annotated @Fresh– Must return new objects– Or, values returned by methods marked @Fresh

• Methods annotated @Local– May update “local” state … – But otherwise must remain pure

class ArrayList implements Collection{ …

@Fresh Object iterator() { return new Iterator(data); }

static class Iterator {

Object[] data; int idx = 0; …

@Pure boolean hasNext() { return idx < data.size(); }

@Local Object next() { return data[idx++]; }

}}

Page 22: JPure: a Modular Purity System for Java

Locality Invariant 2 (Preservation). When the locality of a fresh object is modified, its locality must remain fresh.

Locality Invariant 1 (Construction). When a new object is constructed its locality is always fresh.

class TList {

private int length;

private @Local Object[] data;

private Type type;

@Local public TList(Type t, int m) {

length = 0;

data = new Object[m];

type = t;

}

@Local public void copy(TList dst) {

length = dst.length;

type = dst.type;

data = new Object[dst.length];

for(int i=0;i!=length;++i) { data[i] = dst.data[i]; }

}}

TListdata

length,type

Page 23: JPure: a Modular Purity System for Java

Locality Invariant 2 (Preservation). When the locality of a fresh object is modified, its locality must remain fresh.

Locality Invariant 1 (Construction). When a new object is constructed its locality is always fresh.

class TList {

private int length;

private @Local Object[] data;

private Type type;

@Local public TList(Type t, int m) {

length = 0;

data = new Object[m];

type = t;

}

@Local public void copy(TList dst) {

length = dst.length;

type = dst.type;

data = new Object[dst.length];

for(int i=0;i!=length;++i) { data[i] = dst.data[i]; }

}}

TListdata

length,type

Required forInvariant 1

Page 24: JPure: a Modular Purity System for Java

Locality Invariant 2 (Preservation). When the locality of a fresh object is modified, its locality must remain fresh.

Locality Invariant 1 (Construction). When a new object is constructed its locality is always fresh.

class TList {

private int length;

private @Local Object[] data;

private Type type;

@Local public TList(Type t, int m) {

length = 0;

data = new Object[m];

type = t;

}

@Local public void copy(TList dst) {

length = dst.length;

type = dst.type;

data = new Object[dst.length];

for(int i=0;i!=length;++i) { data[i] = dst.data[i]; }

}}

TListdata

length,type

Required forInvariant 1

Safe underInvariant 2

Page 25: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

Page 26: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

Page 27: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

Page 28: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

Page 29: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

Page 30: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

LTHIS LDST

Page 31: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

LTHIS LDST

LTHIS LDST

Page 32: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

LTHIS LDST

LTHIS LDST

LTHIS LDST

Page 33: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

LTHIS LDST

LTHIS LDST

LTHIS LDST

LTHIS LDST LDST

Page 34: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

LTHIS LDST

LTHIS LDST

LTHIS LDST

LTHIS LDST

LTHIS LDST

LDST

LDST

Page 35: JPure: a Modular Purity System for Java

Detailed Example@Local public void copy(TList dst) {

var tmp = dst.length;

this.length = tmp;

tmp = dst.type;

this.type = tmp;

tmp = new Object[dst.length];

this.data = tmp;

for(int i=0;i!=length;++i) {

tmp = dst.data[i];

this.data[i] = tmp;

} }

LTHIS LDST ?

tmpdstthis

LTHIS LDST

LTHIS LDST

LTHIS LDST ?

LTHIS LDST ?

LTHIS LDST

LTHIS LDST

LTHIS LDST

LTHIS LDST

LTHIS LDST

LDST

LDST

LDST

Page 36: JPure: a Modular Purity System for Java

Checking vs Inference

• Purity Checker:

– Intraprocedural dataflow analysis

– Uses static information about called methods

– Checks fresh objects flow to @Fresh returns

– Checks assignments to @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

– Essentially works in opposite direction to checker

– E.g. if all returned values fresh -> method annotated @Fresh

Page 37: JPure: a Modular Purity System for Java
Page 38: JPure: a Modular Purity System for Java

Limitations

• Disappointment!– Object.equals() not inferred @Pure– Object.hashCode() not inferred @Pure

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;

}}

Page 39: JPure: a Modular Purity System for Java

Conclusion

• 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

Page 40: JPure: a Modular Purity System for Java

Law of Locality

• Example:

– What if other aliased with this?

– Applying Law of Locality seems counter-intuitive

class Test {

private int field;

@Local void f(Test other){

this.field = 1;

}}

Law of Locality. When checking @Local annotations, one can safely assume parameters are not aliased (!)