[libcofoja-java] 05/14: New upstream version 1.3

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Tue Oct 4 09:35:37 UTC 2016


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to branch master
in repository libcofoja-java.

commit 84c12319d68cb5d38282c1a46b9dfc5f7b236a01
Author: Emmanuel Bourg <ebourg at apache.org>
Date:   Tue Oct 4 10:21:22 2016 +0200

    New upstream version 1.3
---
 README                                             | 136 ------
 README.md                                          | 486 +++++++++++++++++++++
 build.xml                                          | 188 +++++---
 default.properties                                 |  18 +-
 ivy.xml                                            |  18 +
 src/com/google/java/contract/ContractImport.java   |  37 ++
 .../java/contract/core/agent/ContractAnalyzer.java |  13 +-
 .../core/agent/ContractClassFileTransformer.java   |   6 +-
 .../core/agent/ContractFixingClassAdapter.java     |  35 +-
 .../java/contract/core/agent/ContractHandle.java   |  11 +-
 .../core/agent/ContractMethodSignatures.java       |   2 +
 .../contract/core/agent/HelperClassAdapter.java    |   4 +-
 .../core/agent/LineNumberingClassAdapter.java      |   2 +-
 .../core/agent/LineNumberingMethodAdapter.java     |   2 +-
 .../core/agent/SpecificationClassAdapter.java      |  15 +-
 .../core/agent/SpecificationMethodAdapter.java     |  17 +-
 .../contract/core/apt/AbstractTypeBuilder.java     |  27 +-
 .../contract/core/apt/AnnotationProcessor.java     | 114 +++--
 .../java/contract/core/apt/ContractCreation.java   |   2 +
 .../contract/core/apt/ContractCreationTrait.java   |   5 +-
 .../core/apt/ContractExpressionTransformer.java    |   4 -
 .../contract/core/apt/ContractJavaCompiler.java    |   9 +-
 .../java/contract/core/apt/ContractWriter.java     |  10 +-
 .../contract/core/apt/MethodContractCreator.java   |  24 +-
 .../contract/core/apt/SourceDependencyParser.java  |   9 +-
 .../core/model/ContractAnnotationModel.java        |   9 +-
 .../java/contract/core/model/ContractKind.java     |   8 +
 .../java/contract/core/model/ElementModel.java     |   6 +-
 .../google/java/contract/core/util/JavaUtils.java  |   5 +-
 .../google/java/contract/core/util/PatternMap.java |  10 +-
 .../java/contract/core/util/SyntheticJavaFile.java |   9 +-
 .../java/contract/tests/ConstantContracts.java     |  24 +
 .../java/contract/tests/ConstantContractsTest.java |  30 ++
 .../java/contract/tests/ConstructorTest.java       |  10 +-
 .../google/java/contract/tests/GenericsTest.java   |   4 +-
 .../java/contract/tests/Java8StreamTest.java       |  97 ++++
 36 files changed, 1078 insertions(+), 328 deletions(-)

diff --git a/README b/README
deleted file mode 100644
index 29adefa..0000000
--- a/README
+++ /dev/null
@@ -1,136 +0,0 @@
-Contracts for Java is a contract programming framework for Java, which
-uses annotation processing and bytecode instrumentation to provide
-run-time checking.
-
-
-1. DEPENDENCIES
-
-Contracts fo Java requires Java version 6 for annotation processing
-and bytecode instrumentation. The bytecode rewriter depends on the ASM
-bytecode manipulation library, version 4.x or higher.
-
-Since the Java agent rewrites bytecode in order to inject contracts
-into the loaded code, the ASM library needs to be available in the
-class path at run time if contract checking is enforced through the
-agent. Pre-contracted class files do not need the ASM library or the
-agent to run with contracts enabled.
-
-
-2. CONFIGURATION
-
-In order for the build script to run properly, you must at least
-specify the correct path to Cofoja's dependencies in your local build
-properties file:
-
-  ./local.properties
-
-This file does not exist by default; you can either create it or start
-from a copy of the default configuration file:
-
-  ./default.properties
-
-That file also contains all user-settable properties with their
-descriptions and default values. Once you're done, set the following
-property to true to let Ant run:
-
-  configured=true
-
-
-3. QUICK START
-
-To build a JAR file containing all Cofoja classes, run:
-
-  ant dist
-
-The JAR file will be located at:
-
-  ./dist/lib/cofoja-<version>.jar
-
-It can be used both as a Java agent and annotation processor and
-should be added to your class path.
-
-To compile code with contract annotations, run:
-
-  javac -processor com.google.java.contract.core.apt.AnnotationProcessor <someclass>.java
-
-To execute code compiled with contract checking enabled, make sure the
-generated files (additional .class and .contracts files) are in your
-class path, and run:
-
-  java -javaagent:path/to/cofoja-<version>.jar <someclass>
-
-
-4. ADVANCED BUILD
-
-Contracts for Java is annotated with its own contracts, which can be
-compiled, tested and bundled into the result JAR file so it checks its
-own contracts when compiling and checking your program's contracts!
-
-Please note that running such a build will necessarily be slower than
-running an unchecked version of Contracts for Java, but is a great way
-for you to contribute to the project by helping exercise its own
-capabilities while using it.
-
-To build a contracted version of Contracts for Java, you need to have
-a Cofoja JAR file ready first (see previous section; or you could
-reuse one you've built with a previous run of this bootstrap
-process). Copy the JAR file to:
-
-  ./build/bootstrap.jar
-
-Then run:
-
-  ant bootstrap
-
-Once the contracted version is complete, you can run the test suite
-with:
-
-  ant test
-
-Aside from self-contracted builds, Cofoja JAR files bundled with ASM
-library classes can also be produced, for the sake of easier
-distribution:
-
-  ant -Dasmjar=path/to/asm-all-<version>.jar dist
-
-
-5. USAGE
-
-Contracts for Java consists of an annotation processor, an
-instrumentation agent, as well as an offline bytecode rewriter. The
-annotation processor compiles annotations into separate contract class
-files. The instrumentation agent weaves these contract files with the
-real classes before they are loaded into the JVM. Alternatively, the
-offline bytecode rewriter can be used to produce pre-weaved class
-files that can be directly deployed without any Cofoja dependency.
-
-The following instructions assume that you have the Cofoja and ASM JAR
-files in your class path.
-
-The annotation processor's entry point is (for use with the -processor
-javac option):
-
-  com.google.java.contract.core.apt.AnnotationProcessor
-
-The Java agent can be invoked from the compiled JAR file (for use with
-the -javaagent java option):
-
-  ./dist/cofoja-<version>.jar
-
-The offline instrumenter can be run with:
-
-  java -Dcom.google.java.contract.classoutput=<outdir> \
-    com.google.java.contract.core.agent.PreMain <someclass>.class
-
-Please refer to the official online documentation for more
-information:
-
-  http://code.google.com/p/cofoja/wiki/QuickReference
-
-
-6. BUGS
-
-Contracts for Java is a very young project. Please help us make it
-better by reporting bugs and posting patches at:
-
-  http://code.google.com/p/cofoja/issues/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d100aec
--- /dev/null
+++ b/README.md
@@ -0,0 +1,486 @@
+# Cofoja
+
+Contracts for Java, or Cofoja for short, is a contract programming
+framework and test tool for Java, which uses annotation processing and
+bytecode instrumentation to provide run-time checking. (In particular,
+this is not a static analysis tool.)
+
+I originally wrote Cofoja when interning at Google in 2010, based on
+prior work on Modern Jass. It was open-sourced in early 2011, and has
+since been maintained by myself, with the help of a small community.
+
+**Contents**
+
+* [Download](#download)
+* [Build](#build)
+  * [Dependencies](#dependencies)
+  * [Configuration](#configuration)
+  * [Ant targets](#ant-targets)
+* [Usage](#usage)
+  * [Annotations](#annotations)
+  * [Method contracts](#method-contracts)
+  * [Class and interface contracts](#class-and-interface-contracts)
+  * [Inheritance](#inheritance)
+  * [Other features](#other-features)
+    * [Import annotations](#import-annotations)
+    * [Java expressions](#java-expressions)
+    * [Constructors](#constructors)
+    * [Exception handling](#exception-handling)
+* [Invocation](#invocation)
+* [Run-time contract configuration](#run-time-contract-configuration)
+  * [Selective contracts](#selective-contracts)
+  * [Blacklist](#blacklist)
+  * [Debug tracing](#debug-tracing)
+* [Quick reference](#quick-reference)
+  * [Annotations](#annotations-1)
+  * [Keywords](#keywords)
+  * [Annotation processor options](#annotation-processor-options)
+  * [Java agent properties](#java-agent-properties)
+  * [Run-time contract configuration methods](#run-time-contract-configuration-methods)
+* [Help](#help)
+
+
+## Download
+
+Pre-built JAR files are available on the GitHub release page:
+https://github.com/nhatminhle/cofoja/releases
+
+Each release comes in four flavours. `cofoja` is the basic version,
+which contains every feature Cofoja has to offer. `+asm` builds are
+bundled with a compatible version of the ASM library. `+contracts` are
+debug builds that include self-check contracts on the annotation
+processor and library themselves.
+
+If you are confused about which version to choose, pick either the
+plain `cofoja` JAR file if you already have the ASM library installed,
+or plan to install it by other means, or the `cofoja+asm` JAR file if
+you want a single JAR file that works out of the box.
+
+The class files are compiled for Java 6. Cofoja itself depends on
+features not available to older versions of Java.
+
+
+## Build
+
+### Dependencies
+
+Building Cofoja requires:
+
+* JDK 6 or higher, for annotation processing and bytecode
+  instrumentation.
+* ASM 5.x (or higher versions with ASM5-compatible API), for bytecode
+  instrumentation. http://asm.ow2.org
+* JUnit 3.8 or 4.x if you want to run tests.
+* Ant 1.9.1 or higher for the build script.
+
+If Ivy is installed, calling the `resolve` Ant target retrieves the
+dependencies automatically.
+
+Alternatively, put JAR files from dependencies under the `lib` folder,
+in the current directory. The directory does not exist by default and
+must be created.
+
+In order to enable contract checking at run time, Cofoja binaries and
+dependencies are needed. Normal execution (with contracts disabled)
+does not require Cofoja or any of its dependencies.
+
+
+### Configuration
+
+The build script reads properties from the user-provided
+`local.properties` file. See `default.properties` for a list of
+configuration options to adjust to fit your build environment. Most
+importantly, you may want to adjust the path to the `tools.jar`
+(`classes.jar` on Mac OS) file, which should be provided by the JDK.
+
+
+### Ant targets
+
+The default target, `dist`, builds all four release JAR files. To
+include only those without self-contracts, execute the `nobootstrap`
+target.
+
+By default, the produced JAR files are placed in the `dist` folder, in
+the current directory.
+
+You should run the `test` target to check that your build of Cofoja
+behaves (somewhat) as expected.
+
+
+## Usage
+
+Cofoja supports a contract model similar to that of Eiffel, with added
+support for a few Java-specific things, such as exceptions.
+
+Some general understanding of contract programming (also called design
+by contract) is required to use Cofoja effectively. If you have no
+idea what this is, Wikipedia may be a good place to start:
+http://en.wikipedia.org/wiki/Design_by_Contract
+
+
+### Annotations
+
+In Cofoja, contracts are written as Java code within quoted strings,
+embedded in annotations. E.g., `@Requires("x < 100")` states that `x`
+must be less than 100. Any Java expression, except anonymous classes,
+may be used, provided the string is properly escaped.
+
+An annotation binds a contract to a code element: either a method or
+a type. Cofoja defines three main annotation types, which live in the
+`com.google.java.contract` package:
+
+* `@Requires` for method preconditions;
+* `@Ensures` for method postconditions;
+* `@Invariant` for class and interface invariants;
+
+Contract annotations work on both classes and interfaces. For
+convenience, arrays of quoted expressions are accepted, and behave as
+if the components were separated by `&&`. E.g., `@Ensures({ "x > 0",
+"x < 50" })` is equivalent to `@Ensures("x > 0 && x < 50")`.
+
+
+### Method contracts
+
+A method may have preconditions and postconditions attached to
+it. Together, they specify the contract between caller and callee: if
+the precondition is satisfied on entry of the method, then the caller
+may assume the postcondition on exit. The precondition is what the
+callee demands of the caller, and in return the caller expects the
+postcondition to hold after the call.
+
+As an example, consider the following specification of the square root
+function, which states that for any non-negative double `x` given,
+`sqrt` will return a non-negative result.
+
+```java
+ at Requires("x >= 0")
+ at Ensures("result >= 0")
+static double sqrt(double x);
+```
+
+As shown in this example, a precondition may access parameter values;
+in fact, preconditions and postconditions are evaluated in the context
+of the method they are bound to. More precisely, each annotation
+behaves as if it were a method, with the same arguments and in the
+same scope as the qualified method. In terms of scoping, the previous
+code is equivalent to the following:
+
+```java
+static void sqrt_Requires(double x) {
+  assert x >= 0;
+}
+static void sqrt_Ensures(double result) {
+  assert result >= 0;
+}
+static double sqrt(double x);
+```
+
+In addition, postconditions may contain a few extensions:
+
+* As we have seen, they may refer to the returned value, using the
+  `result` keyword.
+* Within a postcondition, `old` is a keyword which is followed by
+  a single parenthesized expression, such that `old (x)` evaluates to
+  the value of `x` on entry of the method invocation.
+* An `old` expression is evaluated in the same context as
+  preconditions and has access to the same things, including parameter
+  values.
+
+Given this, a more complete specification of `sqrt` might be:
+
+```java
+ at Requires("x >= 0")
+ at Ensures({ "result >= 0", "Math.abs(x - result * result) < EPS" })
+static double sqrt(double x);
+```
+
+At run time, when contracts are enabled, preconditions and
+postconditions translate to checks on entry and exit, respectively, of
+the method. A failure results in a `PreconditionError` or
+`PostconditionError` being thrown, depending on the origin: failure to
+meet a precondition means that the method was called incorrectly,
+whereas an unsatisfied postcondition points to a bug in the
+implementation of the method itself.
+
+
+### Class and interface contracts
+
+A class or interface may have associated invariants. Instead of
+specifying a contract between a caller and a callee, those invariants
+describe the state of a valid object of the qualified type. Calling
+methods on an object may cause it to change; invariants guarantee that
+after any such change, the object remains in a consistent state.
+
+Of course, internal operations are allowed to muck around and
+temporarily invalidate invariants to do their job, but they agree to
+eventually put everything back into their proper places. Intuitively,
+any operation made against `this` is considered internal and does not
+need to obey the invariants. Only method invocations on other
+variables do.
+
+In Cofoja, when contracts are enabled, invariants are checked on entry
+and exit of method calls on objects not already in the call stack
+(including `this`). Failure results in an `InvariantError` exception
+and indicates that the guilty method has left the object in an
+inconsistent state.
+
+
+### Inheritance
+
+Contracts apply to all objects of the associated type, including any
+instances of derived classes: all implementations of a contracted
+interface must honor the interface contracts, all children of a class
+must honor the contracts of their parent.
+
+In addition, derived types may refine those contracts by adding their
+own preconditions, postconditions and invariants to the mix. However,
+they cannot replace the inherited contracts, only augment them
+according to the following subtyping rules.
+
+Preconditions may be weakened, i.e., methods may be overriden with
+implementations that accept a wider range of inputs. Callers that
+access the object through a superclass or interface need only
+establish the parent contracts. In Cofoja, a method checks that either
+its inherited *or* its own preconditions are satisfied.
+
+Postconditions may be strengthened, i.e., methods may be overriden
+with implementations that produce a smaller range of outputs. Callers
+that access the object through a superclass or interface expect at
+least the parent contracts. In Cofoja, a method checks both its
+inherited *and* its own postconditions.
+
+Invariants may be strengthened, i.e., classes and interfaces may be
+derived to restrict the set of valid states. An object must qualify as
+a consistent value of any of its superclasses or interfaces. In
+Cofoja, a type asserts both its inherited *and* its own invariants.
+
+
+### Other features
+
+#### Import annotations
+
+Previously, we have used `Math.abs` in our examples, which resides in
+`java.lang`, and is thus available to any Java code. For other
+symbols, we may need to import a class or package. Imports for
+contracts are kept separately from those of the main file. The
+`@ContractImport` annotation specifies names to be imported for use in
+contract code within the associated type. It must be put on the
+enclosing type and accepts an array of strings, each one containing an
+import pattern, compatible with the `import` Java statement.
+
+#### Java expressions
+
+Contracts are made of Java expressions, with the addition of several
+keywords. Cofoja uses the Java compiler from the standard tools
+package and hence supports the same language as provided by that
+compiler (which should usually be the same as that of the top-level
+compiler).
+
+However, some expressions may generate bridges or other synthetic
+methods in addition to their direct translations to bytecode. Such
+artifacts need to be identified and handled specially by Cofoja. The
+following features are currently known to require particular
+processing:
+
+Feature                   | Supported | Description
+------------------------- | --------- | -------------------------------
+Inner class accesses      | Yes       | May generate `access$` methods
+Anonymous classes         | No        | Generate additional class files
+Java 8 lambda expressions | Yes       | Generate `lambda$` methods
+
+Since uses of synthetic methods follow no set rules, compilers other
+than Javac may opt for different compilation strategies. At the
+moment, no such incompatible scheme has been reported.
+
+#### Constructors
+
+With respect to Cofoja, constructors behave slightly differently from
+other methods. They assert invariants, but only on exit, and may be
+attached to preconditions and postconditions. However, a very
+important distinction is that constructor preconditions are checked
+*after* any call to a parent constructor. This is due to `super` calls
+being necessarily the first instruction of any constructor call,
+making it impossible to insert precondition checks before them. (This
+is considered a known bug.)
+
+#### Exception handling
+
+When an exception is thrown from within a contracted method, normal
+postconditions are not checked by Cofoja, as they may refer to
+a result that does not exist.
+
+Instead, the `@ThrowEnsures` annotation is provided. It functions
+similarly to `@Ensures` but accepts an alternating list of exception
+type names to catch and matching postcondition expressions, as quoted
+strings. Within those exceptional postconditions, the keyword `signal`
+refers to the exception object that has been thrown.
+
+A possible use of `@ThrowEnsures` is to guarantee that exceptional
+method exits do not inadvertently alter the state of the object.
+
+```java
+class RestrictedInteger {
+  int x;
+
+  @Ensures("x == y")
+  @ThrowEnsures({ "IllegalArgumentException", "x == old (x)" })
+  void setX(int y) throws IllegalArgumentException {
+    ...
+  }
+}
+```
+
+
+### Invocation
+
+Contracts for Java consists of an annotation processor, an
+instrumentation agent, as well as an offline bytecode rewriter. The
+annotation processor compiles annotations into separate contract class
+files. The instrumentation agent weaves these contract files with the
+real classes before they are loaded into the JVM. Alternatively, the
+offline bytecode rewriter can be used to produce pre-weaved class
+files that can be directly deployed without requiring the Java agent
+or ASM library at run time.
+
+The pre-built JAR files contain both the Java agent and annotation
+processor. The latter is named `AnnotationProcessor` (in the
+`com.google.java.contract.core.apt` package); the agent can be loaded
+directly by specifying the JAR file (e.g., as an argument to the
+`-javaagent` argument).
+
+To execute code compiled with contract checking enabled, make sure the
+generated files (additional `.class` and `.contracts` files) are in
+your class path and enable the Cofoja JAR file as a Java agent.
+
+Or use the offline instrumenter, which lives in the `PreMain` class,
+under the `com.google.java.contract.core.agent` package, and takes
+paths to class files as command-line arguments.
+
+
+### Run-time contract configuration
+
+#### Selective contracts
+
+When using the Java agent, contract evaluation can be enabled
+selectively, similar to how assertions can be toggled on and off for
+specific types. Whether contracts are checked on methods of a given
+type is determined at load time and may not be changed afterwards.
+
+These settings are controlled through a user-defined configurator
+object. As part of its early start-up procedure, the Java agent
+attempts to create an instance of the configurator class, whose name
+is provided through the `com.google.java.contract.configurator` JVM
+property. It then calls the `configure` method on the newly created
+object, passing it an argument of type `ContractEnvironment` (from
+package `com.google.java.contract`).
+
+The `ContractEnvironment` object provides methods such as
+`enablePreconditions` and `disablePreconditions`, to enable or disable
+contracts selectively. Each method accepts an import-like string
+pattern. In case of pattern overlap, the behavior specified by the
+last matching call is retained.
+
+Disabling contracts for a specific type does not prevent its contracts
+from being inherited and checked correctly for the derived types.
+
+#### Blacklist
+
+The blacklist is controlled through the `ContractEnvironment` methods
+`ignore` and `unignore`, which also take patterns, similarly to the
+contract selection methods.
+
+Ignoring a pattern is different from disabling contracts for that
+pattern. A blacklisted type is not be examined at all by the Java
+agent; in particular, it is not searched for contracts and its
+bytecode is not modified in any way. It is assumed to contain no
+contracts; thus, derived types inherit nothing from it.
+
+#### Debug tracing
+
+For debug purposes, Cofoja may be instructed to print a trace to
+stderr of contract that are evaluated. Compilation support for tracing
+is provided by the `com.google.java.contract.debug` annotation
+processor flag. The actual trace is obtained by running the contracted
+program with the `com.google.java.contract.log.contract` JVM property
+set to `true`.
+
+
+### Quick reference
+
+#### Annotations
+
+All annotations reside in the `com.google.java.contract` package.
+
+Annotation      | Checked on            | Inheritance
+--------------- | --------------------- | -----------
+`@Invariant`    | Object entry and exit | And
+`@Requires`     | Entry                 | Or
+`@Ensures`      | Normal exit           | And
+`@ThrowEnsures` | Abnormal exit         | And
+
+#### Keywords
+
+Keyword  | May appear in               | Description
+-------- | --------------------------- | ---------------------
+`old`    | `@Ensures`, `@ThrowEnsures` | Value on method entry
+`result` | `@Ensures`                  | Value to be returned
+`signal` | `@ThrowEnsures`             | Exception thrown
+
+#### Annotation processor options
+
+All option names reside in the `com.google.java.contract` name
+space. Options that have a Javac counterpart are passed down to the
+underlying Java compiler used to compile contract code. In most cases,
+they should match those used to compile the main project.
+
+Option        | Type      | Javac option  | Description
+------------- | --------- | ------------- | ------------------------------------
+`classpath`   | path      | `-classpath`  | Class path for contract code
+`sourcepath`  | path      | `-sourcepath` | Where to find source files
+`classoutput` | directory | `-d`          | Where to put compiled contract files
+`debug`       | flag      |               | Enable run-time logging support
+`dump`        | directory |               | Where to put generated source files
+
+Additionally, you may want to pass `-proc:only` (or equivalent) to the
+Java compiler, so it only runs the annotation processor, which will
+generate compiled contract files without producing the normal class
+files. This is recommended for medium-to-large projects.
+
+#### Java agent properties
+
+All properties reside in the `com.google.java.contract` name space.
+
+Property       | Type    | Description
+-------------- | ------- | ----------------------------------------------
+`configurator` | String  | Configurator class name
+`dump`         | String  | Where to dump instrumented class files
+`log.contract` | Boolean | Print a trace of evaluated contracts to stderr
+
+`log.contract` requires contracts compiled with the `debug` annotation
+processor option.
+
+#### Run-time contract configuration methods
+
+All methods live in `com.google.java.contract.ContractEnvironment`.
+
+Method                  | Description
+----------------------- | ---------------------------------------------------
+`enablePreconditions`   | Check preconditions for all methods of class
+`disablePreconditions`  | Do not check preconditions for any method of class
+`enablePostconditions`  | Check postconditions for all methods of class
+`disablePostconditions` | Do not check postconditions for any method of class
+`enableInvariants`      | Check invariants for class
+`disableInvariants`     | Do not check invariants for class
+`ignore`                | Do not search class for contracts
+`unignore`              | Search class for contracts
+
+
+## Help
+
+Contracts for Java is a not-so-young project. Please help make it
+better by reporting bugs at:
+https://github.com/nhatminhle/cofoja/issues
+
+For general questions and help with using Cofoja, you can reach out to
+the dedicated Google discussion group:
+http://groups.google.com/group/cofoja
diff --git a/build.xml b/build.xml
index 9edd215..3410f53 100644
--- a/build.xml
+++ b/build.xml
@@ -1,15 +1,41 @@
-<project name="Cofoja" default="dist" basedir=".">
+<project name="Cofoja" default="dist" basedir="."
+         xmlns:if="ant:if"
+         xmlns:unless="ant:unless"
+         xmlns:ivy="antlib:org.apache.ivy.ant">
   <!-- Configurable properties. -->
+  <condition property="platform.suffix" value=".${platform}" else="">
+    <isset property="platform" />
+  </condition>
   <property file="local.properties" />
+  <property file="local${platform.suffix}.properties" if:set="platform" />
   <property file="default.properties" />
 
   <!-- Private properties. -->
 
+  <condition property="java8">
+    <or>
+      <and>
+        <isset property="rt.jar" />
+        <available ignoresystemclasses="true"
+                   classname="java.lang.invoke.LambdaMetafactory">
+          <classpath location="${rt.jar}" />
+        </available>
+      </and>
+      <and>
+        <not>
+          <isset property="rt.jar" />
+        </not>
+        <available classname="java.lang.invoke.LambdaMetafactory" />
+      </and>
+    </or>
+  </condition>
+
+  <property name="lib.dir" location="lib" />
   <property name="src.dir" location="src" />
   <property name="test.dir" location="test" />
   <property name="build.dir" location="build" />
-  <property name="obj.dir" location="obj" />
-  <property name="dist.dir" location="dist" />
+  <property name="obj.dir" location="obj${platform.suffix}" />
+  <property name="dist.dir" location="dist${platform.suffix}" />
 
   <property name="manifest.path"
             value="${src.dir}/META-INF/MANIFEST.MF" />
@@ -19,21 +45,68 @@
             value="com.google.java.contract.tests.Cofoja" />
 
   <path id="base.class.path">
-    <pathelement path="${asm.jar}" />
+    <fileset dir="${lib.dir}">
+      <include name="asm-*.jar" />
+    </fileset>
+  </path>
+
+  <path id="build.class.path">
+    <path refid="base.class.path" />
     <pathelement path="${tools.jar}" />
   </path>
 
   <path id="test.class.path">
     <path refid="base.class.path" />
-    <pathelement path="${junit.jar}" />
+    <fileset dir="${lib.dir}">
+      <include name="*.jar" />
+    </fileset>
+  </path>
+
+  <path id="test1.class.path">
+    <path refid="test.class.path" />
+    <pathelement path="${obj.dir}/stage2" />
+  </path>
+
+  <path id="test2.class.path">
+    <path refid="test.class.path" />
+    <pathelement path="${obj.dir}/stage2" />
+    <pathelement path="${obj.dir}/test" />
   </path>
 
   <!-- Build macros. -->
 
   <presetdef name="ujavac">
-    <javac encoding="utf-8" debug="${debug}" includeantruntime="false" />
+    <javac encoding="utf-8" debug="${debug}" includeantruntime="false">
+      <bootclasspath location="${rt.jar}" if:set="rt.jar" />
+    </javac>
   </presetdef>
 
+  <macrodef name="cofojavac">
+    <attribute name="srcdir" />
+    <attribute name="destdir" />
+    <attribute name="debug" default="false" />
+    <attribute name="bootstrappath" default="" />
+    <attribute name="classpathid" />
+    <element name="contents" optional="true" implicit="true" />
+    <sequential>
+      <path id="processor.path">
+        <path refid="base.class.path" />
+        <pathelement location="@{bootstrappath}" />
+      </path>
+      <ujavac srcdir="@{srcdir}" destdir="@{destdir}" debug="@{debug}">
+        <classpath refid="@{classpathid}" />
+        <compilerarg value="-processorpath" unless:blank="@{bootstrappath}" />
+        <compilerarg value="${toString:processor.path}" unless:blank="@{bootstrappath}" />
+        <compilerarg value="-processor" />
+        <compilerarg value="${apt.class}" />
+        <compilerarg value="-Acom.google.java.contract.classpath=${toString:@{classpathid}}" />
+        <compilerarg value="-Acom.google.java.contract.sourcepath=@{srcdir}" />
+        <compilerarg value="-Acom.google.java.contract.classoutput=@{destdir}" />
+        <contents />
+      </ujavac>
+    </sequential>
+  </macrodef>
+
   <macrodef name="checkjar">
     <attribute name="label" />
     <attribute name="property" />
@@ -85,7 +158,11 @@
                  provider="${apt.class}" />
         <zipfileset includes="**/*.class"
                     src="@{barejarfile}" />
-        <zipfileset includes="**/*.class" src="${asm.jar}" />
+        <zipfileset includes="**/*.class">
+          <fileset dir="${lib.dir}">
+            <include name="asm-*.jar" />
+          </fileset>
+        </zipfileset>
       </jar>
     </sequential>
   </macrodef>
@@ -96,33 +173,43 @@
     <sequential>
       <mkdir dir="${obj.dir}/tmp@{stage}" />
       <mkdir dir="${obj.dir}/stage@{stage}" />
-      <ujavac srcdir="${src.dir}" destdir="${obj.dir}/tmp@{stage}" debug="true">
-        <classpath refid="base.class.path" />
-        <compilerarg value="-processorpath" />
-        <compilerarg value="@{bootstrappath}:${asm.jar}" />
-        <compilerarg value="-processor" />
-        <compilerarg value="${apt.class}" />
-      </ujavac>
+      <cofojavac srcdir="${src.dir}" destdir="${obj.dir}/tmp@{stage}" debug="true"
+                 bootstrappath="@{bootstrappath}" classpathid="build.class.path" />
       <cofojab srcdir="${obj.dir}/tmp@{stage}"
                destdir="${obj.dir}/stage@{stage}" />
     </sequential>
   </macrodef>
 
+  <!-- Dependency management. -->
+
+  <target name="resolve" description="retrieve dependencies with Ivy">
+    <ivy:retrieve pattern="${lib.dir}/[artifact]-[revision].[ext]"
+                  type="jar" conf="test" />
+  </target>
+
+  <target name="publish" depends="resolve,dist"
+          description="publish artifacts with Ivy">
+    <ivy:publish resolver="local" pubrevision="${version}"
+                 artifactspattern="dist/[artifact]-${cofoja.version}.[ext]" />
+  </target>
+
   <!-- Initialization. -->
 
   <target name="configure">
     <echo message="Configuration" />
     <echo message="-------------" />
-    <checkjar label="ASM JAR:       " property="asm.jar" />
-    <checkjar label="JUnit JAR:     " property="junit.jar" />
+    <sequential if:set="rt.jar">
+      <checkjar label="JRE RT JAR:    " property="rt.jar" />
+    </sequential>
     <checkjar label="JDK Tools JAR: " property="tools.jar" />
-    <checkjar label="Bootstrap JAR: " property="bootstrap.jar" />
+    <sequential if:set="bootstrap.jar">
+      <checkjar label="Bootstrap JAR: " property="bootstrap.jar" />
+    </sequential>
     <echo message="Snapshot:      ${snapshot}" />
     <echo message="Debug:         ${debug}" />
   </target>
 
   <target name="init" depends="configure">
-    <requirejar property="asm.jar" />
     <requirejar property="tools.jar" />
 
     <tstamp />
@@ -132,13 +219,13 @@
       <equals arg1="${snapshot}" arg2="true" />
     </condition>
     <property name="cofoja.jar"
-              value="${dist.dir}/cofoja-${cofoja.version}.jar" />
+              value="${dist.dir}/cofoja+asm-${cofoja.version}.jar" />
     <property name="cofoja.bare.jar"
-              value="${dist.dir}/cofoja-${cofoja.version}-bare.jar" />
+              value="${dist.dir}/cofoja-${cofoja.version}.jar" />
     <property name="cofoja.contracted.jar"
-              value="${dist.dir}/ccofoja-${cofoja.version}.jar" />
+              value="${dist.dir}/cofoja+contracts+asm-${cofoja.version}.jar" />
     <property name="cofoja.contracted.bare.jar"
-              value="${dist.dir}/ccofoja-${cofoja.version}-bare.jar" />
+              value="${dist.dir}/cofoja+contracts-${cofoja.version}.jar" />
 
     <mkdir dir="${obj.dir}" />
     <mkdir dir="${dist.dir}" />
@@ -150,30 +237,35 @@
           description="build class files">
     <mkdir dir="${obj.dir}/bare" />
     <ujavac srcdir="${src.dir}" destdir="${obj.dir}/bare">
-      <classpath refid="base.class.path" />
+      <classpath refid="build.class.path" />
     </ujavac>
   </target>
 
-  <target name="dist" depends="build"
-          description="build JAR files for distribution">
+  <target name="nobootstrap" depends="build"
+          description="build JAR files without self-contracts">
     <barejar jarfile="${cofoja.bare.jar}" basedir="${obj.dir}/bare" />
     <fulljar jarfile="${cofoja.jar}" barejarfile="${cofoja.bare.jar}" />
+    <property name="bootstrap.jar" value="${cofoja.bare.jar}" />
   </target>
 
   <!-- Bootstrap. -->
 
-  <target name="antinit" depends="init">
-    <requirejar property="bootstrap.jar" />
+  <target name="antinit" depends="init,nobootstrap">
     <mkdir dir="${obj.dir}/build" />
     <ujavac srcdir="${build.dir}" destdir="${obj.dir}/build">
       <classpath>
         <path refid="base.class.path" />
         <pathelement path="${java.class.path}" />
-        <pathelement path="${bootstrap.jar}" />
+        <pathelement location="${bootstrap.jar}" />
       </classpath>
     </ujavac>
-    <taskdef name="cofojab" classname="PreAgentAntTask"
-             classpath="${obj.dir}/build:${bootstrap.jar}:${asm.jar}" />
+    <taskdef name="cofojab" classname="PreAgentAntTask">
+      <classpath>
+        <path refid="base.class.path" />
+        <pathelement location="${obj.dir}/build" />
+        <pathelement location="${bootstrap.jar}" />
+      </classpath>
+    </taskdef>
   </target>
 
   <target name="stage0" depends="antinit">
@@ -188,8 +280,8 @@
     <bootstrapcomp stage="2" bootstrappath="${obj.dir}/stage1" />
   </target>
 
-  <target name="bootstrap" depends="stage2"
-          description="build bootstrap-contracted JAR files">
+  <target name="dist" depends="stage2"
+          description="build JAR files for distribution">
     <barejar jarfile="${cofoja.contracted.bare.jar}"
              basedir="${obj.dir}/stage2" />
     <fulljar jarfile="${cofoja.contracted.jar}"
@@ -199,49 +291,39 @@
   <!-- Tests. -->
 
   <target name="buildtest1" depends="stage2">
-    <requirejar property="junit.jar" />
     <mkdir dir="${obj.dir}/test" />
-    <ujavac srcdir="${test.dir}" destdir="${obj.dir}/test">
-      <classpath>
-        <path refid="test.class.path" />
-        <pathelement path="${obj.dir}/stage2" />
-      </classpath>
-      <compilerarg value="-processor" />
-      <compilerarg value="${apt.class}" />
+    <cofojavac srcdir="${test.dir}" destdir="${obj.dir}/test"
+               classpathid="test1.class.path">
       <include name="**/SeparateGenericSuperclass.java" />
       <include name="**/SeparateInterface.java" />
-    </ujavac>
+    </cofojavac>
   </target>
 
   <target name="buildtest2" depends="buildtest1,stage2">
-    <ujavac srcdir="${test.dir}" destdir="${obj.dir}/test">
-      <classpath>
-        <path refid="test.class.path" />
-        <pathelement path="${obj.dir}/stage2" />
-        <pathelement path="${obj.dir}/test" />
-      </classpath>
-      <compilerarg value="-processor" />
-      <compilerarg value="${apt.class}" />
+    <cofojavac srcdir="${test.dir}" destdir="${obj.dir}/test"
+               classpathid="test2.class.path">
       <compilerarg value="-Acom.google.java.contract.dump=${obj.dir}/test/dump" />
       <exclude name="**/SeparateGenericSuperclass.java" />
       <exclude name="**/SeparateInterface.java" />
-    </ujavac>
+      <exclude name="**/Java8*.java" unless:true="${java8}" />
+    </cofojavac>
   </target>
 
-  <target name="test" depends="buildtest2,bootstrap"
+  <target name="test" depends="buildtest2,dist"
           description="run tests">
     <junit printsummary="yes" haltonfailure="yes">
       <classpath>
         <path refid="test.class.path" />
         <pathelement path="${obj.dir}/stage2" />
-        <pathelement location="${obj.dir}/test"/>
+        <pathelement location="${obj.dir}/test" />
       </classpath>
       <jvmarg value="-javaagent:${cofoja.contracted.bare.jar}" />
       <jvmarg value="-Dcom.google.java.contract.configurator=${test.configurator.class}" />
       <formatter type="plain" />
       <batchtest fork="yes" todir="${obj.dir}/test">
         <fileset dir="${obj.dir}/test">
-          <include name="**/*Test.class"/>
+          <include name="**/*Test.class" />
+          <exclude name="**/Java8*.class" unless:true="${java8}" />
         </fileset>
       </batchtest>
     </junit>
diff --git a/default.properties b/default.properties
index a74d238..ba01f4a 100644
--- a/default.properties
+++ b/default.properties
@@ -3,21 +3,13 @@
 
 ## Version information. Set 'snapshot' to false if you are building
 ## a release.
-version=1.1
+version=1.2
 snapshot=true
 debug=true
 
-## Location of an older build of Cofoja, for bootstrapping purposes.
-bootstrap.jar=build/bootstrap.jar
+## Location of an older build of Cofoja, for bootstrapping
+## purposes. Leave unset to use a newly built binary.
+# bootstrap.jar=build/bootstrap.jar
 
-## Location of dependency JAR files needed to build Cofoja. The
-## defaults assume the JAR files are in the 'build' subdirectory,
-## except for 'tools.jar', which is probably not what you want.
-asm.jar=build/asm.jar
-junit.jar=build/junit.jar
+## Location of the JDK 'tools.jar' or 'classes.jar' (on Mac OS X).
 tools.jar=${java.home}/../lib/tools.jar
-
-## If you use Maven, the following may help, instead.
-# maven.repo.dir=${user.home}/.m2/repository
-# asm.jar=${maven.repo.dir}/asm/asm-all/3.3.1/asm-all-3.3.1.jar
-# junit.jar=${maven.repo.dir}/junit/junit/4.8.2/junit-4.8.2.jar
diff --git a/ivy.xml b/ivy.xml
new file mode 100644
index 0000000..7732a6e
--- /dev/null
+++ b/ivy.xml
@@ -0,0 +1,18 @@
+<ivy-module version="2.0">
+  <info organisation="com.google.java.contract" module="cofoja" />
+  <configurations>
+    <conf name="core" description="core libraries" />
+    <conf name="contracts" extends="core"
+          description="core libraries with contracts" />
+    <conf name="test" extends="core" visibility="private"
+          description="for testing" />
+  </configurations>
+  <publications>
+    <artifact conf="core" />
+    <artifact name="cofoja+contracts" conf="contracts" />
+  </publications>
+  <dependencies>
+    <dependency org="org.ow2.asm" name="asm-all" rev="5.+" conf="*->default" />
+    <dependency org="junit" name="junit-dep" rev="[3.8,)" conf="test->default" />
+  </dependencies>
+</ivy-module>
diff --git a/src/com/google/java/contract/ContractImport.java b/src/com/google/java/contract/ContractImport.java
new file mode 100644
index 0000000..962c4b1
--- /dev/null
+++ b/src/com/google/java/contract/ContractImport.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Nhat Minh Lê
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package com.google.java.contract;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies import statements for use in contracts.
+ *
+ * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
+ */
+ at Target(ElementType.TYPE)
+ at Retention(RetentionPolicy.SOURCE)
+public @interface ContractImport {
+  /**
+   * The list of import statements.
+   */
+  String[] value();
+}
diff --git a/src/com/google/java/contract/core/agent/ContractAnalyzer.java b/src/com/google/java/contract/core/agent/ContractAnalyzer.java
index 931dbe3..c5b7012 100644
--- a/src/com/google/java/contract/core/agent/ContractAnalyzer.java
+++ b/src/com/google/java/contract/core/agent/ContractAnalyzer.java
@@ -18,14 +18,11 @@
  */
 package com.google.java.contract.core.agent;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
-import com.google.java.contract.core.model.ClassName;
 import com.google.java.contract.core.model.ContractKind;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -49,7 +46,11 @@ import java.util.Map;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @author johannes.rieken at gmail.com (Johannes Rieken)
  */
- at AllowUnusedImport({ ClassName.class, Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.core.model.ClassName",
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "className == null || ClassName.isBinaryName(className)",
   "classHandles != null",
@@ -71,7 +72,7 @@ class ContractAnalyzer extends ClassVisitor {
    * intended to be filled through its visitor interface.
    */
   ContractAnalyzer() {
-    super(Opcodes.ASM4);
+    super(Opcodes.ASM5);
     classHandles = new ArrayList<ClassContractHandle>();
     methodHandles = new HashMap<String, ArrayList<MethodContractHandle>>();
   }
diff --git a/src/com/google/java/contract/core/agent/ContractClassFileTransformer.java b/src/com/google/java/contract/core/agent/ContractClassFileTransformer.java
index e75bcc3..c36ec36 100644
--- a/src/com/google/java/contract/core/agent/ContractClassFileTransformer.java
+++ b/src/com/google/java/contract/core/agent/ContractClassFileTransformer.java
@@ -18,7 +18,7 @@
  */
 package com.google.java.contract.core.agent;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Requires;
 import com.google.java.contract.core.model.ClassName;
@@ -58,14 +58,14 @@ import javax.tools.JavaFileObject.Kind;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @author johannes.rieken at gmail.com (Johannes Rieken)
  */
- at AllowUnusedImport(ClassName.class)
+ at ContractImport("com.google.java.contract.core.model.ClassName")
 public class ContractClassFileTransformer implements ClassFileTransformer {
   /**
    * Find and store superclass information.
    */
   private class SuperInfoFinder extends ClassVisitor {
     private SuperInfoFinder() {
-      super(Opcodes.ASM4);
+      super(Opcodes.ASM5);
     }
 
     @Override
diff --git a/src/com/google/java/contract/core/agent/ContractFixingClassAdapter.java b/src/com/google/java/contract/core/agent/ContractFixingClassAdapter.java
index 038c3f6..e387e6f 100644
--- a/src/com/google/java/contract/core/agent/ContractFixingClassAdapter.java
+++ b/src/com/google/java/contract/core/agent/ContractFixingClassAdapter.java
@@ -21,6 +21,7 @@ package com.google.java.contract.core.agent;
 import com.google.java.contract.Requires;
 import com.google.java.contract.core.util.JavaUtils;
 import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -52,7 +53,7 @@ class ContractFixingClassAdapter extends ClassVisitor {
      */
     @Requires("mv != null")
     public AccessMethodAdapter(MethodVisitor mv) {
-      super(Opcodes.ASM4, mv);
+      super(Opcodes.ASM5, mv);
     }
 
     /**
@@ -61,14 +62,36 @@ class ContractFixingClassAdapter extends ClassVisitor {
      */
     @Override
     public void visitMethodInsn(int opcode, String owner, String name,
-                                String desc) {
+                                String desc, boolean itf) {
       if (!name.startsWith("access$")) {
-        mv.visitMethodInsn(opcode, owner, name, desc);
+        mv.visitMethodInsn(opcode, owner, name, desc, itf);
       } else {
-        mv.visitMethodInsn(opcode, owner,
-                           JavaUtils.SYNTHETIC_MEMBER_PREFIX + name, desc);
+        String newName = JavaUtils.SYNTHETIC_MEMBER_PREFIX + name;
+        mv.visitMethodInsn(opcode, owner, newName, desc, itf);
       }
     }
+
+    /**
+     * Converts calls to {@code lambda$n} synthetic methods to the
+     * equivalent injected methods.
+     */
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc,
+                                       Handle bsm, Object... bsmArgs) {
+      for (int i = 0; i < bsmArgs.length; ++i) {
+        if (!(bsmArgs[i] instanceof Handle)) {
+          continue;
+        }
+        Handle h = (Handle)bsmArgs[i];
+        if (!h.getName().startsWith("lambda$")) {
+          continue;
+        }
+        String newName = JavaUtils.SYNTHETIC_MEMBER_PREFIX + h.getName();
+        bsmArgs[i] = new Handle(h.getTag(), h.getOwner(),
+                                newName, h.getDesc());
+      }
+      mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+    }
   }
 
   /**
@@ -78,7 +101,7 @@ class ContractFixingClassAdapter extends ClassVisitor {
    */
   @Requires("cv != null")
   public ContractFixingClassAdapter(ClassVisitor cv) {
-    super(Opcodes.ASM4, cv);
+    super(Opcodes.ASM5, cv);
   }
 
   /**
diff --git a/src/com/google/java/contract/core/agent/ContractHandle.java b/src/com/google/java/contract/core/agent/ContractHandle.java
index c8e348b..4cd6587 100644
--- a/src/com/google/java/contract/core/agent/ContractHandle.java
+++ b/src/com/google/java/contract/core/agent/ContractHandle.java
@@ -18,15 +18,12 @@
  */
 package com.google.java.contract.core.agent;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
-import com.google.java.contract.core.model.ClassName;
 import com.google.java.contract.core.model.ContractKind;
 import com.google.java.contract.core.util.JavaUtils;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 import org.objectweb.asm.tree.MethodNode;
 
 import java.util.List;
@@ -39,7 +36,11 @@ import java.util.List;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @author johannes.rieken at gmail.com (Johannes Rieken)
  */
- at AllowUnusedImport({ ClassName.class, Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.core.model.ClassName",
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "getKind() != null",
   "ClassName.isBinaryName(getClassName())",
diff --git a/src/com/google/java/contract/core/agent/ContractMethodSignatures.java b/src/com/google/java/contract/core/agent/ContractMethodSignatures.java
index 2378ac1..2ad3dc2 100644
--- a/src/com/google/java/contract/core/agent/ContractMethodSignatures.java
+++ b/src/com/google/java/contract/core/agent/ContractMethodSignatures.java
@@ -51,6 +51,8 @@ class ContractMethodSignatures {
       return Enum.valueOf(ContractKind.class, pair[1]);
     } else if (contractMethod.name.startsWith("access$")) {
       return ContractKind.ACCESS;
+    } else if (contractMethod.name.startsWith("lambda$")) {
+      return ContractKind.LAMBDA;
     } else {
       return null;
     }
diff --git a/src/com/google/java/contract/core/agent/HelperClassAdapter.java b/src/com/google/java/contract/core/agent/HelperClassAdapter.java
index e32a746..f303916 100644
--- a/src/com/google/java/contract/core/agent/HelperClassAdapter.java
+++ b/src/com/google/java/contract/core/agent/HelperClassAdapter.java
@@ -56,7 +56,7 @@ class HelperClassAdapter extends ClassVisitor {
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
       if (Type.getType(desc).getInternalName().equals(
               "com/google/java/contract/core/agent/ContractMethodSignature")) {
-        return new AnnotationVisitor(Opcodes.ASM4) {
+        return new AnnotationVisitor(Opcodes.ASM5) {
           @Override
           public void visit(String name, Object value) {
             if (name.equals("lines")) {
@@ -71,7 +71,7 @@ class HelperClassAdapter extends ClassVisitor {
 
   @Requires("cv != null")
   public HelperClassAdapter(ClassVisitor cv) {
-    super(Opcodes.ASM4, cv);
+    super(Opcodes.ASM5, cv);
   }
 
   @Override
diff --git a/src/com/google/java/contract/core/agent/LineNumberingClassAdapter.java b/src/com/google/java/contract/core/agent/LineNumberingClassAdapter.java
index 60c81f0..5b2f318 100644
--- a/src/com/google/java/contract/core/agent/LineNumberingClassAdapter.java
+++ b/src/com/google/java/contract/core/agent/LineNumberingClassAdapter.java
@@ -45,7 +45,7 @@ class LineNumberingClassAdapter extends ClassVisitor {
     "ContractMethodSignatures.isLineNumberList(lineNumbers)"
   })
   public LineNumberingClassAdapter(ClassVisitor cv, List<Long> lineNumbers) {
-    super(Opcodes.ASM4, cv);
+    super(Opcodes.ASM5, cv);
     this.lineNumbers = lineNumbers;
   }
 
diff --git a/src/com/google/java/contract/core/agent/LineNumberingMethodAdapter.java b/src/com/google/java/contract/core/agent/LineNumberingMethodAdapter.java
index 882ede7..0e06bc6 100644
--- a/src/com/google/java/contract/core/agent/LineNumberingMethodAdapter.java
+++ b/src/com/google/java/contract/core/agent/LineNumberingMethodAdapter.java
@@ -61,7 +61,7 @@ abstract class LineNumberingMethodAdapter extends AdviceAdapter {
   })
   public LineNumberingMethodAdapter(MethodVisitor mv, int access,
                                     String name, String desc) {
-    super(Opcodes.ASM4, mv, access, name, desc);
+    super(Opcodes.ASM5, mv, access, name, desc);
     lineNumbers = null;
   }
 
diff --git a/src/com/google/java/contract/core/agent/SpecificationClassAdapter.java b/src/com/google/java/contract/core/agent/SpecificationClassAdapter.java
index 6cd1a1e..af0934f 100644
--- a/src/com/google/java/contract/core/agent/SpecificationClassAdapter.java
+++ b/src/com/google/java/contract/core/agent/SpecificationClassAdapter.java
@@ -18,9 +18,8 @@
  */
 package com.google.java.contract.core.agent;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Invariant;
-import com.google.java.contract.core.model.ClassName;
 import com.google.java.contract.core.model.ContractKind;
 import com.google.java.contract.core.util.DebugUtils;
 import org.objectweb.asm.ClassVisitor;
@@ -28,6 +27,7 @@ import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.tree.MethodNode;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -37,7 +37,7 @@ import java.util.List;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @author johannes.rieken at gmail.com (Johannes Rieken)
  */
- at AllowUnusedImport(ClassName.class)
+ at ContractImport("com.google.java.contract.core.model.ClassName")
 @Invariant({
   "getClassName() == null || ClassName.isBinaryName(getClassName())",
   "getContracts() != null",
@@ -49,7 +49,7 @@ class SpecificationClassAdapter extends ClassVisitor {
 
   public SpecificationClassAdapter(ClassVisitor cv,
                                    ContractAnalyzer contracts) {
-    super(Opcodes.ASM4, cv);
+    super(Opcodes.ASM5, cv);
     this.contracts = contracts;
   }
 
@@ -75,9 +75,10 @@ class SpecificationClassAdapter extends ClassVisitor {
   @Override
   public void visitEnd() {
     if (contracts != null) {
-      List<ClassContractHandle> accesses =
-          contracts.getClassHandles(ContractKind.ACCESS);
-      for (ClassContractHandle h : accesses) {
+      List<ClassContractHandle> synths = new ArrayList<ClassContractHandle>();
+      synths.addAll(contracts.getClassHandles(ContractKind.ACCESS));
+      synths.addAll(contracts.getClassHandles(ContractKind.LAMBDA));
+      for (ClassContractHandle h : synths) {
         h.getContractMethod().accept(cv);
       }
 
diff --git a/src/com/google/java/contract/core/agent/SpecificationMethodAdapter.java b/src/com/google/java/contract/core/agent/SpecificationMethodAdapter.java
index f546269..3b600c0 100644
--- a/src/com/google/java/contract/core/agent/SpecificationMethodAdapter.java
+++ b/src/com/google/java/contract/core/agent/SpecificationMethodAdapter.java
@@ -19,15 +19,12 @@
  */
 package com.google.java.contract.core.agent;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
-import com.google.java.contract.core.model.ClassName;
 import com.google.java.contract.core.model.ContractKind;
 import com.google.java.contract.core.util.DebugUtils;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
@@ -49,7 +46,11 @@ import java.util.List;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @author johannes.rieken at gmail.com (Johannes Rieken)
  */
- at AllowUnusedImport({ ClassName.class, Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.core.model.ClassName",
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "methodStart != null",
   "methodEnd != null",
@@ -133,7 +134,7 @@ public class SpecificationMethodAdapter extends AdviceAdapter {
                                     MethodVisitor mv,
                                     int access, String methodName,
                                     String methodDesc) {
-    super(Opcodes.ASM4, mv, access, methodName, methodDesc);
+    super(Opcodes.ASM5, mv, access, methodName, methodDesc);
 
     methodStart = new Label();
     methodEnd = new Label();
@@ -543,10 +544,10 @@ public class SpecificationMethodAdapter extends AdviceAdapter {
   protected void invokeContractMethod(MethodNode contractMethod) {
     if (!statik) {
       mv.visitMethodInsn(INVOKESPECIAL, className,
-                         contractMethod.name, contractMethod.desc);
+                         contractMethod.name, contractMethod.desc, false);
     } else {
       mv.visitMethodInsn(INVOKESTATIC, className,
-                         contractMethod.name, contractMethod.desc);
+                         contractMethod.name, contractMethod.desc, false);
     }
   }
 
diff --git a/src/com/google/java/contract/core/apt/AbstractTypeBuilder.java b/src/com/google/java/contract/core/apt/AbstractTypeBuilder.java
index 2049d44..bb96167 100644
--- a/src/com/google/java/contract/core/apt/AbstractTypeBuilder.java
+++ b/src/com/google/java/contract/core/apt/AbstractTypeBuilder.java
@@ -29,6 +29,7 @@ import com.google.java.contract.core.model.TypeName;
 import com.google.java.contract.core.util.JavaUtils;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -256,19 +257,37 @@ abstract class AbstractTypeBuilder
   @Ensures("result != null")
   @SuppressWarnings("unchecked")
   protected Set<String> getImportNames(Element element) {
+    HashSet<String> importNames = new HashSet<String>();
     if (JavaUtils.classExists("com.sun.source.util.Trees")) {
       try {
-        return (Set<String>) Class
+        Set<String> classImportNames = (Set<String>) Class
             .forName("com.google.java.contract.core.apt.JavacUtils")
             .getMethod("getImportNames", ProcessingEnvironment.class,
                        Element.class)
             .invoke(null, utils.processingEnv, element);
+        importNames.addAll(classImportNames);
       } catch (Exception e) {
-        return Collections.emptySet();
+        /* Ignore. */
+      }
+    }
+
+    /* Add import statements from explicit annotations. */
+    for (AnnotationMirror ann : element.getAnnotationMirrors()) {
+      if (!ann.getAnnotationType().toString()
+          .equals("com.google.java.contract.ContractImport")) {
+        continue;
+      }
+      for (AnnotationValue annotationValue :
+           ann.getElementValues().values()) {
+        @SuppressWarnings("unchecked")
+        List<? extends AnnotationValue> values =
+            (List<? extends AnnotationValue>) annotationValue.getValue();
+        for (AnnotationValue value : values)
+        importNames.add((String) value.getValue());
       }
-    } else {
-      return Collections.emptySet();
     }
+
+    return importNames;
   }
 
   /**
diff --git a/src/com/google/java/contract/core/apt/AnnotationProcessor.java b/src/com/google/java/contract/core/apt/AnnotationProcessor.java
index 732b7ba..f66742e 100644
--- a/src/com/google/java/contract/core/apt/AnnotationProcessor.java
+++ b/src/com/google/java/contract/core/apt/AnnotationProcessor.java
@@ -18,10 +18,7 @@
  */
 package com.google.java.contract.core.apt;
 
-import com.sun.tools.javac.main.OptionName;
-import com.sun.tools.javac.processing.JavacProcessingEnvironment;
-import com.sun.tools.javac.util.Options;
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Requires;
 import com.google.java.contract.core.model.ContractAnnotationModel;
@@ -32,6 +29,7 @@ import com.google.java.contract.core.util.SyntheticJavaFile;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -47,7 +45,6 @@ import javax.annotation.processing.SupportedOptions;
 import javax.annotation.processing.SupportedSourceVersion;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.util.ElementScanner6;
 import javax.tools.JavaCompiler.CompilationTask;
@@ -61,7 +58,7 @@ import javax.tools.JavaFileObject.Kind;
  * @author johannes.rieken at gmail.com (Johannes Rieken)
  * @author chatain at google.com (Leonardo Chatain)
  */
- at AllowUnusedImport(ElementKind.class)
+ at ContractImport("javax.lang.model.element.ElementKind")
 @SupportedAnnotationTypes("*")
 @SupportedOptions({
   AnnotationProcessor.OPT_DEBUG,
@@ -130,6 +127,61 @@ public class AnnotationProcessor extends AbstractProcessor {
   protected boolean debug;
   protected boolean dump;
 
+  private Class<?> javacProcessingEnvironmentClass;
+  private Method getContextMethod;
+  private Method optionsInstanceMethod;
+  private Method optionsGetMethod;
+
+  /**
+   * Initialize classes and methods needed for OpenJDK javac reflection.
+   */
+  private void setupReflection() {
+    try {
+      javacProcessingEnvironmentClass = Class.forName(
+          "com.sun.tools.javac.processing.JavacProcessingEnvironment");
+    } catch (ClassNotFoundException e) {
+      /* Javac isn't on the classpath. */
+      return;
+    }
+
+    try {
+      Class<?> contextClass = Class.forName("com.sun.tools.javac.util.Context");
+      getContextMethod = javacProcessingEnvironmentClass.getMethod("getContext");
+      Class<?> optionsClass = Class.forName("com.sun.tools.javac.util.Options");
+      optionsInstanceMethod = optionsClass.getMethod("instance", contextClass);
+      optionsGetMethod = optionsClass.getMethod("get", String.class);
+    } catch (Exception e) {
+      throw new LinkageError(e.getMessage());
+    }
+  }
+
+  /**
+   * Calls {@code com.sun.tools.javac.util.Options.get(String)} reflectively.
+   */
+  private String getJavacOption(Object options, String name) {
+    try {
+      return (String) optionsGetMethod.invoke(options, name);
+    } catch (Exception e) {
+      throw new LinkageError(e.getMessage());
+    }
+  }
+
+  /**
+   * Calls {@code com.sun.tools.javac.util.Options.instance(Context)}
+   * reflectively.
+   */
+  private Object getJavacOptions() {
+    if (!javacProcessingEnvironmentClass.isInstance(processingEnv)) {
+      return null;
+    }
+    try {
+      return optionsInstanceMethod.invoke(null,
+          getContextMethod.invoke(processingEnv));
+    } catch (Exception e) {
+      throw new LinkageError(e.getMessage());
+    }
+  }
+
   @Override
   public synchronized void init(ProcessingEnvironment processingEnv) {
     super.init(processingEnv);
@@ -145,6 +197,7 @@ public class AnnotationProcessor extends AbstractProcessor {
     utils = new FactoryUtils(processingEnv);
     factory = new TypeFactory(utils, options.get(OPT_DEPSPATH));
 
+    setupReflection();
     setupPaths();
   }
 
@@ -209,39 +262,34 @@ public class AnnotationProcessor extends AbstractProcessor {
     outputDirectory = processingEnv.getOptions().get(OPT_CLASSOUTPUT);
 
     /*
-     * Not using instanceof here because than in every case the JVM
-     * tries to load the JavacProcessingEnvironment-class file which,
-     * for instance, is not possible with an IBM JVM.
-     *
-     * TODO(lenh): This may not work; use reflection call instead.
+     * Load classes in com.sun.tools reflectively for graceful fallback when
+     * the OpenJDK javac isn't available (e.g. with J9, or ecj).
      */
-    if (processingEnv.getClass().getName().equals(
-            "com.sun.tools.javac.processing.JavacProcessingEnvironment")) {
-      JavacProcessingEnvironment javacEnv =
-          (JavacProcessingEnvironment) processingEnv;
-      Options options = Options.instance(javacEnv.getContext());
-
-      if (sourcePath == null) {
-        sourcePath = options.get(OptionName.SOURCEPATH);
-      }
+    Object options = getJavacOptions();
+    if (options == null) {
+      return;
+    }
+
+    if (sourcePath == null) {
+      sourcePath = getJavacOption(options, "-sourcepath");
+    }
 
-      if (classPath == null) {
-        String classPath1 = options.get(OptionName.CP);
-        String classPath2 = options.get(OptionName.CLASSPATH);
-        if (classPath1 != null) {
-          if (classPath2 != null) {
-            classPath = classPath1 + File.pathSeparator + classPath2;
-          } else {
-            classPath = classPath1;
-          }
+    if (classPath == null) {
+      String classPath1 = getJavacOption(options, "-cp");
+      String classPath2 = getJavacOption(options, "-classpath");
+      if (classPath1 != null) {
+        if (classPath2 != null) {
+          classPath = classPath1 + File.pathSeparator + classPath2;
         } else {
-          classPath = classPath2;
+          classPath = classPath1;
         }
+      } else {
+        classPath = classPath2;
       }
+    }
 
-      if (outputDirectory == null) {
-        outputDirectory = options.get(OptionName.D);
-      }
+    if (outputDirectory == null) {
+      outputDirectory = getJavacOption(options, "-d");
     }
   }
 
diff --git a/src/com/google/java/contract/core/apt/ContractCreation.java b/src/com/google/java/contract/core/apt/ContractCreation.java
index 3afbcb0..06a38c8 100644
--- a/src/com/google/java/contract/core/apt/ContractCreation.java
+++ b/src/com/google/java/contract/core/apt/ContractCreation.java
@@ -18,6 +18,7 @@
  */
 package com.google.java.contract.core.apt;
 
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Requires;
 import com.google.java.contract.core.model.ClassName;
@@ -47,6 +48,7 @@ import java.util.Set;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
+ at ContractImport("com.google.java.contract.core.model.ClassName")
 public class ContractCreation {
   static final String RAISE_METHOD =
       "com.google.java.contract.core.runtime.ContractRuntime.raise";
diff --git a/src/com/google/java/contract/core/apt/ContractCreationTrait.java b/src/com/google/java/contract/core/apt/ContractCreationTrait.java
index 47daa81..2f214d5 100644
--- a/src/com/google/java/contract/core/apt/ContractCreationTrait.java
+++ b/src/com/google/java/contract/core/apt/ContractCreationTrait.java
@@ -17,10 +17,9 @@
  */
 package com.google.java.contract.core.apt;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Requires;
-import com.google.java.contract.core.model.ClassName;
 import com.google.java.contract.core.model.ContractAnnotationModel;
 import com.google.java.contract.core.model.VariableModel;
 
@@ -32,7 +31,7 @@ import java.util.List;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
- at AllowUnusedImport(ClassName.class)
+ at ContractImport("com.google.java.contract.core.model.ClassName")
 interface ContractCreationTrait {
   /**
    * Called before any of the query methods of this object. Queries
diff --git a/src/com/google/java/contract/core/apt/ContractExpressionTransformer.java b/src/com/google/java/contract/core/apt/ContractExpressionTransformer.java
index e4218c0..2801de2 100644
--- a/src/com/google/java/contract/core/apt/ContractExpressionTransformer.java
+++ b/src/com/google/java/contract/core/apt/ContractExpressionTransformer.java
@@ -132,10 +132,6 @@ public class ContractExpressionTransformer {
     oldId = 0;
   }
 
-  public void setAcceptOld(boolean acceptOld) {
-    this.acceptOld = acceptOld;
-  }
-
   @Requires({
     "currentBuffer != null",
     "tokenizer != null",
diff --git a/src/com/google/java/contract/core/apt/ContractJavaCompiler.java b/src/com/google/java/contract/core/apt/ContractJavaCompiler.java
index c9b26b6..68dbd09 100644
--- a/src/com/google/java/contract/core/apt/ContractJavaCompiler.java
+++ b/src/com/google/java/contract/core/apt/ContractJavaCompiler.java
@@ -48,6 +48,9 @@ import javax.tools.ToolProvider;
   "fileManager != null"
 })
 public class ContractJavaCompiler {
+  private static final String JAVA_VERSION =
+      System.getProperty("java.specification.version");
+
   /**
    * The compiler options passed to the internal compiler.
    */
@@ -55,7 +58,11 @@ public class ContractJavaCompiler {
       Arrays.asList(
           "-g:source,vars", /* Source file name, for debug attributes. */
           "-proc:none",     /* No further annotations to process. */
-          "-implicit:none"  /* No class files for implicit dependencies. */
+          "-implicit:none", /* No class files for implicit dependencies. */
+          /* Target the highest Java version supported by the runtime, instead
+           * of the highest supported by the compiler. */
+          "-source", JAVA_VERSION,
+          "-target", JAVA_VERSION
       );
 
   /**
diff --git a/src/com/google/java/contract/core/apt/ContractWriter.java b/src/com/google/java/contract/core/apt/ContractWriter.java
index 80a5d36..e4a8e89 100644
--- a/src/com/google/java/contract/core/apt/ContractWriter.java
+++ b/src/com/google/java/contract/core/apt/ContractWriter.java
@@ -18,7 +18,7 @@
  */
 package com.google.java.contract.core.apt;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
@@ -33,8 +33,6 @@ import com.google.java.contract.core.model.TypeName;
 import com.google.java.contract.core.model.VariableModel;
 import com.google.java.contract.core.util.ElementScanner;
 import com.google.java.contract.core.util.Elements;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -57,7 +55,11 @@ import java.util.regex.Pattern;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @author chatain at google.com (Leonardo Chatain)
  */
- at AllowUnusedImport({ Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.core.model.ElementKind",
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "getLineNumberMap() != null",
   "Iterables.all(getLineNumberMap().keySet(), Predicates.between(1L, null))",
diff --git a/src/com/google/java/contract/core/apt/MethodContractCreator.java b/src/com/google/java/contract/core/apt/MethodContractCreator.java
index ff7d249..3377f0f 100644
--- a/src/com/google/java/contract/core/apt/MethodContractCreator.java
+++ b/src/com/google/java/contract/core/apt/MethodContractCreator.java
@@ -50,7 +50,9 @@ import java.util.NoSuchElementException;
  */
 @Invariant({
   "diagnosticManager != null",
-  "transformer != null"
+  "preTransformer != null",
+  "postTransformer != null",
+  "postSignalTransformer != null"
 })
 public class MethodContractCreator extends ElementScanner {
   /**
@@ -67,7 +69,6 @@ public class MethodContractCreator extends ElementScanner {
     @Override
     public boolean transform(List<String> code, List<Long> lineNumbers,
                              Object sourceInfo) {
-      transformer.setAcceptOld(false);
       return super.transform(code, lineNumbers, sourceInfo);
     }
 
@@ -93,7 +94,6 @@ public class MethodContractCreator extends ElementScanner {
     public boolean transform(List<String> code, List<Long> lineNumbers,
                               Object sourceInfo) {
       int id = transformer.getNextOldId();
-      transformer.setAcceptOld(true);
       boolean success = super.transform(code, lineNumbers, sourceInfo);
 
       if (success) {
@@ -242,7 +242,9 @@ public class MethodContractCreator extends ElementScanner {
   protected ContractMethodModel postMethod;
   protected ContractMethodModel postSignalMethod;
 
-  protected ContractExpressionTransformer transformer;
+  protected ContractExpressionTransformer preTransformer;
+  protected ContractExpressionTransformer postTransformer;
+  protected ContractExpressionTransformer postSignalTransformer;
 
   /**
    * Constructs a new MethodContractCreator.
@@ -254,7 +256,12 @@ public class MethodContractCreator extends ElementScanner {
     preMethod = null;
     postMethod = null;
     postSignalMethod = null;
-    transformer = new ContractExpressionTransformer(diagnosticManager, true);
+    preTransformer =
+        new ContractExpressionTransformer(diagnosticManager, false);
+    postTransformer =
+        new ContractExpressionTransformer(diagnosticManager, true);
+    postSignalTransformer =
+        new ContractExpressionTransformer(diagnosticManager, true);
   }
 
   @Override
@@ -271,14 +278,15 @@ public class MethodContractCreator extends ElementScanner {
     List<String> code = annotation.getValues();
 
     if (annotation.getKind().equals(ElementKind.REQUIRES)) {
-      PreMethodCreationTrait trait = new PreMethodCreationTrait(transformer);
+      PreMethodCreationTrait trait = new PreMethodCreationTrait(preTransformer);
       preMethod = createContractMethods(trait, preMethod, annotation);
     } else if (annotation.getKind().equals(ElementKind.ENSURES)) {
-      PostMethodCreationTrait trait = new PostMethodCreationTrait(transformer);
+      PostMethodCreationTrait trait =
+          new PostMethodCreationTrait(postTransformer);
       postMethod = createContractMethods(trait, postMethod, annotation);
     } else if (annotation.getKind().equals(ElementKind.THROW_ENSURES)) {
       PostSignalMethodCreationTrait trait =
-          new PostSignalMethodCreationTrait(transformer);
+          new PostSignalMethodCreationTrait(postSignalTransformer);
       postSignalMethod = createContractMethods(trait, postSignalMethod,
                                                annotation);
     } else {
diff --git a/src/com/google/java/contract/core/apt/SourceDependencyParser.java b/src/com/google/java/contract/core/apt/SourceDependencyParser.java
index be868b8..86ce231 100644
--- a/src/com/google/java/contract/core/apt/SourceDependencyParser.java
+++ b/src/com/google/java/contract/core/apt/SourceDependencyParser.java
@@ -17,7 +17,7 @@
  */
 package com.google.java.contract.core.apt;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
@@ -26,8 +26,6 @@ import com.google.java.contract.core.util.BalancedTokenizer;
 import com.google.java.contract.core.util.JavaTokenizer;
 import com.google.java.contract.core.util.JavaUtils;
 import com.google.java.contract.core.util.JavaUtils.ParseException;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 
 import java.io.Reader;
 import java.util.ArrayList;
@@ -46,7 +44,10 @@ import java.util.Set;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
- at AllowUnusedImport({ Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "!canQueryResults() || getImportNames() != null",
   "!canQueryResults() || !getImportNames().contains(null)",
diff --git a/src/com/google/java/contract/core/model/ContractAnnotationModel.java b/src/com/google/java/contract/core/model/ContractAnnotationModel.java
index 424656e..0aa3485 100644
--- a/src/com/google/java/contract/core/model/ContractAnnotationModel.java
+++ b/src/com/google/java/contract/core/model/ContractAnnotationModel.java
@@ -18,12 +18,10 @@
  */
 package com.google.java.contract.core.model;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -34,7 +32,10 @@ import java.util.List;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
- at AllowUnusedImport({ Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "!isWeakVirtual() || isVirtual()",
   "getValues() != null",
diff --git a/src/com/google/java/contract/core/model/ContractKind.java b/src/com/google/java/contract/core/model/ContractKind.java
index c3c5ea7..62ee985 100644
--- a/src/com/google/java/contract/core/model/ContractKind.java
+++ b/src/com/google/java/contract/core/model/ContractKind.java
@@ -75,6 +75,12 @@ public enum ContractKind {
   ACCESS,
 
   /**
+   * A synthetic lambda method, generated by the Java compiler, used
+   * in contract methods.
+   */
+  LAMBDA,
+
+  /**
    * A contract helper, for indirect contract evaluation.
    *
    * @see ContractCreator
@@ -200,6 +206,8 @@ public enum ContractKind {
         return "com$google$java$contract$EO";
       case ACCESS:
         return "access";
+      case LAMBDA:
+        return "lambda";
       default:
         throw new IllegalArgumentException();
     }
diff --git a/src/com/google/java/contract/core/model/ElementModel.java b/src/com/google/java/contract/core/model/ElementModel.java
index 29279cf..7920653 100644
--- a/src/com/google/java/contract/core/model/ElementModel.java
+++ b/src/com/google/java/contract/core/model/ElementModel.java
@@ -18,13 +18,11 @@
  */
 package com.google.java.contract.core.model;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
-import com.google.java.contract.util.Iterables;
 import com.google.java.contract.util.Predicate;
-import com.google.java.contract.util.Predicates;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -36,7 +34,7 @@ import java.util.List;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
- at AllowUnusedImport({ Iterables.class, Predicates.class })
+ at ContractImport("com.google.java.contract.util.Iterables")
 @Invariant({
   "getEnclosingElement() == null " +
       "|| getEnclosingElement().getEnclosedElements().contains(this)",
diff --git a/src/com/google/java/contract/core/util/JavaUtils.java b/src/com/google/java/contract/core/util/JavaUtils.java
index 155ba0d..58ae209 100644
--- a/src/com/google/java/contract/core/util/JavaUtils.java
+++ b/src/com/google/java/contract/core/util/JavaUtils.java
@@ -17,10 +17,9 @@
  */
 package com.google.java.contract.core.util;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Requires;
-import com.google.java.contract.core.model.ClassName;
 
 import java.io.File;
 import java.io.InputStream;
@@ -37,7 +36,7 @@ import javax.tools.JavaFileObject.Kind;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
- at AllowUnusedImport(ClassName.class)
+ at ContractImport("com.google.java.contract.core.model.ClassName")
 public class JavaUtils {
   /**
    * A parse error; thrown by the parsing functions whenever they read
diff --git a/src/com/google/java/contract/core/util/PatternMap.java b/src/com/google/java/contract/core/util/PatternMap.java
index 177f610..4e8685b 100644
--- a/src/com/google/java/contract/core/util/PatternMap.java
+++ b/src/com/google/java/contract/core/util/PatternMap.java
@@ -17,13 +17,11 @@
  */
 package com.google.java.contract.core.util;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
 import com.google.java.contract.core.model.ClassName;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 
 import java.util.TreeMap;
 
@@ -46,7 +44,11 @@ import java.util.TreeMap;
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  * @param <R> the type of a rule
  */
- at AllowUnusedImport({ Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.core.model.ClassName",
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant("root != null")
 public class PatternMap<R> {
   /**
diff --git a/src/com/google/java/contract/core/util/SyntheticJavaFile.java b/src/com/google/java/contract/core/util/SyntheticJavaFile.java
index 08e8bd0..b2553d5 100644
--- a/src/com/google/java/contract/core/util/SyntheticJavaFile.java
+++ b/src/com/google/java/contract/core/util/SyntheticJavaFile.java
@@ -18,11 +18,9 @@
  */
 package com.google.java.contract.core.util;
 
-import com.google.java.contract.AllowUnusedImport;
+import com.google.java.contract.ContractImport;
 import com.google.java.contract.Invariant;
 import com.google.java.contract.Requires;
-import com.google.java.contract.util.Iterables;
-import com.google.java.contract.util.Predicates;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -35,7 +33,10 @@ import javax.tools.SimpleJavaFileObject;
  *
  * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
  */
- at AllowUnusedImport({ Iterables.class, Predicates.class })
+ at ContractImport({
+  "com.google.java.contract.util.Iterables",
+  "com.google.java.contract.util.Predicates"
+})
 @Invariant({
   "content != null",
   "lineNumberMap == null " +
diff --git a/test/com/google/java/contract/tests/ConstantContracts.java b/test/com/google/java/contract/tests/ConstantContracts.java
index 291c759..bb23682 100644
--- a/test/com/google/java/contract/tests/ConstantContracts.java
+++ b/test/com/google/java/contract/tests/ConstantContracts.java
@@ -20,6 +20,7 @@ package com.google.java.contract.tests;
 
 import com.google.java.contract.Ensures;
 import com.google.java.contract.Requires;
+import com.google.java.contract.ThrowEnsures;
 
 /**
  * This class exposes some constant contracts.
@@ -67,4 +68,27 @@ class ConstantContracts {
   @Ensures("true")
   public void postSuccess3() {
   }
+
+  @Ensures("old (true)")
+  public void oldSuccess() {
+  }
+
+  @Ensures({ "old (true)", "old (true)" })
+  public void oldSuccess1() {
+  }
+
+  @Ensures("old (true)")
+  @ThrowEnsures({ "RuntimeException", "old (true)" })
+  public void oldSuccess2() {
+  }
+
+  @Ensures("old (true)")
+  @ThrowEnsures({ "RuntimeException", "old (true)" })
+  public void oldSuccess3() {
+    throw new RuntimeException();
+  }
+
+  @Ensures("old (false)")
+  public void oldFailure() {
+  }
 }
diff --git a/test/com/google/java/contract/tests/ConstantContractsTest.java b/test/com/google/java/contract/tests/ConstantContractsTest.java
index 060afcd..8b78e33 100644
--- a/test/com/google/java/contract/tests/ConstantContractsTest.java
+++ b/test/com/google/java/contract/tests/ConstantContractsTest.java
@@ -91,4 +91,34 @@ public class ConstantContractsTest extends TestCase {
   public void testPostSuccess3() {
     sample.postSuccess3();
   }
+
+  public void testOldSuccess() {
+    sample.oldSuccess();
+  }
+
+  public void testOldSuccess1() {
+    sample.oldSuccess1();
+  }
+
+  public void testOldSuccess2() {
+    sample.oldSuccess2();
+  }
+
+  public void testOldSuccess3() {
+    try {
+      sample.oldSuccess3();
+      fail();
+    } catch (RuntimeException expected) {
+      /* Expected. */
+    }
+  }
+
+  public void testOldFailure() {
+    try {
+      sample.oldFailure();
+      fail();
+    } catch (PostconditionError expected) {
+      /* Expected. */
+    }
+  }
 }
diff --git a/test/com/google/java/contract/tests/ConstructorTest.java b/test/com/google/java/contract/tests/ConstructorTest.java
index 39dd1ef..d15ddd5 100644
--- a/test/com/google/java/contract/tests/ConstructorTest.java
+++ b/test/com/google/java/contract/tests/ConstructorTest.java
@@ -23,7 +23,6 @@ import com.google.java.contract.PreconditionError;
 import com.google.java.contract.Requires;
 import junit.framework.TestCase;
 
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 
 /**
@@ -82,10 +81,11 @@ public class ConstructorTest extends TestCase {
     }
   }
 
-  private static class In extends FileInputStream {
-    @Requires("!name.equals(\"/dev/null\")")
+  private static class In {
+    @Requires("!\"/dev/null\".equals(name)")
     public In(String name) throws FileNotFoundException {
-      super(name);
+      if (!"/dev/null".equals(name))
+        throw new FileNotFoundException(name);
     }
   }
 
@@ -154,7 +154,7 @@ public class ConstructorTest extends TestCase {
       In in = new In("/dev/null");
       fail();
     } catch (PreconditionError expected) {
-      assertEquals("[!name.equals(\"/dev/null\")]", expected.getMessages().toString());
+      assertEquals("[!\"/dev/null\".equals(name)]", expected.getMessages().toString());
     }
   }
 }
diff --git a/test/com/google/java/contract/tests/GenericsTest.java b/test/com/google/java/contract/tests/GenericsTest.java
index f5f576c..70f9691 100644
--- a/test/com/google/java/contract/tests/GenericsTest.java
+++ b/test/com/google/java/contract/tests/GenericsTest.java
@@ -107,7 +107,7 @@ public class GenericsTest extends TestCase {
     }
   }
 
-  private static class T<X extends Throwable> {
+  private static class TT<X extends Throwable> {
     /* Test ability to contract methods that throw type parameters. */
     @Requires("true")
     public void f(X x) throws X {
@@ -115,7 +115,7 @@ public class GenericsTest extends TestCase {
     }
   }
 
-  private static class Npe extends T<NullPointerException> {
+  private static class Npe extends TT<NullPointerException> {
     @Override
     @Ensures("true")
     public void f(NullPointerException x) throws NullPointerException {
diff --git a/test/com/google/java/contract/tests/Java8StreamTest.java b/test/com/google/java/contract/tests/Java8StreamTest.java
new file mode 100644
index 0000000..e2b2286
--- /dev/null
+++ b/test/com/google/java/contract/tests/Java8StreamTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Nhat Minh Lê
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package com.google.java.contract.tests;
+
+import com.google.java.contract.Invariant;
+import com.google.java.contract.InvariantError;
+import com.google.java.contract.PreconditionError;
+import com.google.java.contract.Requires;
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Tests Java 8 lambdas on collections in contracts.
+ *
+ * @author nhat.minh.le at huoc.org (Nhat Minh Lê)
+ */
+public class Java8StreamTest extends TestCase {
+  @Invariant("xs.stream().allMatch(x -> x >= 0)")
+  private static class A {
+    protected List<Integer> xs;
+    private int k;
+
+    public A(Collection<Integer> xs, int k) {
+      /* Bogus if xs contains negative elements. */
+      this.xs = new ArrayList(xs);
+      this.k = k;
+    }
+
+    @Requires("xs.stream().reduce(0, (x, y) -> x + y) % 2 == 0")
+    public static boolean onlyEven(Collection<Integer> xs) {
+      return true;
+    }
+
+    @Requires("xs.stream().reduce(0, (x, y) -> k + x + y) % 2 == 0")
+    public boolean instanceCapture() {
+      return true;
+    }
+  }
+
+  public void testA() {
+    A b = new A(Arrays.asList(1, 2, 3, 4), 42);
+  }
+
+  public void testABogus() {
+    try {
+      A b = new A(Arrays.asList(1, 2, 3, -4), 42);
+    } catch (InvariantError expected) {
+      assertEquals("[xs.stream().allMatch(x -> x >= 0)]",
+                   expected.getMessages().toString());
+    }
+  }
+
+  public void testOnlyEven() {
+    boolean b = A.onlyEven(Arrays.asList(2, 3, 3));
+  }
+
+  public void testOnlyEvenInvalid() {
+    try {
+      boolean b = A.onlyEven(Arrays.asList(2, 3, 4));
+    } catch (PreconditionError expected) {
+      assertEquals("[xs.stream().reduce(0, (x, y) -> x + y) % 2 == 0]",
+                   expected.getMessages().toString());
+    }
+  }
+
+  public void testInstanceCapture() {
+    boolean b = new A(Arrays.asList(2, 3, 4), 11).instanceCapture();
+  }
+
+  public void testInstanceCaptureInvalid() {
+    try {
+      boolean b = new A(Arrays.asList(2, 3, 3), 11).instanceCapture();
+    } catch (PreconditionError expected) {
+      assertEquals("[xs.stream().reduce(0, (x, y) -> k + x + y) % 2 == 0]",
+                   expected.getMessages().toString());
+    }
+  }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libcofoja-java.git



More information about the pkg-java-commits mailing list