4.5 Objects and Method Side Effects
The RISC ProgramExplorer also supports reasoning over objects as shown in the following
implementation of the abstract datatype “stack”:
public class Stack2 /*@
invariant
(VAR stack).null = FALSE AND
0 <= VAR n AND VAR n <= (VAR stack).length;
@*/
{
private int[] stack;
private int n;
public Stack2() /*@
assignable this;
ensures VAR n = 0;
@*/
{
stack = new int[10];
n = 0;
}
public boolean isEmpty() /*@
ensures VALUE@NEXT = TRUE <=> VAR n = 0;
@*/
{
return n == 0;
}
public int top() /*@
requires VAR n > 0;
ensures VALUE@NEXT = (VAR stack).value[VAR n-1];
@*/
{
return stack[n-1];
}
public int pop() /*@
requires VAR n > 0;
assignable n;
ensures VALUE@NEXT = (VAR stack).value[OLD n-1]
AND VAR n = OLD n-1;
@*/
{
int result = top();
n = n-1;
return result;
}
public void push(int v) /*@
requires (VAR n = (VAR stack).length =>
2*(VAR stack).length+1 <= Base.MAX_INT);
assignable stack, n;
ensures VAR n = OLD n+1
AND (VAR stack).value[OLD n] = VAR v;
@*/
{
if (n == stack.length)
stack = resize();
stack[n] = v;
n = n+1;
}
private int[] resize() /*@
helper;
requires
(VAR stack).null = FALSE AND
0 <= VAR n AND VAR n <= (VAR stack).length AND
2*(VAR stack).length+1 <= Base.MAX_INT;
ensures LET result = VALUE@NEXT IN
result.null = FALSE AND
result.length > (OLD stack).length AND
(FORALL(i: INT): 0 <= i AND i < VAR n =>
result.value[i] = (OLD stack).value[i]);
@*/
{
int[] stack0 = new int[2*stack.length+1];
for (int i=0; i<n; i++) /*@
invariant
(VAR stack).null = FALSE AND
0 <= VAR n AND VAR n <= (VAR stack).length AND
0 <= VAR i AND VAR i <= VAR n AND
(VAR stack0).null = FALSE AND
(VAR stack0).length > (OLD stack).length AND
(FORALL(i: INT): 0 <= i AND i < VAR i =>
(VAR stack0).value[i] = (OLD stack).value[i]);
decreases VAR n - VAR i;
@*/
stack0[i] = stack[i];
return stack0;
}
}
The main features of this program are the following:
- The invariant in the class header specifies an object invariant, i.e. a state condition
- that is added to the postcondition of every constructor and object method,
- that is added to the precondition of every object method (not constructor)
provided the method is not tagged as helper (as is the auxiliary method resize()
above
- The assignable clauses in the method headers constrain the side effects of the method by
indicating which globally visible variables may be changed by the execution of
the method. A specification assignable this indicates that every variable in
the current object may be changed; a specification of assignable var (with
object variable var) specifies that only the variable var in the current object may be
changed (all other variables in the current object remain the same); a specification
assignable object.var specifies that in object object only the variable var may be
changed.
Similar specifications are allowed for class variables (in the current class or other classes using
the syntax assignable class.var). If no assignable clause is given, the method is
“pure”, i.e., does not change any object or class variable.
An object of type Stack2 is represented by the following record type (defined in the automatically
generated theory Stack2):
Stack2: TYPE =
[#stack:
[#value: ARRAY Base.int OF Base.int,
length: Base.nat, null: BOOLEAN#],
n: Base.int, null: BOOLEAN, new: INT#]
The record type contains one field for every object variable as well as the automatically generated
fields null (if set to TRUE, the record represents the null value) and new (set to an unknown value
after the construction of the object; the results of two constructor calls are thus never equal). The
specification variable this is a value of this type.
Based on this representation, the semantics of the object method push is as follows:
We see that the effect of the method is a modification of the value of this (of record type Stack2); if
some object variables are not changed, a corresponding equality is provided as part of the transition
relation.
For each of the object methods, as usual a couple of tasks are generated; however, most of them can
be solved automatically by the validity checker or the scatter strategy. Also the remaining
interactive proofs are very simple and require only minor human guidance.