Eclipse Project Instructions |
Welcome! We’d like to thank you again for bidding on this project.
You will be given a small program, and your job is to describe the
conditions that are necessary to prevent the program from crashing.
For example, a Java program crashes if it dereferences null (a NullPointerException
)
or uses an illegal array index (an ArrayIndexOutOfBoundsException
).
If a method in the program dereferences its argument, then you would
write a specification that says that the
method’s argument must not be null
.
To perform this task, you will write Java-like specifications in the source code, and an Eclipse plugin will tell you whether the specifications you wrote are correct and sufficient.
The job has the following steps:
Note: we wish to learn how long the main task takes. We expect it will take between 1-3 hours. Therefore, we ask that you do not take any breaks when performing the main task. If you have to take a break, please record the time and length of the break. Additionally, we will be monitoring your progress remotely.
If you have any questions about the project, please contact us via vWorker.
A program specification describes the behavior of a program. A verification tool can check the specification and verify that the program behaves as expected. However, for this to work, the specification must be precise and sufficient.
A method specification consists of three parts:
You will write specifications using the Java Modeling Language (JML). We now give some examples.
Consider the following method:
/** Adds five to its argument. */ public Integer addFive(Integer x) throws IllegalArgumentException{ if (x < 0){ throw new IllegalArgumentException("x cannot be negative"); } return x + 5; }
x
is not null”.
(Otherwise, it fails by throwing a NullPointerException
.)
Thus, the precondition is:
@requires x != null
When reasoning about preconditions, it is often helpful to reason "backward" from the place where an error may occur; this helps you determine what must be true in order for the exception not to occur.
null
”, and that
“x
is not negative”, because the method returned without an exception.More
interestingly, we also know that “the return value of the method is equal to
the value of x
plus 5”.
Thus, the postconditions are:
@ensures \result != null @ensures \result = x + 5 @ensures x >= 0
A postcondition describes what must be true when the method exits, including facts about class member variables and return values.
IllegalArgumentException
is
thrown, we know that “x is negative”. Therefore the exceptional
postcondition is:@exsures (IllegalArgumentException) x < 0
An exceptional postcondition states what is true at the moment the exception is thrown. This may not be the same as the conditions when the method was called that caused the exception to be thrown later. There is often some overlap between the two, but they are not the same.
It is often possible to determine some of the specifications from the method’s documentation. When the documentation is not comprehensive, you have to determine them yourself.
Class invariants are properties of a class’s member variables that must be true after the constructor is called; additionally, they are true whenever a public method is called and exits (even with an exception).
You’ll see an example of class invariants in the warm-up project (See Section 3).
Here is an example of a method specification written in JML, the Java
Modeling Language. JML is Java-like, with some additional expressions.
The method preconditions
and postconditions are written
between the method body and the method signature using the
@requires
, @ensures
and @exsures
annotations.
A method can have multiple clauses of each type.
class Queue{ private int currentSize; private int theArray[]; ... /** * Insert a new item into the queue. * @param x the item to insert. * @exception Overflow if queue is full. **/ public void enqueue( Object x ) throws RuntimeException /*@ requires x != null; */ /*@ ensures currentSize == \old(currentSize) + 1; */ /*@ exsures (RuntimeException) \old(currentSize) == theArray.length; */ { if( isFull( ) ) throw new RuntimeException( "Overflow" ); if ( ++back == theArray.length ) back = 0; theArray[ back ] = x; currentSize++; } ... }
In an exceptional postcondition (@exsures
),
the exception type (RuntimeException
above) is written in parentheses
between @exsures
and the expression.
Notice that contracts begin with a @
directly following the
comment opening. Also notice that each contract ends with a semicolon.
Here is table that describes the JML expressions you will need to know for your task:
Expression | Type | Meaning |
a && b | boolean | just like in Java |
a || b | boolean | just like in Java |
!a | boolean | just like in Java |
a ==> b | boolean | if a is true, then b is true; equivalent to (!a) || b |
\forall decl; pred; expr | boolean | used to express that expr holds over the range of values for which pred holds (Section 2.5.2) |
\result | same as the method’s return type | the return value of the method (can only be used in the method’s postcondition) |
\old(expr) | same as the type of expr | refers to the value of expr at the entry of a method (can only be used in a postcondition) |
Implication, written as ==>
, is related to conditional
expressions (i.e., if
and else
). Saying that "a implies
b", written in JML as a ==> b
, means that if a is true then b
must also be true. However, if a is false, b can be anything. In other
words, false implies anything. For example, the statement
"France is in Asia implies apples are oranges" is a true
statement because France is not in Asia.
The \forall statement is used to express that a fact
holds over a range of values. For example, the following expression
states that all of the values in the array array
are
non-null:
(\forall int i; 0 <= i && i < array.length; array[i] != null);
Notice that the middle expression 0 <= i && i < array.length
is
used to specify which values of i
are important.
You can declare multiple variables in a \forall
statement, as in the
following example:
(\forall int i,j; 0 <= i && i < j && j < array.length; array[i] != array[j]);
In the warmup and main project, you may come across the annotations
@modifies
, @set
, @spec_public
, and @pure
. You may also come across the
expression \typeof
. We’ve added these annotations for you in
the correct places, do not delete comments
that contain them. We’ve added additional comments to these lines
to remind you. For example, in the warmup you will see the following comment:
/** * Fill an array with one of two objects, based on whether the index * is in this set. **/ public void fillDigits(Object[] digits, Object zero, Object one) /*@ requires \typeof(digits) == \type(Object[]); */ //DO NOT DELETE THIS LINE { ... }
Before starting the main project, you’ll
familiarize
yourself with the interface by verifying the warmup project
FixedSizeSet
. FixedSizeSet
represents a mutable set
of integers between 0
and 7
.
The walkthrough is meant to introduce and reinforce
the main concepts needed to complete the main task. You
should spend about 1 hour on the warmup.
Don’t hesitate to ask us questions about the tool, or the process of verification, during the warmup.
The Eclipse plugin checks the program properties that the user writes as preconditions, postconditions, and class invariants. The plugin reads the source code (and annotations) and warns about annotations that might not be universally true. The plugin also warns about potential runtime exceptions.
The Eclipse plugin is a modular checker: it reasons about code by
examining the body of one method at a time. Therefore, the plugin both
checks and depends on properties.
For instance, suppose method
A
calls method B
. If you have not written properties
describing the return value of B
, VeriWeb may not be able
to prove a property about method A
.
VERIFY
)
and the host IP that was given to you. Click OK
Browse the library class FixedSizeSet.java
and the client class
FixedSizeSetCheck.java
. You can see that the methods in
FixedSizeSetCheck
throw runtime exceptions if the library class
does not behave as expected.
Let’s run the plugin on the project to see what warnings are reported:
The plugin’s output is shown in the console; additionally, errors are marked and underlined in the source files (just like compilation errors and warnings). You can view the warning message at a location by hovering your mouse over the squiggly red line or over the corresponding red and white "E" on the left side of the editor.
When you first run the checker on the FixedSizeSet
project, the
end of the report will be:
------------------------------------------------------------------------ [0.327 s 53322112 bytes] failed [0.468 s 53365744 bytes total] 6 cautions 8 warnings Escjava checker ended, status code 0
Each of the warnings corresponds to a place where an unexpected exception might be thrown, or a condition might not be met. The console output includes warning information for each method. If a method passes, a message like the following is shown:
FixedSizeSetCheck: checkFillDigits() ... [0.011 s 42779336 bytes] passed
, otherwise the error and associated information is displayed. For
example, look at the console output for the checkConstructor
method in FixedSizeSetCheck
:
FixedSizeSetCheck: checkConstructor() ... ------------------------------------------------------------------------ .../FixedSizeSet/src/FixedSizeSetCheck.java:18: Warning: Possible unexpected exception (Exception) } ^ MARKER ...\FixedSizeSet\src\FixedSizeSetCheck.java 18 Warning: Possible unexpected exception (Exception) Suggestion [18,4]: none Execution trace information: Reached top of loop after 0 iterations in ".../FixedSizeSet/src/FixedSizeSetCheck.java", line 13, col 8. Executed then branch in ".../FixedSizeSet/src/FixedSizeSetCheck.java", line 14, col 33. Executed throw in ".../FixedSizeSet/src/FixedSizeSetCheck.java", line 15, col 16. ------------------------------------------------------------------------ [1.716 s 51704352 bytes] failed
The plugin is reporting that a method can throw an exception that is not declared in throws clause (it is unexpected). The “Execution trace” describes the source code location where the exception is thrown. Note that unexpected exceptions are marked on the line with the method’s closing brace.
Below is the source code for the checkConstructor
method:
You can see that the plugin cannot determine that a newly created set
is contains no number. Eventually, you should add a
postcondition to the constructor FixedSizeSet()
that describes
this property. This will enable the plugin to reason that the
exception cannot be thrown.
NOTE: There is a known bug in the plugin that causes the following caution to be generated for each file when run on Windows:
Caution: Using given file as the .java file, even though it is not the file for XXX on the classpath
You can (and should) safely ignore this caution.
Let’s start our task by looking at the first warning in the
FixedSizeSet
file. There’s a warning in the add
method
that the program may attempt to dereference bits
when it is
null
:
FixedSizeSet: add(int) ... ------------------------------------------------------------------------ .../FixedSizeSet/src/FixedSizeSet.java:23: Warning: Possible null dereference (Null) bits[n] = true; ^ MARKER ../FixedSizeSet\src\FixedSizeSet.java 23 Warning: Possible null dereference (Null) Suggestion [23,12]: perhaps declare instance field 'bits' at 7,41 in .../FixedSizeSet/src/FixedSizeSet.java with 'non_null' ------------------------------------------------------------------------ [0.015 s 75303928 bytes] failed
Here, the plugin also provides a suggestion:
perhaps declare instance field 'bits' at 7,41 in .../FixedSizeSet/src/FixedSizeSet.java with 'non_null'
.
The suggestion is to add a class invariant that
bits != null
. Based on the constructor and the other methods,
you can see that bits
should never be null
. Therefore,
the property is a good candidate for a class invariant (see
Section 2.3):
Add the invariant /*@ invariant bits != null; */
just above the
class’s field declarations.
public class FixedSizeSet{ /*@ invariant bits != null; */ /*@ invariant bits.owner == this; */ //DO NOT DELETE THIS LINE /*@ spec_public */ private boolean[] bits; ... }
After you’ve added the invariant, save the file, and rerun the
checker. Notice that the add
method still has a warning, but
the warning is different. We’ll address this warning in the next
section.
To clear the old plugin output, right click on the console output screen and click “Clear” in the context menu.
Remove the @
from the invariant line you just added, and rerun
the plugin. The warning from before will reappear, since the plugin
now considers the line to be a regular comment rather than a
specification. Re-add the @
and save the file.
Look at the method add
, for which the plugin reports that an
array bounds exception can occur:
FixedSizeSet: add(int) ... ------------------------------------------------------------------------ .../FixedSizeSet/src/FixedSizeSet.java:24: Warning: Possible negative array index (IndexNegative) bits[n] = true; ^ MARKER ...\FixedSizeSet\src\FixedSizeSet.java 24 Warning: Possible negative array index (IndexNegative) Suggestion [24,12]: perhaps declare method 'add' at 22,16 in .../FixedSizeSet/src/FixedSizeSet.java with 'requires 0 <= n;' ------------------------------------------------------------------------ [0.016 s 79646288 bytes] failed
Clearly the input n
must be restricted so that it is
non-negative and is less than the size of bits
. To specify
this, add a @requires
clause to the method: /*@ requires 0 <= n && n < bits.length; */
/** * Adds n to this set **/ public void add(int n) /*@ requires 0 <= n && n < bits.length; */ { bits[n] = true; }
Save the file and rerun the checker on the project. A warning should
no longer be reported for the add
method. You can double-check this by
looking at the plugin’s console output for the add
method:
FixedSizeSet: add(int) ... [0.007 s 63329648 bytes] passed
Next, look at the method union
, which performs the union of
this
set with another set other
by setting all of the
bits set in other
. In other words, when the method exits, a bit
is set if it was set originally in this.bits
or was set in
other.bits
. In JML, for all bits, the following expression
holds: bits[i] == (\old(bits[i]) || other.bits[i]
. Here the
\old(...)
expression is used to refer to the bits array when
the method is called. To indicate that the property holds for every
element in the array, we use a \forall
statement:
/*@ ensures (\forall int i; 0 <= i && i < bits.length; bits[i] == (\old(bits[i]) || other.bits[i])); */
Look at the method similar
which takes a set other
, and
potentially throws a runtime exception. The plugin reports a warning
that the method can throw an unexpected exception (the warning marker
is on the closing brace of the function; the console output describes
where the exception is thrown). To eliminate this warning, we’ll have
to add an @exsures
clause that describes what is true when the
method throws a RuntimeException
. In particular, we know that
other == null
. So, we write the specification accordingly:
/*@ exsures (RuntimeException) other == null; */
Additionally, we must also specify what is true about other
when the method executes normally, using an @ensures
clause:
/*@ ensures other != null; */
This concludes the walkthrough; add additional specifications to
FixedSizeSet.java
until you have eliminated all of the warnings
from the plugin.
If you get stuck after the walkthrough, you can "cheat" by referring
to the included project FixedSizeSet-Solved
, which contains a
set of annotations that permit the plugin to verify FixedSizeSet
and FixedSizeSetCheck
.
This is only one possible solution among many; your solution
might include more / different annotations. The purpose of looking at
the solution is to help your skills in reasoning about programs, not
just to get you through the warmup quickly.
When finished, compare your solution to the one provided in the
FixedSizeSet-Solved
project.
Answer the online quiz to check that you understood the core concepts: http://bit.ly/zSJp5M. Then, contact us via vWorker before continuing with the main project.
Follow these instructions after you have completed the warmup project (see Section 3), and have confirmed with us via vWorker that you should proceed.
StackAr
project, which consists of a library
file StackAr.java
and a client file StackArCheck.java
.
StackAr
project. Re-run the plugin as many (or as few) times as you’d
like. There are 3 important things you should not do:
StackArCheck.java
. This includes conditions.
We have included conditions in StackAr.java
that were
inferred using an automated tool. Feel free to remove/edit
these invariants or to retain (some of) them, whatever you find
most useful.
NOTE: your job is to write verify the library class against the client code, not to create the most detailed specification possible.
Please do not take any breaks when working on the main project; if you need to take a break, record the start time and length of the break.
While we will answer tool / interface questions during the main project, we will not answer questions such as "why can’t the plugin verify this postcondition?"
The Eclipse plugin is a modular checker: it reasons about code by examining one
method at a time. Therefore, the plugin both checks and depends on
annotations.
For instance, suppose method
A
calls method B
. If you have not written properties
describing the return value of B
, VeriWeb may not be able
to prove a property about method A
.
When writing @ensures
and @exsures
clauses, any mention
of an argument x
refers to \old(x)
.
When writing @ensures
and @exsures
clauses,
instead of calling a boolean predicate (e.g., isEmpty()
) try
writing the condition captured by the predicate explicitly.
No! The included conditions are automatically generated suggestions, as such, they might (1) be false, or (2) be unnecessary to verify the library class with respect to the client. Remember: your job is to verify the source code given to you, not to write the most detailed specification possible.
There appears to be a bug in our Eclipse setup that causes the plugin’s configuration to sometimes be cleared. To reconfigure the plugin, follow these steps:
Windows > Preferences > Prover Editor > Simplify
Windows > Preferences > Java > ESC/Java2
After setting these options, you may need to close Eclipse (log out) and log back in for the changes to take effect.