280 likes | 294 Vues
Checking correctness properties of object-oriented programs. K. Rustan M. Leino Microsoft Research, Redmond, WA. Lecture 3 EEF summer school on Specification, Refinement, and Verification 23 Aug 2002, Turku, Finland. Review of methods.
E N D
Checking correctness properties of object-oriented programs K. Rustan M. LeinoMicrosoft Research, Redmond, WA Lecture 3EEF summer school on Specification, Refinement, and Verification23 Aug 2002, Turku, Finland
Review of methods • class T { method m(this, x, y)requires Pre modifies w ensures Post} • class U <: T { mimpl m(this, x, y) isBody}
Review of methods Turku trivia • class T { method m(this, x, y)requires Pre modifies w ensures Post} • class U <: T { mimpl m(this, x, y) isBody} The Other Side This Side
Review of methods this • class T { method m(this, x, y)requires Pre modifies w ensures Post} • class U <: T { mimpl m(this, x, y) isBody} “self”
Subtyping class Counter {method inc(this) …method dec(this) … …} class SimpleCounter <: Counter {field x: intmimpl inc(this) is this.x := this.x + 1mimpl dec(this) is this.x := this.x – 1 …} class OpCounter <: Counter {field i: intfield d: intmimpl inc(this) is this.i := this.i + 1mimpl dec(this) is this.d := this.d + 1 …} What should be the specifications of methods inc and dec?
Abstract variables class Counter {abstractfield n: intmethod inc(this) requires true modifies this.n ensures this.n = this.n0 + 1method dec(this) requires true modifies this.n ensures this.n = this.n0 - 1… } class SimpleCounter <: Counter {field x: int rep this.n this.xmimpl inc(this) is this.x := this.x + 1mimpl dec(this) is this.x := this.x – 1… } class OpCounter <: Counter {field i: intfield d: int rep this.n this.i – this.dmimpl inc(this) is this.i := this.i + 1mimpl dec(this) is this.d := this.d + 1… } • Two questions: • How do we reason about the abstract expressions? • What do modifies clauses with abstract variables mean?
Shape of verification condition UnivBackPred /\ BackPred(Program) ==> VC(MethodImpl)
2-dimensional heap • $ denotes current value of heap • (o,f) denotes a heap location • tr[ o.f ] = get($, o, f) • tr[ o.f0 ] = get($0, o, f) • get($, o, f) = select($, o, f) if f is concrete absfun(o, f)($) if f is abstract
Meaning of rep • class T { rep this.a E }gives rise to the following program-specific axiom: ( this :: this ≠ null /\ typeof(this) <: T ==> absfun(this, a) = (λ $ :: tr[ E ])) • Example:class OpCounter { rep this.n this.i – this.d }gives rise to: ( this :: this ≠ null /\ typeof(this) <: OpCounter ==> absfun(this, n) = (λ $ :: get($,this,i) – get($,this,d)))
Proving postconditions class Counter {abstractfield n: intmethod inc(this) requires true modifies this.n ensures this.n = this.n0 + 1method dec(this) requires true modifies this.n ensures this.n = this.n0 - 1… } class SimpleCounter <: Counter {field x: int rep this.n this.xmimpl inc(this) is this.x := this.x + 1mimpl dec(this) is this.x := this.x – 1… } class OpCounter <: Counter {field i: intfield d: int rep this.n this.i – this.dmimpl inc(this) is this.i := this.i + 1mimpl dec(this) is this.d := this.d + 1… }
Modifies clauses class Counter {abstractfield n: intmethod inc(this) requires true modifies this.n ensures this.n = this.n0 + 1method dec(this) requires true modifies this.n ensures this.n = this.n0 - 1… } class SimpleCounter <: Counter {field x: int rep this.n this.xmimpl inc(this) is this.x := this.x + 1mimpl dec(this) is this.x := this.x – 1… } class OpCounter <: Counter {field i: intfield d: int rep this.n this.i – this.dmimpl inc(this) is this.i := this.i + 1mimpl dec(this) is this.d := this.d + 1… }
Dependency declarations class Counter {abstractfield n: intmethod inc(this) requires true modifies this.n ensures this.n = this.n0 + 1method dec(this) requires true modifies this.n ensures this.n = this.n0 - 1… } class SimpleCounter <: Counter {field x: int in n rep this.n this.xmimpl inc(this) is this.x := this.x + 1mimpl dec(this) is this.x := this.x – 1… } class OpCounter <: Counter {field i: int in nfield d: int in n rep this.n this.i – this.dmimpl inc(this) is this.i := this.i + 1mimpl dec(this) is this.d := this.d + 1… }
Dependency relation • Relation (o,a) –S (p,b)states that in heap S,the value at location (o,a) may depend onthe value at location (p,b) • –S is a reflexive, transitive, and antisymmetric • For every concrete field f:( o, S, S’, E, p, x :: S’ = store(S, o, f, E) ==> get(S, p, x) = get(S’, p, x) \/ (p,x) –S (o,f))
Dependency axioms • Declaration field f in a • allows this.a to be a function of this.f, and • gives rise to the following program-specific axiom: • ( o, S :: (o,a) –S (o,f))
More dependencies class BigOpCounter <: Counter {field i: BigNumber maps x into nfield d: BigNumber maps x into n rep this.n this.i.x – this.d.xmimpl inc(this) is this.i.add(1) mimpl dec(this) is this.d.add(1) …} class BigNumber {abstractfield x: Z method add(this, v)requires true modifies this.x ensures this.x = this.x0 + v …}
More dependency axioms • Declaration field f maps x into a • allows this.a to be a function of this.f.x, and • gives rise to the following program-specific axiom: • ( o, S :: (o,a) –S (get(S,o,f), x))
Meaning of modifies • modifies this.a, …means:modifies $ensures ( o, f :: get($,o,f) = get($0,o,f) \/ (this,a) –$0 (o,f) \/ (o,f) –$0 (this,a) \/ …)
Negative dependency information • For any concrete field f: ( o, X, S, p :: (o,f) –S (p,X) o=p /\ f=X) • Each declaration field f in a, b, cgives rise to the following program-specific axiom: ( o, X, S, p :: (o,X) –S (p,f) ==> (o=p /\ X=f) \/ (o,X) –S (p,a) \/ (o,X) –S (p,b) \/ (o,X) –S (p,c)) • Similar axioms are added for mapsinto
Proving modifies clauses class Counter {abstractfield n: intmethod inc(this) requires true modifies this.n ensures this.n = this.n0 + 1method dec(this) requires true modifies this.n ensures this.n = this.n0 - 1… } class SimpleCounter <: Counter {field x: int rep this.n this.xmimpl inc(this) is this.x := this.x + 1mimpl dec(this) is this.x := this.x – 1… } class OpCounter <: Counter {field i: intfield d: int rep this.n this.i – this.dmimpl inc(this) is this.i := this.i + 1mimpl dec(this) is this.d := this.d + 1… }
Alias confinement • It seems that to support maps into soundly, one needs some form of alias confinement(For details, see “Using data groups to specify and check side effects”, Leino, Poetzsch-Heffter, Zhou, PLDI’02.)
Exercise • Prove the following program correct: class Counter {abstractfield n: intmethod inc(this)requires true modifies this.n ensures this.n = this.n0 + 1method adjust(this, v)requires 0 ≤ v modifies this.n ensures this.n = this.n0 + vmimpl adjust(this, v) isif n=0 thenskipelse this.inc(); this.adjust(n-1)end}
Example: valid/state paradigm class T {abstractfield valid: boolabstractfield state: unit method init(this, x, y, z)requires P(x,y,z) modifies this.valid, this.state ensures this.valid method operation0(this, x, y, z)requires this.valid /\ R0(x,y,z) modifies this.state ensures truemethod operation1(this, x, y, z)requires this.valid /\ R1(x,y,z) modifies this.state ensures true …method close(this)requires this.valid modifies this.valid, this.state ensures true field f: int in valid, statefield g: int in valid, statefield h: int in state rep this.valid this.f < this.g … }
Example: visitor class Container {field x: intfield y: intmethod put(this, z)requires true modifies this.x, this.yensures (this.x = z /\ this.y = this.y0) \/ (this.x = this.x0 /\ this.y = z)method pick(this) returns (z)requires true modifiesεensures z = this.x \/ z = this.ymethod map(this, visitor) …mimpl map(this, visitor) is visitor.apply(this.x); visitor.apply(this.y) } class Visitor {method apply(this, z) …}
Example: visitor class Container {field x: intfield y: int …method map(this, visitor)requires visitor.valid modifies visitor.state ensures truemimpl map(this, visitor) is visitor.apply(this.x); visitor.apply(this.y) } class Visitor {abstractfield valid: boolabstractfield state: unitmethod apply(this, z)requires this.valid modifies this.state ensures true}
Information hiding andmodular verification • Instead of proving Verif(Method, Program): UnivBackPred /\ BackPred(Program)==> VC(MethodImpl) prove Verif(Method, Module): UnivBackPred /\ BackPred(Module)==> VC(MethodImpl) where MethodImpl Module Program
Modular soundness • Well-formed(Module) /\Verif(Method, Module)==> Verif(Method, Program)
Summary • Method specifications apply unchanged to all method overrides • Data abstraction (abstract variables, abstraction dependencies, rep functions) gives subclasses the ability to operate differently • Information hiding poses restrictions on formal system for data abstraction, likely including alias confinement
References • C.A.R. Hoare. “Proof of correctness of data representations”. In Acta Informatica 1(4), pp. 271-281, Springer, 1972. • K. Rustan M. Leino. Toward Reliable Modular Programs. PhD thesis, California Institute of Technology. Technical Report Caltech-CS-TR-95-03, Caltech, 1995. • K. Rustan M. Leino and Greg Nelson. Data abstraction and information hiding. Research Report 160, Compaq SRC, Nov. 2000. To appear in TOPLAS. • Peter Müller. Modular Specification and Verification of Object-Oriented Programs. PhD thesis, FernUniversität Hagen. Volume 2262 of LNCS, Springer, 2002. • K. Rustan M. Leino. “Data groups: Specifying the modification of extended state”. In OOPSLA ’98, pp. 144-153, ACM, 1998. • K. Rustan M. Leino, Arnd Poetzsch-Heffter, and Yunhong Zhou. “Using data groups to specify and check side effects”. In PLDI ’02, SIGPLAN Notices 37(5), pp. 246-257, ACM, May 2002.