Modular Verification of Higher-Order Methods in JML Gary T. Leavens, Steve Shaner, and David A. Naumann Support from US NSF grant CCF-0429567
Mar 26, 2015
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;
//@ 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 areWeakly Specifiedpublic interface Listener {
//@ assignable this.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() { 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 /*@ spec_public @*/ int val = 0;
//@ in objectState;
//@ ensures \result == this.val; public /*@ pure @*/ int getVal() { return
this.val; }
//@ also //@ assignable objectState; //@ ensures this.val == x; public void actionPerformed(int x) { this.val = x; }}
Reasoning Problem:Want strong conclusionsLastVal 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 substitutionsLastVal 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, lnr.objectState; @ ensures this.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();//@ assume c.count == 1;//@ hence_by (* ??? *);//@ assert lv.val == 1;
Higher-Order Specifications?E.g., [Ernst-Navlakhla-Ogden82]:
/*@ forall int x; @ requires \req(this.lnr.actionPerformed)(x); @ assignable this.count, @ \asgn(this.lnr.actionPerformed); @ ensures this.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]
vs.
Better:lnr.actionPerformed()
lnr.actionPerformed()
c.count++
/*@ public model_program { @ normal_behavior @ assignable this.count; @ ensures this.count ==\
old(this.count+1); @ @ this.lnr.actionPerformed(this.count); @ } @*/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 SubstitutionLastVal 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);//@ 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
Strong Conclusions fromCopy + Contextual Knowledge
//@ assert c.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
normal_behavior … following normal_behavior …{ C }
Model program
Method implementati
on
m();
m();
Implementing Higher-Order Methods/*@ public model_program { @ normal_behavior @ assignable this.count; @ ensures this.count == \
old(this.count+1); @ this.lnr.actionPerformed(this.count); @ } @*/public void bump() { /*@ following normal_behavior @ assignable this.count; @ ensures this.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 { following normal_behavior requires P’; ensures Q’;
C
} Q
P ==> P’, Q’ ==> Q,
P’ { 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
preservationMorgan (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 madeSoundarajan 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