The Java Modeling Language Based on: Gary T. Leavens, et al., JML Tutorial at OOPSLA 2009. Gary. T Leavens, et al., Preliminary Design of JML: A Behavioral Interface Specification Language for Java, ACM SIGSOFT Software Engineering Notes, 31(3):1-38, May, 2006.
Outline JML overview Reading and writing JML specifications Abstraction in specification Subtyping and specification inheritance 2 2 2
Overview of JML Java Modeling Language (JML) JML’s goals Formal Sequential Java Functional behavior of APIs JML’s goals Practical, effective for detailed designs Inter-module interfaces Classes and interfaces Data (fields) Methods Existing code Wide range of tools 3 3 3
Basic Approach of JML “Eiffel + Larch for Java” Hoare-style (contracts) Method pre- and post-conditions Invariants 4 4 4
First JML Specification public class ArrayOps { private /*@ spec_public @*/ Object[] a; //@ public invariant 0 < a.length; /*@ requires 0 < arr.length; @ ensures this.a == arr; @*/ public void init(Object[] arr) { this.a = arr; } 5 5 5
Field Specification with spec_public public class ArrayOps { private /*@ spec_public @*/ Object[] a; //@ public invariant 0 < a.length; /*@ requires 0 < arr.length; @ ensures this.a == arr; @*/ public void init(Object[] arr) { this.a = arr; } 6 6 6
Class Invariant public class ArrayOps { private /*@ spec_public @*/ Object[] a; //@ public invariant 0 < a.length; /*@ requires 0 < arr.length; @ ensures this.a == arr; @*/ public void init(Object[] arr) { this.a = arr; } 7 7 7
Method Specification public class ArrayOps { private /*@ spec_public @*/ Object[] a; //@ public invariant 0 < a.length; /*@ requires 0 < arr.length; @ ensures this.a == arr; @*/ public void init(Object[] arr) { this.a = arr; } 8 8 8
Interface Specification JML Specification Syntactic Interface Functional Behavior Java Code 9
Interface Specification /*@ requires 0 < arr.length; @ ensures this.a == arr; @*/ public void init(Object[] arr); requires 0 < arr.length; ensures this.a == arr; public void init(Object[] arr); public void init(Object[] arr) { this.a = arr; } 10
Comparison with Other Formal Specification Languages Like … but for Java VDM, but OO features Eiffel, but Features for formal verification Spec#, but Different invariant methodology More features for formal verification Unlike OCL and Z More Java-like syntax Tailored to Java semantics 11
Many Tools, One Language 12
How JML Tools Complement Each Other Different strengths: Runtime checking — real errors Static checking — better coverage Verification — guarantees Usual ordering: Runtime checker (jmlc and jmlunit) Extended Static Checking (ESC/Java2) Verification tool (e.g., KeY, JACK, Jive) 13
Outline JML overview Reading and writing JML specifications Abstraction in specification Subtyping and specification inheritance 14 14 14
JML Annotations Not Java annotations (starting with @) JML annotation comments Line starting with //@ Between /*@ and @*/, ignoring @’s starting lines 15
Most Important JML Keywords Top-level in classes and interfaces: invariant spec_public nullable For methods and constructors: requires ensures assignable pure 16
Example: BoundedStack Specify bounded stacks of objects Steps Data and invariant Constructor Methods like push, pop, and top 17
BoundedStack’s Data and Invariant public class BoundedStack { private /*@ spec_public nullable @*/ Object[] elems; private /*@ spec_public @*/ int size = 0; //@ public invariant 0 <= size; /*@ public invariant elems != null @ && (\forall int i; @ size <= i && i < elems.length; @ elems[i] == null); @*/ Nullable for elems itself or its elements? size <= elems.length? 18
BoundedStack’s Constructor /*@ requires 0 < n; @ assignable elems; @ ensures elems.length == n; @*/ public BoundedStack(int n) { elems = new Object[n]; } 19
BoundedStack’s push Method /*@ requires size < elems.length - 1; @ assignable elems[size], size; @ ensures size == \old(size + 1); @ ensures elems[size - 1] == x; @ ensures_redundantly @ (\forall int i; 0 <= i && i < size - 1; elems[i] == \old(elems[i])); @*/ public void push(Object x) { elems[size] = x; size++; } 20
BoundedStack’s pop Method /*@ requires 0 < size; @ assignable size, elems[size - 1]; @ ensures size == \old(size - 1); @ ensures_redundantly @ elems[size] == null && @ (\forall int i; 0 <= i && i < size - 1; elems[i] == \old(elems[i])); @*/ public void pop() { size--; elems[size] = null; } 21
BoundedStack’s top Method /*@ requires 0 < size; @ assignable \nothing; @ ensures \result == elems[size - 1]; @*/ public /*@ pure @*/ Object top() { return elems[size – 1]; } 22
JML Keywords Used spec_public nullable invariant must be: Public visibility Only for specification purposes nullable field (and array elements) may be null Default is non_null invariant must be: True at end of constructor Preserved by each method (except for helper methods) 23
JML Keywords Used requires clause: ensures clause: Precondition Obligation on callers, after parameter passing Assumed by implementor ensures clause: Postcondition Obligation on implementor, at return Assumed by caller 24
Semantics of requires and ensures 25
Semantics of requires and ensures 26
Semantics of requires and ensures 27
JML Keywords Used (Cont.) assignable Frame axiom Locations (fields) in pre-state New object fields not covered Mostly checked statically Synonyms: modifies, modifiable pure No side effects Implies assignable \nothing Allows method’s use in specifications 28
Assignable Is a Shorthand assignable gender; ensures gender.equals(g); means ensures \only_assigned(gender) && gender.equals(g); 29
JML Keywords Used (Cont.) Redundant clauses (ensures_redundantly) Alerts reader States something to prove Must be implied by: ensures clauses, assignable clause, invariant, and JML semantics. Also requires_redundantly, etc. 30
Multiple Clauses Semantics: is equivalent to: requires P; requires Q; is equivalent to: requires P && Q; Similarly for ensures, invariant. Note: checkers give more specific messages with multiple clauses. 31
Defaults for Omitted Clauses invariant true; requires true; assignable \everything; ensures true; 32
Expression Keywords \result = method’s return value. \old(E) = pre-state value of E. (\forall T x; P; Q) = {Q | x T P} (\exists T x; P; Q) = {Q | x T P} (\min T x; P; E) = min{E | x T P} (\sum T x; P; E) = {E | x T P} (\num_of T x; P; Q) = {1 | x T P Q} … 33
Steps for Specifying a Type for Public Clients Specify data (spec_public fields) Specify a public invariant Specify each public method using: requires assignable (or pure) ensures 34
Exercise Specify the following stack methods int capacity(): capacity of this stack? int size(): no. of elements? boolean isEmpty(): has no element? int search(Object o): 1-based position of given object in this stack? private /*@ spec_public nullable @*/ Object[] elems; private /*@ spec_public @*/ int size = 0; 35
Solution //@ ensures \result == elems.length; public /*@ pure *@/ int capacity() { …} //@ ensures \result == size; public /*@ pure *@/ int size()() { …} //@ ensures \result == (size == 0); public /*@ pure *@/ boolean isEmpty() { …} private /*@ spec_public nullable @*/ Object[] elems; private /*@ spec_public @*/ int size = 0; 36
Solution /** Return a 1-based position of given object in this stack. */ //*@ ensures (\exists int i; i >= 0 && i < size; elems[i] == o) ? \result > 0 && \result <= size && elems[\result - 1] == o : \result == 0 @*// public /*@ pure *@/ int search(Object o) { …} Q: the first (or last) from the top? private /*@ spec_public nullable @*/ Object[] elems; private /*@ spec_public @*/ int size = 0; 37
Exercise: BagOfInt Specify the following. public class BagOfInt { private int[] a; private int n; // number of elements contained in a /** Initialize to contain input’s elements. */ public BagOfInt(int[] input); /** Return the multiplicity of i. */ public int occurrences(int i); // Hint: use (\num_of T x, P; Q) /** Return and delete the minimum element. */ public int extractMin(); } 38
Solution: Data and Constructor public class BagOfInt { /** Elements. */ private /*@ spec_public non_null @*/ int[] a; /** Number of active elements in a. */ private /*@ spec_public @*/ int n; //@ public invariant 0 <= n && n <= a.length; /** Initialize to contain input’s elements. */ /*@ assignable a, n; @ ensures n == input.length; @ ensures (\forall int i; 0 <= i && i < n; a[i] == input[i]); @*/ public BagOfInt(/*@ non_null @*/ int[] input); non_null for int array? 39
Solution: Methods /** Return the multiplicity of i. */ /*@ ensures \result == (\num_of int j; 0 <= j && j < n; a[j] == i); @*/ public /*@ pure @*/ int occurrences(int i); /** Return and delete the minimum element. */ /*@ requires n > 0; @ assignable n, a, a[*]; @ ensures n == \old(n - 1); @ ensures \result == \old((\min int j; 0 <= j && j < n; a[j])); @ ensures (\forall int j; 0 <= j && j < \old(n); @ (\old(a[j]) != \result ==> @ occurrences(\old(a[j])) == \old(occurrences(a[j]))) && @ (\old(a[j]) == \result ==> @ occurrences(\old(a[j])) == \old(occurrences(a[j]) - 1))); @*/ public int extractMin(); 40
Thought: Using Java 8 Streams Problem JML quantifiers (\forall and \exists) for arrays and collections Low level assertions (long and less readable), e.g., (\exists int i; i >= 0 && i < size; elems[i] == obj) High-level abstractions like OCL collection iterators? Use Java 8 streams and lambda expressions E.g., Stream.of(elems).matchAny(e -> e == obj) Concise and more readable assertions Refer to Java 8 Stream API Q: Effectiveness and comparison with OCL? 41
Java 8 Stream API boolean allMatch(predicate) boolean anyMatch(predicate) boolean nonMatch(predicate) Stream<T> filter(predicate) Stream<R> map(mapper) Stream<R> flatMap(mapper) Optional<T> reduce(identity, accumulator) Optional<T> reduce(identity, accumulator, combiner) Optional<T> findAny() Optional<T> findFirst() Optional<T> max(comparator) Optional<T> min(comparator) … 42
Example: Ship public class Ship { Place 0..1 0..* public class Ship { private /*@ spec_public @*/ final List<Place> places; // Q: Meaning? why? sufficient? /*@ public invariant (\forall int i; 0 <= i && i < places.size(); @ places.get(i).getShip() == this); @*/ //@ public invariant places.stream().matchAll(p -> p.getShip() == this); // Q: A row of consecutive places, either horizontally or vertically? 43
Question for You What’s wrong with this and how to fix it? Hint: Undefinedness public class ScreenPoint { private /*@ spec_public @*/ int x, y; //@ public invariant 0 <= x && 0 <= y; //@ requires 0 <= cs[0] && 0 <= cs[1]; //@ assignable x, y; //@ ensures x == cs[0] && y == cs[1]; public ScreenPoint(int[] cs) { x = cs[0]; y = cs[1]; } 44
Solution A: Undefined expression public class ScreenPoint { private /*@ spec_public @*/ int x, y; //@ public invariant 0 <= x && 0 <= y; //@ requires 2 <= cs.length; //@ requires 0 <= cs[0] && 0 <= cs[1]; //@ assignable x, y; //@ ensures x == cs[0] && y == cs[1]; public ScreenPoint(int[] cs) { x = cs[0]; y = cs[1]; } 45
Writing Protective Specifications Clauses evaluated left to right Short-circuit operators can prevent evaluation G && P, G || P G ==> P, G <== P (implication and backward implication) Use multiple clauses (equivalent to &&) 46
Multiple Specification Cases For different preconditions May overlap Used to specify exceptions Used with specification inheritance 47
Example private /*@ spec_public @*/ int age; /*@ requires 0 <= a && a <= 150; @ assignable age; @ ensures age == a; @ also @ requires a < 0 || a > 150; @ assignable \nothing; @ ensures age == \old(age); @*/ public void setAge(int a) { if (0 <= a && a <= 150) age = a; } 48
Semantics of Multiple Cases 49
Meaning of “also” Rewrite to conjoin (frame must be the same). private /*@ spec_public @*/ int age; /*@ requires 0 <= a && a <= 150; @ assignable age; @ ensures age == a; @ also @ requires a < 0 || a > 150; @ assignable \nothing; @ ensures age == \old(age) && only_assigned(\nothing); @*/ public void setAge(int a) { if (0 <= a && a <= 150) age = a; } 50
Meaning of “also” requires (0 <= a && a <= 150) || (a < 0 || a > 150); assignable age; ensures \old(0 <= a && a <= 150) ==> (age == a); ensures \old(a < 0 || a > 150) ==> (age == \old(age) && \only_assigned(\nothing)); 51
Views of Multiple Cases Client can verify by: Picking one spec case Assert precondition Assume frame and postcondition Picking several cases Compute their join Assert joined precondition Assume frame and joined postcondition Implementor can: Verify each case, or Verify their join 52
Specifying Exceptions Use features such as exceptional_behavior spec cases signals_only clause signals clause
Example public class Actor { private /*@ spec_public @*/ int age; private /*@ spec_public @*/ int fate; //@ public invariant 0 <= age && age <= fate; 54
Example /*@ public normal_behavior @ requires age < fate - 1; @ assignable age; @ ensures age == \old(age+1); @ also @ public exceptional_behavior @ requires age == fate - 1; @ signals_only DeathException; @ signals (DeathException e) age == fate; @*/ public void older() throws DeathException 55
signals (Exception) false; Example /*@ public normal_behavior @ requires age < fate - 1; @ assignable age; @ ensures age == \old(age+1); @ also @ public exceptional_behavior @ requires age == fate - 1; @ signals_only DeathException; @ signals (DeathException e) age == fate; @*/ public void older() throws DeathException semantics: signals (Exception) false; semantics: ensures false; Lightweight vs. heavyweight specs: \not_specified as default
Exercise: Using Multiple Cases Specify the 3x + 1 or “hailstone” function, h, such that: h(n) = (3 *n + 1)/2, if n > 0 and is odd n / 2, if n > 0 and is even and h is undefined on negative numbers. (Collatz conjecture: you shall always eventually reach 1 by repeating the process.) 57
Solution /*@ requires 0 < n; @ requires n % 2 != 0; @ ensures \result == (3*n+1)/2; @ also @ requires 0 < n; @ requires n % 2 == 0; @ ensures \result == n/2; @*/ public static /*@ pure @*/ int h(int n) 58
Solution Using Nesting /*@ requires 0 < n; @ {| requires n % 2 != 0; @ ensures \result == (3*n+1)/2; @ also @ requires n % 2 == 0; @ ensures \result == n/2; @ |} @*/ public static /*@ pure @*/ int h(int n) 59
Exercise: Ship Specify the addPlace() method. public class Ship { 0..1 0..* Specify the addPlace() method. public class Ship { private /*@ spec_public @*/ final List<Place> places; /** Append the given place to the list of places on which this ship is placed. */ public void addPlace(Place place) { if (!places.contains(place)) { places.add(place); } if (place.ship() != this) { place.placeShip(this); } } public class Place { private /*@ spec_public nullable @*/ Ship ship; 60
Outline JML overview Reading and writing JML specifications Abstraction in specification Subtyping and specification inheritance 61 61 61
Motivating Example Specify the remove method given below. Q: Meaning of the second invariant? public class SetOfObject { private /*@ spec_public non_null @*/ Object[] a; /** Number of active elements in a. */ private /*@ spec_public @*/ int n; //@ public invariant 0 <= n && n <= a.length; /*@ public invariant @ (\forall int i; 0 <= i && i < n; @ (\forall int j; 0 <= j && j < n; i != j ==> a[i] != a[j]); @*/ /** Remove the argument from this set. */ public void remove(Object o); non_null for int array? 62
Solution Q: How to know the new set has no other elements? /*@ requires (\exists int i; 0 <= i && i < n; a[i] == o); @ assignable n, a, a[*]; @ ensures n == \old(n - 1); @ ensures (\forall int i; 0 <= i && i < \old(n); @ (\old(a[i]) == o ==> !(\exists int j; 0 <= j && j < n; a[j] == o) && @ (\old(a[i]) != o ==> (\exists int j; 0 <= j && j < n; a[j] == o); @*/ public void remove(Object o); Long and hard to read and understand! Implementation details exposed! 63
Better but still low level; manipulation of concrete representation Better Solution /*@ requires has(o); @ assignable n, a, a[*]; @ ensures n == \old(n - 1); @ ensures (\forall int i; 0 <= i && i < \old(n); @ (\old(a[i]) == o ==> !has(o)) && @ (\old(a[i]) != o ==> has(o)) @*/ public void remove(Object o); //@ ensures \result == (\exists int i; 0 <= i && i < n; a[i] == o); public /@ pure @*/ void has(Object o); Better but still low level; manipulation of concrete representation 64
Need to rewrite all assertions! Another Problem Q: What happens if a representation is changed? public class SetOfObject { private /*@ spec_public non_null @*/ Object[] a; ArrayList<Object> list; /** Number of active elements in a. */ private /*@ spec_public @*/ int n; //@ public invariant 0 <= n && n <= a.length; /*@ public invariant @ (\forall int i; 0 <= i && i < n; @ (\forall int j; 0 <= j && j < n; i != j ==> a[i] != a[j]); @*/ /** Remove the argument from this set. */ public void remove(Object o); Need to rewrite all assertions! non_null for int array? 65
Writing Abstract Specification: Key Idea //@ public T m; abstract state (representation) //@ requires Q(m); public void remove(Object o); <<use>> abstraction function m = f(a, n) Evaluation: Q(f(a,n)) non_null for int array? private Object[] a; private int n; <<use>> //@ requires P(a, n); public void remove(Object o); concrete state (representation)
Solution Using Abstraction Abstract specification using model fields (abstract values) Q: Advantages? public class SetOfObject { //@ public model JMLObjectSet theSet; private Object[] a; //@ in theSet; private int n; //@ in theSet; //@ private represents theSet <- JMLObjectSet.convertFrom(a, n); /*@ requires theSet.has(o); // o theSet @ assignable theSet; @ ensures theSet.equals(\old(theSet.remove(o))); // theSet’ = theSet – {o} @*/ public void remove(Object o); Model field Data group Abstraction function non_null for int array?
Why Use Abstraction in Specification? Ease of maintenance by information hiding Readability: Avoid quantifiers Repeated expressions Specify when no fields available Java “interfaces”
Features Supporting Abstraction model fields and represents clauses pure model methods pure methods protected invariants, spec cases, etc. private invariants, spec cases, etc. model classes (JMLObjectSet, etc.)
Model Fields for Data Abstraction Just for specification Abstraction of Java fields Value from represents
Model Methods Q: What’s wrong with the following specification and how to fix it? Ship Place 0..1 0..* public class Ship { private /*@ spec_public @*/ final List<Place> places; //@ ensures \result == places; public Iterable places() { return places; } 71
Model Methods (Cont.) public class Ship { private /*@ spec_public @*/ final List<Place> places; //@ ensures \result == places; //@ ensures toList(\result).equals(places); public Iterable<Place> places() { … } /*@ public static pure model <T> List<T> toList(Iterable<T> iter) { @ ArrayList<List> result = new ArrayList<>(); @ for(T t: iter) { @ result.add(t); @ } @ return result; @ } @*/ } 72
Exercise: Stack Revisited Rewrite the specification of BoundedStack using model fields. public class BoundedStack { private Object[] elems; private int size; // num of elems contained public BoundedStack(int capacity) { … } public void push(Object o) { … } public void pop() { … } public Object top() { … } public int search(Object o) { … } … } non_null for int array?
Model Fields In Interfaces public interface Gendered { //@ public model instance String gender; //@ ensures \result == gender.equals("female"); public /*@ pure @*/ boolean isFemale(); } Note a new JML modifier instance because it defaults to static in interfaces.
Represents Clauses Use an in clause to specify a data group. public class Animal implements Gendered { protected boolean gen; //@ in gender; //@ protected represents gender <- (gen ? "female" : "male"); public /*@ pure @*/ boolean isFemale() { return gen; } Use an in clause to specify a data group. Protected represents clause (abstraction function). Specification inheritance.
Correctness with Model Fields
Example of Using Model Fields Q: Is Animal’s constructor (below) correct? protected boolean gen; //@ in gender; //@ protected represents gender <- (gen ? "female" : "male"); /*@ requires g.equals("female") || g.equals("male"); @ assignable gender; @ ensures gender.equals(g); @*/ public Animal(final String g) { gen = g.equals("female"); }
Example of Using Model Fields Q: Is Animal’s constructor (below) correct? protected boolean gen; //@ in gender; //@ protected represents gender <- (gen ? "female" : "male"); /*@ requires g.equals("female") || g.equals("male"); @ assignable gender; @ ensures gender.equals(g); @*/ public Animal(final String g) { gen = g.equals("female"); } Yes!
Semantics of spec_public protected /*@ spec_public @*/ int age = 0; shorthand for: //@ public model int age; protected int _age = 0; //@ in age; //@ protected represents age <- _age; and rewriting Java code to use _age.
Data Groups for Assignable Clauses Each field is a data group Membership by in clauses Model field’s group contains fields used in its represents
Data Groups and Assignable Picture
The Semantics of Assignable assignable x, y; means: method only assigns to (concrete) members of DG(x) DG(y). Q: What does “assignable gender;” mean?
In Clauses for Declarations private T x; //@ in g; Immediately follows declaration Same visibility as declaration JML ensures that: If f DG(g), then g visible where f is. If f and g visible, can tell if f DG(g).
Type-Level Specification Features Model fields, in, represents invariant initially constraint
Initially Clauses Hold in constructor post-states Basis for datatype induction import java.util.*; public class Patient extends Person { //@ public invariant 0 <= age && age <= 150; protected /*@ spec_public @*/ List log; //@ public initially log.size() == 0;
History Constraints Relate pre-states and post-states Justifies inductive step in datatype induction import java.util.*; public class Patient extends Person { protected /*@ spec_public @*/ List log; /*@ public constraint log.size() >= \old(log.size()); @ public constraint (\forall int i; 0 <= i && i < \old(log.size()); @ log.get(i).equals(\old(log.get(i)))); @*/
Exercise Specify a bounded counter class by writing invariant, initially, and history constraints. Start with an initial value 0. Return to 0 when it reaches the maximum value, 100. public class BoundedCounter { private int value; /** Increment this counter by one. */ public void increment() { …}
Helper Methods and Constructors A helper method or constructor is: private Exempt from invariants and history constraints Cannot assume them Need not establish them
Outline JML overview Reading and writing JML specifications Abstraction in specification Subtyping and specification inheritance 89 89 89
Problems Duplication of specifications in subtypes Modular verification when use: Subtyping, and Dynamic dispatch
Specification Inheritance Approach Instance fields Type specifications Invariants Initially clauses History constraints Instance methods Method specification cases
Multiple Inheritance Example
Age, NormalSetAge, and ExceptionalSetAge public interface Age { //@ model instance int age; } public interface NormalSetAge implements Age { /*@ requires 0 <= a && a <= 150; @ assignable age; @ ensures age == a; @*/ public void setAge(int a); public interface ExceptionalSetAge implements Age { /*@ requires a < 0 || a > 150; @ assignable \nothing; @ ensures age == \old(age); @*/
What About Animal’s setAge Method? It’s both. Should obey both specifications.
Single Inheritance Q: What is the specification of Animal’s isFemale method? public interface Gendered { //@ ensures \result == gender.equals("female"); public /*@ pure @*/ boolean isFemale(); } public class Animal implements Gendered { public /*@ pure @*/ boolean isFemale() { return gen;
Adding to Specification in Subtype Use the “also” keyword. import java.util.*; public class Patient extends Person { protected /*@ spec_public @*/ boolean ageDiscount = false; //@ in age; /*@ also @ requires (0 <= a && a <= 150) || (a < 0 || a > 150); @ ensures 65 <= age ==> ageDiscount; @*/ public void setAge(final int a) { super.setAge(a); if (65 <= age) { ageDiscount = true; } }
Method Specification Inheritance Q: What is the extended specification of Patient’s setAge method? A: The join of the 3 spec cases shown previously Q: What is the extended specification of Patient’s setAge method? A: The join of the 3 spec cases shown previously /*@ requires 0 <= a && a <= 150; @ assignable age; @ ensures age == a; @ also @ requires a < 0 || a < 10; @ ensures age == \old(age); @*/ /*@ also @ requires (0 <= a && a <= 150) || (a < 0 || a > 150); @ ensures 65 <= age ==> ageDiscount; @*/ /*@ requires 0 <= a && a <= 150; @ assignable age; @ ensures age == a; @ also @ requires a < 0 || a < 10; @ ensures age == \old(age); @*/ /*@ also @ requires (0 <= a && a <= 150) || (a < 0 || a > 150); @ ensures 65 <= age ==> ageDiscount; @*/
Avoiding Duplication of Preconditions Use \same in the requires clause /*@ requires 0 <= a && a <= 150; @ assignable age; @ ensures age == a; @ also @ requires a < 0 || a < 10; @ ensures age == \old(age); @*/ /*@ also @ requires \same; @ ensures 65 <= age ==> ageDiscount; @*/