You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/08/06 06:21:11 UTC

[groovy] branch master updated: GROOVY-9671: Absorb GContracts project into Groovy project - initial code checkin (closes #1337)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 2c8fc42  GROOVY-9671: Absorb GContracts project into Groovy project - initial code checkin (closes #1337)
2c8fc42 is described below

commit 2c8fc42710a5527911c21ca37aea7b6c83d954f9
Author: Andre Steingress <me...@andresteingress.com>
AuthorDate: Sun Jul 19 21:24:24 2020 +1000

    GROOVY-9671: Absorb GContracts project into Groovy project - initial code checkin (closes #1337)
    
    Co-authored-by: Paul King <pa...@asert.com.au>
---
 gradle/upload.gradle                               |   1 +
 settings.gradle                                    |   1 +
 .../codehaus/groovy/ast/tools/GeneralUtils.java    |   2 +-
 src/spec/doc/index.adoc                            |   2 +
 subprojects/groovy-contracts/build.gradle          |  34 ++
 .../src/main/java/groovy/contracts/Contracted.java |  41 ++
 .../src/main/java/groovy/contracts/Ensures.java    |  86 ++++
 .../src/main/java/groovy/contracts/Invariant.java  |  59 +++
 .../src/main/java/groovy/contracts/Requires.java   |  63 +++
 .../groovy/contracts/AssertionViolation.java       |  64 +++
 .../contracts/CircularAssertionCallException.java  |  42 ++
 .../groovy/contracts/ClassInvariantViolation.java  |  58 +++
 .../groovy/contracts/PostconditionViolation.java   |  58 +++
 .../groovy/contracts/PreconditionViolation.java    |  58 +++
 .../apache/groovy/contracts/ViolationTracker.java  |  75 +++
 .../meta/AnnotationProcessorImplementation.java    |  37 ++
 .../contracts/annotations/meta/ClassInvariant.java |  33 ++
 .../annotations/meta/ContractElement.java          |  32 ++
 .../contracts/annotations/meta/Postcondition.java  |  33 ++
 .../contracts/annotations/meta/Precondition.java   |  33 ++
 .../contracts/ast/BaseASTTransformation.java       |  58 +++
 ...osureExpressionEvaluationASTTransformation.java |  84 ++++
 .../contracts/ast/GContractsASTTransformation.java |  78 +++
 .../contracts/ast/visitor/ASTNodeMetaData.java     |  27 ++
 .../ast/visitor/AnnotationClosureVisitor.java      | 534 +++++++++++++++++++++
 .../AnnotationContractParameterVisitor.java        |  69 +++
 .../ast/visitor/AnnotationProcessorVisitor.java    | 243 ++++++++++
 .../groovy/contracts/ast/visitor/BaseVisitor.java  |  51 ++
 .../contracts/ast/visitor/ConfigurationSetup.java  |  54 +++
 .../ast/visitor/ContractElementVisitor.java        |  84 ++++
 .../ast/visitor/DomainModelInjectionVisitor.java   |  99 ++++
 .../ast/visitor/DynamicSetterInjectionVisitor.java | 105 ++++
 .../LifecycleAfterTransformationVisitor.java       |  69 +++
 .../LifecycleBeforeTransformationVisitor.java      |  69 +++
 .../classgen/asm/ContractClosureWriter.java        | 193 ++++++++
 .../contracts/common/base/BaseLifecycle.java       |  52 ++
 .../impl/ClassInvariantAnnotationProcessor.java    |  41 ++
 .../common/impl/EnsuresAnnotationProcessor.java    |  47 ++
 .../common/impl/RequiresAnnotationProcessor.java   |  42 ++
 .../common/impl/lc/ClassInvariantLifecycle.java    |  51 ++
 .../common/impl/lc/PostconditionLifecycle.java     |  65 +++
 .../common/impl/lc/PreconditionLifecycle.java      |  51 ++
 .../contracts/common/spi/AnnotationProcessor.java  |  40 ++
 .../groovy/contracts/common/spi/Lifecycle.java     |  77 +++
 .../common/spi/ProcessingContextInformation.java   | 110 +++++
 .../apache/groovy/contracts/domain/Assertion.java  |  98 ++++
 .../groovy/contracts/domain/AssertionMap.java      |  77 +++
 .../groovy/contracts/domain/ClassInvariant.java    |  38 ++
 .../apache/groovy/contracts/domain/Contract.java   |  67 +++
 .../groovy/contracts/domain/Postcondition.java     |  42 ++
 .../groovy/contracts/domain/Precondition.java      |  35 ++
 .../generation/AssertStatementCreationUtility.java | 235 +++++++++
 .../groovy/contracts/generation/BaseGenerator.java | 194 ++++++++
 .../contracts/generation/CandidateChecks.java      | 150 ++++++
 .../generation/ClassInvariantGenerator.java        | 143 ++++++
 .../groovy/contracts/generation/Configurator.java  | 106 ++++
 .../generation/ContractExecutionTracker.java       | 101 ++++
 .../generation/OldVariableGenerationUtility.java   | 124 +++++
 .../generation/PostconditionGenerator.java         | 155 ++++++
 .../generation/PreconditionGenerator.java          | 120 +++++
 .../generation/TryCatchBlockGenerator.java         | 132 +++++
 .../groovy/contracts/util/AnnotationUtils.java     | 132 +++++
 .../groovy/contracts/util/ExpressionUtils.java     | 137 ++++++
 .../apache/groovy/contracts/util/FieldValues.java  |  60 +++
 .../util/LifecycleImplementationLoader.java        | 211 ++++++++
 .../org/apache/groovy/contracts/util/Validate.java |  34 ++
 ...rg.apache.groovy.contracts.common.spi.Lifecycle |  17 +
 ...org.codehaus.groovy.transform.ASTTransformation |  18 +
 .../src/main/resources/dsld/org.gcontracts.dsld    |  53 ++
 .../src/main/resources/org.gcontracts.gdsl         |  40 ++
 .../src/spec/doc/contracts-userguide.adoc          |  70 +++
 .../src/spec/test/ContractsTest.groovy             | 119 +++++
 .../compability/CompileStaticTests.groovy          |  96 ++++
 .../compability/EqualsAndHashCodeTests.groovy      |  45 ++
 .../contracts/compability/ImmutableTests.groovy    |  40 ++
 .../groovy/contracts/compability/LockTests.groovy  |  55 +++
 .../contracts/compability/SynchronizedTests.groovy |  44 ++
 .../contracts/compability/ToStringTests.groovy     |  46 ++
 .../compability/TupleConstructorTests.groovy       |  39 ++
 .../contracts/compability/TypeCheckedTests.groovy  |  99 ++++
 .../groovy/contracts/domain/ContractTests.groovy   | 114 +++++
 .../ContractExecutionTrackerTests.groovy           |  64 +++
 .../contracts/spock/SpockIntegrationTests.groovy   |  48 ++
 .../contracts/tests/basic/BaseTestClass.groovy     | 174 +++++++
 .../tests/doc/DocumentationExampleTests.groovy     | 151 ++++++
 .../tests/doc/RootClassExampleTests.groovy         | 264 ++++++++++
 .../contracts/tests/doc/StackExampleTests.groovy   | 181 +++++++
 .../AbstractClassInheritanceTests.groovy           | 118 +++++
 .../tests/interfaces/AbstractClassTests.groovy     | 117 +++++
 .../InterfaceAbstractClassMixturesTests.groovy     | 109 +++++
 .../SimpleInterfaceInheritanceTests.groovy         | 132 +++++
 .../tests/interfaces/StackExampleTests.groovy      |  98 ++++
 .../contracts/tests/inv/InheritanceTests.groovy    | 416 ++++++++++++++++
 .../tests/inv/POGOClassInvariantTests.groovy       |  81 ++++
 .../tests/inv/SimpleClassInvariantTests.groovy     | 305 ++++++++++++
 .../tests/other/AbstractClassTests.groovy          |  87 ++++
 .../tests/other/CandidateChecksTests.groovy        |  73 +++
 .../tests/other/CircularAssertionCallTests.groovy  |  83 ++++
 .../other/ClosureExpressionValidationTests.groovy  | 202 ++++++++
 .../tests/other/ContractLabelTests.groovy          | 132 +++++
 .../contracts/tests/other/ContractedTests.groovy   |  89 ++++
 .../contracts/tests/other/GenericTypeTests.groovy  |  79 +++
 .../tests/other/MissingLineNumberTests.groovy      | 117 +++++
 .../tests/other/NotContractedTests.groovy          |  87 ++++
 .../ImplicitVariableNamesPostconditionTests.groovy |  47 ++
 .../contracts/tests/post/InheritanceTests.groovy   | 287 +++++++++++
 .../post/OldVariablePostconditionTests.groovy      | 169 +++++++
 .../ResultAndOldVariablePostconditionTests.groovy  |  66 +++
 .../post/ResultVariablePostconditionTests.groovy   |  59 +++
 .../tests/post/SimplePostconditionTests.groovy     | 296 ++++++++++++
 .../contracts/tests/pre/InheritanceTests.groovy    | 182 +++++++
 .../tests/pre/SimplePreconditionTests.groovy       | 469 ++++++++++++++++++
 .../contracts/util/AnnotationUtilsTests.groovy     |  58 +++
 .../groovy/contracts/util/FieldValuesTests.groovy  |  39 ++
 114 files changed, 11369 insertions(+), 1 deletion(-)

diff --git a/gradle/upload.gradle b/gradle/upload.gradle
index 1055b12..04d3eb9 100644
--- a/gradle/upload.gradle
+++ b/gradle/upload.gradle
@@ -189,6 +189,7 @@ def optionalModules = [
         'groovy-bsf',
         'groovy-cli-commons',
         'groovy-dateutil',
+        'groovy-gcontracts',
         'groovy-jaxb',
         'groovy-testng'
 ]
diff --git a/settings.gradle b/settings.gradle
index f11e894..058ed1f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -35,6 +35,7 @@ def subprojects = ['groovy-ant',
         'groovy-cli-commons',
         'groovy-cli-picocli',
         'groovy-console',
+        'groovy-contracts',
         'groovy-datetime',
         'groovy-dateutil',
         'groovy-docgenerator',
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
index 833e40c..b3fa2b3 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -94,7 +94,7 @@ public class GeneralUtils {
     public static final Token OR = Token.newSymbol(Types.LOGICAL_OR, -1, -1);
     public static final Token CMP = Token.newSymbol(Types.COMPARE_TO, -1, -1);
     public static final Token INSTANCEOF = Token.newSymbol(Types.KEYWORD_INSTANCEOF, -1, -1);
-    private static final Token PLUS = Token.newSymbol(Types.PLUS, -1, -1);
+    public static final Token PLUS = Token.newSymbol(Types.PLUS, -1, -1);
     private static final Token INDEX = Token.newSymbol("[", -1, -1);
 
     public static BinaryExpression andX(final Expression lhv, final Expression rhv) {
diff --git a/src/spec/doc/index.adoc b/src/spec/doc/index.adoc
index 7946e0b..95f2f08 100644
--- a/src/spec/doc/index.adoc
+++ b/src/spec/doc/index.adoc
@@ -75,6 +75,8 @@ include::{projectdir}/subprojects/groovy-xml/{specfolder}/xml-userguide.adoc[lev
 
 include::{projectdir}/subprojects/groovy-yaml/{specfolder}/yaml-userguide.adoc[leveloffset=+2]
 
+include::{projectdir}/subprojects/groovy-contracts/{specfolder}/contracts-userguide.adoc[leveloffset=+2]
+
 === Scripting Ant tasks
 
 Groovy integrates very well with http://ant.apache.org[Apache Ant] thanks to <<_antbuilder,AntBuilder>>.
diff --git a/subprojects/groovy-contracts/build.gradle b/subprojects/groovy-contracts/build.gradle
new file mode 100644
index 0000000..278e2ed
--- /dev/null
+++ b/subprojects/groovy-contracts/build.gradle
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+dependencies {
+    api rootProject // ASTMatcher use ASTNode...
+    testImplementation rootProject.sourceSets.test.runtimeClasspath
+    testImplementation project(':groovy-test')
+    testImplementation project(':groovy-templates')
+    testImplementation ("org.spockframework:spock-core:$spockVersion") {
+        exclude group: 'org.codehaus.groovy'
+    }
+    testImplementation ("org.spockframework:spock-junit4:$spockVersion")
+    testRuntime("org.junit.vintage:junit-vintage-engine:$junit5Version")
+}
+
+test {
+    useJUnitPlatform()
+    systemProperty "spock.iKnowWhatImDoing.disableGroovyVersionCheck", "true"
+}
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Contracted.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Contracted.java
new file mode 100644
index 0000000..050a99e
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Contracted.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.contracts;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>Package-level and class-level annotation indicating that the package is enabled for
+ * class-invariants, pre- and post-conditions.</p>
+ * <p>
+ * For example:
+ * <pre>
+ * &#064;Contracted
+ * package my.package
+ *
+ * import groovy.contracts.*
+ * </pre>
+ */
+@Target({ElementType.PACKAGE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Contracted {
+}
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java
new file mode 100644
index 0000000..49f5e05
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.contracts;
+
+import org.apache.groovy.contracts.annotations.meta.AnnotationProcessorImplementation;
+import org.apache.groovy.contracts.annotations.meta.Postcondition;
+import org.apache.groovy.contracts.common.impl.EnsuresAnnotationProcessor;
+import org.apache.groovy.lang.annotation.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Represents a <b>method postcondition</b>.
+ * </p>
+ * <p>
+ * A postcondition is a condition that is guaranteed to be fulfilled by suppliers.
+ * </p>
+ * <p>
+ * A method's postcondition is executed <i>after</i> a method call
+ * has finished. A successor's postcondition strengthens the postcondition of its parent class, e.g. if A.someMethod
+ * declares a postcondition and B.someMethod overrides the method the postconditions are combined with a boolean AND.
+ * </p>
+ * <p>
+ * Compared to pre-conditions, postcondition annotation closures are optionally called with two additional
+ * closure arguments: <tt>result</tt> and <tt>old</tt>.
+ * </p>
+ * <p>
+ * <tt>result</tt> is available if the corresponding method has a non-void return-type and holds the
+ * result of the method call. Be aware that modifying the internal state of a reference type can lead
+ * to side-effects. Groovy-contracts does not keep track of any sort of modifications, neither any conversion to
+ * immutability.
+ * </p>
+ * <p>
+ * <tt>old</tt> is available in every postcondition. It is a {@link java.util.Map} which holds the values
+ * of value types and {@link Cloneable} types before the method has been executed.
+ * </p>
+ * <p>
+ * Examples:
+ * <p>
+ * Accessing the <tt>result</tt> closure parameter:
+ *
+ * <pre>
+ *   &#064;Ensures({ result -> result != argument1 })
+ *   def T someOperation(def argument1, def argument2)  {
+ *     ...
+ *   }
+ * </pre>
+ * <p>
+ * Accessing the <tt>old</tt> closure parameter:
+ *
+ * <pre>
+ *   &#064;Ensures({ old -> old.counter + 1 == counter })
+ *   def T someOperation(def argument1, def argument2)  {
+ *     ...
+ *   }
+ * </pre>
+ * </p>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
+@Incubating
+@Postcondition
+@AnnotationProcessorImplementation(EnsuresAnnotationProcessor.class)
+public @interface Ensures {
+    Class value();
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java
new file mode 100644
index 0000000..ca9edf3
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.contracts;
+
+import org.apache.groovy.contracts.annotations.meta.AnnotationProcessorImplementation;
+import org.apache.groovy.contracts.annotations.meta.ClassInvariant;
+import org.apache.groovy.contracts.common.impl.ClassInvariantAnnotationProcessor;
+import org.apache.groovy.lang.annotation.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Represents a <b>class-invariant</b>.
+ * </p>
+ *
+ * <p>
+ * The class-invariant defines assertions holding during the entire objects life-time.
+ * </p>
+ * <p>
+ * Class-invariants are verified at runtime at the following pointcuts:
+ * <ul>
+ *  <li>after a constructor call</li>
+ *  <li>before a method call</li>
+ *  <li>after a method call</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Whenever a class has a parent which itself specifies a class-invariant, that class-invariant expression is combined
+ * with the actual class's invariant (by using a logical AND).
+ * </p>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Incubating
+@ClassInvariant
+@AnnotationProcessorImplementation(ClassInvariantAnnotationProcessor.class)
+public @interface Invariant {
+    Class value();
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java
new file mode 100644
index 0000000..26d4c4c
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.contracts;
+
+import org.apache.groovy.contracts.annotations.meta.AnnotationProcessorImplementation;
+import org.apache.groovy.contracts.annotations.meta.Precondition;
+import org.apache.groovy.contracts.common.impl.RequiresAnnotationProcessor;
+import org.apache.groovy.lang.annotation.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Represents a <b>method precondition</b>.
+ * </p>
+ * <p>
+ * A precondition is a condition that must be met by clients of this class. Whenever the
+ * precondition can be satisfied, it is guaranteed that the supplier will fulfil the method's
+ * postcondition.
+ * </p>
+ * <p>
+ * A method's precondition is executed <i>as the first statement</i> within a method call. A
+ * successor's precondition weakens the precondition of its parent class, e.g. if A.someMethod
+ * declares a precondition and B.someMethod overrides the method the preconditions are combined with a boolean OR.
+ * </p>
+ * <p>
+ * Example:
+ *
+ * <pre>
+ *   &#064;Requires({ argument1 != argument2 && argument2 >= 0 })
+ *   void someOperation(def argument1, def argument2)  {
+ *     ...
+ *   }
+ * </pre>
+ * </p>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
+@Incubating
+@Precondition
+@AnnotationProcessorImplementation(RequiresAnnotationProcessor.class)
+public @interface Requires {
+    Class value();
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/AssertionViolation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/AssertionViolation.java
new file mode 100644
index 0000000..7d51049
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/AssertionViolation.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts;
+
+/**
+ * <p>Abstract base class for all assertion violations.</p>
+ */
+public abstract class AssertionViolation extends AssertionError {
+
+    protected AssertionViolation() {
+        ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(Object o) {
+        super(o);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(boolean b) {
+        super(b);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(char c) {
+        super(c);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(int i) {
+        super(i);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(long l) {
+        super(l);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(float v) {
+        super(v);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+
+    protected AssertionViolation(double v) {
+        super(v);
+        if (ViolationTracker.INSTANCE.get() != null) ViolationTracker.INSTANCE.get().track(this);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/CircularAssertionCallException.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/CircularAssertionCallException.java
new file mode 100644
index 0000000..b7ee8d7
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/CircularAssertionCallException.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts;
+
+/**
+ * <p>Thrown whenever pre- or post-conditions are called in a cyclic way.</p>
+ *
+ * @see AssertionViolation
+ */
+public class CircularAssertionCallException extends RuntimeException {
+
+    public CircularAssertionCallException() {
+    }
+
+    public CircularAssertionCallException(String s) {
+        super(s);
+    }
+
+    public CircularAssertionCallException(String s, Throwable throwable) {
+        super(s, throwable);
+    }
+
+    public CircularAssertionCallException(Throwable throwable) {
+        super(throwable);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ClassInvariantViolation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ClassInvariantViolation.java
new file mode 100644
index 0000000..befa807
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ClassInvariantViolation.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts;
+
+/**
+ * <p>Thrown whenever a class invariant violation occurs.</p>
+ *
+ * @see AssertionViolation
+ */
+public class ClassInvariantViolation extends AssertionViolation {
+
+    public ClassInvariantViolation() {
+    }
+
+    public ClassInvariantViolation(Object o) {
+        super(o);
+    }
+
+    public ClassInvariantViolation(boolean b) {
+        super(b);
+    }
+
+    public ClassInvariantViolation(char c) {
+        super(c);
+    }
+
+    public ClassInvariantViolation(int i) {
+        super(i);
+    }
+
+    public ClassInvariantViolation(long l) {
+        super(l);
+    }
+
+    public ClassInvariantViolation(float v) {
+        super(v);
+    }
+
+    public ClassInvariantViolation(double v) {
+        super(v);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/PostconditionViolation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/PostconditionViolation.java
new file mode 100644
index 0000000..4e448e6
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/PostconditionViolation.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts;
+
+/**
+ * <p>Thrown whenever a postcondition violation occurs.</p>
+ *
+ * @see AssertionViolation
+ */
+public class PostconditionViolation extends AssertionViolation {
+
+    public PostconditionViolation() {
+    }
+
+    public PostconditionViolation(Object o) {
+        super(o);
+    }
+
+    public PostconditionViolation(boolean b) {
+        super(b);
+    }
+
+    public PostconditionViolation(char c) {
+        super(c);
+    }
+
+    public PostconditionViolation(int i) {
+        super(i);
+    }
+
+    public PostconditionViolation(long l) {
+        super(l);
+    }
+
+    public PostconditionViolation(float v) {
+        super(v);
+    }
+
+    public PostconditionViolation(double v) {
+        super(v);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/PreconditionViolation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/PreconditionViolation.java
new file mode 100644
index 0000000..195389b
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/PreconditionViolation.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts;
+
+/**
+ * <p>Thrown whenever a precondition violation occurs.</p>
+ *
+ * @see AssertionViolation
+ */
+public class PreconditionViolation extends AssertionViolation {
+
+    public PreconditionViolation() {
+    }
+
+    public PreconditionViolation(Object o) {
+        super(o);
+    }
+
+    public PreconditionViolation(boolean b) {
+        super(b);
+    }
+
+    public PreconditionViolation(char c) {
+        super(c);
+    }
+
+    public PreconditionViolation(int i) {
+        super(i);
+    }
+
+    public PreconditionViolation(long l) {
+        super(l);
+    }
+
+    public PreconditionViolation(float v) {
+        super(v);
+    }
+
+    public PreconditionViolation(double v) {
+        super(v);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ViolationTracker.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ViolationTracker.java
new file mode 100644
index 0000000..85230f9
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ViolationTracker.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts;
+
+import org.apache.groovy.contracts.util.Validate;
+
+import java.util.TreeMap;
+
+/**
+ * <p>
+ * A violation tracker is used to keep a list of pre-, post-condition or class-invariant
+ * violations in chronological order. This is necessary to evaluate all parts of a pre- or postcondition, and still
+ * being able to rethrow assertion errors.
+ * </p>
+ */
+public class ViolationTracker {
+
+    public static ThreadLocal<ViolationTracker> INSTANCE = new ThreadLocal<ViolationTracker>();
+
+    public static void init() {
+        INSTANCE.set(new ViolationTracker());
+    }
+
+    public static void deinit() {
+        INSTANCE.remove();
+    }
+
+    public static boolean violationsOccurred() {
+        return INSTANCE.get().hasViolations();
+    }
+
+    public static void rethrowFirst() {
+        throw INSTANCE.get().first();
+    }
+
+    public static void rethrowLast() {
+        throw INSTANCE.get().last();
+    }
+
+    private TreeMap<Long, AssertionViolation> violations = new TreeMap<Long, AssertionViolation>();
+
+    public void track(final AssertionViolation assertionViolation) {
+        Validate.notNull(assertionViolation);
+
+        violations.put(System.nanoTime(), assertionViolation);
+    }
+
+    public boolean hasViolations() {
+        return violations.size() > 0;
+    }
+
+    public AssertionViolation first() {
+        return violations.firstEntry().getValue();
+    }
+
+    public AssertionViolation last() {
+        return violations.lastEntry().getValue();
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/AnnotationProcessorImplementation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/AnnotationProcessorImplementation.java
new file mode 100644
index 0000000..9d7cfdf
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/AnnotationProcessorImplementation.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.annotations.meta;
+
+import org.apache.groovy.contracts.common.spi.AnnotationProcessor;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * <p>Indicates what {@link AnnotationProcessor} implementation should be used to process
+ * the correlating annotation.</p>
+ *
+ * @see org.apache.groovy.contracts.common.spi.AnnotationProcessor
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnnotationProcessorImplementation {
+    Class<? extends AnnotationProcessor> value();
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/ClassInvariant.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/ClassInvariant.java
new file mode 100644
index 0000000..7dfbd62
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/ClassInvariant.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.annotations.meta;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Indicates that annotations being marked as {@code @ClassInvariant} are to be treated
+ * as class invariant modifying annotations.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@ContractElement
+public @interface ClassInvariant {
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/ContractElement.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/ContractElement.java
new file mode 100644
index 0000000..bd4eaa8
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/ContractElement.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.annotations.meta;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Indicates that annotations being marked as {@code @ContractElement} are to be used
+ * by some contract element being either a class-invariant, pre- or post-condition.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ContractElement {
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/Postcondition.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/Postcondition.java
new file mode 100644
index 0000000..519c1e6
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/Postcondition.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.annotations.meta;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Indicates that annotations being marked as {@code @Postcondition} are to be treated
+ * as post-condition modifying annotations.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@ContractElement
+public @interface Postcondition {
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/Precondition.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/Precondition.java
new file mode 100644
index 0000000..aa6da4f
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/annotations/meta/Precondition.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.annotations.meta;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Indicates that annotations being marked as {@code @Precondition} are to be treated
+ * as pre-condition modifying annotations.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@ContractElement
+public @interface Precondition {
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/BaseASTTransformation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/BaseASTTransformation.java
new file mode 100644
index 0000000..22df11c
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/BaseASTTransformation.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast;
+
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.transform.ASTTransformation;
+
+import java.lang.reflect.Field;
+
+/**
+ * Base AST transformation encapsulating all common helper methods and implementing {@link org.codehaus.groovy.transform.ASTTransformation}.
+ *
+ * @see org.codehaus.groovy.transform.ASTTransformation
+ */
+public abstract class BaseASTTransformation implements ASTTransformation {
+
+    /**
+     * Reads the protected <tt>source1</tt> instance variable of {@link org.codehaus.groovy.control.SourceUnit}.
+     *
+     * @param unit the {@link org.codehaus.groovy.control.SourceUnit} to retrieve the {@link org.codehaus.groovy.control.io.ReaderSource} from
+     * @return the {@link org.codehaus.groovy.control.io.ReaderSource} of the given <tt>unit</tt>.
+     */
+    protected ReaderSource getReaderSource(SourceUnit unit) {
+
+        try {
+            Class sourceUnitClass = unit.getClass();
+
+            while (sourceUnitClass != SourceUnit.class) {
+                sourceUnitClass = sourceUnitClass.getSuperclass();
+            }
+
+            Field field = sourceUnitClass.getDeclaredField("source1");
+            field.setAccessible(true);
+
+            return (ReaderSource) field.get(unit);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/ClosureExpressionEvaluationASTTransformation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/ClosureExpressionEvaluationASTTransformation.java
new file mode 100644
index 0000000..97e699d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/ClosureExpressionEvaluationASTTransformation.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast;
+
+import groovy.contracts.Contracted;
+import org.apache.groovy.contracts.ast.visitor.AnnotationClosureVisitor;
+import org.apache.groovy.contracts.ast.visitor.ConfigurationSetup;
+import org.apache.groovy.contracts.ast.visitor.ContractElementVisitor;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Evaluates {@link org.codehaus.groovy.ast.expr.ClosureExpression} instances in as actual annotation parameters and
+ * generates special contract closure classes from them.
+ */
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+public class ClosureExpressionEvaluationASTTransformation extends BaseASTTransformation {
+
+    private void generateAnnotationClosureClasses(SourceUnit unit, ReaderSource source, List<ClassNode> classNodes) {
+        final AnnotationClosureVisitor annotationClosureVisitor = new AnnotationClosureVisitor(unit, source);
+
+        for (final ClassNode classNode : classNodes) {
+            annotationClosureVisitor.visitClass(classNode);
+
+            if (!CandidateChecks.isContractsCandidate(classNode)) continue;
+
+            final ContractElementVisitor contractElementVisitor = new ContractElementVisitor(unit, source);
+            contractElementVisitor.visitClass(classNode);
+
+            if (!contractElementVisitor.isFoundContractElement()) continue;
+
+            annotationClosureVisitor.visitClass(classNode);
+            markClassNodeAsContracted(classNode);
+
+            new ConfigurationSetup().init(classNode);
+        }
+    }
+
+    /**
+     * {@link org.codehaus.groovy.transform.ASTTransformation#visit(org.codehaus.groovy.ast.ASTNode[], org.codehaus.groovy.control.SourceUnit)}
+     */
+    public void visit(ASTNode[] nodes, SourceUnit unit) {
+        final ModuleNode moduleNode = unit.getAST();
+
+        ReaderSource source = getReaderSource(unit);
+        final List<ClassNode> classNodes = new ArrayList<ClassNode>(moduleNode.getClasses());
+
+        generateAnnotationClosureClasses(unit, source, classNodes);
+    }
+
+    private void markClassNodeAsContracted(final ClassNode classNode) {
+        final ClassNode contractedAnnotationClassNode = ClassHelper.makeWithoutCaching(Contracted.class);
+
+        if (classNode.getAnnotations(contractedAnnotationClassNode).isEmpty())
+            classNode.addAnnotation(new AnnotationNode(contractedAnnotationClassNode));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/GContractsASTTransformation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/GContractsASTTransformation.java
new file mode 100644
index 0000000..23826bf
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/GContractsASTTransformation.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast;
+
+import groovy.contracts.Contracted;
+import groovy.contracts.Ensures;
+import groovy.contracts.Invariant;
+import groovy.contracts.Requires;
+import org.apache.groovy.contracts.ast.visitor.AnnotationProcessorVisitor;
+import org.apache.groovy.contracts.ast.visitor.DomainModelInjectionVisitor;
+import org.apache.groovy.contracts.ast.visitor.DynamicSetterInjectionVisitor;
+import org.apache.groovy.contracts.ast.visitor.LifecycleAfterTransformationVisitor;
+import org.apache.groovy.contracts.ast.visitor.LifecycleBeforeTransformationVisitor;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+/**
+ * <p>
+ * Custom AST transformation that removes closure annotations of {@link Invariant},
+ * {@link Requires} and {@link Ensures} and adds Java
+ * assertions executing the closure-code.
+ * </p>
+ * <p>
+ * Whenever an assertion is broken an {@link org.apache.groovy.contracts.AssertionViolation} descendant class will be thrown.
+ * </p>
+ *
+ * @see org.apache.groovy.contracts.PreconditionViolation
+ * @see org.apache.groovy.contracts.PostconditionViolation
+ * @see org.apache.groovy.contracts.ClassInvariantViolation
+ */
+@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
+public class GContractsASTTransformation extends BaseASTTransformation {
+
+    /**
+     * {@link org.codehaus.groovy.transform.ASTTransformation#visit(org.codehaus.groovy.ast.ASTNode[], org.codehaus.groovy.control.SourceUnit)}
+     */
+    public void visit(ASTNode[] nodes, SourceUnit unit) {
+        final ModuleNode moduleNode = unit.getAST();
+
+        ReaderSource source = getReaderSource(unit);
+        final ClassNode contractedAnnotationClassNode = ClassHelper.makeWithoutCaching(Contracted.class);
+
+        for (final ClassNode classNode : moduleNode.getClasses()) {
+            if (classNode.getAnnotations(contractedAnnotationClassNode).isEmpty()) continue;
+
+            final ProcessingContextInformation pci = new ProcessingContextInformation(classNode, unit, source);
+            new LifecycleBeforeTransformationVisitor(unit, source, pci).visitClass(classNode);
+            new AnnotationProcessorVisitor(unit, source, pci).visitClass(classNode);
+            new DomainModelInjectionVisitor(unit, source, pci).visitClass(classNode);
+            new LifecycleAfterTransformationVisitor(unit, source, pci).visitClass(classNode);
+            new DynamicSetterInjectionVisitor(unit, source).visitClass(classNode);
+        }
+    }
+}
+
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ASTNodeMetaData.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ASTNodeMetaData.java
new file mode 100644
index 0000000..d276ecc
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ASTNodeMetaData.java
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+/**
+ * Holds all constants to be used as AST node meta data keys.
+ */
+public interface ASTNodeMetaData {
+    String PROCESSED = "org.apache.groovy.contracts.CLOSURE_REPLACED";
+    String CLOSURE_REPLACED = "org.apache.groovy.contracts.CLOSURE_REPLACED";
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationClosureVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationClosureVisitor.java
new file mode 100644
index 0000000..5d8ece5
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationClosureVisitor.java
@@ -0,0 +1,534 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import groovy.contracts.Ensures;
+import groovy.contracts.Requires;
+import org.apache.groovy.contracts.ClassInvariantViolation;
+import org.apache.groovy.contracts.PostconditionViolation;
+import org.apache.groovy.contracts.PreconditionViolation;
+import org.apache.groovy.contracts.annotations.meta.ContractElement;
+import org.apache.groovy.contracts.annotations.meta.Postcondition;
+import org.apache.groovy.contracts.classgen.asm.ContractClosureWriter;
+import org.apache.groovy.contracts.generation.AssertStatementCreationUtility;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.generation.TryCatchBlockGenerator;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.apache.groovy.contracts.util.ExpressionUtils;
+import org.apache.groovy.contracts.util.FieldValues;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.TransformingCodeVisitor;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PostfixExpression;
+import org.codehaus.groovy.ast.expr.PrefixExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+
+/**
+ * Visits interfaces &amp; classes and looks for <tt>@Requires</tt> or <tt>@Ensures</tt> and creates {@link groovy.lang.Closure}
+ * classes for the annotation closures.<p/>
+ * <p>
+ * The annotation closure classes are used later on to check interface contract pre- and post-conditions in
+ * implementation classes.
+ *
+ * @see Requires
+ * @see Ensures
+ * @see org.apache.groovy.contracts.ast.visitor.BaseVisitor
+ */
+public class AnnotationClosureVisitor extends BaseVisitor implements ASTNodeMetaData {
+
+    public static final String META_DATA_USE_EXECUTION_TRACKER = "org.apache.groovy.contracts.META_DATA.USE_EXECUTION_TRACKER";
+    public static final String META_DATA_ORIGINAL_TRY_CATCH_BLOCK = "org.apache.groovy.contracts.META_DATA.ORIGINAL_TRY_CATCH_BLOCK";
+
+    private static final String POSTCONDITION_TYPE_NAME = Postcondition.class.getName();
+    private static final ClassNode FIELD_VALUES = ClassHelper.makeCached(FieldValues.class);
+
+    private ClassNode classNode;
+    private final ContractClosureWriter contractClosureWriter = new ContractClosureWriter();
+
+    public AnnotationClosureVisitor(final SourceUnit sourceUnit, final ReaderSource source) {
+        super(sourceUnit, source);
+    }
+
+    @Override
+    public void visitClass(ClassNode node) {
+        if (node == null) return;
+        if (!(CandidateChecks.isInterfaceContractsCandidate(node) || CandidateChecks.isContractsCandidate(node)))
+            return;
+
+        classNode = node;
+
+        if (classNode.getNodeMetaData(PROCESSED) == null && CandidateChecks.isContractsCandidate(node)) {
+            final List<AnnotationNode> annotationNodes = AnnotationUtils.hasMetaAnnotations(node, ContractElement.class.getName());
+            for (AnnotationNode annotationNode : annotationNodes) {
+                Expression expression = annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME);
+                if (expression == null || expression instanceof ClassExpression) continue;
+
+                ClosureExpression closureExpression = (ClosureExpression) expression;
+
+                ClosureExpressionValidator validator = new ClosureExpressionValidator(classNode, null, annotationNode, sourceUnit);
+                validator.visitClosureExpression(closureExpression);
+                validator.secondPass(closureExpression);
+
+                List<Parameter> parameters = new ArrayList<>(Arrays.asList(closureExpression.getParameters()));
+
+                final List<BooleanExpression> booleanExpressions = ExpressionUtils.getBooleanExpression(closureExpression);
+                if (booleanExpressions == null || booleanExpressions.isEmpty()) continue;
+
+                BlockStatement closureBlockStatement = (BlockStatement) closureExpression.getCode();
+
+                BlockStatement newClosureBlockStatement = TryCatchBlockGenerator.generateTryCatchBlock(
+                        ClassHelper.makeWithoutCaching(ClassInvariantViolation.class),
+                        "<" + annotationNode.getClassNode().getName() + "> " + classNode.getName() + " \n\n",
+                        AssertStatementCreationUtility.getAssertionStatements(booleanExpressions)
+                );
+
+                newClosureBlockStatement.setSourcePosition(closureBlockStatement);
+
+                ClosureExpression rewrittenClosureExpression = new ClosureExpression(parameters.toArray(Parameter.EMPTY_ARRAY), newClosureBlockStatement);
+                rewrittenClosureExpression.setSourcePosition(closureExpression);
+                rewrittenClosureExpression.setDeclaringClass(closureExpression.getDeclaringClass());
+                rewrittenClosureExpression.setSynthetic(true);
+                rewrittenClosureExpression.setVariableScope(closureExpression.getVariableScope());
+                rewrittenClosureExpression.setType(closureExpression.getType());
+
+                ClassNode closureClassNode = contractClosureWriter.createClosureClass(classNode, null, rewrittenClosureExpression, false, false, Opcodes.ACC_PUBLIC);
+                classNode.getModule().addClass(closureClassNode);
+
+                final ClassExpression value = new ClassExpression(closureClassNode);
+                value.setSourcePosition(annotationNode);
+
+                BlockStatement value1 = TryCatchBlockGenerator.generateTryCatchBlockForInlineMode(
+                        ClassHelper.makeWithoutCaching(ClassInvariantViolation.class),
+                        "<" + annotationNode.getClassNode().getName() + "> " + classNode.getName() + " \n\n",
+                        AssertStatementCreationUtility.getAssertionStatements(booleanExpressions)
+                );
+                value1.setNodeMetaData(META_DATA_USE_EXECUTION_TRACKER, validator.isMethodCalls());
+
+                value.setNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK, value1);
+
+                annotationNode.setMember(CLOSURE_ATTRIBUTE_NAME, value);
+
+                markClosureReplaced(classNode);
+            }
+        }
+
+        super.visitClass(node);
+
+        // generate closure classes for the super class and all implemented interfaces
+        visitClass(node.getSuperClass());
+        for (ClassNode i : node.getInterfaces()) {
+            visitClass(i);
+        }
+
+        markProcessed(classNode);
+    }
+
+    @Override
+    public void visitConstructorOrMethod(MethodNode methodNode, boolean isConstructor) {
+        if (!CandidateChecks.couldBeContractElementMethodNode(classNode, methodNode) && !(CandidateChecks.isPreconditionCandidate(classNode, methodNode)))
+            return;
+        if (methodNode.getNodeMetaData(PROCESSED) != null) return;
+
+        final List<AnnotationNode> annotationNodes = AnnotationUtils.hasMetaAnnotations(methodNode, ContractElement.class.getName());
+        for (AnnotationNode annotationNode : annotationNodes) {
+            replaceWithClosureClassReference(annotationNode, methodNode);
+        }
+
+        markProcessed(methodNode);
+
+        super.visitConstructorOrMethod(methodNode, isConstructor);
+    }
+
+    private void replaceWithClosureClassReference(AnnotationNode annotationNode, MethodNode methodNode) {
+        Validate.notNull(annotationNode);
+        Validate.notNull(methodNode);
+
+        // check whether this is a pre- or postcondition
+        boolean isPostcondition = AnnotationUtils.hasAnnotationOfType(annotationNode.getClassNode(), org.apache.groovy.contracts.annotations.meta.Postcondition.class.getName());
+
+        Expression expression = annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME);
+        if (expression == null || expression instanceof ClassExpression) return;
+
+        ClosureExpression closureExpression = (ClosureExpression) expression;
+        ClassCodeExpressionTransformer transformer = new OldPropertyExpressionTransformer(methodNode);
+        TransformingCodeVisitor visitor = new TransformingCodeVisitor(transformer);
+        visitor.visitClosureExpression(closureExpression);
+
+        ClosureExpressionValidator validator = new ClosureExpressionValidator(classNode, methodNode, annotationNode, sourceUnit);
+        validator.visitClosureExpression(closureExpression);
+        validator.secondPass(closureExpression);
+
+        List<Parameter> parameters = new ArrayList<>(Arrays.asList(closureExpression.getParameters()));
+
+        parameters.addAll(new ArrayList<>(Arrays.asList(methodNode.getParameters())));
+
+        final List<BooleanExpression> booleanExpressions = ExpressionUtils.getBooleanExpression(closureExpression);
+        if (booleanExpressions == null || booleanExpressions.isEmpty()) return;
+
+        BlockStatement closureBlockStatement = (BlockStatement) closureExpression.getCode();
+
+        BlockStatement newClosureBlockStatement = TryCatchBlockGenerator.generateTryCatchBlock(
+                isPostcondition ? ClassHelper.makeWithoutCaching(PostconditionViolation.class) : ClassHelper.makeWithoutCaching(PreconditionViolation.class),
+                "<" + annotationNode.getClassNode().getName() + "> " + classNode.getName() + "." + methodNode.getTypeDescriptor() + " \n\n",
+                AssertStatementCreationUtility.getAssertionStatements(booleanExpressions)
+        );
+
+        newClosureBlockStatement.setSourcePosition(closureBlockStatement);
+
+        ClosureExpression rewrittenClosureExpression = new ClosureExpression(parameters.toArray(Parameter.EMPTY_ARRAY), newClosureBlockStatement);
+        rewrittenClosureExpression.setSourcePosition(closureExpression);
+        rewrittenClosureExpression.setDeclaringClass(closureExpression.getDeclaringClass());
+        rewrittenClosureExpression.setSynthetic(true);
+        rewrittenClosureExpression.setVariableScope(correctVariableScope(closureExpression.getVariableScope(), methodNode));
+        rewrittenClosureExpression.setType(closureExpression.getType());
+
+        boolean isConstructor = methodNode instanceof ConstructorNode;
+        ClassNode closureClassNode = contractClosureWriter.createClosureClass(classNode, methodNode, rewrittenClosureExpression, isPostcondition && !isConstructor, isPostcondition && !isConstructor, Opcodes.ACC_PUBLIC);
+        classNode.getModule().addClass(closureClassNode);
+
+        final ClassExpression value = new ClassExpression(closureClassNode);
+        value.setSourcePosition(annotationNode);
+
+        BlockStatement value1 = TryCatchBlockGenerator.generateTryCatchBlockForInlineMode(
+                isPostcondition ? ClassHelper.makeWithoutCaching(PostconditionViolation.class) : ClassHelper.makeWithoutCaching(PreconditionViolation.class),
+                "<" + annotationNode.getClassNode().getName() + "> " + classNode.getName() + "." + methodNode.getTypeDescriptor() + " \n\n",
+                AssertStatementCreationUtility.getAssertionStatements(booleanExpressions)
+        );
+        value1.setNodeMetaData(META_DATA_USE_EXECUTION_TRACKER, validator.isMethodCalls());
+
+        value.setNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK, value1);
+        annotationNode.setMember(CLOSURE_ATTRIBUTE_NAME, value);
+
+        markClosureReplaced(methodNode);
+    }
+
+    private VariableScope correctVariableScope(VariableScope variableScope, MethodNode methodNode) {
+        if (variableScope == null) return null;
+        if (methodNode == null || methodNode.getParameters() == null || methodNode.getParameters().length == 0)
+            return variableScope;
+
+        VariableScope copy = copy(variableScope);
+
+        for (Iterator<Variable> iterator = variableScope.getReferencedClassVariablesIterator(); iterator.hasNext(); ) {
+            Variable variable = iterator.next();
+            String name = variable.getName();
+
+            for (Parameter parameter : methodNode.getParameters()) {
+                if (parameter.getName().equals(name)) {
+                    copy.putReferencedLocalVariable(parameter);
+                    break;
+                }
+            }
+
+            if (!copy.isReferencedLocalVariable(name)) {
+                copy.putReferencedClassVariable(variable);
+            }
+        }
+
+        return copy;
+    }
+
+    private VariableScope copy(VariableScope original) {
+        VariableScope copy = new VariableScope(original.getParent());
+        copy.setClassScope(original.getClassScope());
+        copy.setInStaticContext(original.isInStaticContext());
+        return copy;
+    }
+
+    private void markProcessed(ASTNode someNode) {
+        if (someNode.getNodeMetaData(PROCESSED) == null)
+            someNode.setNodeMetaData(PROCESSED, Boolean.TRUE);
+    }
+
+    private void markClosureReplaced(ASTNode someNode) {
+        if (someNode.getNodeMetaData(CLOSURE_REPLACED) == null)
+            someNode.setNodeMetaData(CLOSURE_REPLACED, Boolean.TRUE);
+    }
+
+    static class ClosureExpressionValidator extends ClassCodeVisitorSupport implements Opcodes {
+
+        private final ClassNode classNode;
+        private final MethodNode methodNode;
+        private final AnnotationNode annotationNode;
+        private final SourceUnit sourceUnit;
+
+        private final Map<VariableExpression, StaticMethodCallExpression> variableExpressions;
+
+        private boolean secondPass = false;
+        private boolean methodCalls = false;
+
+        public ClosureExpressionValidator(ClassNode classNode, MethodNode methodNode, AnnotationNode annotationNode, SourceUnit sourceUnit) {
+            this.classNode = classNode;
+            this.methodNode = methodNode;
+            this.annotationNode = annotationNode;
+            this.sourceUnit = sourceUnit;
+            this.variableExpressions = new HashMap<>();
+        }
+
+        @Override
+        public void visitClosureExpression(ClosureExpression expression) {
+            secondPass = false;
+
+            if (expression.getCode() == null || expression.getCode() instanceof EmptyStatement) {
+                addError("[groovy-contracts] Annotation does not contain any expressions (e.g. use '@Requires({ argument1 })').", expression);
+            }
+
+            if (expression.getCode() instanceof BlockStatement &&
+                    ((BlockStatement) expression.getCode()).getStatements().isEmpty()) {
+                addError("[groovy-contracts] Annotation does not contain any expressions (e.g. use '@Requires({ argument1 })').", expression);
+            }
+
+            if (expression.isParameterSpecified() && !AnnotationUtils.hasAnnotationOfType(annotationNode.getClassNode(), POSTCONDITION_TYPE_NAME)) {
+                addError("[groovy-contracts] Annotation does not support parameters (the only exception are postconditions).", expression);
+            }
+
+            if (expression.isParameterSpecified()) {
+                for (Parameter param : expression.getParameters()) {
+                    if (!("result".equals(param.getName()) || "old".equals(param.getName()))) {
+                        addError("[groovy-contracts] Postconditions only allow 'old' and 'result' closure parameters.", expression);
+                    }
+
+                    if (!param.isDynamicTyped()) {
+                        addError("[groovy-contracts] Postconditions do not support explicit types.", expression);
+                    }
+                }
+            }
+
+            super.visitClosureExpression(expression);
+        }
+
+        @Override
+        public void visitVariableExpression(VariableExpression expression) {
+
+            // in case of a FieldNode, checks whether the FieldNode can be replaced with a Parameter
+            Variable accessedVariable = getParameterCandidate(expression.getAccessedVariable());
+            if (accessedVariable instanceof FieldNode) {
+                FieldNode fieldNode = (FieldNode) accessedVariable;
+
+                if (fieldNode.isPrivate() && !classNode.hasProperty(fieldNode.getName())) {
+                    // if this is a class invariant we'll change the field node access
+                    StaticMethodCallExpression field = callX(FIELD_VALUES, "fieldValue", args(VariableExpression.THIS_EXPRESSION, constX(fieldNode.getName()), classX(fieldNode.getType())));
+                    variableExpressions.put(expression, field);
+                }
+            }
+
+            if (accessedVariable instanceof Parameter) {
+                Parameter parameter = (Parameter) accessedVariable;
+                if ("it".equals(parameter.getName())) {
+                    addError("[groovy-contracts] Access to 'it' is not supported.", expression);
+                }
+            }
+
+            expression.setAccessedVariable(accessedVariable);
+
+            super.visitVariableExpression(expression);
+        }
+
+        @Override
+        public void visitPostfixExpression(PostfixExpression expression) {
+            checkOperation(expression, expression.getOperation());
+
+            if (secondPass) {
+                if (expression.getExpression() instanceof VariableExpression) {
+                    VariableExpression variableExpression = (VariableExpression) expression.getExpression();
+                    if (variableExpressions.containsKey(variableExpression)) {
+                        expression.setExpression(variableExpressions.get(variableExpression));
+                    }
+                }
+            }
+
+            super.visitPostfixExpression(expression);
+        }
+
+        @Override
+        public void visitPrefixExpression(PrefixExpression expression) {
+            checkOperation(expression, expression.getOperation());
+
+            if (secondPass) {
+                if (expression.getExpression() instanceof VariableExpression) {
+                    VariableExpression variableExpression = (VariableExpression) expression.getExpression();
+                    if (variableExpressions.containsKey(variableExpression)) {
+                        expression.setExpression(variableExpressions.get(variableExpression));
+                    }
+                }
+            }
+
+            super.visitPrefixExpression(expression);
+        }
+
+        @Override
+        public void visitBinaryExpression(BinaryExpression expression) {
+            checkOperation(expression, expression.getOperation());
+
+            if (secondPass) {
+                if (expression.getLeftExpression() instanceof VariableExpression) {
+                    VariableExpression variableExpression = (VariableExpression) expression.getLeftExpression();
+                    if (variableExpressions.containsKey(variableExpression)) {
+                        expression.setLeftExpression(variableExpressions.get(variableExpression));
+                    }
+                }
+                if (expression.getRightExpression() instanceof VariableExpression) {
+                    VariableExpression variableExpression = (VariableExpression) expression.getRightExpression();
+                    if (variableExpressions.containsKey(variableExpression)) {
+                        expression.setRightExpression(variableExpressions.get(variableExpression));
+                    }
+                }
+            }
+
+            super.visitBinaryExpression(expression);
+        }
+
+        @Override
+        public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
+            methodCalls = true;
+            super.visitStaticMethodCallExpression(call);
+        }
+
+        @Override
+        public void visitMethodCallExpression(MethodCallExpression call) {
+            methodCalls = true;
+            super.visitMethodCallExpression(call);
+        }
+
+        @Override
+        public void visitConstructorCallExpression(ConstructorCallExpression call) {
+            methodCalls = true;
+            super.visitConstructorCallExpression(call);
+        }
+
+        private void checkOperation(Expression expression, Token operation) {
+            if (Types.ofType(operation.getType(), Types.ASSIGNMENT_OPERATOR)) {
+                addError("[groovy-contracts] Assignment operators are not supported.", expression);
+            }
+            if (Types.ofType(operation.getType(), Types.POSTFIX_OPERATOR)) {
+                addError("[groovy-contracts] State changing postfix & prefix operators are not supported.", expression);
+            }
+        }
+
+        private Variable getParameterCandidate(Variable variable) {
+            if (variable == null || methodNode == null) return variable;
+            if (variable instanceof Parameter) return variable;
+
+            String name = variable.getName();
+            for (Parameter param : methodNode.getParameters()) {
+                if (name.equals(param.getName())) return param;
+            }
+
+            return variable;
+        }
+
+        public void secondPass(ClosureExpression closureExpression) {
+            secondPass = true;
+            super.visitClosureExpression(closureExpression);
+        }
+
+        public boolean isMethodCalls() {
+            return methodCalls;
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return sourceUnit;
+        }
+    }
+
+    private static class OldPropertyExpressionTransformer extends ClassCodeExpressionTransformer {
+        private final MethodNode methodNode;
+        private CastExpression currentCast = null;
+
+        public OldPropertyExpressionTransformer(MethodNode methodNode) {
+            this.methodNode = methodNode;
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+
+        @Override
+        public Expression transform(Expression expr) {
+            if (expr instanceof CastExpression) {
+                CastExpression saved = currentCast;
+                currentCast = (CastExpression) expr;
+                Expression result = expr.transformExpression(this);
+                currentCast = saved;
+                return result;
+            }
+            if (expr instanceof PropertyExpression && expr.getNodeMetaData(PROCESSED) == null && (currentCast == null || expr != currentCast.getExpression())) {
+                // add a cast but only if an explicit cast is not already there and we haven't been here before
+                PropertyExpression propExpr = (PropertyExpression) super.transform(expr);
+                Expression objExpr = propExpr.getObjectExpression();
+                if (objExpr instanceof VariableExpression) {
+                    VariableExpression varExpr = (VariableExpression) objExpr;
+                    if ("old".equals(varExpr.getName())) {
+                        String propName = propExpr.getPropertyAsString();
+                        ClassNode declaringClass = methodNode.getDeclaringClass();
+                        if (declaringClass != null && declaringClass.getField(propName) != null) {
+                            CastExpression adjusted = new CastExpression(declaringClass.getField(propName).getType(), expr);
+                            adjusted.setSourcePosition(expr);
+                            expr.setNodeMetaData(PROCESSED, Boolean.TRUE);
+                            return adjusted;
+                        }
+                    }
+                }
+            }
+            return expr.transformExpression(this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationContractParameterVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationContractParameterVisitor.java
new file mode 100644
index 0000000..9f09d55
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationContractParameterVisitor.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import java.util.List;
+
+/**
+ * This {@link BaseVisitor} walks up the class hierarchy for the given {@link org.codehaus.groovy.ast.ClassNode}
+ * and adds {@link org.apache.groovy.contracts.annotations.meta.AnnotationContract} annotations to method parameters.
+ */
+public class AnnotationContractParameterVisitor extends BaseVisitor {
+
+    private MethodNode currentMethodNode;
+
+    public AnnotationContractParameterVisitor(final SourceUnit sourceUnit, final ReaderSource source) {
+        super(sourceUnit, source);
+    }
+
+    @Override
+    public void visitClass(ClassNode node) {
+        if (node == null) return;
+
+        // walk up the class hierarchy
+        super.visitClass(node.getSuperClass());
+
+        // walk through all interfaces
+        for (ClassNode i : node.getAllInterfaces()) {
+            super.visitClass(i);
+        }
+    }
+
+    @Override
+    public void visitMethod(MethodNode node) {
+        currentMethodNode = node;
+        super.visitMethod(node);
+        currentMethodNode = null;
+    }
+
+    @Override
+    public void visitAnnotations(AnnotatedNode node) {
+        if (!(node instanceof Parameter) || currentMethodNode == null) return;
+        List<AnnotationNode> annotationNodes = AnnotationUtils.hasMetaAnnotations(node, "org.apache.groovy.contracts.annotations.meta.AnnotationContract");
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java
new file mode 100644
index 0000000..eccdae9
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java
@@ -0,0 +1,243 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.annotations.meta.ContractElement;
+import org.apache.groovy.contracts.common.spi.AnnotationProcessor;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+
+/**
+ * Visits annotations of meta-type {@link ContractElement} and applies the AST transformations of the underlying
+ * {@link org.apache.groovy.contracts.common.spi.AnnotationProcessor} implementation.
+ *
+ * @see org.apache.groovy.contracts.common.spi.AnnotationProcessor
+ */
+public class AnnotationProcessorVisitor extends BaseVisitor {
+
+    private ProcessingContextInformation pci;
+
+    public AnnotationProcessorVisitor(final SourceUnit sourceUnit, final ReaderSource source, final ProcessingContextInformation pci) {
+        super(sourceUnit, source);
+        Validate.notNull(pci);
+
+        this.pci = pci;
+    }
+
+    @Override
+    public void visitClass(ClassNode type) {
+        handleClassNode(type);
+
+        List<MethodNode> methodNodes = new ArrayList<MethodNode>();
+        methodNodes.addAll(type.getMethods());
+        methodNodes.addAll(type.getDeclaredConstructors());
+
+        for (MethodNode methodNode : methodNodes) {
+            if (!CandidateChecks.isClassInvariantCandidate(type, methodNode) && !CandidateChecks.isPreOrPostconditionCandidate(type, methodNode))
+                continue;
+
+            handleMethodNode(methodNode, AnnotationUtils.hasMetaAnnotations(methodNode, ContractElement.class.getName()));
+        }
+
+        // visit all interfaces of this class
+        visitInterfaces(type, type.getInterfaces());
+        visitAbstractBaseClassesForInterfaceMethodNodes(type, type.getSuperClass());
+    }
+
+    private void visitAbstractBaseClassesForInterfaceMethodNodes(ClassNode origin, ClassNode superClass) {
+        if (superClass == null) return;
+        if (!Modifier.isAbstract(superClass.getModifiers())) return;
+
+        for (ClassNode interfaceClassNode : superClass.getInterfaces()) {
+            List<MethodNode> methodNodes = new ArrayList<MethodNode>();
+            methodNodes.addAll(interfaceClassNode.getMethods());
+
+            for (MethodNode interfaceMethodNode : methodNodes) {
+                final List<AnnotationNode> annotationNodes = AnnotationUtils.hasMetaAnnotations(interfaceMethodNode, ContractElement.class.getName());
+                if (annotationNodes == null || annotationNodes.isEmpty()) continue;
+
+                MethodNode implementingMethodNode = superClass.getMethod(interfaceMethodNode.getName(), interfaceMethodNode.getParameters());
+
+                // if implementingMethodNode == null, then superClass is abstract and does not implement
+                // the current interface methodNode
+                if (implementingMethodNode != null) continue;
+
+                MethodNode implementationInOriginClassNode = origin.getMethod(interfaceMethodNode.getName(), interfaceMethodNode.getParameters());
+                if (implementationInOriginClassNode == null) continue;
+
+                handleMethodNode(implementationInOriginClassNode, annotationNodes);
+            }
+        }
+    }
+
+    private void visitInterfaces(final ClassNode classNode, final ClassNode[] interfaces) {
+        for (ClassNode interfaceClassNode : interfaces) {
+            List<MethodNode> methodNodes = new ArrayList<MethodNode>();
+            methodNodes.addAll(interfaceClassNode.getMethods());
+
+            // @ContractElement annotations are by now only supported on method interfaces
+            for (MethodNode interfaceMethodNode : methodNodes) {
+                MethodNode implementingMethodNode = classNode.getMethod(interfaceMethodNode.getName(), interfaceMethodNode.getParameters());
+                if (implementingMethodNode == null) continue;
+
+                final List<AnnotationNode> annotationNodes = AnnotationUtils.hasMetaAnnotations(interfaceMethodNode, ContractElement.class.getName());
+                handleInterfaceMethodNode(classNode, implementingMethodNode, annotationNodes);
+            }
+
+            visitInterfaces(classNode, interfaceClassNode.getInterfaces());
+        }
+    }
+
+    private void handleClassNode(final ClassNode classNode) {
+        final List<AnnotationNode> annotationNodes = AnnotationUtils.hasMetaAnnotations(classNode, ContractElement.class.getName());
+
+        for (AnnotationNode annotationNode : annotationNodes) {
+            final AnnotationProcessor annotationProcessor = createAnnotationProcessor(annotationNode);
+
+            if (annotationProcessor != null && annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME) instanceof ClassExpression) {
+                final ClassExpression closureClassExpression = (ClassExpression) annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME);
+
+                MethodCallExpression doCall = callX(
+                        ctorX(closureClassExpression.getType(), args(VariableExpression.THIS_EXPRESSION, VariableExpression.THIS_EXPRESSION)),
+                        "doCall"
+                );
+                doCall.setMethodTarget(closureClassExpression.getType().getMethods("doCall").get(0));
+
+                final BooleanExpression booleanExpression = boolX(doCall);
+                booleanExpression.setSourcePosition(annotationNode);
+
+                annotationProcessor.process(pci, pci.contract(), classNode, closureClassExpression.getNodeMetaData(AnnotationClosureVisitor.META_DATA_ORIGINAL_TRY_CATCH_BLOCK), booleanExpression);
+            }
+        }
+    }
+
+    private void handleInterfaceMethodNode(ClassNode type, MethodNode methodNode, List<AnnotationNode> annotationNodes) {
+        handleMethodNode(type.getMethod(methodNode.getName(), methodNode.getParameters()), annotationNodes);
+    }
+
+    private void handleMethodNode(MethodNode methodNode, List<AnnotationNode> annotationNodes) {
+        if (methodNode == null) return;
+
+        for (AnnotationNode annotationNode : annotationNodes) {
+            final AnnotationProcessor annotationProcessor = createAnnotationProcessor(annotationNode);
+
+            if (annotationProcessor != null && annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME) instanceof ClassExpression) {
+                boolean isPostcondition = AnnotationUtils.hasAnnotationOfType(annotationNode.getClassNode(), org.apache.groovy.contracts.annotations.meta.Postcondition.class.getName());
+
+                ClassExpression closureClassExpression = (ClassExpression) annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME);
+
+                ArgumentListExpression closureArgumentList = new ArgumentListExpression();
+
+                for (Parameter parameter : methodNode.getParameters()) {
+                    closureArgumentList.addExpression(varX(parameter));
+                }
+
+                if (methodNode.getReturnType() != ClassHelper.VOID_TYPE && isPostcondition && !(methodNode instanceof ConstructorNode)) {
+                    closureArgumentList.addExpression(localVarX("result", methodNode.getReturnType()));
+                }
+
+                if (isPostcondition && !(methodNode instanceof ConstructorNode)) {
+                    closureArgumentList.addExpression(localVarX("old", new ClassNode(Map.class)));
+                }
+
+                MethodCallExpression doCall = callX(
+                        ctorX(annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME).getType(), args(VariableExpression.THIS_EXPRESSION, VariableExpression.THIS_EXPRESSION)),
+                        "doCall",
+                        closureArgumentList
+                );
+                ClassNode type = annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME).getType();
+                doCall.setMethodTarget(type.getMethods("doCall").get(0));
+
+                final BooleanExpression booleanExpression = boolX(doCall);
+                booleanExpression.setSourcePosition(annotationNode);
+
+                annotationProcessor.process(pci, pci.contract(), methodNode.getDeclaringClass(), methodNode, closureClassExpression.getNodeMetaData(AnnotationClosureVisitor.META_DATA_ORIGINAL_TRY_CATCH_BLOCK), booleanExpression);
+
+                // if the implementation method has no annotation, we need to set a dummy marker in order to find parent pre/postconditions
+                if (!AnnotationUtils.hasAnnotationOfType(methodNode, annotationNode.getClassNode().getName())) {
+                    AnnotationNode annotationMarker = new AnnotationNode(annotationNode.getClassNode());
+                    annotationMarker.setMember(CLOSURE_ATTRIBUTE_NAME, annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME));
+                    annotationMarker.setRuntimeRetention(true);
+                    annotationMarker.setSourceRetention(false);
+
+                    methodNode.addAnnotation(annotationMarker);
+                }
+            }
+        }
+    }
+
+    private AnnotationProcessor createAnnotationProcessor(AnnotationNode annotationNode) {
+        ClassExpression annotationProcessingAnno = null;
+
+        List<AnnotationNode> annotations = annotationNode.getClassNode().redirect().getAnnotations();
+        for (AnnotationNode anno : annotations) {
+            Class typeClass = anno.getClassNode().getTypeClass();
+
+            if (typeClass.getName().equals("org.apache.groovy.contracts.annotations.meta.AnnotationProcessorImplementation")) {
+                annotationProcessingAnno = (ClassExpression) anno.getMember("value");
+                break;
+            }
+        }
+
+        if (annotationProcessingAnno == null)
+            throw new GroovyBugError("Annotation processing class could not be found! This indicates a bug in groovy-contracts, please file an issue!");
+
+        try {
+            final Class clz = Class.forName(annotationProcessingAnno.getType().getTypeClass().getName());
+            return (AnnotationProcessor) clz.getDeclaredConstructor().newInstance();
+        } catch (InstantiationException e) {
+        } catch (IllegalAccessException e) {
+        } catch (ClassNotFoundException e) {
+        } catch (NoSuchMethodException e) {
+        } catch (InvocationTargetException e) {
+        }
+
+        throw new GroovyBugError("Annotation processing class could not be instantiated! This indicates a bug in groovy-contracts, please file an issue!");
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/BaseVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/BaseVisitor.java
new file mode 100644
index 0000000..f620378
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/BaseVisitor.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+/**
+ * <p>
+ * Base class for {@link org.codehaus.groovy.ast.ClassCodeVisitorSupport} descendants. This class is used in groovy-contracts
+ * as root class for all code visitors directly used by global AST transformations.
+ * </p>
+ *
+ * @see org.codehaus.groovy.ast.ClassCodeVisitorSupport
+ */
+public abstract class BaseVisitor extends ClassCodeVisitorSupport {
+
+    public static final String GCONTRACTS_ENABLED_VAR = "$GCONTRACTS_ENABLED";
+
+    public static final String CLOSURE_ATTRIBUTE_NAME = "value";
+
+    protected SourceUnit sourceUnit;
+    protected ReaderSource source;
+
+    public BaseVisitor(final SourceUnit sourceUnit, final ReaderSource source) {
+        this.sourceUnit = sourceUnit;
+        this.source = source;
+    }
+
+    @Override
+    protected SourceUnit getSourceUnit() {
+        return null;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ConfigurationSetup.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ConfigurationSetup.java
new file mode 100644
index 0000000..2fe85c7
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ConfigurationSetup.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.generation.Configurator;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.objectweb.asm.Opcodes;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+
+/**
+ * Makes some initialization in order to use the {@link Configurator} for determining
+ * which assertions in what packages will be executed.
+ *
+ * @see Configurator
+ */
+public class ConfigurationSetup {
+
+    /**
+     * Adds an instance field which allows to control whether GContract assertions
+     * are enabled or not. Before assertions are evaluated this field will be checked.
+     *
+     * @param type the current {@link ClassNode}
+     * @see Configurator
+     */
+    public void init(final ClassNode type) {
+        Validate.notNull(type);
+        StaticMethodCallExpression checkAssertionsEnabledMethodCall = callX(ClassHelper.makeWithoutCaching(Configurator.class), "checkAssertionsEnabled", args(constX(type.getName())));
+        final FieldNode fieldNode = type.addField(BaseVisitor.GCONTRACTS_ENABLED_VAR, Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_FINAL, ClassHelper.boolean_TYPE, checkAssertionsEnabledMethodCall);
+        fieldNode.setSynthetic(true);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ContractElementVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ContractElementVisitor.java
new file mode 100644
index 0000000..4d3b38d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/ContractElementVisitor.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import groovy.contracts.Contracted;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+/**
+ * Checks whether the given {@link org.codehaus.groovy.ast.ClassNode} is relevant for
+ * further processing.
+ */
+public class ContractElementVisitor extends BaseVisitor implements ASTNodeMetaData {
+
+    private ClassNode classNode;
+    private boolean foundContractElement = false;
+
+    public ContractElementVisitor(final SourceUnit sourceUnit, final ReaderSource source) {
+        super(sourceUnit, source);
+    }
+
+    @Override
+    public void visitClass(ClassNode node) {
+        if (!CandidateChecks.isContractsCandidate(node) && !CandidateChecks.isInterfaceContractsCandidate(node)) return;
+
+        classNode = node;
+
+        // check for the @Contracted shortcut
+        if (AnnotationUtils.hasAnnotationOfType(node, Contracted.class.getName())) {
+            foundContractElement = true;
+            return;
+        }
+
+        foundContractElement |= classNode.getNodeMetaData(CLOSURE_REPLACED) != null;
+
+        if (!foundContractElement) {
+            super.visitClass(node);
+        }
+
+        // check base classes
+        if (!foundContractElement && node.getSuperClass() != null) {
+            visitClass(node.getSuperClass());
+        }
+
+        // check interfaces
+        if (!foundContractElement) {
+            for (ClassNode interfaceNode : node.getInterfaces()) {
+                visitClass(interfaceNode);
+                if (foundContractElement) return;
+            }
+        }
+    }
+
+    @Override
+    protected void visitConstructorOrMethod(MethodNode methodNode, boolean isConstructor) {
+        if (!CandidateChecks.couldBeContractElementMethodNode(classNode, methodNode) && !(CandidateChecks.isPreconditionCandidate(classNode, methodNode)))
+            return;
+        foundContractElement |= methodNode.getNodeMetaData(CLOSURE_REPLACED) != null;
+    }
+
+    public boolean isFoundContractElement() {
+        return foundContractElement;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/DomainModelInjectionVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/DomainModelInjectionVisitor.java
new file mode 100644
index 0000000..300d0b1
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/DomainModelInjectionVisitor.java
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.domain.ClassInvariant;
+import org.apache.groovy.contracts.domain.Contract;
+import org.apache.groovy.contracts.domain.Postcondition;
+import org.apache.groovy.contracts.domain.Precondition;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.generation.ClassInvariantGenerator;
+import org.apache.groovy.contracts.generation.PostconditionGenerator;
+import org.apache.groovy.contracts.generation.PreconditionGenerator;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import java.util.Map;
+
+/**
+ * Visits the given {@link ClassNode} and injects the current {@link org.apache.groovy.contracts.domain.Contract} into the given AST
+ * nodes.
+ *
+ * @see org.apache.groovy.contracts.domain.Contract
+ */
+public class DomainModelInjectionVisitor extends BaseVisitor {
+
+    private final ProcessingContextInformation pci;
+    private final Contract contract;
+
+    public DomainModelInjectionVisitor(final SourceUnit sourceUnit, final ReaderSource source, final ProcessingContextInformation pci) {
+        super(sourceUnit, source);
+        Validate.notNull(pci);
+        Validate.notNull(pci.contract());
+
+        this.pci = pci;
+        this.contract = pci.contract();
+    }
+
+    @Override
+    public void visitClass(ClassNode type) {
+        injectClassInvariant(type, contract.classInvariant());
+
+        for (Map.Entry<MethodNode, Precondition> entry : contract.preconditions()) {
+            injectPrecondition(entry.getKey(), entry.getValue());
+        }
+
+        for (Map.Entry<MethodNode, Postcondition> entry : contract.postconditions()) {
+            injectPostcondition(entry.getKey(), entry.getValue());
+        }
+    }
+
+    public void injectClassInvariant(final ClassNode type, final ClassInvariant classInvariant) {
+        if (!pci.isClassInvariantsEnabled() || !CandidateChecks.isContractsCandidate(type)) return;
+
+        final ReaderSource source = pci.readerSource();
+        final ClassInvariantGenerator classInvariantGenerator = new ClassInvariantGenerator(source);
+
+        classInvariantGenerator.generateInvariantAssertionStatement(type, classInvariant);
+    }
+
+    public void injectPrecondition(final MethodNode method, final Precondition precondition) {
+        if (!pci.isPreconditionsEnabled() || !CandidateChecks.isPreconditionCandidate(method.getDeclaringClass(), method))
+            return;
+
+        final ReaderSource source = pci.readerSource();
+        final PreconditionGenerator preconditionGenerator = new PreconditionGenerator(source);
+
+        preconditionGenerator.generatePreconditionAssertionStatement(method, precondition);
+    }
+
+    public void injectPostcondition(final MethodNode method, final Postcondition postcondition) {
+        if (!pci.isPostconditionsEnabled() || !CandidateChecks.isPostconditionCandidate(method.getDeclaringClass(), method))
+            return;
+
+        final ReaderSource source = pci.readerSource();
+        final PostconditionGenerator postconditionGenerator = new PostconditionGenerator(source);
+
+        postconditionGenerator.generatePostconditionAssertionStatement(method, postcondition);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/DynamicSetterInjectionVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/DynamicSetterInjectionVisitor.java
new file mode 100644
index 0000000..acfefbb
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/DynamicSetterInjectionVisitor.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.generation.BaseGenerator;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.apache.groovy.util.BeanUtils;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.objectweb.asm.Opcodes;
+
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+
+/**
+ * <p>
+ * Implements contract support for setter methods and default constructors of POGOs.
+ * </p>
+ *
+ * @see BaseVisitor
+ */
+public class DynamicSetterInjectionVisitor extends BaseVisitor {
+
+    private static final String SPRING_STEREOTYPE_PACKAGE = "org.springframework.stereotype";
+
+    private BlockStatement invariantAssertionBlockStatement;
+
+    public DynamicSetterInjectionVisitor(final SourceUnit sourceUnit, final ReaderSource source) {
+        super(sourceUnit, source);
+    }
+
+    protected Statement createSetterBlock(final ClassNode classNode, final FieldNode field, final Parameter parameter) {
+        return block(
+                invariantAssertionBlockStatement, // check invariant before assignment
+                assignS(fieldX(field), varX(parameter)), // do assignment
+                invariantAssertionBlockStatement  // check invariant after assignment
+        );
+    }
+
+    @Override
+    public void visitProperty(PropertyNode node) {
+        final ClassNode classNode = node.getDeclaringClass();
+        final String setterName = "set" + BeanUtils.capitalize(node.getName());
+
+        final Statement setterBlock = node.getSetterBlock();
+        final Parameter parameter = param(node.getType(), "value");
+
+        if (CandidateChecks.isClassInvariantCandidate(node) && (setterBlock == null && classNode.getMethod(setterName, new Parameter[]{parameter}) == null)) {
+            final Statement setterBlockStatement = createSetterBlock(classNode, node.getField(), parameter);
+            node.setSetterBlock(setterBlockStatement);
+        }
+    }
+
+    @Override
+    public void visitClass(ClassNode classNode) {
+        // if a class invariant is available visit all property nodes else skip this class
+        final MethodNode invariantMethodNode = BaseGenerator.getInvariantMethodNode(classNode);
+        if (invariantMethodNode == null || AnnotationUtils.hasAnnotationOfType(classNode, SPRING_STEREOTYPE_PACKAGE))
+            return;
+
+        invariantAssertionBlockStatement = block(stmt(callThisX(invariantMethodNode.getName())));
+
+        List<ConstructorNode> declaredConstructors = classNode.getDeclaredConstructors();
+        if (declaredConstructors == null || declaredConstructors.isEmpty()) {
+            // create default constructor with class invariant check
+            ConstructorNode constructor = new ConstructorNode(Opcodes.ACC_PUBLIC, invariantAssertionBlockStatement);
+            constructor.setSynthetic(true);
+            classNode.addConstructor(constructor);
+        }
+
+        super.visitClass(classNode);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/LifecycleAfterTransformationVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/LifecycleAfterTransformationVisitor.java
new file mode 100644
index 0000000..ec7c3c9
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/LifecycleAfterTransformationVisitor.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.common.spi.Lifecycle;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.util.LifecycleImplementationLoader;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>AST transformation visitor which is triggered after applying {@link org.apache.groovy.contracts.common.spi.AnnotationProcessor}
+ * related transformations.</p>
+ *
+ * @see AnnotationProcessorVisitor
+ */
+public class LifecycleAfterTransformationVisitor extends BaseVisitor {
+
+    private final ProcessingContextInformation pci;
+
+    public LifecycleAfterTransformationVisitor(SourceUnit sourceUnit, ReaderSource source, final ProcessingContextInformation pci) {
+        super(sourceUnit, source);
+
+        Validate.notNull(pci);
+        this.pci = pci;
+    }
+
+    @Override
+    public void visitClass(ClassNode node) {
+        super.visitClass(node);
+
+        List<MethodNode> methods = new ArrayList<>(node.getMethods());
+        List<MethodNode> constructors = new ArrayList<>(node.getDeclaredConstructors());
+
+        for (Lifecycle lifecyle : LifecycleImplementationLoader.load(Lifecycle.class, getClass().getClassLoader())) {
+            lifecyle.afterProcessingClassNode(pci, node);
+
+            for (MethodNode constructor : constructors) {
+                lifecyle.afterProcessingConstructorNode(pci, node, constructor);
+            }
+
+            for (MethodNode method : methods) {
+                lifecyle.afterProcessingMethodNode(pci, node, method);
+            }
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/LifecycleBeforeTransformationVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/LifecycleBeforeTransformationVisitor.java
new file mode 100644
index 0000000..b4db706
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/LifecycleBeforeTransformationVisitor.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.ast.visitor;
+
+import org.apache.groovy.contracts.common.spi.Lifecycle;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.util.LifecycleImplementationLoader;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>AST transformation visitor which is triggered before applying {@link org.apache.groovy.contracts.common.spi.AnnotationProcessor}
+ * related transformations.</p>
+ *
+ * @see AnnotationProcessorVisitor
+ */
+public class LifecycleBeforeTransformationVisitor extends BaseVisitor {
+
+    private final ProcessingContextInformation pci;
+
+    public LifecycleBeforeTransformationVisitor(SourceUnit sourceUnit, ReaderSource source, final ProcessingContextInformation pci) {
+        super(sourceUnit, source);
+
+        Validate.notNull(pci);
+        this.pci = pci;
+    }
+
+    @Override
+    public void visitClass(ClassNode node) {
+        super.visitClass(node);
+
+        List<MethodNode> methods = new ArrayList<>(node.getMethods());
+        List<MethodNode> constructors = new ArrayList<>(node.getDeclaredConstructors());
+
+        for (Lifecycle lifecyle : LifecycleImplementationLoader.load(Lifecycle.class, getClass().getClassLoader())) {
+            lifecyle.beforeProcessingClassNode(pci, node);
+
+            for (MethodNode constructor : constructors) {
+                lifecyle.beforeProcessingConstructorNode(pci, node, constructor);
+            }
+
+            for (MethodNode method : methods) {
+                lifecyle.beforeProcessingMethodNode(pci, node, method);
+            }
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java
new file mode 100644
index 0000000..cd195c0
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java
@@ -0,0 +1,193 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.classgen.asm;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.DynamicVariable;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+
+/**
+ * <p>Replaces annotation closures with closure implementation classes.</p>
+ *
+ * <p>Attention: large parts of this class have been backported from Groovy 1.8 and customized
+ * for usage in groovy-contracts.</p>
+ */
+public class ContractClosureWriter {
+
+    private int closureCount = 1;
+
+    public ClassNode createClosureClass(ClassNode classNode, MethodNode methodNode, ClosureExpression expression, boolean addOldVariable, boolean addResultVariable, int mods) {
+        ClassNode outerClass = getOutermostClass(classNode);
+        String name = outerClass.getName() + "$" + getClosureInnerName(outerClass, classNode);
+
+        // fetch all method parameters, and possibly add 'old' and 'result'
+        ArrayList<Parameter> parametersTemp = new ArrayList<Parameter>(Arrays.asList(expression.getParameters()));
+        removeParameter("old", parametersTemp);
+        removeParameter("result", parametersTemp);
+
+        if (methodNode != null && addResultVariable && methodNode.getReturnType() != ClassHelper.VOID_TYPE) {
+            parametersTemp.add(new Parameter(methodNode.getReturnType(), "result"));
+        }
+
+        if (addOldVariable) {
+            parametersTemp.add(new Parameter(new ClassNode(Map.class), "old"));
+        }
+
+        // contains all params of the original method
+        ArrayList<Parameter> closureParameters = new ArrayList<Parameter>();
+        for (Parameter param : parametersTemp) {
+            Parameter closureParameter = new Parameter(param.getType().getPlainNodeReference(), param.getName());
+            closureParameters.add(closureParameter);
+        }
+
+        ClassNode answer = new ClassNode(name, mods | ACC_FINAL, ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
+        answer.setSynthetic(true);
+        answer.setSourcePosition(expression);
+
+        MethodNode method =
+                answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.Boolean_TYPE, closureParameters.toArray(new Parameter[closureParameters.size()]), ClassNode.EMPTY_ARRAY, expression.getCode());
+        method.setSourcePosition(expression);
+
+        VariableScope varScope = expression.getVariableScope();
+        if (varScope == null) {
+            throw new RuntimeException(
+                    "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
+        } else {
+            method.setVariableScope(varScope.copy());
+        }
+
+        // let's add a typesafe call method
+        ArgumentListExpression arguments = new ArgumentListExpression();
+        for (Parameter parameter : closureParameters) {
+            arguments.addExpression(varX(parameter));
+        }
+
+        MethodNode call = answer.addMethod(
+                "call",
+                ACC_PUBLIC,
+                ClassHelper.Boolean_TYPE,
+                closureParameters.toArray(new Parameter[closureParameters.size()]),
+                ClassNode.EMPTY_ARRAY,
+                returnS(callThisX("doCall", arguments)));
+
+        call.setSourcePosition(expression);
+        call.setSynthetic(true);
+
+        // let's make the constructor
+        BlockStatement block = new BlockStatement();
+        // this block does not get a source position, because we don't
+        // want this synthetic constructor to show up in corbertura reports
+        VariableExpression outer = varX("_outerInstance");
+        outer.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(outer);
+        VariableExpression thisObject = varX("_thisObject");
+        thisObject.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(thisObject);
+        TupleExpression conArgs = new TupleExpression(outer, thisObject);
+        block.addStatement(stmt(ctorSuperX(conArgs)));
+
+        Parameter[] consParams = params(
+                param(ClassHelper.OBJECT_TYPE, "_outerInstance"),
+                param(ClassHelper.OBJECT_TYPE, "_thisObject"));
+
+        ASTNode sn = answer.addConstructor(ACC_PUBLIC, consParams, ClassNode.EMPTY_ARRAY, block);
+        sn.setSourcePosition(expression);
+        correctAccessedVariable(method, expression);
+        return answer;
+    }
+
+    private void removeParameter(String name, List<Parameter> parameters) {
+        parameters.removeIf(parameter -> parameter.getName().equals(name));
+    }
+
+    private ClassNode getOutermostClass(ClassNode outermostClass) {
+        while (outermostClass instanceof InnerClassNode) {
+            outermostClass = outermostClass.getOuterClass();
+        }
+        return outermostClass;
+    }
+
+    private void correctAccessedVariable(final MethodNode methodNode, ClosureExpression ce) {
+        CodeVisitorSupport visitor = new CodeVisitorSupport() {
+            @Override
+            public void visitVariableExpression(VariableExpression expression) {
+                Variable v = expression.getAccessedVariable();
+                if (v == null) return;
+                String name = expression.getName();
+                if (v instanceof DynamicVariable) {
+                    for (Parameter param : methodNode.getParameters()) {
+                        if (name.equals(param.getName())) {
+                            expression.setAccessedVariable(param);
+                        }
+                    }
+
+                }
+            }
+        };
+        visitor.visitClosureExpression(ce);
+    }
+
+    private String getClosureInnerName(ClassNode owner, ClassNode enclosingClass) {
+        String ownerShortName = owner.getNameWithoutPackage();
+        String classShortName = enclosingClass.getNameWithoutPackage();
+        if (classShortName.equals(ownerShortName)) {
+            classShortName = "";
+        } else {
+            classShortName += "_";
+        }
+        // remove $
+        int dp = classShortName.lastIndexOf("$");
+        if (dp >= 0) {
+            classShortName = classShortName.substring(++dp);
+        }
+        // remove leading _
+        if (classShortName.startsWith("_")) {
+            classShortName = classShortName.substring(1);
+        }
+
+        return "_gc_" + classShortName + "closure" + closureCount++;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/base/BaseLifecycle.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/base/BaseLifecycle.java
new file mode 100644
index 0000000..4c85458
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/base/BaseLifecycle.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.base;
+
+import org.apache.groovy.contracts.common.spi.Lifecycle;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+
+/**
+ * Base implementation class for interface {@link Lifecycle}. This class is supposed
+ * tp be extended by {@link Lifecycle} implementation classes and provides empty
+ * method bodies for all interface methods.
+ *
+ * @see Lifecycle
+ */
+public abstract class BaseLifecycle implements Lifecycle {
+
+    public void beforeProcessingClassNode(ProcessingContextInformation processingContextInformation, ClassNode classNode) {
+    }
+
+    public void afterProcessingClassNode(ProcessingContextInformation processingContextInformation, ClassNode classNode) {
+    }
+
+    public void beforeProcessingMethodNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+    }
+
+    public void afterProcessingMethodNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+    }
+
+    public void beforeProcessingConstructorNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode constructorNode) {
+    }
+
+    public void afterProcessingConstructorNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode constructorNode) {
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/ClassInvariantAnnotationProcessor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/ClassInvariantAnnotationProcessor.java
new file mode 100644
index 0000000..976a47a
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/ClassInvariantAnnotationProcessor.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.impl;
+
+import org.apache.groovy.contracts.common.spi.AnnotationProcessor;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.domain.ClassInvariant;
+import org.apache.groovy.contracts.domain.Contract;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+/**
+ * Internal {@link AnnotationProcessor} implementation for class-invariants.
+ */
+public class ClassInvariantAnnotationProcessor extends AnnotationProcessor {
+
+    @Override
+    public void process(ProcessingContextInformation processingContextInformation, Contract contract, ClassNode classNode, BlockStatement blockStatement, BooleanExpression booleanExpression) {
+        if (!processingContextInformation.isClassInvariantsEnabled()) return;
+        if (booleanExpression == null) return;
+
+        contract.setClassInvariant(new ClassInvariant(blockStatement, booleanExpression));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/EnsuresAnnotationProcessor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/EnsuresAnnotationProcessor.java
new file mode 100644
index 0000000..5a3740c
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/EnsuresAnnotationProcessor.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.impl;
+
+import org.apache.groovy.contracts.common.spi.AnnotationProcessor;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.domain.Contract;
+import org.apache.groovy.contracts.domain.Postcondition;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+import java.util.List;
+
+/**
+ * Internal {@link AnnotationProcessor} implementation for post-conditions.
+ */
+public class EnsuresAnnotationProcessor extends AnnotationProcessor {
+
+    @Override
+    public void process(ProcessingContextInformation processingContextInformation, Contract contract, ClassNode classNode, MethodNode methodNode, BlockStatement blockStatement, BooleanExpression booleanExpression) {
+        if (!processingContextInformation.isPostconditionsEnabled()) return;
+        if (booleanExpression == null) return;
+
+        final List<ConstructorNode> declaredConstructors = classNode.getDeclaredConstructors();
+
+        contract.postconditions().and(methodNode, new Postcondition(blockStatement, booleanExpression, declaredConstructors.contains(methodNode)));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/RequiresAnnotationProcessor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/RequiresAnnotationProcessor.java
new file mode 100644
index 0000000..4fc2b10
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/RequiresAnnotationProcessor.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.impl;
+
+import org.apache.groovy.contracts.common.spi.AnnotationProcessor;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.domain.Contract;
+import org.apache.groovy.contracts.domain.Precondition;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+/**
+ * Internal {@link AnnotationProcessor} implementation for pre-conditions.
+ */
+public class RequiresAnnotationProcessor extends AnnotationProcessor {
+
+    @Override
+    public void process(ProcessingContextInformation processingContextInformation, Contract contract, ClassNode classNode, MethodNode methodNode, BlockStatement blockStatement, BooleanExpression booleanExpression) {
+        if (!processingContextInformation.isPreconditionsEnabled()) return;
+        if (booleanExpression == null) return;
+
+        contract.preconditions().or(methodNode, new Precondition(blockStatement, booleanExpression));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/ClassInvariantLifecycle.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/ClassInvariantLifecycle.java
new file mode 100644
index 0000000..c9f2654
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/ClassInvariantLifecycle.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.impl.lc;
+
+import org.apache.groovy.contracts.common.base.BaseLifecycle;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.generation.ClassInvariantGenerator;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+
+/**
+ * Internal {@link org.apache.groovy.contracts.common.spi.Lifecycle} implementation for class-invariants.
+ */
+public class ClassInvariantLifecycle extends BaseLifecycle {
+
+    @Override
+    public void afterProcessingMethodNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+        if (!CandidateChecks.isClassInvariantCandidate(classNode, methodNode)) return;
+        if (processingContextInformation.contract().hasDefaultClassInvariant()) return;
+
+        final ClassInvariantGenerator classInvariantGenerator = new ClassInvariantGenerator(processingContextInformation.readerSource());
+        classInvariantGenerator.addInvariantAssertionStatement(classNode, methodNode);
+    }
+
+    @Override
+    public void afterProcessingConstructorNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode constructorNode) {
+        if (!CandidateChecks.isClassInvariantCandidate(classNode, constructorNode)) return;
+        if (!processingContextInformation.isConstructorAssertionsEnabled()) return;
+        if (processingContextInformation.contract().hasDefaultClassInvariant()) return;
+
+        final ClassInvariantGenerator classInvariantGenerator = new ClassInvariantGenerator(processingContextInformation.readerSource());
+        classInvariantGenerator.addInvariantAssertionStatement(classNode, constructorNode);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/PostconditionLifecycle.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/PostconditionLifecycle.java
new file mode 100644
index 0000000..f373b2b
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/PostconditionLifecycle.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.impl.lc;
+
+import org.apache.groovy.contracts.annotations.meta.Postcondition;
+import org.apache.groovy.contracts.common.base.BaseLifecycle;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.generation.PostconditionGenerator;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+
+/**
+ * Internal {@link org.apache.groovy.contracts.common.spi.Lifecycle} implementation for post-conditions.
+ */
+public class PostconditionLifecycle extends BaseLifecycle {
+
+    @Override
+    public void beforeProcessingClassNode(ProcessingContextInformation processingContextInformation, ClassNode classNode) {
+        final PostconditionGenerator postconditionGenerator = new PostconditionGenerator(processingContextInformation.readerSource());
+        postconditionGenerator.addOldVariablesMethod(classNode);
+    }
+
+    @Override
+    public void afterProcessingConstructorNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode constructorNode) {
+        generatePostcondition(processingContextInformation, classNode, constructorNode);
+    }
+
+    @Override
+    public void afterProcessingMethodNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+        generatePostcondition(processingContextInformation, classNode, methodNode);
+    }
+
+    private void generatePostcondition(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+        if (!processingContextInformation.isPostconditionsEnabled()) return;
+        if (!CandidateChecks.isPostconditionCandidate(classNode, methodNode)) return;
+
+        final PostconditionGenerator postconditionGenerator = new PostconditionGenerator(processingContextInformation.readerSource());
+
+        if (!(methodNode instanceof ConstructorNode) && AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(classNode, methodNode, ClassHelper.makeWithoutCaching(Postcondition.class)).size() > 0) {
+            postconditionGenerator.generateDefaultPostconditionStatement(classNode, methodNode);
+        } else {
+            postconditionGenerator.generateDefaultPostconditionStatement(classNode, methodNode);
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/PreconditionLifecycle.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/PreconditionLifecycle.java
new file mode 100644
index 0000000..8179ff1
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/impl/lc/PreconditionLifecycle.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.impl.lc;
+
+import org.apache.groovy.contracts.common.base.BaseLifecycle;
+import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.apache.groovy.contracts.generation.PreconditionGenerator;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+
+/**
+ * Internal {@link org.apache.groovy.contracts.common.spi.Lifecycle} implementation for pre-conditions.
+ */
+public class PreconditionLifecycle extends BaseLifecycle {
+
+    @Override
+    public void beforeProcessingConstructorNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode constructorNode) {
+        generatePrecondition(processingContextInformation, classNode, constructorNode);
+    }
+
+    @Override
+    public void afterProcessingMethodNode(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+        generatePrecondition(processingContextInformation, classNode, methodNode);
+    }
+
+    private void generatePrecondition(ProcessingContextInformation processingContextInformation, ClassNode classNode, MethodNode methodNode) {
+        if (!processingContextInformation.isPreconditionsEnabled()) return;
+        if (!CandidateChecks.isPreconditionCandidate(classNode, methodNode)) return;
+        if (processingContextInformation.contract().preconditions().contains(methodNode)) return;
+
+        final PreconditionGenerator preconditionGenerator = new PreconditionGenerator(processingContextInformation.readerSource());
+        preconditionGenerator.generateDefaultPreconditionStatement(classNode, methodNode);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/AnnotationProcessor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/AnnotationProcessor.java
new file mode 100644
index 0000000..87cad54
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/AnnotationProcessor.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.spi;
+
+import org.apache.groovy.contracts.domain.Contract;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+/**
+ * <p>Base class for modifying the internal domain model, starting at {@link Contract}, and adding parts to it.</p>
+ *
+ * @see org.apache.groovy.contracts.annotations.meta.AnnotationProcessorImplementation
+ */
+public abstract class AnnotationProcessor {
+
+    public void process(final ProcessingContextInformation processingContextInformation, final Contract contract, final ClassNode classNode, final BlockStatement blockStatement, final BooleanExpression booleanExpression) {
+    }
+
+    public void process(final ProcessingContextInformation processingContextInformation, final Contract contract, final ClassNode classNode, final MethodNode methodNode, final BlockStatement blockStatement, final BooleanExpression booleanExpression) {
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/Lifecycle.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/Lifecycle.java
new file mode 100644
index 0000000..04ec888
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/Lifecycle.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.spi;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+
+/**
+ * <p>Specifies life-cycle hook-ins for applying AST transformation logic before and
+ * after the annotation processors have been run.</p>
+ *
+ * <p>During excution of GContracts AST transformations, the following process is applied on each {@link ClassNode}
+ * instance which qualifies for contract annotations:</P>
+ *
+ * <ol>
+ *    <li>Generation of closure classes.</li>
+ *    <li>Handling of {@link AnnotationProcessor} implementation classes</li>
+ *    <li>Domain Model Conversion and Injection</li>
+ * </ol>
+ *
+ * <h3>Generation of closure classes</h3>
+ *
+ * <p>In order to support Groovy 1.7.x GContracts backported Groovy 1.8 handling of annotation closures. This is done
+ * by extracting {@link org.codehaus.groovy.ast.expr.ClosureExpression} from annotations and creating {@link groovy.lang.Closure}
+ * implementation classes.</p>
+ *
+ * <h3>Handling of AnnotationProcessor implementation classes</h3>
+ *
+ * <p>{@link AnnotationProcessor} implementatios are used to modify domain classes found in <tt>org.apache.groovy.contracts.domain</tt>. For that
+ * reason, concrete annotation processor often don't modify AST nodes directly, but simply work with domain classes like
+ * {@link org.apache.groovy.contracts.domain.Contract}. Whenever an annotation processor is done, it has finished its work on the
+ * underlying domain model. </p>
+ *
+ * <p>{@link #beforeProcessingClassNode(ProcessingContextInformation, org.codehaus.groovy.ast.ClassNode)},
+ * {@link #beforeProcessingMethodNode(ProcessingContextInformation, org.codehaus.groovy.ast.ClassNode, org.codehaus.groovy.ast.MethodNode)},
+ * {@link #beforeProcessingConstructorNode(ProcessingContextInformation, org.codehaus.groovy.ast.ClassNode, org.codehaus.groovy.ast.MethodNode)} are fired
+ * before annotation processors are executed.</p>
+ *
+ * <h3>Domain Model Conversion and Injection</h3>
+ *
+ * <p>Takes a look at the domain model instances and generates the corresponding AST transformation code.</p>
+ *
+ * <p>{@link #afterProcessingClassNode(ProcessingContextInformation, org.codehaus.groovy.ast.ClassNode)},
+ * {@link #afterProcessingMethodNode(ProcessingContextInformation, org.codehaus.groovy.ast.ClassNode, org.codehaus.groovy.ast.MethodNode)},
+ * {@link #afterProcessingConstructorNode(ProcessingContextInformation, org.codehaus.groovy.ast.ClassNode, org.codehaus.groovy.ast.MethodNode)} are fired
+ * after domain model conversion and injection is done.</p>
+ */
+public interface Lifecycle {
+
+    public void beforeProcessingClassNode(final ProcessingContextInformation processingContextInformation, final ClassNode classNode);
+
+    public void afterProcessingClassNode(final ProcessingContextInformation processingContextInformation, final ClassNode classNode);
+
+    public void beforeProcessingMethodNode(final ProcessingContextInformation processingContextInformation, final ClassNode classNode, final MethodNode methodNode);
+
+    public void afterProcessingMethodNode(final ProcessingContextInformation processingContextInformation, final ClassNode classNode, final MethodNode methodNode);
+
+    public void beforeProcessingConstructorNode(final ProcessingContextInformation processingContextInformation, final ClassNode classNode, final MethodNode constructorNode);
+
+    public void afterProcessingConstructorNode(final ProcessingContextInformation processingContextInformation, final ClassNode classNode, final MethodNode constructorNode);
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/ProcessingContextInformation.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/ProcessingContextInformation.java
new file mode 100644
index 0000000..b6d5196
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/common/spi/ProcessingContextInformation.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.common.spi;
+
+import org.apache.groovy.contracts.domain.Contract;
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>Holds all context-specific information which is needed during the transformation
+ * phase of a single {@link Contract} and its related {@link ClassNode}.</p>
+ */
+public class ProcessingContextInformation {
+
+    private Contract contract;
+    private SourceUnit sourceUnit;
+    private ReaderSource source;
+
+    private boolean constructorAssertionsEnabled = true;
+    private boolean preconditionsEnabled = true;
+    private boolean postconditionsEnabled = true;
+    private boolean classInvariantsEnabled = true;
+
+    private Map<String, Object> extra = new HashMap<String, Object>();
+
+    public ProcessingContextInformation(ClassNode classNode, SourceUnit sourceUnit, ReaderSource source) {
+        Validate.notNull(classNode);
+
+        this.contract = new Contract(classNode);
+        this.sourceUnit = sourceUnit;
+        this.source = source;
+    }
+
+    public void setConstructorAssertionsEnabled(boolean other) {
+        constructorAssertionsEnabled = other;
+    }
+
+    public boolean isConstructorAssertionsEnabled() {
+        return constructorAssertionsEnabled;
+    }
+
+    public boolean isPreconditionsEnabled() {
+        return preconditionsEnabled;
+    }
+
+    public boolean isPostconditionsEnabled() {
+        return postconditionsEnabled;
+    }
+
+    public boolean isClassInvariantsEnabled() {
+        return classInvariantsEnabled;
+    }
+
+    public Contract contract() {
+        return contract;
+    }
+
+    public ReaderSource readerSource() {
+        return source;
+    }
+
+    public SourceUnit sourceUnit() {
+        return sourceUnit;
+    }
+
+    public void put(String key, Object value) {
+        Validate.notNull(key);
+
+        extra.put(key, value);
+    }
+
+    public Object get(String key) {
+        Validate.notNull(key);
+
+        return extra.get(key);
+    }
+
+    public void addError(String msg, ASTNode expr) {
+        int line = expr.getLineNumber();
+        int col = expr.getColumnNumber();
+        SourceUnit source = sourceUnit();
+        source.getErrorCollector().addErrorAndContinue(
+                new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source)
+        );
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Assertion.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Assertion.java
new file mode 100644
index 0000000..7751f29
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Assertion.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain;
+
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+
+/**
+ * <p>Base class for all assertion types.</p>
+ *
+ * @param <T>
+ */
+public abstract class Assertion<T extends Assertion> {
+
+    private BlockStatement originalBlockStatement;
+    private BooleanExpression booleanExpression;
+
+    public Assertion() {
+        this.booleanExpression = new BooleanExpression(ConstantExpression.TRUE);
+    }
+
+    public Assertion(final BlockStatement blockStatement, final BooleanExpression booleanExpression) {
+        Validate.notNull(booleanExpression);
+
+        this.originalBlockStatement = blockStatement; // the BlockStatement might be null! we do not always have the original expression available
+        this.booleanExpression = booleanExpression;
+    }
+
+    public BooleanExpression booleanExpression() {
+        return booleanExpression;
+    }
+
+    public BlockStatement originalBlockStatement() {
+        return originalBlockStatement;
+    }
+
+    public void renew(BooleanExpression booleanExpression) {
+        Validate.notNull(booleanExpression);
+
+        // don't renew the source position to keep the new assertion expression without source code replacement
+        // booleanExpression.setSourcePosition(this.booleanExpression);
+
+        this.booleanExpression = booleanExpression;
+    }
+
+    public void and(T other) {
+        Validate.notNull(other);
+
+        BooleanExpression newBooleanExpression =
+                new BooleanExpression(
+                        new BinaryExpression(
+                                booleanExpression(),
+                                Token.newSymbol(Types.LOGICAL_AND, -1, -1),
+                                other.booleanExpression()
+                        )
+                );
+        newBooleanExpression.setSourcePosition(booleanExpression());
+
+        renew(newBooleanExpression);
+    }
+
+    public void or(T other) {
+        Validate.notNull(other);
+
+        BooleanExpression newBooleanExpression =
+                new BooleanExpression(
+                        new BinaryExpression(
+                                booleanExpression(),
+                                Token.newSymbol(Types.LOGICAL_OR, -1, -1),
+                                other.booleanExpression()
+                        )
+                );
+        newBooleanExpression.setSourcePosition(booleanExpression());
+
+        renew(newBooleanExpression);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/AssertionMap.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/AssertionMap.java
new file mode 100644
index 0000000..e61f1e2
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/AssertionMap.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain;
+
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.MethodNode;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class AssertionMap<T extends Assertion<T>> implements Iterable<Map.Entry<MethodNode, T>> {
+
+    private final Map<MethodNode, T> internalMap;
+
+    public AssertionMap() {
+        this.internalMap = new HashMap<MethodNode, T>();
+    }
+
+    public void and(final MethodNode methodNode, final T assertion) {
+        Validate.notNull(methodNode);
+        Validate.notNull(assertion);
+
+        if (!internalMap.containsKey(methodNode)) {
+            internalMap.put(methodNode, assertion);
+        } else {
+            internalMap.get(methodNode).and(assertion);
+        }
+    }
+
+    public void or(final MethodNode methodNode, final T assertion) {
+        Validate.notNull(methodNode);
+        Validate.notNull(assertion);
+
+        if (!internalMap.containsKey(methodNode)) {
+            internalMap.put(methodNode, assertion);
+        } else {
+            internalMap.get(methodNode).or(assertion);
+        }
+    }
+
+    public void join(final MethodNode methodNode, final T assertion) {
+        and(methodNode, assertion);
+    }
+
+    public boolean contains(final MethodNode methodNode) {
+        return internalMap.containsKey(methodNode);
+    }
+
+    public Iterator<Map.Entry<MethodNode, T>> iterator() {
+        return internalMap.entrySet().iterator();
+    }
+
+    public int size() {
+        return internalMap.size();
+    }
+
+    public T get(final MethodNode methodNode) {
+        return internalMap.get(methodNode);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/ClassInvariant.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/ClassInvariant.java
new file mode 100644
index 0000000..538ef4b
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/ClassInvariant.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain;
+
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+/**
+ * <p>A class-invariant assertion.</p>
+ */
+public class ClassInvariant extends Assertion<ClassInvariant> {
+
+    public static final ClassInvariant DEFAULT = new ClassInvariant(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)));
+
+    public ClassInvariant() {
+    }
+
+    public ClassInvariant(BlockStatement blockStatement, BooleanExpression booleanExpression) {
+        super(blockStatement, booleanExpression);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Contract.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Contract.java
new file mode 100644
index 0000000..66c17b6
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Contract.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain;
+
+import org.apache.groovy.contracts.util.Validate;
+import org.codehaus.groovy.ast.ClassNode;
+
+/**
+ * <p>Represents a contract between a supplier and a customer of a class.</p>
+ */
+public class Contract {
+
+    private final ClassNode classNode;
+
+    private ClassInvariant classInvariant = ClassInvariant.DEFAULT;
+    private final AssertionMap<Precondition> preconditions;
+    private final AssertionMap<Postcondition> postconditions;
+
+    public Contract(final ClassNode classNode) {
+        Validate.notNull(classNode);
+
+        this.classNode = classNode;
+        this.preconditions = new AssertionMap<>();
+        this.postconditions = new AssertionMap<>();
+    }
+
+    public ClassNode classNode() {
+        return classNode;
+    }
+
+    public void setClassInvariant(final ClassInvariant classInvariant) {
+        Validate.notNull(classInvariant);
+        this.classInvariant = classInvariant;
+    }
+
+    public AssertionMap<Precondition> preconditions() {
+        return preconditions;
+    }
+
+    public AssertionMap<Postcondition> postconditions() {
+        return postconditions;
+    }
+
+    public boolean hasDefaultClassInvariant() {
+        return classInvariant == ClassInvariant.DEFAULT;
+    }
+
+    public ClassInvariant classInvariant() {
+        return classInvariant;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Postcondition.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Postcondition.java
new file mode 100644
index 0000000..fb2210e
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Postcondition.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain;
+
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+/**
+ * <p>A post-condition assertion.</p>
+ */
+public class Postcondition extends Assertion<Postcondition> {
+
+    private boolean isPartOfConstructor = false;
+
+    public Postcondition() {
+    }
+
+    public Postcondition(BlockStatement blockStatement, BooleanExpression booleanExpression, boolean isPartOfConstructor) {
+        super(blockStatement, booleanExpression);
+        this.isPartOfConstructor = isPartOfConstructor;
+    }
+
+    public boolean isPartOfConstructor() {
+        return isPartOfConstructor;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Precondition.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Precondition.java
new file mode 100644
index 0000000..5bd193c
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/domain/Precondition.java
@@ -0,0 +1,35 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain;
+
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+
+/**
+ * <p>A pre-condition assertion.</p>
+ */
+public class Precondition extends Assertion<Precondition> {
+
+    public Precondition() {
+    }
+
+    public Precondition(BlockStatement blockStatement, BooleanExpression booleanExpression) {
+        super(blockStatement, booleanExpression);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/AssertStatementCreationUtility.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/AssertStatementCreationUtility.java
new file mode 100644
index 0000000..0fbbe61
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/AssertStatementCreationUtility.java
@@ -0,0 +1,235 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+
+/**
+ * Central place to create {@link org.codehaus.groovy.ast.stmt.AssertStatement} instances in groovy-contracts.
+ * Utilized to centralize {@link AssertionError} message generation.
+ *
+ * @see org.codehaus.groovy.ast.stmt.AssertStatement
+ * @see AssertionError
+ */
+public final class AssertStatementCreationUtility {
+
+    /**
+     * Reusable method for creating assert statements for the given <tt>booleanExpression</tt>.
+     *
+     * @param booleanExpressions the assertion's {@link org.codehaus.groovy.ast.expr.BooleanExpression} instances
+     * @return a newly created {@link org.codehaus.groovy.ast.stmt.AssertStatement}
+     */
+    public static BlockStatement getAssertionStatements(final List<BooleanExpression> booleanExpressions) {
+
+        List<Statement> assertStatements = new ArrayList<>();
+        for (BooleanExpression booleanExpression : booleanExpressions) {
+            assertStatements.add(getAssertionStatement(booleanExpression));
+        }
+
+        final BlockStatement blockStatement = block();
+        blockStatement.getStatements().addAll(assertStatements);
+
+        return blockStatement;
+    }
+
+    /**
+     * Reusable method for creating assert statements for the given <tt>booleanExpression</tt>.
+     *
+     * @param booleanExpression the assertion's {@link org.codehaus.groovy.ast.expr.BooleanExpression}
+     * @return a newly created {@link org.codehaus.groovy.ast.stmt.AssertStatement}
+     */
+    public static AssertStatement getAssertionStatement(final BooleanExpression booleanExpression) {
+
+        final AssertStatement assertStatement = new AssertStatement(booleanExpression);
+        assertStatement.setStatementLabel(booleanExpression.getNodeMetaData("statementLabel"));
+        assertStatement.setSourcePosition(booleanExpression);
+
+        return assertStatement;
+    }
+
+    /**
+     * Gets a list of {@link org.codehaus.groovy.ast.stmt.ReturnStatement} instances from the given {@link MethodNode}.
+     *
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} that holds the given <tt>lastStatement</tt>
+     * @return a {@link org.codehaus.groovy.ast.stmt.ReturnStatement} or <tt>null</tt>
+     */
+    public static List<ReturnStatement> getReturnStatements(MethodNode method) {
+
+        final ReturnStatementVisitor returnStatementVisitor = new ReturnStatementVisitor();
+        returnStatementVisitor.visitMethod(method);
+
+        final List<ReturnStatement> returnStatements = returnStatementVisitor.getReturnStatements();
+        final BlockStatement blockStatement = (BlockStatement) method.getCode();
+
+        if (returnStatements.isEmpty()) {
+            final int statementCount = blockStatement.getStatements().size();
+            if (statementCount > 0) {
+                final Statement lastStatement = blockStatement.getStatements().get(statementCount - 1);
+                if (lastStatement instanceof ExpressionStatement) {
+                    final ReturnStatement returnStatement = new ReturnStatement((ExpressionStatement) lastStatement);
+                    returnStatement.setSourcePosition(lastStatement);
+                    blockStatement.getStatements().remove(lastStatement);
+                    blockStatement.addStatement(returnStatement);
+                    returnStatements.add(returnStatement);
+                }
+            }
+        }
+
+        return returnStatements;
+    }
+
+    public static void injectResultVariableReturnStatementAndAssertionCallStatement(BlockStatement statement, ClassNode returnType, ReturnStatement returnStatement, BlockStatement assertionCallStatement) {
+        final AddResultReturnStatementVisitor addResultReturnStatementVisitor = new AddResultReturnStatementVisitor(returnStatement, returnType, assertionCallStatement);
+        addResultReturnStatementVisitor.visitBlockStatement(statement);
+    }
+
+    public static void addAssertionCallStatementToReturnStatement(BlockStatement statement, ReturnStatement returnStatement, Statement assertionCallStatement) {
+        final AddAssertionCallStatementToReturnStatementVisitor addAssertionCallStatementToReturnStatementVisitor = new AddAssertionCallStatementToReturnStatementVisitor(returnStatement, assertionCallStatement);
+        addAssertionCallStatementToReturnStatementVisitor.visitBlockStatement(statement);
+    }
+
+    /**
+     * Collects all {@link ReturnStatement} instances from a given code block.
+     */
+    public static class ReturnStatementVisitor extends ClassCodeVisitorSupport {
+
+        private final List<ReturnStatement> returnStatements = new ArrayList<>();
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+
+        @Override
+        public void visitReturnStatement(ReturnStatement statement) {
+            returnStatements.add(statement);
+        }
+
+        @Override
+        public void visitClosureExpression(ClosureExpression expression) {
+            // do nothing to prevent getting return statements from closures
+        }
+
+        public List<ReturnStatement> getReturnStatements() {
+            return returnStatements;
+        }
+    }
+
+    /**
+     * Replaces a given {@link ReturnStatement} with the appropriate assertion call statement and returns a result variable expression.
+     */
+    public static class AddResultReturnStatementVisitor extends ClassCodeVisitorSupport {
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+
+        private final ReturnStatement returnStatement;
+        private final ClassNode returnType;
+        private final BlockStatement assertionCallBlock;
+
+        public AddResultReturnStatementVisitor(ReturnStatement returnStatement, ClassNode returnType, BlockStatement assertionCallBlock) {
+            this.returnStatement = returnStatement;
+            this.returnType = returnType;
+            this.assertionCallBlock = assertionCallBlock;
+        }
+
+        @Override
+        public void visitBlockStatement(BlockStatement block) {
+
+            List<Statement> blockStatementsCopy = new ArrayList<>(block.getStatements());
+
+            for (Statement statement : blockStatementsCopy) {
+                if (statement == returnStatement) {
+                    block.getStatements().remove(statement);
+                    block.addStatements(assertionCallBlock.getStatements());
+
+                    VariableExpression variableExpression = localVarX("result", returnType);
+
+                    block.addStatement(returnS(variableExpression));
+                    return; // we found the return statement under target, let's cancel tree traversal
+                }
+            }
+
+            super.visitBlockStatement(block);
+        }
+    }
+
+    /**
+     * Replaces a given {@link ReturnStatement} with the appropriate assertion call statement and returns a result variable expression.
+     */
+    public static class AddAssertionCallStatementToReturnStatementVisitor extends ClassCodeVisitorSupport {
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+
+        private final ReturnStatement returnStatement;
+        private final Statement assertionCallStatement;
+
+        public AddAssertionCallStatementToReturnStatementVisitor(ReturnStatement returnStatement, Statement assertionCallStatement) {
+            this.returnStatement = returnStatement;
+            this.assertionCallStatement = assertionCallStatement;
+        }
+
+        @Override
+        public void visitBlockStatement(BlockStatement block) {
+            List<Statement> blockStatementsCopy = new ArrayList<>(block.getStatements());
+
+            for (Statement statement : blockStatementsCopy) {
+                if (statement == returnStatement) {
+                    block.getStatements().remove(statement);
+
+                    final VariableExpression $_gc_result = localVarX("$_gc_result", ClassHelper.DYNAMIC_TYPE);
+                    block.addStatement(declS($_gc_result, returnStatement.getExpression()));
+                    block.addStatement(assertionCallStatement);
+
+                    final Statement gcResultReturn = returnS($_gc_result);
+                    gcResultReturn.setSourcePosition(returnStatement);
+                    block.addStatement(gcResultReturn);
+                    return; // we found the return statement under target, let's cancel tree traversal
+                }
+            }
+
+            super.visitBlockStatement(block);
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java
new file mode 100644
index 0000000..6e2f989
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java
@@ -0,0 +1,194 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import org.apache.groovy.contracts.ViolationTracker;
+import org.apache.groovy.contracts.ast.visitor.BaseVisitor;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.apache.groovy.contracts.util.ExpressionUtils;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+
+/**
+ * <pe
+ * Base class for groovy-contracts code generators.
+ * </p>
+ */
+public abstract class BaseGenerator {
+
+    public static final String INVARIANT_CLOSURE_PREFIX = "invariant";
+    public static final String META_DATA_USE_INLINE_MODE = "org.apache.groovy.contracts.USE_INLINE_MODE";
+
+    protected final ReaderSource source;
+
+    public BaseGenerator(final ReaderSource source) {
+        this.source = source;
+    }
+
+    /**
+     * @param classNode the {@link org.codehaus.groovy.ast.ClassNode} used to look up the invariant closure field
+     * @return the field name of the invariant closure field of the given <tt>classNode</tt>
+     */
+    public static String getInvariantMethodName(final ClassNode classNode) {
+        return INVARIANT_CLOSURE_PREFIX + "_" + classNode.getName().replaceAll("\\.", "_");
+    }
+
+    /**
+     * @param classNode the {@link org.codehaus.groovy.ast.ClassNode} used to look up the invariant closure field
+     * @return the {@link org.codehaus.groovy.ast.MethodNode} which contains the invariant of the given <tt>classNode</tt>
+     */
+    public static MethodNode getInvariantMethodNode(final ClassNode classNode) {
+        return classNode.getDeclaredMethod(getInvariantMethodName(classNode), Parameter.EMPTY_ARRAY);
+    }
+
+    protected BlockStatement getInlineModeBlockStatement(BlockStatement blockStatement) {
+        final BooleanExpression combinedBooleanExpression = ExpressionUtils.getBooleanExpression(ExpressionUtils.getBooleanExpressionsFromAssertionStatements(blockStatement));
+        return block(ifS(
+                boolX(varX(BaseVisitor.GCONTRACTS_ENABLED_VAR, ClassHelper.boolean_TYPE)),
+                block(ifS(notX(combinedBooleanExpression), blockStatement))));
+    }
+
+    protected BlockStatement wrapAssertionBooleanExpression(ClassNode type, MethodNode methodNode, BooleanExpression classInvariantExpression, String assertionType) {
+
+        final ClassNode violationTrackerClassNode = ClassHelper.makeWithoutCaching(ViolationTracker.class);
+        final VariableExpression $_gc_result = varX("$_gc_result", ClassHelper.boolean_TYPE);
+        $_gc_result.setAccessedVariable($_gc_result);
+
+        final BlockStatement ifBlockStatement = block(
+                declS($_gc_result, ConstantExpression.FALSE),
+                stmt(callX(classX(violationTrackerClassNode), "init")),
+                assignS($_gc_result, classInvariantExpression),
+                ifS(
+                        boolX(notX(callX($_gc_result, "booleanValue"))),
+                        ifS(
+                                boolX(callX(classX(violationTrackerClassNode), "violationsOccurred")),
+                                tryCatchS(
+                                        stmt(callX(classX(violationTrackerClassNode), "rethrowFirst")),
+                                        block(stmt(callX(classX(violationTrackerClassNode), "deinit"))))
+                        )
+                )
+        );
+
+        final TryCatchStatement lockTryCatchStatement = tryCatchS(
+                block(ifS(
+                        boolX(callX(classX(ClassHelper.make(ContractExecutionTracker.class)), "track", args(constX(type.getName()), constX(methodNode.getTypeDescriptor()), constX(assertionType), methodNode.isStatic() ? ConstantExpression.TRUE : ConstantExpression.FALSE))),
+                        ifBlockStatement)),
+                block(new VariableScope(), stmt(callX(
+                        classX(ClassHelper.make(ContractExecutionTracker.class)),
+                        "clear",
+                        args(constX(type.getName()), constX(methodNode.getTypeDescriptor()), constX(assertionType), methodNode.isStatic() ? ConstantExpression.TRUE : ConstantExpression.FALSE)
+                ))));
+
+        return block(ifS(boolX(varX(BaseVisitor.GCONTRACTS_ENABLED_VAR, ClassHelper.boolean_TYPE)), lockTryCatchStatement));
+    }
+
+    // TODO: what about constructor method nodes - does it find a constructor node in the super class?
+    protected BooleanExpression addCallsToSuperMethodNodeAnnotationClosure(final ClassNode type, final MethodNode methodNode, final Class<? extends Annotation> annotationType, BooleanExpression booleanExpression, boolean isPostcondition) {
+
+        final List<AnnotationNode> nextContractElementAnnotations = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), methodNode, ClassHelper.makeWithoutCaching(annotationType));
+        if (nextContractElementAnnotations.isEmpty()) {
+            if (methodNode.getNodeMetaData(META_DATA_USE_INLINE_MODE) == null)
+                methodNode.setNodeMetaData(META_DATA_USE_INLINE_MODE, Boolean.TRUE);
+            return booleanExpression;
+        }
+
+        for (AnnotationNode nextContractElementAnnotation : nextContractElementAnnotations) {
+            ClassExpression classExpression = (ClassExpression) nextContractElementAnnotation.getMember(BaseVisitor.CLOSURE_ATTRIBUTE_NAME);
+            if (classExpression == null) continue;
+
+            ArgumentListExpression callArgumentList = new ArgumentListExpression();
+            for (Parameter parameter : methodNode.getParameters()) {
+                callArgumentList.addExpression(varX(parameter));
+            }
+
+            if (isPostcondition && methodNode.getReturnType() != ClassHelper.VOID_TYPE && !(methodNode instanceof ConstructorNode)) {
+                callArgumentList.addExpression(localVarX("result", methodNode.getReturnType()));
+            }
+
+            if (isPostcondition && !(methodNode instanceof ConstructorNode)) {
+                callArgumentList.addExpression(localVarX("old", new ClassNode(Map.class)));
+            }
+
+            ArgumentListExpression newInstanceArguments = args(
+                    classExpression,
+                    new ArrayExpression(
+                            ClassHelper.DYNAMIC_TYPE,
+                            Arrays.<Expression>asList(VariableExpression.THIS_EXPRESSION, VariableExpression.THIS_EXPRESSION)
+                    )
+            );
+
+            MethodCallExpression doCall = callX(
+                    callX(ClassHelper.makeWithoutCaching(InvokerHelper.class), "invokeConstructorOf", newInstanceArguments),
+                    "doCall",
+                    callArgumentList);
+            doCall.setMethodTarget(classExpression.getType().getMethods("doCall").get(0));
+
+            booleanExpression.setSourcePosition(nextContractElementAnnotation);
+
+            booleanExpression = boolX(
+                    binX(
+                            booleanExpression,
+                            isPostcondition ? Token.newSymbol(Types.LOGICAL_AND, -1, -1) : Token.newSymbol(Types.LOGICAL_OR, -1, -1),
+                            boolX(doCall))
+            );
+        }
+
+        return booleanExpression;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/CandidateChecks.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/CandidateChecks.java
new file mode 100644
index 0000000..7c2b541
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/CandidateChecks.java
@@ -0,0 +1,150 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.PropertyNode;
+
+/**
+ * <p>
+ * Functions in this class are used to determine whether a certain AST node fulfills certain assertion
+ * requirements. E.g. whether a class node is a class invariant candidate or not.
+ * </p>
+ */
+public class CandidateChecks {
+
+    /**
+     * Checks whether the given {@link org.codehaus.groovy.ast.ClassNode} is a candidate
+     * for applying contracts. <p/>
+     * <p>
+     * If the given class node has already been processed in this compilation run, this
+     * method will return <tt>false</tt>.
+     *
+     * @param type the {@link org.codehaus.groovy.ast.ClassNode} to be checked
+     * @return whether the given <tt>type</tt> is a candidate for applying contract assertions
+     */
+    public static boolean isContractsCandidate(final ClassNode type) {
+        return type != null && !type.isSynthetic() && !type.isInterface() && !type.isEnum() && !type.isGenericsPlaceHolder() && !type.isScript() && !type.isScriptBody() && !isRuntimeClass(type);
+    }
+
+    /**
+     * Checks whether the given {@link org.codehaus.groovy.ast.ClassNode} is a candidate
+     * for applying interface contracts.
+     *
+     * @param type the {@link org.codehaus.groovy.ast.ClassNode} to be checked
+     * @return whether the given <tt>type</tt> is a candidate for applying interface contract assertions
+     */
+    public static boolean isInterfaceContractsCandidate(final ClassNode type) {
+        return type != null && type.isInterface() && !type.isSynthetic() && !type.isEnum() && !type.isGenericsPlaceHolder() && !type.isScript() && !type.isScriptBody() && !isRuntimeClass(type);
+    }
+
+    /**
+     * Decides whether the given <tt>propertyNode</tt> is a candidate for class invariant injection.
+     *
+     * @param propertyNode the {@link org.codehaus.groovy.ast.PropertyNode} to check
+     * @return whether the <tt>propertyNode</tt> is a candidate for injecting the class invariant or not
+     */
+    public static boolean isClassInvariantCandidate(final PropertyNode propertyNode) {
+        return propertyNode != null &&
+                propertyNode.isPublic() && !propertyNode.isStatic() && !propertyNode.isInStaticContext() && !propertyNode.isClosureSharedVariable();
+    }
+
+    /**
+     * Decides whether the given <tt>method</tt> is a candidate for a pre- or postcondition.
+     *
+     * @param type   the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} to check for pre- or postcondition compliance
+     * @return whether the given {@link org.codehaus.groovy.ast.MethodNode} is a candidate for pre- or postconditions
+     */
+    public static boolean isPreOrPostconditionCandidate(final ClassNode type, final MethodNode method) {
+        if (!isPreconditionCandidate(type, method) && !isPostconditionCandidate(type, method)) return false;
+
+        return true;
+    }
+
+    /**
+     * Decides whether the given <tt>method</tt> is a candidate for class invariants.
+     *
+     * @param type   the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} to check for class invariant compliance
+     * @return whether the given {@link org.codehaus.groovy.ast.MethodNode} is a candidate for class invariants
+     */
+    public static boolean isClassInvariantCandidate(final ClassNode type, final MethodNode method) {
+        if (method.isSynthetic() || method.isAbstract() || method.isStatic() || !method.isPublic()) return false;
+        if (method.getDeclaringClass() != type) return false;
+
+        return true;
+    }
+
+    /**
+     * Decides whether the given <tt>method</tt> is a candidate for a pre-condition.
+     *
+     * @param type   the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} to check for pre-condition compliance
+     * @return whether the given {@link org.codehaus.groovy.ast.MethodNode} is a candidate for pre-conditions
+     */
+    public static boolean isPreconditionCandidate(final ClassNode type, final MethodNode method) {
+        if (method.isSynthetic() || method.isAbstract()) return false;
+        if (method.getDeclaringClass() != type) return false;
+
+        return true;
+    }
+
+    /**
+     * Decides whether the given <tt>method</tt> is a candidate for a post-condition.
+     *
+     * @param type   the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} to check for post-condition compliance
+     * @return whether the given {@link org.codehaus.groovy.ast.MethodNode} is a candidate for post-conditions
+     */
+    public static boolean isPostconditionCandidate(final ClassNode type, final MethodNode method) {
+        if (!isPreconditionCandidate(type, method)) return false;
+        if (method.isStatic()) return false;
+
+        return true;
+    }
+
+    /**
+     * Checks whether the given {@link MethodNode} could be a candidate for an arbitrary {@link org.apache.groovy.contracts.annotations.meta.ContractElement}
+     * annotation.
+     *
+     * @param type   the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} to check for {@link org.apache.groovy.contracts.annotations.meta.ContractElement} compliance
+     * @return whether the given method node could be a candidate or not
+     */
+    public static boolean couldBeContractElementMethodNode(final ClassNode type, final MethodNode method) {
+        if (method.isSynthetic() || !method.isPublic()) return false;
+        if (method.getDeclaringClass() != null && !method.getDeclaringClass().getName().equals(type.getName()))
+            return false;
+
+        return true;
+    }
+
+    /**
+     * Checks whether the given {@link ClassNode} is part of the Groovy/Java runtime.
+     *
+     * @param type the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @return <tt>true</tt> whether the current {@link org.codehaus.groovy.ast.ClassNode} is a Groovy/Java system class
+     */
+    public static boolean isRuntimeClass(final ClassNode type) {
+        String name = type.getName();
+        return name.startsWith("java.") || (name.startsWith("groovy.") && !name.startsWith("groovy.contracts."));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java
new file mode 100644
index 0000000..5a6ccf5
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java
@@ -0,0 +1,143 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import groovy.contracts.Invariant;
+import org.apache.groovy.contracts.annotations.meta.ClassInvariant;
+import org.apache.groovy.contracts.ast.visitor.BaseVisitor;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.AND;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+
+/**
+ * <p>
+ * Code generator for class invariants.
+ * </p>
+ */
+public class ClassInvariantGenerator extends BaseGenerator {
+
+    public ClassInvariantGenerator(final ReaderSource source) {
+        super(source);
+    }
+
+    /**
+     * Reads the {@link Invariant} boolean expression and generates a synthetic
+     * method holding this class invariant. This is used for heir calls to find out about inherited class
+     * invariants.
+     *
+     * @param type           the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param classInvariant the {@link org.apache.groovy.contracts.domain.ClassInvariant} the assertion statement should be generated from
+     */
+    public void generateInvariantAssertionStatement(final ClassNode type, final org.apache.groovy.contracts.domain.ClassInvariant classInvariant) {
+
+        BooleanExpression classInvariantExpression = addCallsToSuperAnnotationClosure(type, ClassInvariant.class, classInvariant.booleanExpression());
+
+        final BlockStatement blockStatement = new BlockStatement();
+
+        // add a local protected method with the invariant closure - this is needed for invariant checks in inheritance lines
+        MethodNode methodNode = type.addMethod(getInvariantMethodName(type), Opcodes.ACC_PROTECTED | Opcodes.ACC_SYNTHETIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, blockStatement);
+        methodNode.setSynthetic(true);
+
+        blockStatement.addStatements(wrapAssertionBooleanExpression(type, methodNode, classInvariantExpression, "invariant").getStatements());
+    }
+
+    private BooleanExpression addCallsToSuperAnnotationClosure(final ClassNode type, final Class<? extends Annotation> annotationType, BooleanExpression booleanExpression) {
+
+        final List<AnnotationNode> nextContractElementAnnotations = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), ClassHelper.makeWithoutCaching(annotationType));
+        if (nextContractElementAnnotations.isEmpty()) return booleanExpression;
+
+        for (AnnotationNode nextContractElementAnnotation : nextContractElementAnnotations) {
+            ClassExpression classExpression = (ClassExpression) nextContractElementAnnotation.getMember(BaseVisitor.CLOSURE_ATTRIBUTE_NAME);
+            if (classExpression == null) continue;
+
+            MethodCallExpression doCall = callX(
+                    ctorX(classExpression.getType(), args(VariableExpression.THIS_EXPRESSION, VariableExpression.THIS_EXPRESSION)),
+                    "doCall"
+            );
+            doCall.setMethodTarget(classExpression.getType().getMethods("doCall").get(0));
+
+            final BooleanExpression rightExpression = boolX(doCall);
+            booleanExpression.setSourcePosition(nextContractElementAnnotation);
+            booleanExpression = boolX(binX(booleanExpression, AND, rightExpression));
+        }
+
+        return booleanExpression;
+    }
+
+    /**
+     * Adds the current class-invariant to the given <tt>method</tt>.
+     *
+     * @param type   the {@link org.codehaus.groovy.ast.ClassNode} which declared the given {@link org.codehaus.groovy.ast.MethodNode}
+     * @param method the current {@link org.codehaus.groovy.ast.MethodNode}
+     */
+    public void addInvariantAssertionStatement(final ClassNode type, final MethodNode method) {
+
+        final String invariantMethodName = getInvariantMethodName(type);
+        final MethodNode invariantMethod = type.getDeclaredMethod(invariantMethodName, Parameter.EMPTY_ARRAY);
+        if (invariantMethod == null) return;
+
+        Statement invariantMethodCall = stmt(callThisX(invariantMethod.getName()));
+
+        final Statement statement = method.getCode();
+        if (statement instanceof BlockStatement && method.getReturnType() != ClassHelper.VOID_TYPE && !(method instanceof ConstructorNode)) {
+            final BlockStatement blockStatement = (BlockStatement) statement;
+
+            final List<ReturnStatement> returnStatements = AssertStatementCreationUtility.getReturnStatements(method);
+            for (ReturnStatement returnStatement : returnStatements) {
+                AssertStatementCreationUtility.addAssertionCallStatementToReturnStatement(blockStatement, returnStatement, invariantMethodCall);
+            }
+
+            if (returnStatements.isEmpty()) blockStatement.addStatement(invariantMethodCall);
+
+        } else if (statement instanceof BlockStatement) {
+            final BlockStatement blockStatement = (BlockStatement) statement;
+            blockStatement.addStatement(invariantMethodCall);
+        } else {
+            final BlockStatement assertionBlock = new BlockStatement();
+            assertionBlock.addStatement(statement);
+            assertionBlock.addStatement(invariantMethodCall);
+            method.setCode(assertionBlock);
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/Configurator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/Configurator.java
new file mode 100644
index 0000000..eb78396
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/Configurator.java
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>Handles {@code -ea} and {@code -da} runtime input arguments for enabling and
+ * disabling contract elements.</p>
+ */
+public final class Configurator {
+
+    public static final String DISABLED_ASSERTIONS = "-da";
+    public static final String ENABLED_ASSERTIONS = "-ea";
+
+    public static final String PACKAGE_PREFIX = ":";
+    public static final String ENABLE_PACKAGE_ASSERTIONS = ENABLED_ASSERTIONS + PACKAGE_PREFIX;
+    public static final String DISABLE_PACKAGE_ASSERTIONS = DISABLED_ASSERTIONS + PACKAGE_PREFIX;
+    public static final String PACKAGE_POSTFIX = "...";
+
+    private static Map<String, Boolean> assertionConfiguration;
+
+    static {
+        initAssertionConfiguration();
+    }
+
+    private static void initAssertionConfiguration() {
+
+        assertionConfiguration = new HashMap<String, Boolean>();
+        // per default assertion are enabled (Groovy like)
+        assertionConfiguration.put(null, Boolean.TRUE);
+
+        RuntimeMXBean runtimemxBean = ManagementFactory.getRuntimeMXBean();
+        for (String arg : runtimemxBean.getInputArguments()) {
+            if (DISABLED_ASSERTIONS.equals(arg)) {
+                assertionConfiguration.put(null, Boolean.FALSE);
+
+            } else if (arg.startsWith(ENABLE_PACKAGE_ASSERTIONS) && arg.endsWith(PACKAGE_POSTFIX)) {
+                final String packageName = arg.substring(ENABLE_PACKAGE_ASSERTIONS.length(), arg.length() - PACKAGE_POSTFIX.length());
+                assertionConfiguration.put(packageName, Boolean.TRUE);
+
+            } else if (arg.startsWith(DISABLE_PACKAGE_ASSERTIONS) && arg.endsWith(PACKAGE_POSTFIX)) {
+                final String packageName = arg.substring(DISABLE_PACKAGE_ASSERTIONS.length(), arg.length() - PACKAGE_POSTFIX.length());
+
+                assertionConfiguration.put(packageName, Boolean.FALSE);
+            } else if (arg.startsWith(ENABLE_PACKAGE_ASSERTIONS)) {
+                final String className = arg.substring(ENABLE_PACKAGE_ASSERTIONS.length(), arg.length());
+                assertionConfiguration.put(className, Boolean.TRUE);
+
+            } else if (arg.startsWith(DISABLE_PACKAGE_ASSERTIONS)) {
+                final String className = arg.substring(DISABLE_PACKAGE_ASSERTIONS.length(), arg.length());
+
+                assertionConfiguration.put(className, Boolean.FALSE);
+            }
+        }
+    }
+
+    /**
+     * This static method is used within generated code to check whether assertions have been disabled for the current class or not.
+     *
+     * @param className the class name to look up in the assertion configuration
+     * @return whether assertion checking is enabled or not
+     */
+    public static boolean checkAssertionsEnabled(final String className) {
+        return internalMethod(className);
+    }
+
+    private static boolean internalMethod(String className) {
+        if (className == null || className.length() == 0) return false;
+
+        if (assertionConfiguration.containsKey(className)) return assertionConfiguration.get(className);
+        if (className.lastIndexOf('.') < 0) return assertionConfiguration.get(null);
+
+        String packageName = className.substring(0, className.lastIndexOf('.'));
+
+        while (!assertionConfiguration.containsKey(packageName)) {
+            int dotIndex = packageName.lastIndexOf('.');
+            if (dotIndex < 0) return assertionConfiguration.get(null);
+
+            packageName = packageName.substring(0, dotIndex);
+        }
+
+        if (assertionConfiguration.containsKey(packageName)) return assertionConfiguration.get(packageName);
+
+        return assertionConfiguration.get(null);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ContractExecutionTracker.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ContractExecutionTracker.java
new file mode 100644
index 0000000..9549c14
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ContractExecutionTracker.java
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import java.util.HashSet;
+import java.util.Objects;
+
+/**
+ * Keeps track of contract executions to avoid cyclic contract checks.
+ */
+public class ContractExecutionTracker {
+
+    public static final class ContractExecution {
+        final String className;
+        final String methodIdentifier;
+        final String assertionType;
+        final boolean isStatic;
+
+        public ContractExecution(String className, String methodIdentifier, String assertionType, boolean isStatic) {
+            this.className = className;
+            this.methodIdentifier = methodIdentifier;
+            this.assertionType = assertionType;
+            this.isStatic = isStatic;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            ContractExecution that = (ContractExecution) o;
+
+            if (isStatic != that.isStatic)
+                return false;
+            if (!Objects.equals(assertionType, that.assertionType))
+                return false;
+            if (!Objects.equals(className, that.className))
+                return false;
+            if (!Objects.equals(methodIdentifier, that.methodIdentifier))
+                return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = className != null ? className.hashCode() : 0;
+            result = 31 * result + (methodIdentifier != null ? methodIdentifier.hashCode() : 0);
+            result = 31 * result + (assertionType != null ? assertionType.hashCode() : 0);
+            result = 31 * result + (isStatic ? 1 : 0);
+            return result;
+        }
+    }
+
+
+    static class ContractExecutionThreadLocal extends ThreadLocal<HashSet<ContractExecution>> {
+
+        @Override
+        protected HashSet<ContractExecution> initialValue() {
+            return new HashSet<ContractExecution>();
+        }
+    }
+
+    private static ThreadLocal<HashSet<ContractExecution>> executions = new ContractExecutionThreadLocal();
+
+    public static boolean track(String className, String methodIdentifier, String assertionType, boolean isStatic) {
+        final ContractExecution ce = new ContractExecution(className, methodIdentifier, assertionType, isStatic);
+        final HashSet<ContractExecution> contractExecutions = executions.get();
+
+        if (!contractExecutions.contains(ce)) {
+            contractExecutions.add(ce);
+            return true;
+        }
+
+        return false;
+    }
+
+    public static void clear(String className, String methodIdentifier, String assertionType, boolean isStatic) {
+        final HashSet<ContractExecution> contractExecutions = executions.get();
+
+        contractExecutions.remove(new ContractExecution(className, methodIdentifier, assertionType, isStatic));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/OldVariableGenerationUtility.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/OldVariableGenerationUtility.java
new file mode 100644
index 0000000..03b4943
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/OldVariableGenerationUtility.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callSuperX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+
+/**
+ * <p>Central place where code generation for the <tt>old</tt> closure variable
+ * takes place.</p>
+ */
+public class OldVariableGenerationUtility {
+
+    public static final String OLD_VARIABLES_METHOD = "$_gc_computeOldVariables";
+
+    /**
+     * Creates a synthetic method handling generation of the <tt>old</tt> variable map. If a super class declares
+     * the same synthetic method it will be called and the results will be merged.
+     *
+     * @param classNode which contains postconditions, so an old variable generating method makes sense here.
+     */
+    public static void addOldVariableMethodNode(final ClassNode classNode) {
+        if (classNode.getDeclaredMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null) return;
+
+        final BlockStatement methodBlockStatement = new BlockStatement();
+
+        final MapExpression oldVariablesMap = new MapExpression();
+
+        // create variable assignments for old variables
+        for (final FieldNode fieldNode : classNode.getFields()) {
+            if (fieldNode.getName().startsWith("$")) continue;
+
+            final ClassNode fieldType = ClassHelper.getWrapper(fieldNode.getType());
+
+            if (fieldType.getName().startsWith("java.lang") || ClassHelper.isPrimitiveType(fieldType) || fieldType.getName().startsWith("java.math") ||
+                    fieldType.getName().startsWith("java.util") ||
+                    fieldType.getName().startsWith("java.sql") ||
+                    fieldType.getName().equals("groovy.lang.GString") ||
+                    fieldType.getName().equals("java.lang.String")) {
+
+                MethodNode cloneMethod = fieldType.getMethod("clone", Parameter.EMPTY_ARRAY);
+                // if a clone classNode is available, the value is cloned
+                if (cloneMethod != null && fieldType.implementsInterface(ClassHelper.make("java.lang.Cloneable"))) {
+
+                    final MethodCallExpression cloneField = callX(fieldX(fieldNode), "clone");
+                    // return null if field is null
+                    cloneField.setSafe(true);
+
+                    VariableExpression oldVariable = localVarX("$old$" + fieldNode.getName(), fieldNode.getType());
+                    Statement oldVariableAssignment = declS(oldVariable, cloneField);
+
+                    methodBlockStatement.addStatement(oldVariableAssignment);
+                    oldVariablesMap.addMapEntryExpression(new MapEntryExpression(constX(oldVariable.getName().substring("$old$".length())), oldVariable));
+
+                } else if (ClassHelper.isPrimitiveType(fieldType)
+                        || ClassHelper.isNumberType(fieldType)
+                        || fieldType.getName().startsWith("java.math")
+                        || fieldType.getName().equals("groovy.lang.GString")
+                        || fieldType.getName().equals("java.lang.String")) {
+
+                    VariableExpression oldVariable = localVarX("$old$" + fieldNode.getName(), fieldNode.getType());
+                    Statement oldVariableAssignment = declS(oldVariable, fieldX(fieldNode));
+
+                    methodBlockStatement.addStatement(oldVariableAssignment);
+                    oldVariablesMap.addMapEntryExpression(new MapEntryExpression(constX(oldVariable.getName().substring("$old$".length())), oldVariable));
+                }
+            }
+        }
+
+        VariableExpression oldVariable = localVarX("old", new ClassNode(Map.class));
+        methodBlockStatement.addStatement(declS(oldVariable, oldVariablesMap));
+        VariableExpression mergedOldVariables = null;
+
+        // let's ask the super class for old variables...
+        if (classNode.getSuperClass() != null && classNode.getSuperClass().getMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null) {
+            mergedOldVariables = localVarX("mergedOldVariables", new ClassNode(Map.class));
+            methodBlockStatement.addStatement(declS(mergedOldVariables,
+                    callX(oldVariable, "plus", args(callSuperX(OLD_VARIABLES_METHOD)))));
+        }
+
+        methodBlockStatement.addStatement(returnS(mergedOldVariables != null ? mergedOldVariables : oldVariable));
+
+        final MethodNode preconditionMethodNode = classNode.addMethod(OLD_VARIABLES_METHOD, Opcodes.ACC_PROTECTED, new ClassNode(Map.class), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, methodBlockStatement);
+        preconditionMethodNode.setSynthetic(true);
+
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java
new file mode 100644
index 0000000..0b39d82
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import org.apache.groovy.contracts.annotations.meta.Postcondition;
+import org.apache.groovy.contracts.ast.visitor.AnnotationClosureVisitor;
+import org.apache.groovy.contracts.ast.visitor.BaseVisitor;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+
+/**
+ * <p>
+ * Code generator for postconditions.
+ * </p>
+ */
+public class PostconditionGenerator extends BaseGenerator {
+
+    public PostconditionGenerator(final ReaderSource source) {
+        super(source);
+    }
+
+    /**
+     * Adds a synthetic method to the given <tt>classNode</tt> which can be used
+     * to create a map of most instance variables found in this class. Used for the <tt>old</tt> variable
+     * mechanism.
+     *
+     * @param classNode the {@link org.codehaus.groovy.ast.ClassNode} to add the synthetic method to
+     */
+    public void addOldVariablesMethod(final ClassNode classNode) {
+        OldVariableGenerationUtility.addOldVariableMethodNode(classNode);
+    }
+
+    /**
+     * Injects a postcondition assertion statement in the given <tt>method</tt>, based on the <tt>booleanExpression</tt>.
+     *
+     * @param method        the {@link org.codehaus.groovy.ast.MethodNode} for assertion injection
+     * @param postcondition the {@link org.apache.groovy.contracts.domain.Postcondition} the assertion statement should be generated from
+     */
+    public void generatePostconditionAssertionStatement(MethodNode method, org.apache.groovy.contracts.domain.Postcondition postcondition) {
+
+        final BooleanExpression postconditionBooleanExpression = addCallsToSuperMethodNodeAnnotationClosure(method.getDeclaringClass(), method, Postcondition.class, postcondition.booleanExpression(), true);
+
+
+        BlockStatement blockStatement;
+        final BlockStatement originalBlockStatement = postcondition.originalBlockStatement();
+        // if use execution tracker flag is found in the meta-data the annotation closure visitor discovered
+        // method calls which might be subject to cycling boolean expressions -> no inline mode possible
+        final boolean useExecutionTracker = originalBlockStatement == null || Boolean.TRUE.equals(originalBlockStatement.getNodeMetaData(AnnotationClosureVisitor.META_DATA_USE_EXECUTION_TRACKER));
+
+        if (!useExecutionTracker && Boolean.TRUE.equals(method.getNodeMetaData(META_DATA_USE_INLINE_MODE))) {
+            blockStatement = getInlineModeBlockStatement(originalBlockStatement);
+        } else {
+            blockStatement = wrapAssertionBooleanExpression(method.getDeclaringClass(), method, postconditionBooleanExpression, "postcondition");
+        }
+
+        addPostcondition(method, blockStatement);
+    }
+
+    /**
+     * Adds a default postcondition if a postcondition has already been defined for this {@link org.codehaus.groovy.ast.MethodNode}
+     * in a super-class.
+     *
+     * @param type   the current {@link org.codehaus.groovy.ast.ClassNode} of the given <tt>methodNode</tt>
+     * @param method the {@link org.codehaus.groovy.ast.MethodNode} to create the default postcondition for
+     */
+    public void generateDefaultPostconditionStatement(final ClassNode type, final MethodNode method) {
+
+        // if another precondition is available we'll evaluate to false
+        boolean isAnotherPostconditionAvailable = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), method, ClassHelper.makeWithoutCaching(Postcondition.class)).size() > 0;
+        if (!isAnotherPostconditionAvailable) return;
+
+        // if another post-condition is available we need to add a default expression of TRUE
+        // since post-conditions are usually connected with a logical AND
+        final BooleanExpression postconditionBooleanExpression = addCallsToSuperMethodNodeAnnotationClosure(method.getDeclaringClass(), method, Postcondition.class, new BooleanExpression(ConstantExpression.TRUE), true);
+        if (postconditionBooleanExpression.getExpression() == ConstantExpression.TRUE) return;
+
+        final BlockStatement blockStatement = wrapAssertionBooleanExpression(type, method, postconditionBooleanExpression, "postcondition");
+        addPostcondition(method, blockStatement);
+    }
+
+    private void addPostcondition(MethodNode method, BlockStatement postconditionBlockStatement) {
+        final BlockStatement block = (BlockStatement) method.getCode();
+
+        // if return type is not void, than a "result" variable is provided in the postcondition expression
+        final List<Statement> statements = block.getStatements();
+        if (statements.size() > 0) {
+            Expression contractsEnabled = localVarX(BaseVisitor.GCONTRACTS_ENABLED_VAR, ClassHelper.boolean_TYPE);
+
+            if (method.getReturnType() != ClassHelper.VOID_TYPE) {
+                List<ReturnStatement> returnStatements = AssertStatementCreationUtility.getReturnStatements(method);
+
+                for (ReturnStatement returnStatement : returnStatements) {
+                    BlockStatement localPostconditionBlockStatement = block(new VariableScope(), postconditionBlockStatement.getStatements());
+
+                    Expression result = localVarX("result", method.getReturnType());
+                    localPostconditionBlockStatement.getStatements().add(0, declS(result, returnStatement.getExpression()));
+                    AssertStatementCreationUtility.injectResultVariableReturnStatementAndAssertionCallStatement(block, method.getReturnType().redirect(), returnStatement, localPostconditionBlockStatement);
+                }
+                setOldVariablesIfEnabled(block, contractsEnabled);
+
+            } else if (method instanceof ConstructorNode) {
+                block.addStatements(postconditionBlockStatement.getStatements());
+            } else {
+                setOldVariablesIfEnabled(block, contractsEnabled);
+                block.addStatements(postconditionBlockStatement.getStatements());
+            }
+        }
+    }
+
+    private void setOldVariablesIfEnabled(BlockStatement block, Expression contractsEnabled) {
+        // Assign the return statement expression to a local variable: Map old
+        final Expression oldVariableExpression = localVarX("old", new ClassNode(Map.class));
+        Statement oldVariableStatement = assignS(oldVariableExpression, callThisX(OldVariableGenerationUtility.OLD_VARIABLES_METHOD));
+        block.getStatements().add(0, declS(oldVariableExpression, ConstantExpression.NULL));
+        block.getStatements().add(1, ifS(boolX(contractsEnabled), block(oldVariableStatement)));
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PreconditionGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PreconditionGenerator.java
new file mode 100644
index 0000000..29b9db9
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PreconditionGenerator.java
@@ -0,0 +1,120 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import groovy.contracts.Requires;
+import org.apache.groovy.contracts.annotations.meta.Precondition;
+import org.apache.groovy.contracts.ast.visitor.AnnotationClosureVisitor;
+import org.apache.groovy.contracts.util.AnnotationUtils;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.io.ReaderSource;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+
+/**
+ * Code generator for preconditions.
+ */
+public class PreconditionGenerator extends BaseGenerator {
+
+    public PreconditionGenerator(final ReaderSource source) {
+        super(source);
+    }
+
+    /**
+     * Injects a precondition assertion statement in the given <tt>method</tt>, based on the given <tt>annotation</tt> of
+     * type {@link Requires}.
+     *
+     * @param method       the {@link org.codehaus.groovy.ast.MethodNode} for assertion injection
+     * @param precondition the {@link org.apache.groovy.contracts.domain.Precondition} the assertion statement should be generated from
+     */
+    public void generatePreconditionAssertionStatement(final MethodNode method, final org.apache.groovy.contracts.domain.Precondition precondition) {
+        final BooleanExpression preconditionBooleanExpression = addCallsToSuperMethodNodeAnnotationClosure(method.getDeclaringClass(), method, Precondition.class, precondition.booleanExpression(), false);
+
+        BlockStatement blockStatement;
+
+        final BlockStatement originalBlockStatement = precondition.originalBlockStatement();
+        // if use execution tracker flag is found in the meta-data the annotation closure visitor discovered
+        // method calls which might be subject to cycling boolean expressions -> no inline mode possible
+        final boolean useExecutionTracker = originalBlockStatement == null || Boolean.TRUE.equals(originalBlockStatement.getNodeMetaData(AnnotationClosureVisitor.META_DATA_USE_EXECUTION_TRACKER));
+
+        if (!useExecutionTracker && Boolean.TRUE.equals(method.getNodeMetaData(META_DATA_USE_INLINE_MODE))) {
+            blockStatement = getInlineModeBlockStatement(precondition.originalBlockStatement());
+        } else {
+            blockStatement = wrapAssertionBooleanExpression(method.getDeclaringClass(), method, preconditionBooleanExpression, "precondition");
+        }
+
+        addPrecondition(method, blockStatement);
+    }
+
+    /**
+     * Generates the default precondition statement for {@link org.codehaus.groovy.ast.MethodNode} instances with
+     * the {@link org.apache.groovy.contracts.annotations.meta.Precondition} annotation.
+     *
+     * @param type       the current {@link org.codehaus.groovy.ast.ClassNode}
+     * @param methodNode the {@link org.codehaus.groovy.ast.MethodNode} with a {@link org.apache.groovy.contracts.annotations.meta.Precondition} annotation
+     */
+    public void generateDefaultPreconditionStatement(final ClassNode type, final MethodNode methodNode) {
+
+        // if another precondition is available we'll evaluate to false
+        boolean isAnotherPreconditionAvailable = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), methodNode, ClassHelper.makeWithoutCaching(Precondition.class)).size() > 0;
+        if (!isAnotherPreconditionAvailable) return;
+
+        // if there is another preconditio up the inheritance path, we need a default precondition with FALSE
+        // e.g. C1 <no precondition> : C2 <item != null> == false || item != null
+        BooleanExpression preconditionBooleanExpression = boolX(ConstantExpression.FALSE);
+        preconditionBooleanExpression = addCallsToSuperMethodNodeAnnotationClosure(type, methodNode, Precondition.class, preconditionBooleanExpression, false);
+        // if precondition could not be found in parent class, let's return
+        if (preconditionBooleanExpression.getExpression() == ConstantExpression.FALSE)
+            return;
+
+        final BlockStatement blockStatement = wrapAssertionBooleanExpression(type, methodNode, preconditionBooleanExpression, "precondition");
+
+        addPrecondition(methodNode, blockStatement);
+    }
+
+    private void addPrecondition(MethodNode method, BlockStatement blockStatement) {
+        final BlockStatement modifiedMethodCode = new BlockStatement();
+        modifiedMethodCode.addStatements(blockStatement.getStatements());
+
+        if (method.getCode() instanceof BlockStatement) {
+
+            BlockStatement methodBlock = (BlockStatement) method.getCode();
+            for (Statement statement : methodBlock.getStatements()) {
+                if (method instanceof ConstructorNode && statement instanceof ExpressionStatement && ((ExpressionStatement) statement).getExpression() instanceof ConstructorCallExpression) {
+                    modifiedMethodCode.getStatements().add(0, statement);
+                } else {
+                    modifiedMethodCode.getStatements().add(statement);
+                }
+            }
+        } else {
+            modifiedMethodCode.addStatement(method.getCode());
+        }
+
+        method.setCode(modifiedMethodCode);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/TryCatchBlockGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/TryCatchBlockGenerator.java
new file mode 100644
index 0000000..ef148fc
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/TryCatchBlockGenerator.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.PLUS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.catchS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+
+/**
+ * Creates a try-catch block around a given {@link org.codehaus.groovy.ast.stmt.AssertStatement} and catches
+ * a PowerAssertionError to reuse the generated visual output.
+ */
+public class TryCatchBlockGenerator {
+
+    public static BlockStatement generateTryCatchBlockForInlineMode(final ClassNode assertionErrorClass, final String message, final Statement assertStatement) {
+
+        final Class powerAssertionErrorClass = loadPowerAssertionErrorClass();
+
+        if (powerAssertionErrorClass == null)
+            throw new GroovyBugError("groovy-contracts needs Groovy 1.7 or above!");
+
+        VariableExpression newErrorVariableExpression = localVarX("newError", assertionErrorClass);
+
+        Statement expr = declS(newErrorVariableExpression,
+                ctorX(assertionErrorClass,
+                        args(binX(constX(message), PLUS, callX(varX(param(ClassHelper.makeWithoutCaching(powerAssertionErrorClass), "error")), "getMessage")))));
+
+        Statement exp2 = stmt(callX(newErrorVariableExpression, "setStackTrace", args(
+                callX(varX(param(ClassHelper.makeWithoutCaching(powerAssertionErrorClass), "error")), "getStackTrace")
+        )));
+
+        final TryCatchStatement tryCatchStatement = tryCatchS(assertStatement);
+        tryCatchStatement.addCatch(catchS(
+                param(ClassHelper.makeWithoutCaching(powerAssertionErrorClass), "error"),
+                block(expr, exp2, throwS(newErrorVariableExpression))));
+
+        return block(tryCatchStatement);
+    }
+
+    public static BlockStatement generateTryCatchBlock(final ClassNode assertionErrorClass, final String message, final Statement assertStatement) {
+
+        final String $_gc_closure_result = "$_gc_closure_result";
+
+        final VariableExpression variableExpression = localVarX($_gc_closure_result, ClassHelper.Boolean_TYPE);
+
+        // if the assert statement is successful the return variable will be true else false
+        final BlockStatement overallBlock = new BlockStatement();
+        overallBlock.addStatement(declS(variableExpression, ConstantExpression.FALSE));
+
+        final BlockStatement assertBlockStatement = block(
+                assertStatement,
+                assignS(variableExpression, ConstantExpression.TRUE)
+        );
+
+        final Class powerAssertionErrorClass = loadPowerAssertionErrorClass();
+
+        if (powerAssertionErrorClass == null)
+            throw new GroovyBugError("groovy-contracts needs Groovy 1.7 or above!");
+
+        VariableExpression newErrorVariableExpression = localVarX("newError", assertionErrorClass);
+
+        Statement expr = declS(newErrorVariableExpression, ctorX(assertionErrorClass,
+                args(binX(constX(message), PLUS, callX(varX(param(ClassHelper.makeWithoutCaching(powerAssertionErrorClass), "error")), "getMessage")))));
+
+        Statement exp2 = stmt(callX(newErrorVariableExpression, "setStackTrace", args(
+                callX(varX(param(ClassHelper.makeWithoutCaching(powerAssertionErrorClass), "error")), "getStackTrace")
+        )));
+
+        final TryCatchStatement tryCatchStatement = tryCatchS(assertBlockStatement);
+        tryCatchStatement.addCatch(catchS(param(ClassHelper.makeWithoutCaching(powerAssertionErrorClass), "error"), block(expr, exp2)));
+
+        overallBlock.addStatement(tryCatchStatement);
+        overallBlock.addStatement(returnS(variableExpression));
+
+        return overallBlock;
+    }
+
+    private static Class loadPowerAssertionErrorClass() {
+
+        Class result = null;
+
+        try {
+            result = TryCatchBlockGenerator.class.getClassLoader().loadClass("org.codehaus.groovy.transform.powerassert.PowerAssertionError");
+        } catch (ClassNotFoundException e) {
+            try {
+                result = TryCatchBlockGenerator.class.getClassLoader().loadClass("org.codehaus.groovy.runtime.powerassert.PowerAssertionError");
+            } catch (ClassNotFoundException ignore) {
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/AnnotationUtils.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/AnnotationUtils.java
new file mode 100644
index 0000000..1cd89eb
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/AnnotationUtils.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.util;
+
+import org.apache.groovy.contracts.generation.CandidateChecks;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Helper methods for reading/getting {@link org.codehaus.groovy.ast.AnnotationNode} instances.</p>
+ */
+public class AnnotationUtils {
+
+    /**
+     * Checks whether the given {@link org.codehaus.groovy.ast.ClassNode} is annotated
+     * with an annotations of the given package or full annotatedNode name.
+     *
+     * @param annotatedNode     the {@link org.codehaus.groovy.ast.AnnotatedNode} to search for the given annotation
+     * @param typeOrPackageName can either be a part of the package or the complete annotation class name
+     * @return <tt>true</tt> if an annotation was found, <tt>false</tt> otherwise
+     */
+    public static boolean hasAnnotationOfType(AnnotatedNode annotatedNode, String typeOrPackageName) {
+        for (AnnotationNode annotation : annotatedNode.getAnnotations()) {
+            if (annotation.getClassNode().getName().startsWith(typeOrPackageName)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets the next {@link org.codehaus.groovy.ast.AnnotationNode} instance in the inheritance line which is annotated
+     * with the given Annotation class <tt>anno</tt>.
+     *
+     * @param type the {@link org.codehaus.groovy.ast.ClassNode} to check for the annotation
+     * @param anno the annotation to watch out for
+     * @return the next {@link org.codehaus.groovy.ast.AnnotationNode} in the inheritance line, or <tt>null</tt>
+     */
+    public static List<AnnotationNode> getAnnotationNodeInHierarchyWithMetaAnnotation(ClassNode type, ClassNode anno) {
+        List<AnnotationNode> result = new ArrayList<AnnotationNode>();
+        for (AnnotationNode annotation : type.getAnnotations()) {
+            if (annotation.getClassNode().getAnnotations(anno).size() > 0) {
+                result.add(annotation);
+            }
+        }
+
+        if (result.isEmpty() && type.getSuperClass() != null) {
+            return getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), anno);
+        } else {
+            return result;
+        }
+    }
+
+    /**
+     * <p>Checks whether there exists a {@link MethodNode} up the inheritance tree where exists an annotation which is annotated
+     * with <tt>metaAnnotationClassNode</tt>.</p>
+     *
+     * @param type                    the origin {@link ClassNode}
+     * @param originMethodNode        the origin {@link MethodNode}
+     * @param metaAnnotationClassNode the {@link ClassNode} of the meta-annotation
+     * @return a list of {@link AnnotationNode} all annotated with <tt>metaAnnotationClassNode</tt>
+     */
+    public static List<AnnotationNode> getAnnotationNodeInHierarchyWithMetaAnnotation(ClassNode type, MethodNode originMethodNode, ClassNode metaAnnotationClassNode) {
+        List<AnnotationNode> result = new ArrayList<AnnotationNode>();
+
+        while (type != null) {
+            MethodNode methodNode = type.getMethod(originMethodNode.getName(), originMethodNode.getParameters());
+            if (methodNode != null) {
+                for (AnnotationNode annotation : methodNode.getAnnotations()) {
+                    if (annotation.getClassNode().getAnnotations(metaAnnotationClassNode).size() > 0) {
+                        result.add(annotation);
+                    }
+                }
+
+                if (result.size() > 0) return result;
+            }
+
+            type = type.getSuperClass();
+        }
+
+        return result;
+    }
+
+    /**
+     * Loads all annotation nodes of the given {@link org.codehaus.groovy.ast.AnnotatedNode} instance which are marked
+     * with the annotation <tt>metaAnnotationClassName</tt>.
+     *
+     * @param annotatedNode           an {@link org.codehaus.groovy.ast.AnnotatedNode} from which the annotations are checked
+     * @param metaAnnotationClassName the name of the meta annotation
+     * @return a list of {@link AnnotationNode} instances which implement the given <tt>metaAnnotationClass</tt>
+     */
+    public static List<AnnotationNode> hasMetaAnnotations(AnnotatedNode annotatedNode, String metaAnnotationClassName) {
+
+        ArrayList<AnnotationNode> result = new ArrayList<AnnotationNode>();
+
+        for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) {
+            if (CandidateChecks.isRuntimeClass(annotationNode.getClassNode())) continue;
+
+            // is the annotation marked with the given meta annotation?
+            List<AnnotationNode> metaAnnotations = annotationNode.getClassNode().getAnnotations(ClassHelper.makeWithoutCaching(metaAnnotationClassName));
+            if (metaAnnotations.isEmpty()) {
+                metaAnnotations = hasMetaAnnotations(annotationNode.getClassNode(), metaAnnotationClassName);
+            }
+
+            if (metaAnnotations.size() > 0) result.add(annotationNode);
+        }
+        return result;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/ExpressionUtils.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/ExpressionUtils.java
new file mode 100644
index 0000000..54a1a81
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/ExpressionUtils.java
@@ -0,0 +1,137 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.util;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.AND;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+
+/**
+ * <p>Internal utility class for extracting a boolean expression from the given expression or statement.</p>
+ *
+ * @see ClosureExpression
+ * @see BooleanExpression
+ */
+public class ExpressionUtils {
+
+    /**
+     * Returns all {@link BooleanExpression} instances found in the given {@link ClosureExpression}.
+     */
+    public static List<BooleanExpression> getBooleanExpression(ClosureExpression closureExpression) {
+        if (closureExpression == null) return null;
+
+        final BlockStatement closureBlockStatement = (BlockStatement) closureExpression.getCode();
+        return getBooleanExpressions(closureBlockStatement);
+    }
+
+    /**
+     * Returns all {@link BooleanExpression} instances found in the given {@link BlockStatement}.
+     */
+    private static List<BooleanExpression> getBooleanExpressions(BlockStatement closureBlockStatement) {
+        final List<Statement> statementList = closureBlockStatement.getStatements();
+
+        List<BooleanExpression> booleanExpressions = new ArrayList<BooleanExpression>();
+
+        for (Statement stmt : statementList) {
+            BooleanExpression tmp = null;
+
+            if (stmt instanceof ExpressionStatement && ((ExpressionStatement) stmt).getExpression() instanceof BooleanExpression) {
+                tmp = (BooleanExpression) ((ExpressionStatement) stmt).getExpression();
+                tmp.setNodeMetaData("statementLabel", stmt.getStatementLabel());
+            } else if (stmt instanceof ExpressionStatement) {
+                Expression expression = ((ExpressionStatement) stmt).getExpression();
+                tmp = boolX(expression);
+                tmp.setSourcePosition(expression);
+                tmp.setNodeMetaData("statementLabel", stmt.getStatementLabel());
+            }
+
+            booleanExpressions.add(tmp);
+        }
+
+        return booleanExpressions;
+    }
+
+    /**
+     * Returns all {@link BooleanExpression} instances found in the given {@link BlockStatement}.
+     */
+    public static List<BooleanExpression> getBooleanExpressionsFromAssertionStatements(BlockStatement blockStatement) {
+        AssertStatementCollector collector = new AssertStatementCollector();
+        collector.visitBlockStatement(blockStatement);
+
+        List<AssertStatement> assertStatements = collector.assertStatements;
+        if (assertStatements.isEmpty()) return Collections.emptyList();
+
+        List<BooleanExpression> booleanExpressions = new ArrayList<BooleanExpression>();
+        for (AssertStatement assertStatement : assertStatements) {
+            booleanExpressions.add(assertStatement.getBooleanExpression());
+        }
+
+        return booleanExpressions;
+    }
+
+    public static BooleanExpression getBooleanExpression(List<BooleanExpression> booleanExpressions) {
+        if (booleanExpressions == null || booleanExpressions.isEmpty())
+            return boolX(ConstantExpression.TRUE);
+
+        BooleanExpression result = null;
+        for (BooleanExpression booleanExpression : booleanExpressions) {
+            if (result == null) {
+                result = booleanExpression;
+            } else {
+                result = boolX(binX(result, AND, booleanExpression));
+            }
+        }
+
+        return result;
+    }
+
+    static class AssertStatementCollector extends ClassCodeVisitorSupport implements Opcodes {
+
+        public List<AssertStatement> assertStatements = new ArrayList<AssertStatement>();
+
+        @Override
+        public void visitAssertStatement(AssertStatement statement) {
+            assertStatements.add(statement);
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/FieldValues.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/FieldValues.java
new file mode 100644
index 0000000..fc3d895
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/FieldValues.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.util;
+
+import java.lang.reflect.Field;
+
+/**
+ * This utility is meant to be used to replace direct calls to private
+ * field variables in class invariants.
+ */
+public class FieldValues {
+
+    @SuppressWarnings("unchecked")
+    public static <T> T fieldValue(Object obj, String fieldName, Class<T> type) throws IllegalAccessException {
+        Validate.notNull(obj);
+        Validate.notNull(fieldName);
+
+        Field f = findField(obj.getClass(), "thisObject");
+        if (f == null) throw new IllegalArgumentException("Field thisObject could not be found!");
+        f.setAccessible(true);
+
+        Object target = f.get(obj);
+
+        f = findField(target.getClass(), fieldName);
+        if (f == null) throw new IllegalArgumentException("Field " + fieldName + " could not be found!");
+        f.setAccessible(true);
+
+        return (T) f.get(target);
+    }
+
+    private static Field findField(Class<?> clazz, String name) {
+        Class<?> next = clazz;
+        while (!Object.class.equals(next) && next != null) {
+            Field[] fields = next.getDeclaredFields();
+            for (Field field : fields) {
+                if ((name.equals(field.getName()))) {
+                    return field;
+                }
+            }
+            next = next.getSuperclass();
+        }
+        return null;
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/LifecycleImplementationLoader.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/LifecycleImplementationLoader.java
new file mode 100644
index 0000000..bd4e6b5
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/LifecycleImplementationLoader.java
@@ -0,0 +1,211 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.ServiceConfigurationError;
+
+/**
+ * <p>Finds and loads implementation classes of interface {@link org.apache.groovy.contracts.common.spi.Lifecycle}.</p>
+ **/
+public final class LifecycleImplementationLoader<S> implements Iterable<S> {
+
+    private static final String PREFIX = "META-INF/services/";
+
+    private Class<S> service;
+    private ClassLoader loader;
+    private LinkedHashMap<String, S> providers = new LinkedHashMap<String, S>();
+    private LazyIterator lookupIterator;
+
+    public void reload() {
+        providers.clear();
+        lookupIterator = new LazyIterator(service, loader);
+    }
+
+    private LifecycleImplementationLoader(Class<S> svc, ClassLoader cl) {
+        service = svc;
+        loader = cl;
+        reload();
+    }
+
+    private static void fail(Class service, String msg, Throwable cause) throws ServiceConfigurationError {
+        throw new ServiceConfigurationError(service.getName() + ": " + msg, cause);
+    }
+
+    private static void fail(Class service, String msg) throws ServiceConfigurationError {
+        throw new ServiceConfigurationError(service.getName() + ": " + msg);
+    }
+
+    private static void fail(Class service, URL u, int line, String msg) throws ServiceConfigurationError {
+        fail(service, u + ":" + line + ": " + msg);
+    }
+
+    private int parseLine(Class service, URL u, BufferedReader r, int lc, List<String> names) throws IOException, ServiceConfigurationError {
+        String ln = r.readLine();
+        if (ln == null) {
+            return -1;
+        }
+        int ci = ln.indexOf('#');
+        if (ci >= 0) ln = ln.substring(0, ci);
+        ln = ln.trim();
+        int n = ln.length();
+        if (n != 0) {
+            if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
+                fail(service, u, lc, "Illegal configuration-file syntax");
+            int cp = ln.codePointAt(0);
+            if (!Character.isJavaIdentifierStart(cp))
+                fail(service, u, lc, "Illegal provider-class name: " + ln);
+            for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
+                cp = ln.codePointAt(i);
+                if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
+                    fail(service, u, lc, "Illegal provider-class name: " + ln);
+            }
+            if (!providers.containsKey(ln) && !names.contains(ln))
+                names.add(ln);
+        }
+        return lc + 1;
+    }
+
+    private Iterator<String> parse(Class service, URL u) throws ServiceConfigurationError {
+        InputStream in = null;
+        BufferedReader r = null;
+        ArrayList<String> names = new ArrayList<String>();
+        try {
+            in = u.openStream();
+            r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+            int lc = 1;
+            while ((lc = parseLine(service, u, r, lc, names)) >= 0) ;
+        } catch (IOException x) {
+            fail(service, "Error reading configuration file", x);
+        } finally {
+            try {
+                if (r != null) r.close();
+                if (in != null) in.close();
+            } catch (IOException y) {
+                fail(service, "Error closing configuration file", y);
+            }
+        }
+        return names.iterator();
+    }
+
+    private class LazyIterator implements Iterator<S> {
+
+        Class<S> service;
+        ClassLoader loader;
+        Enumeration<URL> configs = null;
+        Iterator<String> pending = null;
+        String nextName = null;
+
+        private LazyIterator(Class<S> service, ClassLoader loader) {
+            this.service = service;
+            this.loader = loader;
+        }
+
+        public boolean hasNext() {
+            if (nextName != null) {
+                return true;
+            }
+            if (configs == null) {
+                try {
+                    String fullName = PREFIX + service.getName();
+                    if (loader == null)
+                        configs = ClassLoader.getSystemResources(fullName);
+                    else
+                        configs = loader.getResources(fullName);
+                } catch (IOException x) {
+                    fail(service, "Error locating configuration files", x);
+                }
+            }
+            while ((pending == null) || !pending.hasNext()) {
+                if (!configs.hasMoreElements()) {
+                    return false;
+                }
+                pending = parse(service, configs.nextElement());
+            }
+            nextName = pending.next();
+            return true;
+        }
+
+        public S next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            String cn = nextName;
+            nextName = null;
+            try {
+                S p = service.cast(Class.forName(cn, true, loader).newInstance());
+                providers.put(cn, p);
+                return p;
+            } catch (ClassNotFoundException x) {
+                fail(service, "Provider " + cn + " not found");
+            } catch (Throwable x) {
+                fail(service, "Provider " + cn + " could not be instantiated: " + x, x);
+            }
+            throw new Error();          // This cannot happen
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+    public Iterator<S> iterator() {
+        return new Iterator<S>() {
+
+            Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator();
+
+            public boolean hasNext() {
+                if (knownProviders.hasNext())
+                    return true;
+                return lookupIterator.hasNext();
+            }
+
+            public S next() {
+                if (knownProviders.hasNext())
+                    return knownProviders.next().getValue();
+                return lookupIterator.next();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+        };
+    }
+
+    /**
+     * Creates a new {@link org.apache.groovy.contracts.common.spi.Lifecycle} for the given type and class
+     * loader.
+     */
+    public static <S> LifecycleImplementationLoader<S> load(Class<S> service, ClassLoader loader) {
+        return new LifecycleImplementationLoader<S>(service, loader);
+    }
+}
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/Validate.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/Validate.java
new file mode 100644
index 0000000..807d47b
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/util/Validate.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.util;
+
+/**
+ * <p>Internal utility class for in-code assertion checks.</p>
+ */
+public class Validate {
+
+    public static void notNull(Object obj) {
+        if (obj == null) throw new AssertionError("obj must not be null");
+    }
+
+    public static void isTrue(boolean expression) {
+        if (!expression) throw new AssertionError("expression must be true");
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/main/resources/META-INF/services/org.apache.groovy.contracts.common.spi.Lifecycle b/subprojects/groovy-contracts/src/main/resources/META-INF/services/org.apache.groovy.contracts.common.spi.Lifecycle
new file mode 100644
index 0000000..60a0afa
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/resources/META-INF/services/org.apache.groovy.contracts.common.spi.Lifecycle
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+org.apache.groovy.contracts.common.impl.lc.PreconditionLifecycle
+org.apache.groovy.contracts.common.impl.lc.PostconditionLifecycle
+org.apache.groovy.contracts.common.impl.lc.ClassInvariantLifecycle
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation b/subprojects/groovy-contracts/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
new file mode 100644
index 0000000..a5c12e0
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# global xforms for groovy-contracts
+org.apache.groovy.contracts.ast.GContractsASTTransformation
+org.apache.groovy.contracts.ast.ClosureExpressionEvaluationASTTransformation
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/resources/dsld/org.gcontracts.dsld b/subprojects/groovy-contracts/src/main/resources/dsld/org.gcontracts.dsld
new file mode 100644
index 0000000..97f0821
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/resources/dsld/org.gcontracts.dsld
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package dsld
+
+contribute(bind(closure: enclosingClosure()) & bind(method: enclosingMethod(annotatedBy('groovy.contracts.Ensures')))) {
+	property name : 'old', doc: 'java.util.Map of all property values before the method call.', provider: 'groovy-contracts', type : java.util.Map
+
+	if (method.isEmpty()) return
+
+    methodNode.parameters.each  {
+		property name : it.name, provider: 'groovy-contracts', type : it.type
+	}
+
+	def methodNode = method.first()
+	def returnType = methodNode.returnType
+	if (returnType.name == 'void') return
+
+	property name : 'result', doc: 'The return value of this method.', provider: 'groovy-contracts', type : returnType
+}
+
+contribute(bind(closure: enclosingClosure()) & bind(method: enclosingMethod(annotatedBy('groovy.contracts.Requires')))) {
+	if (method.isEmpty()) return
+
+	def methodNode = method.first()
+	methodNode.parameters.each {
+		property name : it.name, provider: 'groovy-contracts', type : it.type
+	}
+}
+
+contribute(bind(closure: enclosingClosure()) & bind(clazz: enclosingClass(annotatedBy('groovy.contracts.Invariant')))) {
+	if (clazz.isEmpty()) return
+
+	def classNode = clazz.first()
+	classNode.properties.each  {
+		property name : it.name, provider: 'groovy-contracts', type : it.type
+	}
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/main/resources/org.gcontracts.gdsl b/subprojects/groovy-contracts/src/main/resources/org.gcontracts.gdsl
new file mode 100644
index 0000000..231c089
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/resources/org.gcontracts.gdsl
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiType
+
+PsiMethod findParentMethod(PsiElement start)  {
+    def psiElement = start
+    while (!(psiElement instanceof PsiMethod) && psiElement != null) psiElement = psiElement.parent
+    psiElement
+}
+
+contributor(scope: closureScope(annotationName: 'groovy.contracts.Ensures'))  {
+    variable(name: 'old', type: 'Map')
+
+    PsiMethod method = findParentMethod(place)
+    if (method == null) return
+
+    // if we have a void return type then we're done
+    if (method.returnType == PsiType.VOID) return
+
+    def type = method.returnType.canonicalText
+    if (type) variable(name: 'result', type: type)
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/spec/doc/contracts-userguide.adoc b/subprojects/groovy-contracts/src/spec/doc/contracts-userguide.adoc
new file mode 100644
index 0000000..a11244d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/spec/doc/contracts-userguide.adoc
@@ -0,0 +1,70 @@
+//////////////////////////////////////////
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+
+//////////////////////////////////////////
+
+= Groovy Contracts – design by contract support for Groovy
+
+This module provides contract annotations that support the specification of class-invariants,
+pre- and post-conditions on Groovy classes and interfaces.
+Special support is provided so that post-conditions may refer to the old value of variables
+or to the result value associated with calling a method.
+
+== Applying @Invariant, @Requires and @Ensures
+
+With GContracts in your class-path, contracts can be applied on a Groovy class or interface by using one of the assertions found in package org.gcontracts.annotations.
+
+[source,groovy]
+----
+include::{rootProjectDir}/subprojects/groovy-contracts/src/spec/test/ContractsTest.groovy[tags=basic_example,indent=0]
+----
+
+== More Features
+
+GContracts supports the following feature set:
+
+* definition of class invariants, pre- and post-conditions via @Invariant, @Requires and @Ensures
+* inheritance of class invariants, pre- and post-conditions of concrete predecessor classes
+* inheritance of class invariants, pre- and post-conditions in implemented interfaces
+* usage of old and result variable in post-condition assertions
+* assertion injection in Plain Old Groovy Objects (POGOs)
+* human-readable assertion messages, based on Groovy power asserts
+* enabling contracts at package- or class-level with @AssertionsEnabled
+* enable or disable contract checking with Java's -ea and -da VM parameters
+* annotation contracts: a way to reuse reappearing contract elements in a project domain model
+* detection of circular assertion method calls
+
+== The Stack Example
+
+Currently, Groovy contracts supports 3 annotations: @Invariant, @Requires and @Ensures – all of them work
+as annotations with closures, where closures allow you to specify arbitrary code pieces as annotation parameters:
+
+[source,groovy]
+----
+include::{rootProjectDir}/subprojects/groovy-contracts/src/spec/test/ContractsTest.groovy[tags=stack_prelim,indent=0]
+include::{rootProjectDir}/subprojects/groovy-contracts/src/spec/test/ContractsTest.groovy[tags=stack_example,indent=0]
+----
+
+The example above specifies a class-invariant and methods with pre- and post-conditions.
+Note, that preconditions may reference method arguments and post-conditions have access
+to the method’s result with the result variable and old instance variables values with old.
+
+Indeed, Groovy AST transformations change these assertion annotations into Java assertion
+statements (can be turned on and off with a JVM param) and inject them at appropriate places,
+e.g. class-invariants are used to check an object's state before and after each method call.
diff --git a/subprojects/groovy-contracts/src/spec/test/ContractsTest.groovy b/subprojects/groovy-contracts/src/spec/test/ContractsTest.groovy
new file mode 100644
index 0000000..f32c456
--- /dev/null
+++ b/subprojects/groovy-contracts/src/spec/test/ContractsTest.groovy
@@ -0,0 +1,119 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import groovy.test.GroovyTestCase
+
+class ContractsTest extends GroovyTestCase {
+
+    void testBasicExample() {
+        assertScript '''
+        // tag::basic_example[]
+        package acme
+
+        import groovy.contracts.*
+
+        @Invariant({ speed() >= 0 })
+        class Rocket {
+            int speed = 0
+            boolean started = true
+
+            @Requires({ isStarted() })
+            @Ensures({ old.speed < speed })
+            def accelerate(inc) { speed += inc }
+
+            def isStarted() { started }
+
+            def speed() { speed }
+        }
+
+        def r = new Rocket()
+        r.accelerate(5)
+        // end::basic_example[]
+        '''
+    }
+
+    void testStackExample() {
+        assertScript '''
+        /*
+        // tag::stack_prelim[]
+        @Grab(group='org.apache.groovy', module='groovy-contracts', version='4.0.0')
+        // end::stack_prelim[]
+        */
+        // tag::stack_example[]
+        import groovy.contracts.*
+
+        @Invariant({ elements != null })
+        class Stack<T> {
+
+            List<T> elements
+
+            @Ensures({ is_empty() })
+            def Stack()  {
+                elements = []
+            }
+
+            @Requires({ preElements?.size() > 0 })
+            @Ensures({ !is_empty() })
+            def Stack(List<T> preElements)  {
+                elements = preElements
+            }
+
+            boolean is_empty()  {
+                elements.isEmpty()
+            }
+
+            @Requires({ !is_empty() })
+            T last_item()  {
+                elements.get(count() - 1)
+            }
+
+            def count() {
+                elements.size()
+            }
+
+            @Ensures({ result == true ? count() > 0 : count() >= 0  })
+            boolean has(T item)  {
+                elements.contains(item)
+            }
+
+            @Ensures({ last_item() == item })
+            def push(T item)  {
+               elements.add(item)
+            }
+
+            @Requires({ !is_empty() })
+            @Ensures({ last_item() == item })
+            def replace(T item)  {
+                remove()
+                elements.add(item)
+            }
+
+            @Requires({ !is_empty() })
+            @Ensures({ result != null })
+            T remove()  {
+                elements.remove(count() - 1)
+            }
+
+            String toString() { elements.toString() }
+        }
+
+        def stack = new Stack<Integer>()
+        // end::stack_example[]
+        '''
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/CompileStaticTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/CompileStaticTests.groovy
new file mode 100644
index 0000000..6535484
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/CompileStaticTests.groovy
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+import org.apache.groovy.contracts.PostconditionViolation
+import org.apache.groovy.contracts.PreconditionViolation
+
+class CompileStaticTests extends GroovyShellTestCase {
+
+    void testPrecondition() {
+        evaluate """
+            import groovy.contracts.*
+
+            @groovy.transform.CompileStatic
+            class A {
+                @Requires({ param.size() > 0 })
+                void someOperation(String param) { }
+            }
+            new A()
+        """
+    }
+
+    void testPreconditionViolation() {
+        shouldFail PreconditionViolation, {
+            evaluate """
+                import groovy.contracts.*
+
+                @groovy.transform.CompileStatic
+                class A {
+                    @Requires({ param.length() > 0 })
+                    void someOperation(String param) { }
+                }
+                new A().someOperation('')
+            """
+        }
+    }
+
+    void testPostcondition() {
+        evaluate """
+                import groovy.contracts.*
+
+                @groovy.transform.CompileStatic
+                class A {
+                    @Ensures({ result == 3  })
+                    Integer add() { return 1 + 1 }
+                }
+                new A()
+            """
+    }
+
+    void testPostconditionViolation() {
+        shouldFail PostconditionViolation, {
+            evaluate """
+                import groovy.contracts.*
+
+                @groovy.transform.CompileStatic
+                class A {
+                    @Ensures({ result == 3  })
+                    Integer add() { return 1 + 1 }
+                }
+                new A().add()
+            """
+        }
+    }
+
+    void testClassInvariant() {
+        evaluate """
+            import groovy.contracts.*
+
+            @groovy.transform.CompileStatic
+            @Invariant({ speed >= 0 })
+            class A {
+                Integer speed = 1
+            }
+            new A()
+        """
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/EqualsAndHashCodeTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/EqualsAndHashCodeTests.groovy
new file mode 100644
index 0000000..caea455
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/EqualsAndHashCodeTests.groovy
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class EqualsAndHashCodeTests extends GroovyShellTestCase {
+
+    void testEqualsAndHashCode() {
+
+        def result = evaluate """
+            import groovy.contracts.*
+
+            @Invariant({ name && lastName })
+            @groovy.transform.EqualsAndHashCode class Person {
+                String name
+                String lastName
+
+                def Person(name, lastName)  {
+                    this.name = name
+                    this.lastName = lastName
+                }
+            }
+            new Person('Max', 'Mustermann').equals(new Person('Max', 'Mustermann'))
+        """
+
+        assertTrue result as boolean
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/ImmutableTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/ImmutableTests.groovy
new file mode 100644
index 0000000..a7668db
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/ImmutableTests.groovy
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class ImmutableTests extends GroovyShellTestCase {
+
+    void testSimpleImmutableClass() {
+
+        evaluate """
+           import groovy.contracts.*
+
+           @groovy.transform.Immutable
+           @Invariant({ name })
+           class Person {
+               String name
+           }
+
+           def p = new Person(name: 'John Doe')
+        """
+
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/LockTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/LockTests.groovy
new file mode 100644
index 0000000..bc00478
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/LockTests.groovy
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class LockTests extends GroovyShellTestCase {
+
+    void test_withReadAndWriteLock() {
+
+        def result = evaluate """
+        import groovy.transform.*;
+        import groovy.contracts.*
+
+        public class ResourceProvider {
+
+            private final Map<String, String> data = new HashMap<String, String>();
+
+            @Requires({ key })
+            @WithReadLock
+            public String getResource(String key) throws Exception {
+                return data.get(key)
+            }
+
+            @WithWriteLock
+            public void refresh() throws Exception {
+                data['test'] = 'test'
+            }
+        }
+
+        def resourceProvider = new ResourceProvider()
+        resourceProvider.refresh()
+
+        resourceProvider.getResource('test')
+        """
+
+        assert result == 'test'
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/SynchronizedTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/SynchronizedTests.groovy
new file mode 100644
index 0000000..6982cef
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/SynchronizedTests.groovy
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class SynchronizedTests extends GroovyShellTestCase {
+
+    void test_Synchronized_on_methods() {
+
+        def source = """
+            import groovy.contracts.*
+
+            class A {
+
+                @groovy.transform.Synchronized
+                @Requires({ a >= 0 })
+                def m(int a) { return a}
+
+            }
+
+            def a = new A()
+            a.m(12)
+        """
+
+        evaluate source
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/ToStringTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/ToStringTests.groovy
new file mode 100644
index 0000000..bd606cc
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/ToStringTests.groovy
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class ToStringTests extends GroovyShellTestCase {
+
+    void testToString() {
+
+        def result = evaluate """
+        import groovy.contracts.*
+
+        @Invariant({ name })
+        @groovy.transform.ToString
+        class Person {
+            String name
+
+            def Person(String name)  {
+                this.name = name
+            }
+        }
+
+        new Person('Max Mustermann').toString()
+        """
+
+        assertEquals result, 'Person(Max Mustermann)'
+
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/TupleConstructorTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/TupleConstructorTests.groovy
new file mode 100644
index 0000000..45e6cc2
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/TupleConstructorTests.groovy
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class TupleConstructorTests extends GroovyShellTestCase {
+
+    void testTupleConstructor() {
+        evaluate """
+        import groovy.contracts.*
+
+        @Invariant({ firstName && lastName })
+        @groovy.transform.TupleConstructor class Person {
+            String firstName
+            String lastName
+        }
+
+        new Person('Max', 'Mustermann')
+        """
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/TypeCheckedTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/TypeCheckedTests.groovy
new file mode 100644
index 0000000..bc6d275
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/compability/TypeCheckedTests.groovy
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.compability
+
+import groovy.test.GroovyShellTestCase
+
+class TypeCheckedTests extends GroovyShellTestCase {
+
+    void testPrecondition() {
+        evaluate '''
+            import groovy.contracts.*
+
+            @groovy.transform.TypeChecked
+            class A {
+                @Requires({ some?.size() > 0 })
+                def op(String some) {
+                }
+            }
+
+            def a = new A()
+        '''
+
+        evaluate '''
+            import groovy.contracts.*
+
+            @groovy.transform.TypeChecked
+            class A {
+                @Requires({ some?.size() > 0 })
+                def op(def some) {
+                }
+            }
+
+            def a = new A()
+        '''
+    }
+
+    void testPostcondition() {
+        evaluate '''
+            import groovy.contracts.*
+
+            @groovy.transform.TypeChecked
+            class A {
+
+                @Ensures({ result.size() > 0 })
+                String op(String some) {
+                    some
+                }
+            }
+
+            def a = new A()
+        '''
+
+        evaluate '''
+            import groovy.contracts.*
+
+            @groovy.transform.TypeChecked
+            class A {
+                private int i = 12
+
+                @Ensures({ old.i + 2 == 12 })
+                def op(String some) {
+                    some
+                }
+            }
+
+            def a = new A()
+        '''
+    }
+
+    void testClassInvariant() {
+        evaluate '''
+            import groovy.contracts.*
+
+            @groovy.transform.TypeChecked
+            @Invariant({ i >= 0 })
+            class A {
+                private int i = 12
+            }
+
+            def a = new A()
+        '''
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/domain/ContractTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/domain/ContractTests.groovy
new file mode 100644
index 0000000..534ca2d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/domain/ContractTests.groovy
@@ -0,0 +1,114 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.domain
+
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.Parameter
+import org.codehaus.groovy.ast.builder.AstBuilder
+import org.codehaus.groovy.ast.expr.BooleanExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.syntax.Types
+import org.junit.Before
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertNotNull
+import static org.junit.Assert.assertTrue
+
+class ContractTests {
+
+    ClassNode classNode
+    MethodNode methodNode
+
+    @Before
+    public void setUp() {
+        def source = '''
+        class Tester {
+
+           void some_method()  {}
+        }
+'''
+
+        def astNodes = new AstBuilder().buildFromString(CompilePhase.SEMANTIC_ANALYSIS, false, source)
+        classNode = astNodes[1]
+        assertNotNull(classNode)
+
+        methodNode = classNode.getMethod("some_method", Parameter.EMPTY_ARRAY)
+        assertNotNull(methodNode)
+    }
+
+    @Test
+    void create_simple_contract() {
+        Contract contract = new Contract(classNode)
+
+        Precondition precondition = new Precondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)))
+        contract.preconditions().or(classNode.getMethod("some_method", [] as Parameter[]), precondition)
+
+        assertEquals(1, contract.preconditions().size())
+    }
+
+    @Test
+    void anding_precondition_causes_logical_or() {
+
+        Contract contract = new Contract(classNode)
+
+        Precondition precondition1 = new Precondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)))
+        Precondition precondition2 = new Precondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)))
+
+        contract.preconditions().or(methodNode, precondition1)
+        contract.preconditions().or(methodNode, precondition2)
+
+        assertEquals(1, contract.preconditions().size())
+        assertTrue(contract.preconditions().get(methodNode).booleanExpression().expression.operation.type == Types.LOGICAL_OR)
+    }
+
+    @Test
+    void anding_postcondition_causes_logical_and() {
+
+        Contract contract = new Contract(classNode)
+
+        Postcondition postcondition = new Postcondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)), false)
+        Postcondition postcondition1 = new Postcondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)), false)
+
+        contract.postconditions().and(methodNode, postcondition)
+        contract.postconditions().and(methodNode, postcondition1)
+
+        assertEquals(1, contract.postconditions().size())
+        assertTrue(contract.postconditions().get(methodNode).booleanExpression().expression.operation.type == Types.LOGICAL_AND)
+    }
+
+    @Test
+    void joining_preconditions() {
+
+        Contract contract = new Contract(classNode)
+
+        Precondition precondition1 = new Precondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)))
+        Precondition precondition2 = new Precondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)))
+
+        contract.preconditions().join(methodNode, precondition1)
+        contract.preconditions().join(methodNode, precondition2)
+
+        assertEquals(1, contract.preconditions().size())
+        assertTrue(contract.preconditions().get(methodNode).booleanExpression().expression.operation.type == Types.LOGICAL_AND)
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/generation/ContractExecutionTrackerTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/generation/ContractExecutionTrackerTests.groovy
new file mode 100644
index 0000000..e789e26
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/generation/ContractExecutionTrackerTests.groovy
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.generation
+
+import org.junit.Test
+
+class ContractExecutionTrackerTests {
+
+    @Test
+    void track_double_execution() {
+
+        ContractExecutionTracker.clear('Dummy', 'method 1', 'pre', false)
+
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', false)
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', false) == false
+
+        ContractExecutionTracker.clear('Dummy', 'method 1', 'pre', false)
+    }
+
+    @Test
+    void clear_only_for_first_stack_element() {
+
+        ContractExecutionTracker.clear('Dummy', 'method 1', 'pre', false)
+        ContractExecutionTracker.clear('Dummy', 'method 2', 'pre', false)
+
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', false)
+        assert ContractExecutionTracker.track('Dummy', 'method 2', 'pre', false)
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', false) == false
+
+        ContractExecutionTracker.clear('Dummy', 'method 2', 'pre', false)
+
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', false) == false
+        ContractExecutionTracker.clear('Dummy', 'method 1', 'pre', false)
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', false)
+    }
+
+    @Test
+    void track_static_method() {
+
+        ContractExecutionTracker.clear('Dummy', 'method 1', 'pre', false)
+
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', true)
+        assert ContractExecutionTracker.track('Dummy', 'method 1', 'pre', true) == false
+
+        ContractExecutionTracker.clear('Dummy', 'method 1', 'pre', true)
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/spock/SpockIntegrationTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/spock/SpockIntegrationTests.groovy
new file mode 100644
index 0000000..372b21e
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/spock/SpockIntegrationTests.groovy
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.spock
+
+import groovy.contracts.Requires
+import spock.lang.Specification
+import org.apache.groovy.contracts.PreconditionViolation
+
+class ContractsSpec extends Specification {
+    def "contracted method with precondition violation"(String dir, String file, String path) {
+        when:
+        contractedMethod(dir, file, path)
+        then:
+        thrown(PreconditionViolation)
+        where:
+        dir | file | path
+        42  | ''   | null
+    }
+
+    @Requires({ dir && file && path })
+    private contractedMethod(String dir, String file, String path) { }
+
+    @spock.lang.Requires({ count < max })
+    def "spock Requires annotation still works with groovy-contracts"(Integer count, Integer max) {
+        expect:
+        count < max
+        where:
+        count | max
+        10    | 20
+        20    | 10 // should be aborted/ignored and not throw a groovy-contracts related exception
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/basic/BaseTestClass.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/basic/BaseTestClass.groovy
new file mode 100644
index 0000000..49d3225
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/basic/BaseTestClass.groovy
@@ -0,0 +1,174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.basic
+
+import groovy.text.GStringTemplateEngine
+import groovy.text.TemplateEngine
+import org.junit.Before
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter
+
+import static org.junit.Assert.assertTrue
+import static org.junit.Assert.fail
+
+class BaseTestClass {
+
+    private static final int MAX_NESTED_EXCEPTIONS = 10;
+
+    private TemplateEngine templateEngine
+    private GroovyClassLoader loader;
+
+    @Before
+    void setUp() {
+        templateEngine = new GStringTemplateEngine()
+        loader = new GroovyClassLoader(getClass().getClassLoader())
+    }
+
+    String createSourceCodeForTemplate(final String template, final Map binding) {
+        templateEngine.createTemplate(template).make(binding).toString()
+    }
+
+    def create_instance_of(final String sourceCode) {
+        return create_instance_of(sourceCode, new Object[0])
+    }
+
+    def create_instance_of(final String sourceCode, def constructor_args) {
+
+        def clazz = add_class_to_classpath(sourceCode)
+
+        return clazz.newInstance(constructor_args as Object[])
+    }
+
+    def add_class_to_classpath(final String sourceCode) {
+        loader.parseClass(sourceCode)
+    }
+
+    /**
+     * Asserts that the given code closure fails when it is evaluated
+     *
+     * @param code
+     * @return the message of the thrown Throwable
+     */
+    protected String shouldFail(Closure code) {
+        boolean failed = false;
+        String result = null;
+        try {
+            code.call();
+        }
+        catch (GroovyRuntimeException gre) {
+            failed = true;
+            result = ScriptBytecodeAdapter.unwrap(gre).getMessage();
+        }
+        catch (Throwable e) {
+            failed = true;
+            result = e.getMessage();
+        }
+        assertTrue("Closure " + code + " should have failed", failed);
+        return result;
+    }
+
+    /**
+     * Asserts that the given code closure fails when it is evaluated
+     * and that a particular exception is thrown.
+     *
+     * @param clazz the class of the expected exception
+     * @param code the closure that should fail
+     * @return the message of the expected Throwable
+     */
+    protected String shouldFail(Class clazz, Closure code) {
+        Throwable th = null;
+        try {
+            code.call();
+        } catch (GroovyRuntimeException gre) {
+            th = ScriptBytecodeAdapter.unwrap(gre);
+        } catch (Throwable e) {
+            th = e;
+        }
+
+        if (th == null) {
+            fail("Closure " + code + " should have failed with an exception of type " + clazz.getName());
+        } else if (!clazz.isInstance(th)) {
+            fail("Closure " + code + " should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th);
+        }
+        return th.getMessage();
+    }
+
+    /**
+     * Asserts that the given code closure fails when it is evaluated
+     * and that a particular exception can be attributed to the cause.
+     * The expected exception class is compared recursively with any nested
+     * exceptions using getCause() until either a match is found or no more
+     * nested exceptions exist.
+     * <p/>
+     * If a match is found the error message associated with the matching
+     * exception is returned. If no match was found the method will fail.
+     *
+     * @param clazz the class of the expected exception
+     * @param code the closure that should fail
+     * @return the message of the expected Throwable
+     */
+    protected String shouldFailWithCause(Class clazz, Closure code) {
+        Throwable th = null;
+        Throwable orig = null;
+        int level = 0;
+        try {
+            code.call();
+        } catch (GroovyRuntimeException gre) {
+            orig = ScriptBytecodeAdapter.unwrap(gre);
+            th = orig.getCause();
+        } catch (Throwable e) {
+            orig = e;
+            th = orig.getCause();
+        }
+
+        while (th != null && !clazz.isInstance(th) && th != th.getCause() && level < MAX_NESTED_EXCEPTIONS) {
+            th = th.getCause();
+            level++;
+        }
+
+        if (orig == null) {
+            fail("Closure " + code + " should have failed with an exception caused by type " + clazz.getName());
+        } else if (th == null || !clazz.isInstance(th)) {
+            fail("Closure " + code + " should have failed with an exception caused by type " + clazz.getName() + ", instead found these Exceptions:\n" + buildExceptionList(orig));
+        }
+        return th.getMessage();
+    }
+
+    private String buildExceptionList(Throwable th) {
+        StringBuilder sb = new StringBuilder();
+        int level = 0;
+        while (th != null) {
+            if (level > 1) {
+                for (int i = 0; i < level - 1; i++) sb.append("   ");
+            }
+            if (level > 0) sb.append("-> ");
+            if (level > MAX_NESTED_EXCEPTIONS) {
+                sb.append("...");
+                break;
+            }
+            sb.append(th.getClass().getName()).append(": ").append(th.getMessage()).append("\n");
+            if (th == th.getCause()) {
+                break;
+            }
+            th = th.getCause();
+            level++;
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/DocumentationExampleTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/DocumentationExampleTests.groovy
new file mode 100644
index 0000000..2bc1109
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/DocumentationExampleTests.groovy
@@ -0,0 +1,151 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.doc
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+
+class DocumentationExampleTests extends BaseTestClass {
+
+    def example_person = '''
+    @Contracted
+    package tests
+    
+    import groovy.contracts.*
+    
+    @Invariant({ firstName != null && lastName != null })
+    class Person {
+        String firstName
+        String lastName
+    
+        @Requires ({ delimiter in ['.', ',', ' '] })
+        @Ensures({ result -> result == (firstName + delimiter + lastName) })
+        def String getName(String delimiter) {
+            return delimiter
+        }
+    }
+    '''
+
+    def example_eiffel_stack = '''
+    @Contracted
+    package tests
+
+    import groovy.contracts.*
+
+    @Invariant({ elements != null })
+    class EiffelStack {
+
+        private List elements
+
+        @Ensures({ is_empty() })
+        public EiffelStack()  {
+            elements = []
+        }
+
+        @Requires({ preElements?.size() > 0 })
+        @Ensures({ !is_empty() })
+        public EiffelStack(List preElements)  {
+            elements = preElements
+        }
+
+        def boolean is_empty()  {
+            elements.isEmpty()
+        }
+
+        @Requires({ !is_empty() })
+        def last_item()  {
+            elements.last()
+        }
+
+        def count() {
+            elements.size()
+        }
+
+        @Ensures({ result == true ? count() > 0 : count() >= 0  })
+        def boolean has(def item)  {
+            elements.contains(item)
+        }
+
+        @Ensures({ last_item() == item })
+        def put(def item)  {
+           elements.push(item)
+        }
+
+        @Requires({ !is_empty() })
+        @Ensures({ last_item() == item })
+        def replace(def item)  {
+            remove()
+            elements.push(item)
+        }
+
+        @Requires({ !is_empty() })
+        @Ensures({ result != null })
+        def remove()  {
+            elements.pop()
+        }
+    }    
+    '''
+
+
+    @Test
+    void test_stack_creation() {
+        create_instance_of(example_eiffel_stack)
+    }
+
+    @Test
+    void test_stack_creation_with_list() {
+        create_instance_of(example_eiffel_stack, [[1, 2, 3, 4]])
+    }
+
+    @Test
+    void test_stack_put() {
+        def stack = create_instance_of(example_eiffel_stack)
+        stack.put("hello world")
+
+        assertTrue stack.last_item() == 'hello world'
+    }
+
+    @Test
+    void test_stack_replace() {
+        def stack = create_instance_of(example_eiffel_stack)
+        stack.put("hello world")
+        stack.replace("hallo welt")
+
+        assertTrue stack.last_item() == 'hallo welt'
+        assertTrue stack.count() == 1
+    }
+
+    @Test
+    void test_stack_remove() {
+        def stack = create_instance_of(example_eiffel_stack)
+        stack.put("hello world")
+        stack.remove()
+
+        assertTrue stack.count() == 0
+    }
+
+    @Test
+    void test_person_creation() {
+        shouldFail AssertionError, {
+            create_instance_of(example_person)
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/RootClassExampleTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/RootClassExampleTests.groovy
new file mode 100644
index 0000000..4ec00fd
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/RootClassExampleTests.groovy
@@ -0,0 +1,264 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.doc
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
+
+class RootClassExampleTests extends BaseTestClass {
+
+    def source = ''' 
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ field1 > 0 })                            
+class RootClass {                                     
+    
+  // made field protected due to groovy compilation bug
+  protected Integer field1              
+  private Integer field2
+  private Integer field3                                                                 
+                                  
+  private Date dateField1                   
+                                                         
+  Integer property1                                                            
+                                                                   
+  RootClass(final Integer attribute)  {                
+    field1 = attribute                                                           
+  }                                                  
+
+  @Requires({ paramAttribute1 > 1 && paramAttribute2 > 1 })
+  void some_operation(final Integer paramAttribute1, final Integer paramAttribute2)  {
+    this.field1 = paramAttribute1
+    this.field2 = paramAttribute2
+  }                                                                                                            
+                                                   
+  @Ensures({ field1 == paramAttribute1 })                     
+  void some_operation2(final Integer paramAttribute1)  {
+    field1 = paramAttribute1
+  }                                   
+
+  @Ensures({ old -> old.field1 != paramAttribute1 })            
+  void some_operation3(final Integer paramAttribute1)  {
+    field1 = paramAttribute1
+  }
+
+  @Ensures({ result -> result == paramAttribute1 + paramAttribute2 })
+  def int some_operation4(final Integer paramAttribute1, final Integer paramAttribute2)  {
+    return paramAttribute1 + paramAttribute2
+  }               
+                                                     
+  @Ensures({ result -> result == field3 })
+  def int some_operation5(final Integer paramAttribute1, final Integer paramAttribute2)  {
+    field3 = paramAttribute1 + paramAttribute2
+    return field3
+  }
+                                                                                                            
+  @Ensures({ old, result -> old.field1 != field1 && old.field2 != field2 && field3 == result })
+  def some_operation6(def param1, def param2)  {
+    field1 = param1
+    field2 = param2                                                       
+    field3 = param1 + param2                                                        
+    return field3
+  }                                                                        
+                                                                                                                 
+  @Ensures({ result, old -> old.field1 != field1 && old.field2 != field2 && field3 == result })
+  def some_operation7(def param1, def param2)  {
+    field1 = param1
+    field2 = param2
+    field3 = param1 + param2                                                                                                       
+    return field3
+  }                             
+
+  @Ensures({ result -> result == param1 })                           
+  def some_operation8(def param1)  {                                                                                            
+    param1
+  }
+
+  @Ensures({ result -> result == param1 + param2})
+  def some_operation9(def param1, def param2)  {
+    param1 + param2
+  }                                                  
+
+  @Ensures({ old -> old.dateField1 != param1 && dateField1 == param1 })
+  void some_operation10(def param1)  {
+    dateField1 = param1
+  }
+
+  @Requires({ param1 > 10 })
+  void some_operation11(def param1)  {
+    field1 = param1
+  }
+                           
+  @Ensures({ old -> old.dateField1 != param1 && dateField1 == param1 })
+  void some_operation12(def param1)  {
+    dateField1 = param1
+  }
+}
+'''
+
+    @Test
+    void test_class_invariant() {
+        create_instance_of(source, [1])
+    }
+
+    @Test
+    void test_class_invariant_fail() {
+
+        shouldFail AssertionError, {
+            create_instance_of(source, [0])
+        }
+    }
+
+    @Test
+    void test_class_invariant_with_default_constructor() {
+        shouldFail AssertionError, { create_instance_of(source) }
+    }
+
+    @Test
+    void test_precondition_with_multiple_arguments() {
+
+        def root = create_instance_of(source, [1])
+
+        root.some_operation 2, 2
+    }
+
+    @Test
+    void test_precond_with_first_argument_fail() {
+
+        def root = create_instance_of(source, [1])
+
+        shouldFail AssertionError, {
+            root.some_operation(1, 2)
+        }
+    }
+
+    @Test
+    void test_precond_with_second_argument_fail() {
+
+        def root = create_instance_of(source, [1])
+
+        shouldFail AssertionError, {
+            root.some_operation 2, 1
+        }
+    }
+
+    @Test
+    void test_postcond_with_single_argument() {
+        def root = create_instance_of(source, [1])
+
+        root.some_operation2 2
+    }
+
+    @Test
+    void test_postcond_with_single_argument_and_old_var() {
+        def root = create_instance_of(source, [1])
+
+        root.some_operation3 2
+    }
+
+    @Test
+    void test_postcond_with_single_argument_and_old_var_fail() {
+        def root = create_instance_of(source, [1])
+
+        shouldFail AssertionError, {
+            root.some_operation3 1
+        }
+    }
+
+    @Test
+    void test_postcond_with_result_variable() {
+        def root = create_instance_of(source, [1])
+
+        def result = root.some_operation4(1, 1)
+
+        assertEquals 2, result
+    }
+
+    @Test
+    void test_postcond_with_result_variable_and_field() {
+        def root = create_instance_of(source, [1])
+
+        def result = root.some_operation5(1, 1)
+
+        assertEquals 2, result
+    }
+
+    @Test
+    void test_postcond_with_result_and_old_variables() {
+        def root = create_instance_of(source, [1])
+
+        def result = root.some_operation6(2, 2)
+
+        assertEquals 4, result
+    }
+
+    void test_postcond_with_result_and_old_variables_switched() {
+        def root = create_instance_of(source, [1])
+
+        def result = root.some_operation7(2, 2)
+
+        assertEquals 4, result
+    }
+
+    @Test
+    void test_postcond_with_implicit_return_statement() {
+        def root = create_instance_of(source, [1])
+
+        def result = root.some_operation8(2)
+
+        assertEquals 2, result
+    }
+
+    @Test
+    void test_postcond_with_complex_return_statement() {
+        def root = create_instance_of(source, [1])
+
+        def result = root.some_operation9(2, 2)
+
+        assertEquals 4, result
+    }
+
+
+    @Test
+    void test_multiple_preconditions() {
+        def root = create_instance_of(source, [1])
+
+        root.some_operation11 12
+    }
+
+    @Test
+    void test_multiple_precondition_fail() {
+        def root = create_instance_of(source, [1])
+
+        shouldFail AssertionError, { root.some_operation11 10 }
+    }
+
+    @Test
+    void test_multiple_postconditions() {
+        def root = create_instance_of(source, [1])
+
+        root.some_operation10(new Date())
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/StackExampleTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/StackExampleTests.groovy
new file mode 100644
index 0000000..339754d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/doc/StackExampleTests.groovy
@@ -0,0 +1,181 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.doc
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class StackExampleTests extends BaseTestClass {
+
+    def source_stack = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ list != null && anotherName != null })
+class Stack  {
+
+  protected def list
+  def anotherName = ""
+  def protected name = ""
+
+  public Stack()  {
+    this.list = []
+  }
+
+  public Stack(def list)  {
+    this.list = list
+  }
+
+  @Requires({ item != null })
+  @Ensures({ list[-1] == item })
+  void push(def item)  {
+    list.add item
+  }
+
+  @Requires({ item1 != null && item2 != null })
+  void multi_push(def item1, def item2)  {
+    push item1
+    push item2
+  }
+
+//  @Requires({ list.size() > 0 })
+//  @Ensures({ result != null })
+//  def Object pop()  {
+//    list[-1]
+//  }
+
+  @Ensures({ result -> result == list.size() })
+  def int size()  {
+    return list.size()
+  }
+
+  @Ensures({ result -> comp1 != null && comp2 != null && result > 0 })
+  def int size(def comp1, comp2)  {
+      return comp1 + comp2
+  }
+
+  @Ensures({ result -> result == 'tostring'})
+  @Override
+  def String toString()  {
+    return 'tostring'
+  }
+
+  void modifyClassInvariant()  {
+    anotherName = null
+  }
+}
+'''
+
+    def source_stack_descendant = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ count >= 0 })
+class StackDescendant extends Stack implements Serializable {
+
+  def private int count = 0
+
+  def StackDescendant() {
+     super()
+  }
+
+  def StackDescendant(list) {
+    super(list)
+  }
+
+  @Override
+  void push(def item)  {
+    count++
+    super.push item
+  }
+
+  void push_fail(def item)  {
+    count++
+    list = null
+  }
+
+  @Ensures({ old -> old.count < count })
+  void test_count()  {
+    count++
+  }
+
+  @Ensures({ result, old -> true })
+  def int test_count_with_result_variable()  {
+    count++
+    return count
+  }
+}
+'''
+
+    @Test
+    void creation() {
+        create_instance_of(source_stack)
+        create_instance_of(source_stack_descendant)
+    }
+
+    @Test
+    void inherited_invariant() {
+        create_instance_of(source_stack)
+        def stack = create_instance_of(source_stack_descendant)
+
+        stack.push 'item 1'
+    }
+
+
+    @Test
+    void inherited_invariant_failure() {
+        create_instance_of(source_stack)
+
+        shouldFail AssertionError, {
+            create_instance_of(source_stack_descendant, [null])
+        }
+    }
+
+    @Test
+    void inherited_invariant_fail_on_method_call() {
+        create_instance_of(source_stack)
+        def stack = create_instance_of(source_stack_descendant)
+
+        shouldFail AssertionError, {
+            stack.push_fail 'item 1'
+        }
+    }
+
+    @Test
+    void old_variable() {
+        create_instance_of(source_stack)
+        def stack = create_instance_of(source_stack_descendant)
+
+        stack.test_count()
+    }
+
+
+    @Test
+    void old_and_result_variable() {
+        create_instance_of(source_stack)
+        def stack = create_instance_of(source_stack_descendant)
+
+        stack.test_count_with_result_variable()
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/AbstractClassInheritanceTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/AbstractClassInheritanceTests.groovy
new file mode 100644
index 0000000..80962d0
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/AbstractClassInheritanceTests.groovy
@@ -0,0 +1,118 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.interfaces
+
+import org.apache.groovy.contracts.PreconditionViolation
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class AbstractClassInheritanceTests extends BaseTestClass {
+
+    def source_stackable = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+abstract class Stackable {
+
+  @Requires({ item != null })
+  abstract void push(def item)
+
+  @Requires({ item1 != null && item2 != null })
+  abstract void multi_push(def item1, def item2)
+}
+'''
+
+    def source_stack = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ list != null && anotherName != null })
+class Stack extends Stackable  {
+
+  protected def list
+  def anotherName = ""
+  def protected name = ""
+
+  public Stack()  {
+    this.list = []
+  }
+
+  public Stack(def list)  {
+    this.list = list
+  }
+
+  @Ensures({ list[-1] == item })
+  void push(def item)  {
+    list.add item
+  }
+
+  void multi_push(def item1, def item2)  {
+    push item1
+    push item2
+  }
+
+//  @Requires({ list.size() > 0 })
+//  @Ensures({ result != null })
+//  def Object pop()  {
+//    list[-1]
+//  }
+
+  @Ensures({ result -> result == list.size() })
+  def int size()  {
+    return list.size()
+  }
+
+  @Ensures({ result -> comp1 != null && comp2 != null && result > 0 })
+  def int size(def comp1, comp2)  {
+      return comp1 + comp2
+  }
+
+  @Ensures({ result -> result == 'tostring'})
+  @Override
+  def String toString()  {
+    return 'tostring'
+  }
+
+  void modifyClassInvariant()  {
+    anotherName = null
+  }
+}
+'''
+
+    @Test
+    void creation() {
+        add_class_to_classpath(source_stackable)
+        create_instance_of(source_stack)
+    }
+
+    @Test
+    void push_precondition() {
+        add_class_to_classpath(source_stackable)
+
+        def stack = create_instance_of(source_stack)
+
+        shouldFail PreconditionViolation.class, {
+            stack.push null
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/AbstractClassTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/AbstractClassTests.groovy
new file mode 100644
index 0000000..1756151
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/AbstractClassTests.groovy
@@ -0,0 +1,117 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.interfaces
+
+import org.apache.groovy.contracts.PreconditionViolation
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class AbstractClassTests extends BaseTestClass {
+
+    def source_stackable = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+abstract class Stackable {
+
+  @Requires({ item != null })
+  abstract void push(def item)
+
+  @Requires({ item1 != null && item2 != null })
+  abstract void multi_push(def item1, def item2)
+}
+'''
+
+    def source_stack = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ list != null && anotherName != null })
+class Stack extends Stackable {
+
+  protected def list
+  def anotherName = ""
+  def protected name = ""
+
+  public Stack()  {
+    this.list = []
+  }
+
+  public Stack(def list)  {
+    this.list = list
+  }
+
+  @Ensures({ list[-1] == item })
+  void push(def item)  {
+    list.add item
+  }
+
+  void multi_push(def item1, def item2)  {
+    push item1
+    push item2
+  }
+
+//  @Requires({ list.size() > 0 })
+//  @Ensures({ result != null })
+//  def Object pop()  {
+//    list[-1]
+//  }
+
+  @Ensures({ result -> result == list.size() })
+  def int size()  {
+    return list.size()
+  }
+
+  @Ensures({ result -> comp1 != null && comp2 != null && result > 0 })
+  def int size(def comp1, comp2)  {
+      return comp1 + comp2
+  }
+
+  @Ensures({ result -> result == 'tostring'})
+  @Override
+  def String toString()  {
+    return 'tostring'
+  }
+
+  void modifyClassInvariant()  {
+    anotherName = null
+  }
+}
+'''
+
+    @Test
+    void creation() {
+        add_class_to_classpath(source_stackable)
+        create_instance_of(source_stack)
+    }
+
+    @Test
+    void push_precondition() {
+        add_class_to_classpath(source_stackable)
+        def stack = create_instance_of(source_stack)
+
+        shouldFail PreconditionViolation.class, {
+            stack.push null
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/InterfaceAbstractClassMixturesTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/InterfaceAbstractClassMixturesTests.groovy
new file mode 100644
index 0000000..b1f81f8
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/InterfaceAbstractClassMixturesTests.groovy
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.interfaces
+
+import org.apache.groovy.contracts.PostconditionViolation
+import org.apache.groovy.contracts.PreconditionViolation
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class InterfaceAbstractClassMixturesTests extends BaseTestClass {
+
+    @Test
+    void class_with_abstract_class_and_interface() {
+
+        def s1 = '''
+        @Contracted
+        package tests
+
+        import groovy.contracts.*
+
+        interface Stackable {
+            @Ensures({ result != null })
+            def pop()
+        }
+
+        abstract class StackableAbstract implements Stackable {
+            abstract def pop()
+        }
+        '''
+
+        def s2 = '''
+        @Contracted
+        package tests
+
+        import groovy.contracts.*
+
+        class Stack extends StackableAbstract {
+            def pop() { return null }
+        }
+        '''
+
+        add_class_to_classpath(s1)
+        def stack = create_instance_of(s2)
+
+        shouldFail PostconditionViolation, {
+            stack.pop()
+        }
+    }
+
+    @Test
+    void interface_and_abstract_class_both_contain_abstract_methods() {
+
+        def s1 = '''
+        @Contracted
+        package tests
+
+        import groovy.contracts.*
+
+        interface Stackable {
+            @Ensures({ result != null })
+            def pop()
+        }
+
+        abstract class StackableAbstract implements Stackable {
+            @Requires({ item != null })
+            abstract def push(def item)
+        }
+        '''
+
+        def s2 = '''
+        @Contracted
+        package tests
+
+        import groovy.contracts.*
+
+        class Stack extends StackableAbstract {
+            def pop() { return null }
+            def push(def item) {}
+        }
+        '''
+
+        add_class_to_classpath(s1)
+        def stack = create_instance_of(s2)
+
+        shouldFail PostconditionViolation.class, {
+            stack.pop()
+        }
+
+        shouldFail PreconditionViolation.class, {
+            stack.push(null)
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/SimpleInterfaceInheritanceTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/SimpleInterfaceInheritanceTests.groovy
new file mode 100644
index 0000000..92966ba
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/SimpleInterfaceInheritanceTests.groovy
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.interfaces
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class SimpleInterfaceInheritanceTests extends BaseTestClass {
+
+    def source_stackable = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+abstract class Stackable {
+
+  @Requires({ item != null })
+  abstract void push(def item)
+}
+
+'''
+
+    def source_stack = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ list != null && anotherName != null })
+class Stack extends Stackable {
+
+  protected def list
+  def anotherName = ""
+  def protected name = ""
+
+  public Stack()  {
+    this.list = []
+  }
+
+  public Stack(def list)  {
+    this.list = list
+  }
+
+  @Requires({ item > 2 })
+  @Ensures({ list[-1] == item })
+  void push(def item)  {
+    list.add item
+  }
+}
+'''
+
+    def source_implicit_interface = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+interface A {
+   @Ensures({ old != null && result != null })
+   def some_method()
+}
+
+class B implements A {
+
+   def some_method() { return null }
+
+}
+
+class C extends B {
+   def some_method() { return null }
+}
+'''
+
+    def source_implicit_interface2 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+class C extends B {
+   def some_method() { return null }
+}
+'''
+
+    @Test
+    void creation() {
+        add_class_to_classpath(source_stackable)
+        create_instance_of(source_stack)
+    }
+
+    @Test
+    void push_precondition() {
+        add_class_to_classpath(source_stackable)
+
+        def stack = create_instance_of(source_stack)
+
+        shouldFail AssertionError, {
+            stack.push null
+        }
+
+        stack.push 1
+        stack.push 2
+    }
+
+    @Test
+    void postcondition_in_indirect_parent_interface() {
+        add_class_to_classpath(source_implicit_interface)
+        def c = create_instance_of(source_implicit_interface2)
+
+        shouldFail AssertionError, {
+            c.some_method()
+        }
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/StackExampleTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/StackExampleTests.groovy
new file mode 100644
index 0000000..7a16d21
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/interfaces/StackExampleTests.groovy
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.interfaces
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+
+class StackExampleTests extends BaseTestClass {
+
+    def source_stackable = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+interface Stackable {
+
+  @Requires({ item != null })
+  void push(def item)
+
+  @Ensures({ result != null && old != null })
+  def isEmpty()
+}
+'''
+
+    def source_stack = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+class Stack implements Stackable  {
+
+  protected def list
+
+  public Stack()  {
+    this.list = []
+  }
+
+  public Stack(def list)  {
+    this.list = list
+  }
+
+  @Ensures({ list.last() == item })
+  void push(def item)  {
+    list.add item
+  }
+
+  def isEmpty()  {
+    return list.size() == 0
+  }
+}
+'''
+
+    @Test
+    void creation() {
+        add_class_to_classpath(source_stackable)
+        create_instance_of(source_stack)
+    }
+
+    @Test
+    void push_precondition() {
+        add_class_to_classpath(source_stackable)
+        def stack = create_instance_of(source_stack)
+
+        stack.push 1
+        shouldFail AssertionError, {
+            stack.push null
+        }
+    }
+
+    @Test
+    void old_variable_in_postcondition() {
+        add_class_to_classpath(source_stackable)
+        def stack = create_instance_of(source_stack)
+
+        assertTrue(stack.isEmpty())
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/InheritanceTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/InheritanceTests.groovy
new file mode 100644
index 0000000..d142d74
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/InheritanceTests.groovy
@@ -0,0 +1,416 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.inv
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+import org.apache.groovy.contracts.ClassInvariantViolation
+
+class InheritanceTests extends BaseTestClass {
+
+    def source1 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property != null })
+class A {
+  def property
+
+  def A(def value) { property = value }
+}
+'''
+
+    def source2 = '''
+@Contracted
+package tests
+
+
+import groovy.contracts.*
+
+@Invariant({ property2 != null })
+class B extends A {
+  def property2
+
+  def B(def value, def value2) { super(value); property2 = value2 }
+
+  def set_values(def value, def value2)  {
+    property = value
+    property2 = value2
+  }
+}
+
+'''
+
+    def source3 = '''
+@Contracted
+package tests
+
+
+import groovy.contracts.*
+
+@Invariant({ property2 != null })
+class C extends B {
+  def property3
+
+  def C(def value, def value2, def value3) { super(value, value2); property3 = value3 }
+}
+
+'''
+
+    def source11 = '''
+@Contracted
+package tests
+
+
+import groovy.contracts.*
+
+@Invariant({ property?.size() > 0 })
+class A {
+   private String property
+
+   def A(String value) { property = value }
+}
+
+'''
+
+    def source12 = '''
+@Contracted
+package tests
+
+
+import groovy.contracts.*
+
+class B extends A {
+   def B(String value) { super(value) }
+}
+
+'''
+
+    def source21 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ prop1 != null && prop2 != null })
+class PrivateConstructor {
+
+  def prop1
+  def prop2
+
+  def PrivateConstructor(def arg1, def arg2)  {
+    prop1 = arg1
+    prop2 = arg2
+  }
+
+  private PrivateConstructor()  {}
+}
+'''
+
+    def source31 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ isAvailable() == true })
+abstract class A {
+
+  abstract boolean isAvailable()
+}
+
+'''
+
+    def source32 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+class B extends A {
+  boolean isAvailable() { return true }
+}
+
+'''
+
+    def source41 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ isAvailable() == true })
+class A {
+    def boolean isAvailable() { return true }
+}
+'''
+
+    def source51 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property1 != null })
+class A {
+
+  def property1 = "test"
+
+  def A() {}
+
+  def set_values(def prop) { property1 = prop }
+}
+'''
+
+    def source52 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property2 != null })
+class B extends A {
+
+  def property2 = "test"
+
+  def B() {}
+
+  def set_values(def prop) { property2 = prop }
+}
+'''
+
+    def source61 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property1 != null })
+class A {
+
+  private def property1 = "test"
+
+  def A() {}
+
+  def set_values(def prop) { property1 = prop }
+}
+'''
+
+    def source62 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property2 != null })
+class B extends A {
+
+  def property2 = "test"
+
+  def B() {}
+
+  def set_values(def prop) { property2 = prop }
+}
+'''
+
+    def source71 = '''
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ getBalance() >= 0.0 })
+class Account {
+
+    protected BigDecimal balance = 0.0
+
+    def Account( def amount = 0.0 )
+    {
+        balance = amount
+    }
+
+    @Requires({ amount >= 0.0 })
+    @Ensures({ balance == old.balance + amount })
+    void deposit( def amount )
+    {
+        balance += amount
+    }
+
+    @Requires({ amount >= 0.0 && getBalance() >= amount })
+    @Ensures({ balance == old.balance - amount })
+    def withdraw( def amount )
+    {
+        balance -= amount
+        return amount
+    }
+
+    def getBalance()
+    {
+        balance
+    }
+}
+'''
+
+
+    @Test
+    void two_way_inheritance_path() {
+        create_instance_of(source1, ['test'])
+        create_instance_of(source2, ['test', 'test2'])
+
+        shouldFail AssertionError, {
+            create_instance_of(source2, [null, 'test2'])
+        }
+    }
+
+    @Test
+    void three_way_inheritance_path() {
+        create_instance_of(source1, ['test'])
+        create_instance_of(source2, ['test', 'test2'])
+        create_instance_of(source3, ['test', 'test2', 'test3'])
+
+        shouldFail AssertionError, {
+            create_instance_of(source3, [null, 'test2', 'test3'])
+        }
+
+        shouldFail AssertionError, {
+            create_instance_of(source3, [null, null, 'test3'])
+        }
+
+        shouldFail AssertionError, {
+            create_instance_of(source3, ['test', null, 'test3'])
+        }
+    }
+
+    /*
+   see: http://gcontracts.lighthouseapp.com/projects/71511/tickets/3-accessing-private-variables-from-invariant
+   @Test void with_private_instance_variable_in_super_class()  {
+      create_instance_of(source11, ['test'])
+      create_instance_of(source12, ['test'])
+  
+      shouldFail AssertionError, {
+        create_instance_of(source12, [''])
+      }
+    }*/
+
+    @Test
+    void invariant_check_on_method_call() {
+        create_instance_of(source1, ['test'])
+        def b = create_instance_of(source2, ['test', 'test2'])
+
+        shouldFail AssertionError, {
+            b.set_values(null, null)
+        }
+
+        shouldFail AssertionError, {
+            b.set_values(null, '')
+        }
+
+        shouldFail AssertionError, {
+            b.set_values('', null)
+        }
+    }
+
+    @Test
+    void private_constructor_creation() {
+        create_instance_of(source21)
+    }
+
+    @Test
+    void public_constructor_creation() {
+        shouldFail AssertionError, {
+            create_instance_of(source21, ['test1', null])
+        }
+    }
+
+    @Test
+    void inherited_class_invariant() {
+        add_class_to_classpath(source51)
+        def b = create_instance_of(source52, [])
+
+        shouldFail AssertionError, {
+            b.set_values(null)
+        }
+    }
+
+    @Test
+    void inherited_class_invariant_with_private_instance_variable() {
+        add_class_to_classpath(source61)
+        def b = create_instance_of(source62, [])
+
+        shouldFail AssertionError, {
+            b.set_values(null)
+        }
+
+    }
+
+    @Test
+    void recursive_class_invariant() {
+
+        def b = create_instance_of(source71)
+        assert b != null
+    }
+
+    @Test
+    void abstract_method_with_postcondition() {
+
+        add_class_to_classpath """
+      package tests
+
+      import groovy.contracts.*
+
+      abstract class Base {
+        @Ensures({ result })
+        abstract List<String> sources()
+      }
+      """
+
+        def c = add_class_to_classpath """
+      package tests
+
+      class DirectImpl extends Base {
+
+          List<String> sources() { ['a','b','c'] }
+
+      }
+      """
+
+        c.newInstance().sources()
+    }
+
+    @Test(expected = ClassInvariantViolation)
+    void separate_class_invariant() {
+        def c = add_class_to_classpath """
+            package tests
+      
+            import groovy.contracts.*
+      
+            @Invariant({
+                i_never_null: i != null
+                j_never_null: j != null
+            })
+            class Test {
+                private def i
+                private def j
+            }
+            """
+
+        c.newInstance()
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/POGOClassInvariantTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/POGOClassInvariantTests.groovy
new file mode 100644
index 0000000..e1950b8
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/POGOClassInvariantTests.groovy
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.inv
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+/**
+ * POGO class invariant tests.
+ */
+class POGOClassInvariantTests extends BaseTestClass {
+
+    def dynamic_constructor_class_code = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property != null })
+class DynamicConstructor {
+
+  def property
+}
+'''
+
+    def dynamic_setter_class_code = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ string1 != null && string2 != null && string3 != null })
+class DynamicSetter {
+
+  String string1 = ''
+  def String string2 = ''
+  final String string3 = ''
+}
+
+'''
+
+    @Test
+    void dynamic_constructor_class_invariant() {
+        shouldFail AssertionError, {
+            create_instance_of dynamic_constructor_class_code;
+        }
+    }
+
+    @Test
+    void dynamic_setter_methods() {
+        def instance = create_instance_of(dynamic_setter_class_code)
+
+        shouldFail AssertionError, {
+            instance.string1 = null
+        }
+
+        shouldFail AssertionError, {
+            instance.string2 = null
+        }
+
+        shouldFail AssertionError, {
+            instance.string3 = null
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/SimpleClassInvariantTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/SimpleClassInvariantTests.groovy
new file mode 100644
index 0000000..3f319e5
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/inv/SimpleClassInvariantTests.groovy
@@ -0,0 +1,305 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.inv
+
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+import org.apache.groovy.contracts.ClassInvariantViolation
+
+class SimpleClassInvariantTests extends BaseTestClass {
+
+    def source1 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property != null })
+class A {
+
+  def property
+
+  def A(def someValue)  {
+    property = someValue
+  }
+}
+'''
+
+    def source2 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property != null })
+class A {
+
+  private property
+
+  def A(def someValue)  {
+    property = someValue
+  }
+}
+'''
+
+    def source3 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property != null })
+class A {
+
+  private property
+
+  def A(def someValue)  {
+    property = someValue
+  }
+
+  static me = "me"
+}
+'''
+
+    @Test
+    void class_invariant() {
+        create_instance_of(source1, ['test'])
+
+        shouldFail AssertionError, {
+            create_instance_of(source1, [null])
+        }
+    }
+
+    @Test
+    void class_invariant_with_private_instance_variable() {
+        create_instance_of(source2, ['test'])
+
+        shouldFail AssertionError, {
+            create_instance_of(source2, [null])
+        }
+    }
+
+    @Test
+    void class_with_constant() {
+        create_instance_of(source3, ['test'])
+    }
+
+
+    @Test
+    void multiple_return_statements() {
+
+        def source = """
+        import groovy.contracts.*
+
+@Invariant({ property != 0 })
+class Account {
+
+   def property = 1
+
+   def some_method()  {
+     if (true)  {
+         property = 0
+         return;
+     }
+
+     return;
+   }
+}
+    """
+
+        def source2 = """
+        import groovy.contracts.*
+
+@Invariant({ property != 0 })
+class Account {
+
+   def property = 1
+
+   def some_method()  {
+     if (false)  {
+         property = 1
+         return;
+     }
+
+     property = 0
+     return;
+   }
+}
+    """
+
+        def a = create_instance_of(source2)
+        shouldFail ClassInvariantViolation, {
+            a.some_method()
+        }
+    }
+
+    @Test
+    void duplicate_return_statements() {
+
+        def source = '''
+        import groovy.contracts.*
+
+        @Invariant({ elements != null })
+        class Stack {
+             def elements = []
+
+             def push(def item) {
+                 elements.push(item)
+             }
+
+             def pop()  {
+                 elements.pop()
+             }
+        }
+        '''
+
+        def stack = create_instance_of(source)
+
+        stack.push(1)
+        stack.push(2)
+
+        assert stack.pop() == 2
+        assert stack.pop() == 1
+    }
+
+    @Test
+    void avoid_invariant_on_read_only_methods() {
+
+        def source = """
+import groovy.contracts.*
+
+@Invariant({ speed() >= 0.0 })
+class Rocket {
+
+    def speed() { 1.0 }
+}
+
+    """
+
+        create_instance_of(source)
+    }
+
+
+    @Test
+    void recursive_invariant_with_getter_method() {
+
+        def source = """
+    import groovy.contracts.*
+
+    @Invariant({ speed >= 0.0 })
+    class Rocket {
+
+        @Requires({ true })
+        def getSpeed() { 1.0 }
+    }
+
+        """
+
+        create_instance_of(source)
+    }
+
+    @Test
+    void direct_field_access() {
+
+        def source = """
+        import groovy.contracts.*
+
+        @Invariant({ speed >= 0.0 })
+        class Rocket {
+            def speed = 0.0
+
+            def increase() {
+                this.speed -= 1
+            }
+        }
+
+            """
+
+        def rocket = create_instance_of(source)
+
+        shouldFail(ClassInvariantViolation) {
+            rocket.increase()
+        }
+    }
+
+    @Test
+    void direct_field_access_in_class_invariant() {
+
+        add_class_to_classpath """
+            import groovy.contracts.*
+
+            @Invariant({ this.speed >= 0.0 })
+            class Rocket {
+                def speed = 0.0
+
+                def increase() {
+                    this.speed -= 1
+                }
+            }"""
+    }
+
+    @Test
+    void private_field_access_in_direct_class() {
+
+        def c = add_class_to_classpath """
+                import groovy.contracts.*
+
+                @Invariant({ speed >= 0.0 })
+                class Rocket {
+                    private double speed = 0.0
+
+                    def increase() {
+                        this.speed -= 1
+                    }
+                }"""
+
+        def rocket = c.newInstance()
+
+        shouldFail ClassInvariantViolation, {
+            rocket.increase()
+        }
+    }
+
+    @Test
+    void private_field_access_in_descendant_class() {
+
+        def c = add_class_to_classpath """
+                    import groovy.contracts.*
+
+                    @Invariant({ speed >= 0.0 })
+                    class Rocket {
+                        private double speed = 0.0
+
+                        def increase() {
+                            this.speed -= 1
+                        }
+                    }
+                    """
+
+        def c2 = add_class_to_classpath """
+            class BetterRocket extends Rocket {}
+        """
+
+        def betterRocket = c2.newInstance()
+
+        shouldFail ClassInvariantViolation, {
+            betterRocket.increase()
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/AbstractClassTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/AbstractClassTests.groovy
new file mode 100644
index 0000000..935f2f7
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/AbstractClassTests.groovy
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.other
+
+import org.apache.groovy.contracts.PreconditionViolation
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class AbstractClassTests extends BaseTestClass {
+
+    def source1 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+@Invariant({ property != null })
+abstract class A {
+
+  def property
+
+  def A(def someValue)  {
+    property = someValue
+  }
+
+  @Requires({ param != null })
+  def some_operation(def param)  {
+    // noop
+  }
+}
+'''
+
+    def source2 = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+class B extends A  {
+
+  def B(def someValue)  {
+    super(someValue)
+  }
+
+  def some_operation(def param)  {
+    // noop
+  }
+}
+'''
+
+    @Test
+    void inherited_class_invariant() {
+        add_class_to_classpath source1
+
+        shouldFail AssertionError, {
+            create_instance_of(source2, [null])
+        }
+    }
+
+    @Test
+    void inherited_precondition() {
+        add_class_to_classpath source1
+
+        def bInstance = create_instance_of(source2, ["test"])
+
+        shouldFail PreconditionViolation, {
+            bInstance.some_operation null
+        }
+    }
+
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/CandidateChecksTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/CandidateChecksTests.groovy
new file mode 100644
index 0000000..24cdab7
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/CandidateChecksTests.groovy
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.other
+
+import org.codehaus.groovy.ast.ClassHelper
+import org.apache.groovy.contracts.generation.CandidateChecks
+import org.junit.Test
+import org.codehaus.groovy.ast.Parameter
+
+import static org.junit.Assert.assertFalse
+import static org.junit.Assert.assertTrue
+
+class A {
+
+}
+
+interface B {
+
+}
+
+enum C {
+
+}
+
+class D {
+    private D() {}
+
+    private def method() {}
+}
+
+/**
+ * all test cases for {@link CandidateChecks}.
+ *
+ * @see CandidateChecks
+ */
+class CandidateChecksTests {
+
+    @Test
+    void testContractsCandidateChecks() {
+        assert !CandidateChecks.isContractsCandidate(ClassHelper.make(B.class))
+        assert !CandidateChecks.isContractsCandidate(ClassHelper.make(C.class))
+        assert CandidateChecks.isContractsCandidate(ClassHelper.make(A.class))
+    }
+
+    // refs #22
+    @Test
+    void testPrivateConstructors() {
+        def classNode = ClassHelper.make(D.class)
+        assertTrue "private constructors should support preconditions",
+                CandidateChecks.isPreconditionCandidate(classNode, classNode.getDeclaredConstructors().first())
+        assertTrue "private methods should support preconditions",
+                CandidateChecks.isPreconditionCandidate(classNode, classNode.getMethod("method", [] as Parameter[]))
+
+        assertFalse "private constructors should by now NOT support class invariants",
+                CandidateChecks.isClassInvariantCandidate(classNode, classNode.getDeclaredConstructors().first())
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/CircularAssertionCallTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/CircularAssertionCallTests.groovy
new file mode 100644
index 0000000..3c069e1
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/CircularAssertionCallTests.groovy
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.other
+
+import org.apache.groovy.contracts.PreconditionViolation
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class CircularAssertionCallTests extends BaseTestClass {
+
+    @Test
+    void detectCircularAssertionCalls() {
+
+        def source = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+class A {
+
+  @Requires({ isConditionB() })
+  def isConditionA() { return false }
+
+  @Requires({ isConditionA() })
+  def isConditionB() { return true }
+}
+'''
+
+        def a = create_instance_of(source)
+
+        shouldFail PreconditionViolation, {
+            a.isConditionB()
+        }
+    }
+
+    @Test
+    void detect_diamon_assertion_calls() {
+
+        def source = '''
+@Contracted
+package tests
+
+import groovy.contracts.*
+
+class A {
+
+  @Requires({ isConditionC() })
+  def the_method_to_call() {}
+
+  @Requires({ isConditionA() && isConditionB() })
+  def isConditionC() {}
+
+  @Requires({ isConditionC() })
+  def isConditionA() {}
+
+  @Requires({ isConditionC() })
+  def isConditionB() {}
+}
+'''
+
+        def a = create_instance_of(source)
+        shouldFail PreconditionViolation, {
+            a.isConditionB()
+        }
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/ClosureExpressionValidationTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/ClosureExpressionValidationTests.groovy
new file mode 100644
index 0000000..5a42686
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/ClosureExpressionValidationTests.groovy
@@ -0,0 +1,202 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.other
+
+import groovy.test.GroovyShellTestCase
+import org.codehaus.groovy.control.CompilationFailedException
+
+class ClosureExpressionValidationTests extends GroovyShellTestCase {
+
+    void testCheckMissingExpressionsClassInvariant() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+            import groovy.contracts.*
+
+            @Invariant({})
+            class A {}
+
+            def a = new A()
+            """
+        }
+
+        assertTrue msg.contains("Annotation does not contain any expressions")
+    }
+
+    void testCheckMissingExpressionsPrecondition() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                import groovy.contracts.*
+
+                class A {
+                   @Requires({})
+                   def op() {}
+                }
+
+                def a = new A()
+                """
+        }
+
+        assertTrue msg.contains("Annotation does not contain any expressions")
+    }
+
+    void testCheckMissingExpressionsPostcondition() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                    import groovy.contracts.*
+
+                    class A {
+                       @Ensures({})
+                       def op() {}
+                    }
+
+                    def a = new A()
+                    """
+        }
+
+        assertTrue msg.contains("Annotation does not contain any expressions")
+    }
+
+    void testParameterSpecifiedClassInvariant() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                import groovy.contracts.*
+
+                @Invariant({ some -> 1 == 1 })
+                class A {}
+
+                def a = new A()
+                """
+        }
+
+        assertTrue msg.contains("Annotation does not support parameters")
+    }
+
+    void testParameterSpecifiedPostcondition() {
+
+        evaluate """
+                import groovy.contracts.*
+
+                class A {
+
+                    @Ensures({ result })
+                    def op() {}
+                }
+
+                def a = new A()
+        """
+    }
+
+    void testParameterNamesPostcondition() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+            import groovy.contracts.*
+
+            class A {
+
+                @Ensures({ test -> 1 == 1 })
+                def op() {}
+            }
+
+            def a = new A()
+        """
+        }
+
+        assertTrue msg.contains("Postconditions only allow 'old' and 'result' closure parameters")
+    }
+
+    void testParameterWithExplicitTypePostcondition() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                import groovy.contracts.*
+
+                class A {
+
+                    @Ensures({ java.util.Map<String, Object> result -> 1 == 1 })
+                    def op() {}
+                }
+
+                def a = new A()
+            """
+        }
+
+        assertTrue msg.contains("Postconditions do not support explicit types")
+    }
+
+    void testClosureItAccess() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                        import groovy.contracts.*
+
+                        @Invariant({ it })
+                        class A {
+
+                        }
+
+                        def a = new A()
+                    """
+        }
+
+        assertTrue msg.contains("Access to 'it' is not supported.")
+    }
+
+    void testPrefixOperatorUsage() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                import groovy.contracts.*
+
+                class A {
+
+                    @Requires({ ++arg })
+                    def op(def arg) {}
+                }
+
+                def a = new A()
+            """
+        }
+
+        assertTrue msg.contains("State changing postfix & prefix operators are not supported.")
+    }
+
+    void testPostfixOperatorUsage() {
+
+        def msg = shouldFail CompilationFailedException, {
+            evaluate """
+                    import groovy.contracts.*
+
+                    class A {
+
+                        @Requires({ arg++ })
+                        def op(def arg) {}
+                    }
+
+                    def a = new A()
+                """
+        }
+
+        assertTrue msg.contains("State changing postfix & prefix operators are not supported.")
+    }
+}
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/ContractLabelTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/ContractLabelTests.groovy
new file mode 100644
index 0000000..40e858d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/other/ContractLabelTests.groovy
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.contracts.tests.other
+
+import org.apache.groovy.contracts.ClassInvariantViolation
+import org.apache.groovy.contracts.PostconditionViolation
+import org.apache.groovy.contracts.PreconditionViolation
+import org.apache.groovy.contracts.tests.basic.BaseTestClass
+import org.junit.Test
+
+class ContractLabelTests extends BaseTestClass {
+
+    @Test
+    void class_invariant() {
+        def source1 = '''
+package tests
+
+import groovy.contracts.*
+
+@Invariant({
+    not_null_property: property != null
+})
+class A {
+
+  def property
+}
+'''
+
+        shouldFail ClassInvariantViolation, {
... 2217 lines suppressed ...