[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