Exercises with JML and ESC/Java2
Taxpayer exercise
Part 1
Download the file Taxpayer.java.
This Java class is used in an information system at the tax office
to record information about persons.
The tax office wants to ensure that the following properties hold:
- Persons are either male or female.
- Persons can only marry to people of the opposite sex. (A bit outdated
maybe, but makes for interesting properties to specify).
- A very obvious property, but easy to overlook:
if person x is married to person y, then person should of
course also be married, and to person x.
Your assignment is to
- add invariants to the code below to express the properties mentioned
above, in JML syntax, plus any other sensible properties you can
think of;
- use ESC/Java2 to detect possible violations, which are reported as warnings
- for the Java methods that ESC/Java2 complains about fix this, by
- correcting the code, or
- adding a (sensible) precondition for the
method, using a JML requires clause, or
- sometimes, adding an invariant to record another
property can help in verification.
Hints
- Only look at the first warning that ESC/Java2 produces, and ignore the
others, until you've managed to get rid of this first one.
- The code of the methods marry and divorce
in the class Taxpayer is not correct. ESC/Java2
should detect this and complain about these methods once you've
added your invariants, and you will have to add a few lines of
code to fix these methods, and possibly also add a precondition
by means of a requires clause.
-
It is easiest to introduce one invariant at a time, then fix the errors
this brings to light, and only then move on to the next one.
-
If the tool produces some warnings, ie. if the tool thinks
that some property is not satisfied by the code, this
can have several causes:
- There is an error in the code, which results in a violation in
the Java code. For example, an assignment to some field may be missing.
- There is an error in the JML specification. For example,
maybe you have written "and" in a specifcation where you meant "or",
maybe you have written age > 18
when you meant age >= 18 in some constraint
- There may be some properties missing, which are needed by the
tool for the verification.
Note that the tool does not know any of the things that may be obvious to you
-- for example, that if some person x is married to y then y is also married to x
-- and such properties may be needed for verification.
- In general, a warning might be caused by the fact that some
property is too difficult for the theorem prover to proof:
an automated theorem prover can only ever prove properties of
a certain, limited complexity. However, for the exercises here
you should not run into this problem.
To stop the tool from complaining, you can
- correct the Java program; for the exercise here this should only involve
adding some simple assignments to the offending method;
- correct the JML specification;
- specify some additional properties, either as a JML invariant
or as JML precondition aka requires clause.
In the end, ESC/Java2 should run without any complaints on the annotated
code, meaning that it has succeeded in verifying that the code meets
the formal specification.
JML syntax
Below the syntax for Java and JML logical connectives, in order
of decreasing precedence:
logical connective |
Java/JML syntax |
not |
! |
equality |
== |
inquality |
!= |
conjunction (and) |
&& |
disjunction (or) |
|| |
implication |
==> , <== |
(in)equivalence |
<==> , <=!=> |
In addition to this, you can use standard Java syntax,
so you can say things such as
this.spouse != null ==>
(this.spouse.spouse.isFemale ==> this.isFemale)
Of course, you can write spouse instead of this.spouse,
leaving this implicit.
Note that in Java, as in most programming languages, references
such as the spouse field, can be null. Often
fields should not be null under certain circumstances,
which can be expressed by an invariant of the form
//@ invariant .... ==> spouse !=null;
Often arguments of method should not be null, which can
be expressed by a precondition of the form
//@ requires new_spouse != null;
void marry (Taxpayer new_spouse) {
Part 2
The tax system uses an income tax allowance (in Dutch: belastingvrije voet):
- Every person has an income tax allowance, over which
means they do not have to pay tax over the first part of their
income. There is a default tax allowance of 5000.
- Married persons can pool their tax allowance, as long as the sum
of their tax allowances remains the same. For example, the wife could
have a tax allowance of 10000, and the husband a tax allowance of 0.
Add invariants that express these constraints,
and if necessary fix/improve the code to ensure that they are not violated.
Part 3
The new government introduces a measure that people aged 65 and over
have a higher tax allowance, of 7000. The rules for pooling tax
allowances remain the same.
Add invariants that express these constraints,
and if necessary fix/improve the code to ensure that they are not violated.
Part 4
WARNING: things may get a bit hairy here, so don't be too depressed if you
don't manage to finish this.
The minister for Jeugd en Gezin, André Rouvoet, introduces a new tax measure
giving anyone with underage children an additional tax allowance of 1000 euro.
The taxpayer class is extended with an additional field to keep track
of the youngest child
Taxpayer youngestChild; // youngest child of this person
and the following three lines are added to the constructor
Taxpayer(boolean jongetje, Taxpayer mum, Taxpayer dad) {
...
youngestChild = null; // NEW
mum.youngestChild = this; // NEW
dad.youngestChild = this; // NEW
}
Add the three lines above to your code, add invariant(s) to express
the new government policy above, and re-run ESC/Java2 and fix the code
to ensure that it is not violated.
General hints
Some hints to keep you out of trouble with the tool:
- Don't use method invocations inside methods for this exercise.
- Don't use the Java shorthand x+=10 for x = x+10.
- Whenever possible, split invariants than contain conjunctions into
several smaller invariants. This improves feedback of the tool.
So instead of
//@ invariant A && B;
write
//@ invariant A;
//@ invariant B;>
The same goes for preconditions (aka requires clauses).
- If the tool complains of invariant violations, it produces a lot of
feedback that is hard to interpret. The crucial information to look
at is
- the line number saying which invariant is
violated, and
- the line number saying where this
invariant is violated.
NB beware that calling a method may break on some object
may break the invariant of another object; for instance,
in the taxpayer exercise, calling a method
on a TaxPayer may break the invariant of his or her spouse.