Modular Verification of Higher-Order Methods in JML Gary T. Leavens, Steve Shaner, and David A. Naumann Support from US NSF grant CCF
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 JMLJML
Setting JML Pre- and postcondition specifications Several verification tools: ESC/Java2 Jack, LOOP, … Java Libraries: Swing I/O frameworks Jakarta Commons
Background: Higher-Order Methods A higher-order method (HOM) makes mandatory calls to weakly-specified methods
Example (part 1) Class with a `method parameter public class Counter { protected int count = 0; protected Listener lnr; assignable this.lnr; ensures this.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 are Weakly Specified public interface Listener { assignable this.objectState; void actionPerformed(int x); }
Subtype of Listener public class LastVal implements Listener { private int val = 0; in objectState; ensures \result == this.val; public int getVal() { return this.val; } also assignable objectState; ensures this.val == x; public void actionPerformed(int x) { this.val = x; } }
Subtype of Listener public class LastVal implements Listener { private int val = 0; in objectState; ensures \result == this.val; public int getVal() { return this.val; } also assignable objectState; ensures this.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(); assert lv.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); assert lv.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 … assignable this.count, ensures this.count 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(); assume c.count == 1; hence_by (* ??? *); assert lv.val == 1;
Higher-Order Specifications? E.g., [Ernst-Navlakhla-Ogden82]: forall int requires assignable ensures 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] vs. Better: lnr.actionPerformed() c.count++
public model_program assignable ensures public void bump() { /* … */ } Greybox Approach in JML: Model Program Specification
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(); assert lv.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; assignable ensures c.count == c.lnr.actionPerformed(c.count); assert lv.val == 1;
Rule for HOM Calls (Copy Rule + Substitution) P { y.m(z); } Q y: T, methType(T,m) = x:S -> void, specFor(T,m) = C, C = C [y,z/this,x], P { C } Q
Rule for Higher-Order Calls (Copy Rule + Substitution) P A { y.m(z); } Q y: T, methType(T,m) = x:S -> void, SpecFor(T,m) = C, C = C [y,z/this,x], P A { C } Q
Strong Conclusions from Copy + Contextual Knowledge assert c.lnr == lv; c.bump(); Copy/Substitute model program assignable ensures c.count == c.lnr.actionPerformed(c.count); Context lv.actionPerformed(c.count); +
Soundness from Restricting Implementations For soundness of HOM call rule: (copy rule) body refines model program (use of context) restrict refinement so mandatory 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 normal_behavior … following normal_behavior … { C } Model program Method implementation m();
Implementing Higher-Order Methods public model_program assignable ensures this.count == public void bump() { following assignable ensures this.count == { 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 { following normal_behavior requires P; ensures Q; C } Q P ==> P, Q ==> Q, P { C } Q
Rule for following P A { following normal_behavior requires P; assignable A; ensures Q; C } Q P ==> P, locs(A) locs(A), Q ==> Q, P { C } Q
Future Work Soundness argument Connection to Büchi and Wecks 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 Doesnt 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