330 likes | 466 Views
Modular Verification of Higher-Order Methods in JML. Gary T. Leavens, Steve Shaner, and David A. Naumann Support from US NSF grant CCF-0429567. Summary. Problem How to reason about higher-order methods? Approach
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