jContractor


> Home
> Documentation
> Download
> Git Repository
> Project page
> Links


Hosted by
SourceForge

jContractor Crash Course

Author: Parker Abercrombie
Last updated: 1/26/03

Contents

What is jContractor?
The goal of this tutorial
Writing contracts
   Preconditions
   Postconditions
   Invariants
   What happens when a contract is violated?
Separate contract classes
Inheritance and interfaces
Running jContractor

What is jContractor

jContractor is a 100% pure Java implementation of Design By Contract for the Java language. Contracts are written as methods that follow a simple naming convention. jContractor provides runtime contract checking by instrumenting the bytecode of classes that define contracts. jContractor can either add contract checking code to class files to be executed later, or it can instrument classes at runtime as they are loaded. All contracts are written in standard Java, so there is no need to learn a special contract specification language. Contracts can be written in the class that they apply too, or in a separate contract class. This allows developers to add contracts to classes for which they do not have source code (3rd party libraries, for example). jContractor understands preconditions, postconditions, and class invariants, with full support for inheritance.

The Goal of this Tutorial

This tutorial assumes familiarity with Design by Contract, but no knowledge of jContractor. Please read a Design by Contract tutorial if you lack this familiarity. Links are provided on the jContractor web page. This tutorial seeks to provide a very quick and very rough introduction to jContractor; just enough to get people started.

Writing contracts

Contracts in jContractor are written as methods following a naming convention. Each contract method returns a boolean value. True indicates that the contract passed, false indicates that the contract was violated. The discussion below will refer to this example class:
  class Stack implements Cloneable {
    private Stack OLD;

    private Vector implementation;

    public Stack () { ... }

    public Stack (Object [] initialContents) { ... }

    public void push (Object o) { ... }

    public Object pop () { ... }

    public Object peek () { ... }

    public void clear () { ... }

    public int size () { ... }

    public Object clone () { ... }

    private int searchStack (Object o) { ... }
  }

Preconditions

The a method's precondition is checked when execution enters the method. Constructors are a small exception, because the call to the superclass constructor must be the first statement in a constructor. If such a call is present, it is executed before the precondition is checked. Precondition methods return boolean and take the same arguments as the non-contract method that they correspond to. The method name is given a "_Precondition" suffix.
  protected boolean push_Precondition (Object o) {
    return o != null;
  }

  private boolean searchStack_Precondition (Object o) {
    return o != null;
  }
Preconditions for constructors follow the same convention:
  protected boolean Stack_Precondition (Object [] initialContents) {
    return (initialContents != null) && (initialContents.length > 0);
  }
Rules about precondtions:
  • Contract methods may not have preconditions.
  • Native methods may not have preconditions.
  • The main(String [] args) method may not have a precondition.
  • The precondition for a static method must be static.
  • The precondition for a non-static method must not be static.
  • The precondition for a non-private method must be protected.
  • The precondition for a private method must be private.

Postconditions

A method's postcondition is checked just before the method returns. Postcondition methods return boolean, and are named with a "_Postcondition" suffix. A postcondition method takes the same arguments as the method that it corresponds to, plus an additional argument of the method's return type (or java.lang.Void for methods that return void). This argument must be the last in the list, and holds the value returned by the method. The recommended name for this argument is "RESULT", but jContractor does not enforce this.
  protected boolean push_Postcondition (Object o, Void RESULT) {
    return implementation.contains(o) && (size() == OLD.size() + 1);
  }

  protected boolean size_Postcondition (int RESULT) {
    return RESULT >= 0;
  }
Postconditions for constructors follow the same convention. The return type for constructors is Void.
  protected boolean Stack_Postcondition (Object [] initialContents,
                                         Void RESULT) {
    return size() == initialContents.length;
  }
Postconditions may refer to the state of the object at method entry through the OLD instance variable. This variable must be declared private, and of the type of the class that contains it (Stack in this case). If a class defines an OLD variable, it must also implement Cloneable and provide a clone() method. When execution enters a method, a clone of the object will be created and stored in OLD.

Rules about postcondtions:

  • Contract methods may not have postconditions.
  • Native methods may not have postconditions.
  • The postcondition for a static method must be static.
  • The postcondition for a non-static method must not be static.
  • The postcondition for a non-private method must be protected.
  • The postcondition for a private method must be private.
  • Postconditions for constructors cannot refer to OLD.

Invariants

Class invariants are checked at the entry and exit of every public method in the class. The invariant is defined in a method called "_Invariant" that takes no arguments and returns a boolean. This method must be declared protected.
  protected boolean _Invariant () {
    return size() >= 0;
  }
Rules about invariants:
  • Invariants are not checked for contract methods.
  • Invariants are not checked for static methods.
  • Invariants are not checked for native methods.
  • Invariants are checked only at the exit of a constructor.
  • The _Invariant() method must be protected and non-static.

What happens when a contract is violated?

When a contract is violated, an error is thrown, usually ending in program termination with an informative error message. The error classes are edu.ucsb.ccs.jcontractor.PreconditionViolationError, edu.ucsb.ccs.jcontractor.PostconditionViolationError, and edu.ucsb.ccs.jcontractor.InvariantViolationError. While nothing prevents you from catching and handling these errors, it is best to think twice before doing so. A contract violation usually points to a bug that should fixed before release, not handled at runtime.

Separate contract classes

Instead of writing contracts directly into your source code, you can write contract classes. Contract classes are given names with a "_CONTRACT" suffix, and do nothing but define contracts.
  class Stack_CONTRACT extends Stack {
    private Stack OLD;

    private Vector implementation;

    protected boolean Stack_Postcondition (Object [] initialContents,
                                           Void RESULT) {
      return size() == initialContents.length;
    }

    protected boolean Stack_Precondition (Object [] initialContents) {
      return (initialContents != null) && (initialContents.length > 0);
    }

    protected boolean push_Precondition (Object o) {
      return o != null;
    }

    protected boolean push_Postcondition (Object o, Void RESULT) {
      return implementation.contains(o) && (size() == OLD.size() + 1);
    }

    private int searchStack (Object o) {
      return 0;
    }

    private boolean searchStack_Precondition (Object o) {
      return o != null;
    }

    protected boolean _Invariant () {
      return size() >= 0;
    }
  }
When jContractor instruments a class, the code for contracts defined in contract class is inserted into the non-contract class. Once there, it will have access to any members and variables in the of the non-contract class. However, to get the compiler to accept the code, it is sometimes necessary to provide fake variables and methods, such as implementation and searchStack(Object) in the contract class above. These members are never actually used, but they must be present so that the compiler accepts the code. A convenient way to get access to non-private fields and methods in the non-contract class is to make the contract class a subclass of the non-contract class.

Using contract classes, it is possible to add contracts to classes for which you do not have source code.

When a contract is specified in the non-contract class and in a contract class, the two are logical and-ed together.

Inheritance and interfaces

Contracts are inherited, just like methods. When a method is overridden in a subclass, that class may specify its own contracts to modify those on the superclass method. The for rules combining contracts go like this:

A subclass method must:

  • Accept all input valid to the superclass method.
  • Meet all guarantees of the superclass method.
Put another way, the method may "weaken the precondition and strengthen the postcondition". What this means is that the preconditions for the subclass method are logical or-ed with the superclass preconditions, and the postconditions are logical and-ed. This requires that the subclass accept all input valid to the super class method, and may accept more that is invalid to the superclass. However, it must abide by the guarantees made by its parent. Like postconditions, class invariants are logical and-ed.

Interfaces may have contracts too (written in contract classes), so contracts can inherit multiply. Contracts from interfaces are logical or-ed with the superclass and subclass contracts in the case of preconditions, and logical-anded in the case of postconditions and invariants, just as you would expect.

Using predicate logic expressions

jContractor supports predicate logic quantifiers such as forall and exists using JaQuaL, Java Quantification Library. See the JaQuaL manual for an introduction.

Running jContractor

  Usage: jContractor [options] class
jInstrument [options] class
-d <directory> Specifies the directory in which instrumented class files should be saved (jInstrument only).
-f <file> Specifies a file that holds instrumentation levels.
-none <class or package> Suppresses instrumentation of the given class(es).
-pre <class or package> Instruments the given class(es) for preconditions checks.
-post <class or package> Instruments the given class(es) for precondition and postcondition checks.
-all <class or package> Instruments the given class(es) for all contract checks.
-verbose Prints the names of classes as they are instrumented.
-version Prints the program version and exits.
-help Prints this table and exits.

To run your program with contract checking enabled you need only run jContractor and pass the name of the class to run as a command line argument. Any command line arguments that appear after the class name are passed to the main(String [] args) method of that class. For example, if you want to run the class MyProgram.class (which is somewhere on the class path), use a command like this:

$ java jContractor MyProgram
It is also possible to add contract checking code to class files so that they may be run with only a usual Java runtime environment. The jInstrument program is used for this. Invoke it, and pass the name of the class file as an argument. jInstrument will add contract checking code, and write the instrumented file to the directory specified by the "-d" option, or the current directory by default. To use jInstrument with the example above, do this:
 $ java  jInstrument ./MyProgram.class
 $ java MyProgram
jContractor and jInstrument can also be run by the Ant build tool produced by the Apache Jakarta project. The classes edu.ucsb.ccs.jcontractor.extras.jContractorTask and edu.ucsb.ccs.jcontractor.extras.jInstrumentTask provide this capability.

Controlling the instrumentation level

jContractor allows the user to specify the level of instrumentation (preconditions only, preconditions and postconditions, or all contracts) for each class in the system. To execute the Foo program checking only preconditions, but preconditions and postconditions in the bar package, and all contracts in class FooBar, One would enter
$ java jContractor -pre * -post bar.* -all bar.FooBar Foo
jContractor supports wildcards similar to those used in Java import statements, allowing the user to concisely specify the instrumentation level. The instrumentation levels may also be read from a file. The rational for this system is that for a method's postcondition to be satisfied, the precondition must have been satisfied. And the invariant can only be satisfied if both preconditions and postconditions have been met. So it is senseless to check the postcondition without checking the precondition, or the invariant without preconditions and postconditions. The supported instrumentation levels are:
  1. none - No contracts are checked.
  2. pre - Preconditions are checked.
  3. post - Preconditions and postconditions are checked.
  4. all - Preconditions, postconditions, and invariants.
Instrumentation commands can also be specified in a file. The example above could be expressed in this way:
  $ cat Foo.instr
  *              pre
  bar.*          post
  bar.FooBar     all

  $ java jContractor -f Foo.instr Foo
Instrumentation levels can be specified on the command line and in a file at the same time.

Removing contract methods from compiled classes

jContractor contracts have no runtime effect when the class is run without jContractor, but they do increase the size of the bytecode. jInstrument includes a "strip mode" which can automatically remove contract methods from bytecode in order to decrease the file size. The "-s" flag on the command line tells jInstrument to remove contract methods instead of adding calls to them. Obviously, you cannot later instrument the class to check contracts that have been removed. Likewise, you cannot strip contracts from a class that has been instrumented, because the class becomes dependent on its contract methods to run correctly.

Here's a simple example:

  $ javac Test.java
  $ javap Test       
  Compiled from Test.java
  public class Test extends java.lang.Object {
      public Test();
      public void foo();
      protected boolean foo_Precondition();
      protected boolean foo_Postcondition(java.lang.Void);
      protected boolean _Invariant();
      public static void main(java.lang.String[]);
  }
  $ java jInstrument -s Test.class
  $ javap Test       
  Compiled from Test.java
  public class Test extends java.lang.Object {
      public Test();
      public void foo();
      public static void main(java.lang.String[]);
  }
Note that the contract methods have been removed. Instrumentation filters have different meaning when it comes to removing contracts:
  1. none - No contracts are removed.
  2. pre - Postconditions and invariants are removed.
  3. post - Invariants are removed.
  4. all - All contracts are removed.