330 likes | 479 Vues
This paper addresses the challenges of reasoning about higher-order methods (HOMs) through a structured verification approach. The proposed solution integrates greybox specifications, the copy rule, and substitution techniques to enable sound reasoning about mandatory calls in HOMs. By allowing strong conclusions while suppressing additional details, the method enhances modular verification. The contribution includes advancements in structural matching for refinement and the integration of these concepts within the Java Modeling Language (JML), making it a vital resource for software engineers working with design patterns involving HOMs.
E N D
Modular Verification of Higher-Order Methods in JML Gary T. Leavens,Steve Shaner, and David A. NaumannSupport from US NSF grant CCF-0429567
Summary Problem • How to reason about higher-order methods? Approach • Greybox [Büchi-Weck97,99] specifications, Copy rule / substitution [Morgan88], Structurally-restricted refinement Contribution • Structural matching for refinement • Integration with JML
Background:Higher-Order Methods • A higher-order method(HOM)makes mandatory callsto weakly-specified methods
Example (part 1)Class with a `method parameter’ public class Counter { protected/*@ spec_public @*/int count = 0; protected/*@ spec_public @*/ Listener lnr; //@ assignablethis.lnr; //@ ensuresthis.lnr == lnr; public Counter(Listener lnr) { // parameter this.lnr = lnr; }
Example (part 2): HOM public void bump() { this.count = this.count + 1; this.lnr.actionPerformed(this.count); } }
Mandatory Calls areWeakly Specified public interface Listener { //@ assignablethis.objectState; void actionPerformed(int x); }
Subtype of Listener public class LastVal implements Listener { private/*@ spec_public @*/int val = 0; //@ in objectState; //@ ensures\result == this.val; public/*@ pure @*/int getVal() { returnthis.val; } //@ also //@ assignable objectState; //@ ensuresthis.val == x; public void actionPerformed(int x) { this.val = x; } }
Subtype of Listener public class LastVal implements Listener { private/*@ spec_public @*/int val = 0; //@ in objectState; //@ ensures\result == this.val; public/*@ pure @*/int getVal() { returnthis.val; } //@ also //@ assignable objectState; //@ ensuresthis.val == x; public void actionPerformed(int x) { this.val = x; } }
Reasoning Problem:Want strong conclusions LastVal lv = new LastVal(); //@ assert lv.val == 0; Counter c = new Counter(lv); //@ assert c.lnr == lv && c.count == 0; c.bump(); //@ assertlv.val == 1;
Why is strong conclusion valid?Copy rule and substitutions LastVal lv = new LastVal(); //@ assert lv.val == 0; Counter c = new Counter(lv); //@ assert c.lnr == lv && c.count == 0; c.count = c.count+1; c.lnr.actionPerformed(c.count); //@ assertlv.val == 1;
Problem Summary • Specification of higher-order methods(challenge 8 in [Leavens-Leino-Müller06]) • Abstract • Specifies mandatory calls • Suppress other details • Allows strong conclusions (challenge 9) • Copy rule and substitution • Soundness • Must make mandatory calls
Use of Higher-Order Methods • Key in design patterns [Gamma-etal95]: • Observer • Template Method • Chain of Responsibility • Clients of design patterns: • Interpreter • Command • State • Strategy • Visitor
How to Specify HOMs?Standard Pre/Post … /*@ assignablethis.count, lnr.objectState; @ ensuresthis.count ==\old(this.count+1); @*/ public void bump() { this.count = this.count + 1; this.lnr.actionPerformed(this.count); }
… Is Not Enough LastVal lv = new LastVal(); //@ assert lv.val == 0; Counter c = new Counter(lv); //@ assert c.lnr == lv && c.count == 0; c.bump(); //@ assumec.count == 1; //@ hence_by(* ??? *); //@ assertlv.val == 1;
Higher-Order Specifications? E.g., [Ernst-Navlakhla-Ogden82]: /*@ forallint x; @ requires\req(this.lnr.actionPerformed)(x); @ assignablethis.count, @\asgn(this.lnr.actionPerformed); @ ensuresthis.count ==\old(this.count+1) @ &&\ens(this.lnr.actionPerformed)(this.count); @*/ public void bump() { /* … */ }
Problems with Higher-Order Specifications • Often longer and more complex than code • Harder to learn and teach • Harder for tools to work with? • Calls are not mandatory
Greybox (Ref. Calc.) Approach[Büchi–Weck97, 99] c.count++ lnr.actionPerformed() lnr.actionPerformed() vs. Better:
Greybox Approach in JML:Model Program Specification /*@ public model_program { @ normal_behavior @ assignablethis.count; @ ensuresthis.count ==\old(this.count+1); @ @this.lnr.actionPerformed(this.count); @ } @*/ public void bump() { /* … */ }
Reasoning About HOM Calls LastVal lv = new LastVal(); //@ assert lv.val == 0; Counter c = new Counter(lv); //@ assert c.lnr == lv && c.count == 0; c.bump(); //@ assertlv.val == 1;
Approach: Model Program Copy Rule and Substitution LastVal lv = new LastVal(); //@ assert lv.val == 0; Counter c = new Counter(lv); //@ assert c.lnr == lv && c.count == 0; /*@ normal_behavior @ assignable c.count; @ ensures c.count == \old(c.count+1); @*/ c.lnr.actionPerformed(c.count); //@ assertlv.val == 1;
Rule for HOM Calls(Copy Rule + Substitution) y: T, methType(T,m) = x:S -> void, specFor(T,m) = C, C’ = C [y,z/this,x], P { C’ } Q P { y.m(z); } Q
Strong Conclusions fromCopy + Contextual Knowledge //@ assertc.lnr == lv; c.bump(); Copy/Substitute model program /*@ normal_behavior @ assignable c.count; @ ensures c.count == \old(c.count+1); @*/ c.lnr.actionPerformed(c.count); + Context lv.actionPerformed(c.count);
Soundness fromRestricting Implementations • For soundness of HOM call rule: • (copy rule) body refines model program • (use of context) restrict refinement somandatory calls must happen in specified states
Notion of Refinement with Mandatory Calls in Given States • Need to define “structure-preserving refinement” • Approach: • Restrict implementations • Pattern matching • Model program vs. • Implementation
Kinds of Patterns • Refinable (holes, wildcards) • Specification statements (normal_behavior) • Mandatory • Calls • Everything else
Pattern Matching Method implementation Model program followingnormal_behavior …{ C } normal_behavior … m(); m();
Implementing Higher-Order Methods /*@ public model_program { @ normal_behavior @ assignablethis.count; @ ensuresthis.count == \old(this.count+1); @this.lnr.actionPerformed(this.count); @ } @*/ public void bump() { /*@ following normal_behavior @ assignable this.count; @ ensuresthis.count == \old(this.count+1);@*/ { this.count = this.count+1; } this.lnr.actionPerformed(this.count); }
Refinement P ⊑ C iff • C pattern matches against P • The resulting following statements are provable
Rule for following P ==> P’, Q’ ==> Q, P’ { C } Q’ P { following normal_behaviorrequires P’; ensures Q’; C } Q
Future Work • Soundness argument • Connection to Büchi and Weck’s work(refinement of traces) • Case studies • Implementation in JML tools • Exceptions • Concurrency
Related Work Büchi and Weck (1997, 99): • Idea of greybox approach • Trace semantics • Doesn’t focus on reasoning about calls • Needs definition of structure preservation Morgan (1988): • Uses adaptation and substitution for procedures
Related Work Ernst, Navlakhla, and Ogden (1982): • Higher-order logic specifications • Harder to write and use • Mandatory calls might not be made Soundarajan and Fridella (2004): • Higher-order trace-based specifications • Can ensure mandatory calls are made • Harder to write and use
Conclusions • Greybox (refinement-style) model programs • Clear specification of HOMs • Allow strong conclusions • Soundness: • Restrictions on refinement • Use of pattern matching