390 likes | 515 Vues
This paper presents advancements in object-oriented (OO) verification through the application of separation logic, focusing on the challenges associated with behavioral subtyping, class inheritance, and the need for precise specifications. By enhancing specification subsumption and introducing both static and dynamic specifications, we aim to minimize code re-verification while supporting class invariants and calls. Our approach demonstrates the effectiveness of separation logic in managing shared mutable data structures, paving the way for more efficient verification processes in OO programming.
E N D
Enhancing Modular OO Verification with Separation Logic Wei-Ngan Chin1,2Cristina David1 Huu Hai Nguyen2 Shengchao Qin3 1 National University of Singapore 2 Singapore-MIT Alliance 3 Durham University POPL 2008
Challenges of OO Verification Must support behavioral subtyping. Must support class inheritance. Must support casting. Good to support class invariants. Good to support super/direct calls. • precision • efficiency(minimize code re-verification) Tokyo 2008
Separation Logic and Abstraction [Parkinson&Bierman, POPL’05] • Introduces abstract predicate family • allows a definition for each class type • very powerful idea • Re-verification when method inherited • Cannot handle super calls • Specs may be imprecise Tokyo 2008
Separation Logic • Foundations: • O’Hearn and Pym, “The Logic of Bunched Implications”, Bulletin of Symbolic Logic 1999 • Reynolds, “Separation Logic: A Logic for Shared Mutable Data Structures”, LICS 2002 • Extension to Hoare logic to reason about shared mutable data structures p1* p2: the heap can be split into two disjoint parts (p1 holds for one part and p2 holds for the other) Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
Behavioral Subtyping • Liskov's Substitutivity Principle (1988) : • an object of a subclass can always be passed to a location where an object of its superclass is expected • enforce behavioral subtyping with a subsumption relation Tokyo 2008
covariance contravariance Specification Subsumption Relation class A { t mn(..) where preA *! postA {...} } class B extends A { t mn(..) where preB *! postB {...} } Spec (preB*!postB) is a subtype of (preA*!postA) if: (preB *! postB) <:B (preA *! postA) preA old(preA)Æ ) preB postB ) postA Leavens&Naumann(‘06) Tokyo 2008
Enhanced Specification Subsumption `{P} c {Q} `{P *} c {Q *} • With the help of frame rule • Spec (preB*! postB) is a subtype of (preA*! postA) if: • )preB postB • (preB*!postB) <:B (preA*!postA) preA * )postA * Ætype(this)<:B Castagna(TOPLAS‘95) Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
Static and Dynamic Spec • A static spec: • describes just a single method • used for statically-dispatched calls (e.g. super/direct) • can be very precise • A dynamic spec: • describes a method and its overriding methods • used for dynamically-dispatched calls • less precise Tokyo 2008
Static and Dynamic Specs : Example static this::Cnt<n>$ *! this::Cnt<n+1>$ dynamic this::Cnt<n>$ *! this::Cnt<b>$ • class Cnt { int val; • Cnt(int v) {this.val:=v} • void tick() {this.val:=this.val+1} • int get() {this.val} • void set(int x) {this.val:=x} } • class FastCnt extends Cnt { • FastCnt(int v) {this.val:=v} • void tick() {this.val:=this.val+2} } • class PosCnt extends Cnt inv this.val¸0 { • PosCnt(int v) {this.val:=v} • void set(int x) {if x¸0 then this.val:=x else error()} } Tokyo 2008
Æ n¸0 Æ n+1·b·n+2 Cnt.tick is overridden ) weaken the postcond PosCnt has inv this.val¸0 ) strengthen the precond Static and Dynamic Specs : Example static this::Cnt<n>$ *! this::Cnt<n+1>$ dynamic this::Cnt<n>$ *! this::Cnt<b>$ • class Cnt { int val; • Cnt(int v) {this.val:=v} • void tick() {this.val:=this.val+1} • int get() {this.val} • void set(int x) {this.val:=x} } • class FastCnt extends Cnt { • FastCnt(int v) {this.val:=v} • void tick() {this.val:=this.val+2} } • class PosCnt extends Cnt inv this.val¸0 { • PosCnt(int v) {this.val:=v} • void set(int x) {if x¸0 then this.val:=x else error()} } Æ n+1·b·n+2 Æ b=n+1 Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
min. re-verification Static-Spec(A.mn) <: Dyn-Spec(A.mn) Key Principles • Static spec must be given for each new method. • Code verification is done only for static spec. • Dynamic spec is either given or derived. • Subsumption relations: class A { // defines mn } Tokyo 2008
min. re-verification Static-Spec(A.mn) <: Dyn-Spec(A.mn) ensures behavioral subtyping <: Dyn-Spec(B.mn) Key Principles • Static spec must be given for each new method. • Code verification is done only for static spec. • Dynamic spec is either given or derived. • Subsumption relations: class A { // defines mn } class B extends A { // overrides mn } Tokyo 2008
min. re-verification Static-Spec(A.mn) <: Dyn-Spec(A.mn) ensures behavioral subtyping min. re- verification <: <: Dyn-Spec(B.mn) Static-Spec(C.mn) Key Principles • Static spec must be given for each new method. • Code verification is done only for static spec. • Dynamic spec is either given or derived. • Subsumption relations: class A { // defines mn } class B extends A { // overrides mn } class C extends A { // inherits mn } Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
extension record for the extra fields of B Object Representation fields of A fields of B class A { // fields v1..n .... } y::A<t,v1..n,q>*q::Ext<B,w1..m,p> upcast downcast class B extends A { // fields w1..m .... } y::B<t,v1..n,w1..m,p> actual type extension fields Tokyo 2008
Object Representation class A { // fields v1..n .... } y::A<t,v1..n,q>*q::Ext<B,w1..m,p> upcast downcast class B extends A { // fields w1..m .... } y::B<t,v1..n,w1..m,p> LOSSLESS CASTING Tokyo 2008
Partial and Full Views Seen as a c-class obj (actual type t) Fields v1..n of c-class Other fields w1..m of subclass t of c x Partial view: - x::c<t,v1..n,p> - no extension records - shorthand x::c<v1..n> • Static specs • Improves precision Full view: - x::c<t,v1..n,p>* p::ExtAll<c,t> - ExtAll<c,t> captures all the extensions of subclass t of c - shorthand x::c<v1..n>$ • Dynamic specs Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
Ensuring Class Invariants Q: How and when to check for class inv? class PosCnt extends Cnt inv this.val¸0 { ... } Invariant-enhanced predicate: root::PosCnt#I<t,v,p> == root::PosCnt<t,v,p> * v¸0 Tokyo 2008
Invariant-Enhanced Predicate : Example inv is temporarily broken • class PosCnt extends Cnt inv this.val¸0 { • ... • void set(int x) static this::PosCnt<v> Æ x¸0 *! this::PosCnt#I<x> • void tick() static this::PosCnt#I<v> *! this::PosCnt#I<v+1> • ... • } Tokyo 2008
Invariant-Enhanced Predicate : Example inv is temporarily broken • class PosCnt extends Cnt inv this.val¸0 { • ... • void set(int x) static this::PosCnt<v> Æ x¸0 *! this::PosCnt#I<x> • void tick() static this::PosCnt#I<v> *! this::PosCnt#I<v+1> • ... • } • inv enforced at each call site • assumed at the beginning of method decl Tokyo 2008
Invariant-Enhanced Predicate : Example inv is temporarily broken • class PosCnt extends Cnt inv this.val¸0 { • ... • void set(int x) static this::PosCnt<v> Æ x¸0 *! this::PosCnt#I<x> • void tick() static this::PosCnt#I<v> *! this::PosCnt#I<v+1> • ... • } • inv enforced at the end of the method decl • assumed after each call site • inv enforced at each call site • assumed at the beginning of method decl Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
Is there a need to re-verify spB against body of mn? Re-verification of Inherited Methods class A { // fields v* ... t mn(…) static spA { ..body .. } } class B extends A { // fields w* .. t mn(…) static spB // method mn is inherited } Tokyo 2008
Statically-Inherited Method: Intuition Seen as a c-class obj (actual type t) Fields v1..n of c-class Other fields w1..m of subclass t of c this Will a call this.mn(...) modify w*? NO YES B.mn is statically inherited NOT statically inherited Tokyo 2008
Statically-Inherited Method: Definition class A { // fields v* ... t mn(…) static spA { ... this.mn2(); ... } } • A.mn is statically-inherited into B if: • it is not overridden in B class B extends A { // fields w* .. t mn(…) static spB // method mn is inherited } Tokyo 2008
Statically-Inherited Method: Definition class A { // fields v* // defines mn2 ... t mn(…) static spA { ... this.mn2(); ... } } • A.mn is statically-inherited into B if: • it is not overridden in B • for all the calls this.mn2(..) with • mnmn2, B.mn2 is statically-inherited from A.mn2 class B extends A { // fields w* // inherits mn2 .. t mn(…) static spB // method mn is inherited } Tokyo 2008
Re-verification of Inherited Methods Q: Verify spB against the body of A.mn? B.mn statically inherited or full views used? YES NO spB inherited? NO YES NO VERIFICATION spA<:spB verify spB against the body of A.mn Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
Deriving Specs (2) • Specification Specialization • strengthen dynamic spec of overriding method with the dynamic spec of overridden method • intersection type • Specification Abstraction • weaken dynamic spec of overridden method with dynamic spec of the overriding method • union type Tokyo 2008
Outline • Introduction • Our Approach • Enhanced Spec Subsumption • Static & Dynamic Specs • Key Principles • Object Representation • Class Invariants • Avoiding Re-verification • Deriving Specs • Experimental Results • Conclusion Tokyo 2008
Initial Experiment • Code Verification > Spec Subsumption Checking Tokyo 2008
Conclusion • Advocate co-existence of static and dynamic specs • Key principles: • use static spec where possible )precision • keep code re-verifications to a minimum )efficiency • Slight emphasis on static specs: • can derive dynamic specs • Must support: • behavioral subtyping, class inheritance, casting • Good to support: • class invariants, super/direct calls • Danger: • lose precision, efficiency Tokyo 2008
Thank you! • Questions? Tokyo 2008
Spec Subsumption Checking • Static_spec(Cnt.set) <: Dyn_spec(Cnt.set) • this::Cnt<t,v,p>*->this::Cnt<t,x,p> <: • this::Cnt<t,v,q>*q::ExtAll<Cnt,t> /\ x>=0 • *->this::Cnt<t,x,q>*q::ExtAll<Cnt,t> • Contravariance on precond: • this::Cnt<t,v,q>*q::ExtAll<Cnt,t> /\ x>=0 |-this::Cnt<t,v,p>* • = p::ExtAll<Cnt,t> /\ x>=0 • Covariance on postcond: • this::Cnt<t,x,p>*|- this::Cnt<t,x,q>*q::ExtAll<Cnt,t> Tokyo 2008
Spec Subsumption Checking • Dyn_spec(PosCnt.set) <: Dyn_spec(Cnt.set) • this::PosCnt<v>$ *! this::PosCnt#I<x>$ <: • this::Cnt<v>$ /\ x>=0 /\ (type(this)<:PosCnt) • *! this::Cnt<x>$ • Contravariance on precond: • this::Cnt<v>$ /\ x>=0 /\ (type(this)<:PosCnt)|-this::PosCnt<v>$ * • = x>=0 • Covariance on postcond: • this::PosCnt#I<x>$ * |- this::Cnt<t,x,q>$ Tokyo 2008