From 6725458207ba1dd89dbb45824f7310de8b461ef1 Mon Sep 17 00:00:00 2001
From: Paul King
Date: Wed, 17 Jul 2024 20:01:03 +1000
Subject: [PATCH] GROOVY-11443: Support multiple Requires/Ensures/Invariant
annotations in groovy-contracts
---
.../groovy/ast/tools/GeneralUtils.java | 4 +
.../main/java/groovy/contracts/Ensures.java | 4 +-
.../groovy/contracts/EnsuresConditions.java | 36 +++++++++
.../main/java/groovy/contracts/Invariant.java | 4 +-
.../java/groovy/contracts/Invariants.java | 36 +++++++++
.../main/java/groovy/contracts/Requires.java | 4 +-
.../groovy/contracts/RequiresConditions.java | 36 +++++++++
...ExpressionEvaluationASTTransformation.java | 33 +++++----
.../ast/visitor/AnnotationClosureVisitor.java | 4 +-
.../visitor/AnnotationProcessorVisitor.java | 74 ++++++++++++++-----
.../contracts/ast/visitor/BaseVisitor.java | 2 +-
.../classgen/asm/ContractClosureWriter.java | 3 +-
.../ClassInvariantAnnotationProcessor.java | 10 +++
.../impl/EnsuresAnnotationProcessor.java | 8 +-
.../impl/lc/PostconditionLifecycle.java | 12 +--
.../spi/ProcessingContextInformation.java | 8 +-
.../groovy/contracts/domain/Assertion.java | 31 ++------
.../groovy/contracts/domain/AssertionMap.java | 2 +-
.../contracts/domain/ClassInvariant.java | 7 +-
.../AssertStatementCreationUtility.java | 6 +-
.../contracts/generation/BaseGenerator.java | 24 ++++--
.../generation/ClassInvariantGenerator.java | 16 ++--
.../contracts/generation/Configurator.java | 4 +-
.../generation/ContractExecutionTracker.java | 13 ++--
.../OldVariableGenerationUtility.java | 12 +--
.../generation/PostconditionGenerator.java | 17 +++--
.../generation/PreconditionGenerator.java | 13 ++--
.../generation/TryCatchBlockGenerator.java | 3 +-
.../contracts/util/AnnotationUtils.java | 37 ++++++----
.../contracts/util/ExpressionUtils.java | 7 +-
.../util/LifecycleImplementationLoader.java | 2 +-
.../contracts/domain/ContractTests.groovy | 21 +++---
.../tests/pre/SimplePreconditionTests.groovy | 49 +++++++++++-
33 files changed, 373 insertions(+), 169 deletions(-)
create mode 100644 subprojects/groovy-contracts/src/main/java/groovy/contracts/EnsuresConditions.java
create mode 100644 subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariants.java
create mode 100644 subprojects/groovy-contracts/src/main/java/groovy/contracts/RequiresConditions.java
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 bcf2d10e060..f94be91dc7c 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -770,6 +770,10 @@ public static MapEntryExpression mapEntryX(final String key, final Expression va
return new MapEntryExpression(constX(key), valueExpr);
}
+ public static MapExpression mapX() {
+ return new MapExpression();
+ }
+
public static MapExpression mapX(final List expressions) {
return new MapExpression(expressions);
}
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java
index 7b905e54d5b..930a403097d 100644
--- a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Ensures.java
@@ -24,6 +24,7 @@
import org.apache.groovy.lang.annotation.Incubating;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -80,7 +81,8 @@
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@Incubating
@Postcondition
+@Repeatable(EnsuresConditions.class)
@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/EnsuresConditions.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/EnsuresConditions.java
new file mode 100644
index 00000000000..8e5a21210dc
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/EnsuresConditions.java
@@ -0,0 +1,36 @@
+/*
+ * 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.lang.annotation.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents multiple postconditions.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
+@Incubating
+public @interface EnsuresConditions {
+ Ensures[] value();
+}
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java
index ca9edf384b9..8512cd49e2b 100644
--- a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariant.java
@@ -24,6 +24,7 @@
import org.apache.groovy.lang.annotation.Incubating;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -53,7 +54,8 @@
@Target(ElementType.TYPE)
@Incubating
@ClassInvariant
+@Repeatable(Invariants.class)
@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/Invariants.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariants.java
new file mode 100644
index 00000000000..ac37f83b607
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Invariants.java
@@ -0,0 +1,36 @@
+/*
+ * 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.lang.annotation.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents multiple invariants
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Incubating
+public @interface Invariants {
+ Invariant[] value();
+}
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java
index b9635afeca5..5e4a276943e 100644
--- a/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/Requires.java
@@ -24,6 +24,7 @@
import org.apache.groovy.lang.annotation.Incubating;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -58,6 +59,7 @@
@Incubating
@Precondition
@AnnotationProcessorImplementation(RequiresAnnotationProcessor.class)
+@Repeatable(RequiresConditions.class)
public @interface Requires {
Class value();
-}
\ No newline at end of file
+}
diff --git a/subprojects/groovy-contracts/src/main/java/groovy/contracts/RequiresConditions.java b/subprojects/groovy-contracts/src/main/java/groovy/contracts/RequiresConditions.java
new file mode 100644
index 00000000000..0ff81b3332d
--- /dev/null
+++ b/subprojects/groovy-contracts/src/main/java/groovy/contracts/RequiresConditions.java
@@ -0,0 +1,36 @@
+/*
+ * 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.lang.annotation.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents multiple preconditions.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
+@Incubating
+public @interface RequiresConditions {
+ Requires[] value();
+}
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
index afbe5783380..f63b679df14 100644
--- 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
@@ -37,45 +37,46 @@
import java.util.List;
/**
- * Evaluates {@link org.codehaus.groovy.ast.expr.ClosureExpression} instances in as actual annotation parameters and
+ * Evaluates {@link org.codehaus.groovy.ast.expr.ClosureExpression} instances in annotation parameters and
* generates special contract closure classes from them.
*/
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class ClosureExpressionEvaluationASTTransformation extends BaseASTTransformation {
+ /**
+ * {@link org.codehaus.groovy.transform.ASTTransformation#visit(org.codehaus.groovy.ast.ASTNode[], org.codehaus.groovy.control.SourceUnit)}
+ */
+ @Override
+ public void visit(ASTNode[] nodes, SourceUnit unit) {
+ final ModuleNode moduleNode = unit.getAST();
+
+ ReaderSource source = getReaderSource(unit);
+ final List classNodes = new ArrayList<>(moduleNode.getClasses());
+
+ generateAnnotationClosureClasses(unit, source, classNodes);
+ }
+
private void generateAnnotationClosureClasses(SourceUnit unit, ReaderSource source, List classNodes) {
final AnnotationClosureVisitor annotationClosureVisitor = new AnnotationClosureVisitor(unit, source);
for (final ClassNode classNode : classNodes) {
annotationClosureVisitor.visitClass(classNode);
- if (!CandidateChecks.isContractsCandidate(classNode)) continue;
+ if (!CandidateChecks.isContractsCandidate(classNode) && !CandidateChecks.isInterfaceContractsCandidate(classNode))
+ continue;
final ContractElementVisitor contractElementVisitor = new ContractElementVisitor(unit, source);
contractElementVisitor.visitClass(classNode);
-
if (!contractElementVisitor.isFoundContractElement()) continue;
annotationClosureVisitor.visitClass(classNode);
markClassNodeAsContracted(classNode);
+ if (classNode.isInterface()) continue;
new ConfigurationSetup().init(classNode);
}
}
- /**
- * {@link org.codehaus.groovy.transform.ASTTransformation#visit(org.codehaus.groovy.ast.ASTNode[], org.codehaus.groovy.control.SourceUnit)}
- */
- @Override
- public void visit(ASTNode[] nodes, SourceUnit unit) {
- final ModuleNode moduleNode = unit.getAST();
-
- ReaderSource source = getReaderSource(unit);
- final List classNodes = new ArrayList(moduleNode.getClasses());
-
- generateAnnotationClosureClasses(unit, source, classNodes);
- }
-
private void markClassNodeAsContracted(final ClassNode classNode) {
final ClassNode contractedAnnotationClassNode = ClassHelper.makeWithoutCaching(Contracted.class);
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
index f3d2e284c90..a8d1770bcb3 100644
--- 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
@@ -107,11 +107,11 @@ public AnnotationClosureVisitor(final SourceUnit sourceUnit, final ReaderSource
@Override
public void visitClass(ClassNode classNode) {
- if (classNode == null || !(CandidateChecks.isInterfaceContractsCandidate(classNode) || CandidateChecks.isContractsCandidate(classNode))) return;
+ if (classNode == null || !(CandidateChecks.isContractsCandidate(classNode) || CandidateChecks.isInterfaceContractsCandidate(classNode))) return;
this.classNode = classNode;
- if (classNode.getNodeMetaData(PROCESSED) == null && CandidateChecks.isContractsCandidate(classNode)) {
+ if (classNode.getNodeMetaData(PROCESSED) == null) {
List annotationNodes = AnnotationUtils.hasMetaAnnotations(classNode, ContractElement.class.getName());
for (AnnotationNode annotationNode : annotationNodes) {
ClosureExpression closureExpression = getOriginalCondition(annotationNode);
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
index b23e38ab968..f87235ccd05 100644
--- 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
@@ -41,9 +41,13 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static org.apache.groovy.contracts.ast.visitor.AnnotationClosureVisitor.META_DATA_ORIGINAL_TRY_CATCH_BLOCK;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.andX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
@@ -55,7 +59,8 @@
*/
public class AnnotationProcessorVisitor extends BaseVisitor {
- private ProcessingContextInformation pci;
+ private static final String CONTRACT_ELEMENT_CLASSNAME = ContractElement.class.getName();
+ private final ProcessingContextInformation pci;
public AnnotationProcessorVisitor(final SourceUnit sourceUnit, final ReaderSource source, final ProcessingContextInformation pci) {
super(sourceUnit, source);
@@ -73,11 +78,10 @@ public void visitClass(ClassNode type) {
for (MethodNode methodNode : methodNodes) {
if (CandidateChecks.isClassInvariantCandidate(type, methodNode) || CandidateChecks.isPreOrPostconditionCandidate(type, methodNode)) {
- handleMethodAnnotations(methodNode, AnnotationUtils.hasMetaAnnotations(methodNode, ContractElement.class.getName()));
+ handleMethodAnnotations(methodNode, AnnotationUtils.hasMetaAnnotations(methodNode, CONTRACT_ELEMENT_CLASSNAME));
}
}
- // visit all interfaces of this class
visitInterfaces(type, type.getInterfaces());
visitAbstractBaseClassesForInterfaceMethodNodes(type, type.getSuperClass());
}
@@ -87,7 +91,7 @@ private void visitAbstractBaseClassesForInterfaceMethodNodes(ClassNode origin, C
for (ClassNode interfaceNode : superClass.getInterfaces()) {
List interfaceMethods = new ArrayList<>(interfaceNode.getMethods());
for (MethodNode interfaceMethod : interfaceMethods) {
- List annotationNodes = AnnotationUtils.hasMetaAnnotations(interfaceMethod, ContractElement.class.getName());
+ List annotationNodes = AnnotationUtils.hasMetaAnnotations(interfaceMethod, CONTRACT_ELEMENT_CLASSNAME);
if (annotationNodes == null || annotationNodes.isEmpty()) continue;
MethodNode implementingMethod = superClass.getMethod(interfaceMethod.getName(), interfaceMethod.getParameters());
@@ -104,13 +108,15 @@ private void visitAbstractBaseClassesForInterfaceMethodNodes(ClassNode origin, C
private void visitInterfaces(final ClassNode classNode, final ClassNode[] interfaces) {
for (ClassNode interfaceNode : interfaces) {
+ List interfaceNodes = AnnotationUtils.hasMetaAnnotations(interfaceNode, CONTRACT_ELEMENT_CLASSNAME);
+ processAnnotationNodes(classNode, interfaceNodes);
List interfaceMethods = new ArrayList<>(interfaceNode.getMethods());
// @ContractElement annotations are by now only supported on method interfaces
for (MethodNode interfaceMethod : interfaceMethods) {
MethodNode implementingMethod = classNode.getMethod(interfaceMethod.getName(), interfaceMethod.getParameters());
if (implementingMethod == null) continue;
- List annotationNodes = AnnotationUtils.hasMetaAnnotations(interfaceMethod, ContractElement.class.getName());
+ List annotationNodes = AnnotationUtils.hasMetaAnnotations(interfaceMethod, CONTRACT_ELEMENT_CLASSNAME);
handleInterfaceMethodNode(classNode, implementingMethod, annotationNodes);
}
@@ -119,13 +125,17 @@ private void visitInterfaces(final ClassNode classNode, final ClassNode[] interf
}
private void handleClassNode(final ClassNode classNode) {
- List annotationNodes = AnnotationUtils.hasMetaAnnotations(classNode, ContractElement.class.getName());
+ List annotationNodes = AnnotationUtils.hasMetaAnnotations(classNode, CONTRACT_ELEMENT_CLASSNAME);
+ processAnnotationNodes(classNode, annotationNodes);
+ }
+
+ private void processAnnotationNodes(ClassNode classNode, List annotationNodes) {
for (AnnotationNode annotationNode : annotationNodes) {
- AnnotationProcessor annotationProcessor = createAnnotationProcessor(annotationNode);
- if (annotationProcessor != null) {
+ AnnotationProcessor processor = createAnnotationProcessor(annotationNode);
+ if (processor != null) {
Expression valueExpression = getReplacedCondition(annotationNode);
BlockStatement blockStatement = valueExpression.getNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK);
- annotationProcessor.process(pci, pci.contract(), classNode, blockStatement, asConditionExecution(annotationNode));
+ processor.process(pci, pci.contract(), classNode, blockStatement, asConditionExecution(annotationNode));
}
}
}
@@ -136,15 +146,30 @@ private void handleInterfaceMethodNode(ClassNode type, MethodNode methodNode, Li
private void handleMethodAnnotations(MethodNode methodNode, List annotationNodes) {
if (methodNode == null) return;
+ Map> collectedPreconditions = new HashMap<>();
+ AnnotationProcessor annotationProcessor;
for (AnnotationNode annotationNode : annotationNodes) {
- AnnotationProcessor annotationProcessor = createAnnotationProcessor(annotationNode);
+ annotationProcessor = createAnnotationProcessor(annotationNode);
if (annotationProcessor != null && getReplacedCondition(annotationNode) != null) {
- handleMethodAnnotation(methodNode, annotationNode, annotationProcessor);
+ handleMethodAnnotation(methodNode, annotationNode, annotationProcessor, collectedPreconditions);
}
}
+ // Manually and the expressions if multiple annotations are used
+ for (String processorName : collectedPreconditions.keySet()) {
+ AnnotationProcessor processor = getAnnotationProcessor(processorName);
+ BooleanExpression collectedExpression = null;
+ for (BooleanExpression boolExp : collectedPreconditions.get(processorName)) {
+ if (collectedExpression == null) {
+ collectedExpression = boolExp;
+ } else {
+ collectedExpression = boolX(andX(collectedExpression, boolExp));
+ }
+ }
+ processor.process(pci, pci.contract(), methodNode.getDeclaringClass(), methodNode, null, collectedExpression);
+ }
}
- private void handleMethodAnnotation(MethodNode methodNode, AnnotationNode annotationNode, AnnotationProcessor annotationProcessor) {
+ private void handleMethodAnnotation(MethodNode methodNode, AnnotationNode annotationNode, AnnotationProcessor annotationProcessor, Map> collected) {
boolean isPostcondition = AnnotationUtils.hasAnnotationOfType(annotationNode.getClassNode(), Postcondition.class.getName());
ArgumentListExpression argumentList = new ArgumentListExpression();
@@ -162,7 +187,13 @@ private void handleMethodAnnotation(MethodNode methodNode, AnnotationNode annota
BooleanExpression booleanExpression = asConditionExecution(annotationNode);
((MethodCallExpression) booleanExpression.getExpression()).setArguments(argumentList);
BlockStatement blockStatement = valueExpression.getNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK);
- annotationProcessor.process(pci, pci.contract(), methodNode.getDeclaringClass(), methodNode, blockStatement, booleanExpression);
+ if (isPostcondition) {
+ annotationProcessor.process(pci, pci.contract(), methodNode.getDeclaringClass(), methodNode, blockStatement, booleanExpression);
+ } else {
+ String name = annotationProcessor.getClass().getName();
+ collected.putIfAbsent(name, new ArrayList<>());
+ collected.get(name).add(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())) {
@@ -186,13 +217,20 @@ private AnnotationProcessor createAnnotationProcessor(AnnotationNode annotationN
}
if (annotationProcessor != null) {
- try {
- var apt = Class.forName(annotationProcessor.getType().getName());
- return (AnnotationProcessor) apt.getDeclaredConstructor().newInstance();
- } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException ignore) {
- }
+ String name = annotationProcessor.getType().getName();
+ AnnotationProcessor apt = getAnnotationProcessor(name);
+ if (apt != null) return apt;
}
throw new GroovyBugError("Annotation processing class could not be instantiated! This indicates a bug in groovy-contracts, please file an issue!");
}
+
+ public static AnnotationProcessor getAnnotationProcessor(String name) {
+ try {
+ var apt = Class.forName(name);
+ return (AnnotationProcessor) apt.getDeclaredConstructor().newInstance();
+ } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException ignore) {
+ }
+ return null;
+ }
}
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
index 2337cdd1ef5..520698bf0de 100644
--- 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
@@ -58,7 +58,7 @@ public BaseVisitor(final SourceUnit sourceUnit, final ReaderSource source) {
this.sourceUnit = sourceUnit;
}
- public static BooleanExpression asConditionExecution(final AnnotationNode annotation) {
+ public static BooleanExpression asConditionExecution(final AnnotationNode annotation) {
var conditionClass = annotation.getMember("value").getType();
var createInstance = ctorX(conditionClass, args(VariableExpression.THIS_EXPRESSION, VariableExpression.THIS_EXPRESSION));
final MethodCallExpression doCall = callX(createInstance, "doCall");
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
index c2a02247b41..2a8d3220bac 100644
--- 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
@@ -39,6 +39,7 @@
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+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.ctorSuperX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
@@ -112,7 +113,7 @@ public ClassNode createClosureClass(ClassNode classNode, MethodNode methodNode,
call.setSynthetic(true);
// let's make the constructor
- BlockStatement block = new BlockStatement();
+ BlockStatement block = block();
// 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");
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
index 976a47af913..95a1573ccf1 100644
--- 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
@@ -23,9 +23,15 @@
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.BinaryExpression;
import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.andX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+
/**
* Internal {@link AnnotationProcessor} implementation for class-invariants.
*/
@@ -36,6 +42,10 @@ public void process(ProcessingContextInformation processingContextInformation, C
if (!processingContextInformation.isClassInvariantsEnabled()) return;
if (booleanExpression == null) return;
+ Expression currentInvariant = contract.classInvariant().booleanExpression().getExpression();
+ if (currentInvariant instanceof MethodCallExpression || currentInvariant instanceof BinaryExpression) {
+ booleanExpression = boolX(andX(booleanExpression, currentInvariant));
+ }
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
index 5a3740cdac7..3b1806220a9 100644
--- 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
@@ -23,13 +23,10 @@
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.
*/
@@ -40,8 +37,7 @@ public void process(ProcessingContextInformation processingContextInformation, C
if (!processingContextInformation.isPostconditionsEnabled()) return;
if (booleanExpression == null) return;
- final List declaredConstructors = classNode.getDeclaredConstructors();
-
- contract.postconditions().and(methodNode, new Postcondition(blockStatement, booleanExpression, declaredConstructors.contains(methodNode)));
+ boolean isConstructor = classNode.getDeclaredConstructors().contains(methodNode);
+ contract.postconditions().and(methodNode, new Postcondition(blockStatement, booleanExpression, isConstructor));
}
}
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
index f373b2ba8c1..a2b08af7753 100644
--- 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
@@ -18,15 +18,11 @@
*/
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;
/**
@@ -36,6 +32,7 @@ public class PostconditionLifecycle extends BaseLifecycle {
@Override
public void beforeProcessingClassNode(ProcessingContextInformation processingContextInformation, ClassNode classNode) {
+ if (classNode.isInterface()) return;
final PostconditionGenerator postconditionGenerator = new PostconditionGenerator(processingContextInformation.readerSource());
postconditionGenerator.addOldVariablesMethod(classNode);
}
@@ -55,11 +52,6 @@ private void generatePostcondition(ProcessingContextInformation processingContex
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);
- }
+ postconditionGenerator.generateDefaultPostconditionStatement(classNode, methodNode);
}
}
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
index b6d519656ed..9b6e632b5ae 100644
--- 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
@@ -36,16 +36,16 @@
*/
public class ProcessingContextInformation {
- private Contract contract;
- private SourceUnit sourceUnit;
- private ReaderSource source;
+ private final Contract contract;
+ private final SourceUnit sourceUnit;
+ private final ReaderSource source;
private boolean constructorAssertionsEnabled = true;
private boolean preconditionsEnabled = true;
private boolean postconditionsEnabled = true;
private boolean classInvariantsEnabled = true;
- private Map extra = new HashMap();
+ private final Map extra = new HashMap<>();
public ProcessingContextInformation(ClassNode classNode, SourceUnit sourceUnit, ReaderSource source) {
Validate.notNull(classNode);
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
index 7751f2960df..f3dc7982256 100644
--- 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
@@ -19,12 +19,13 @@
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;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.andX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.orX;
/**
* Base class for all assertion types.
@@ -37,7 +38,7 @@ public abstract class Assertion {
private BooleanExpression booleanExpression;
public Assertion() {
- this.booleanExpression = new BooleanExpression(ConstantExpression.TRUE);
+ this.booleanExpression = boolX(ConstantExpression.TRUE);
}
public Assertion(final BlockStatement blockStatement, final BooleanExpression booleanExpression) {
@@ -66,33 +67,15 @@ public void renew(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()
- )
- );
+ BooleanExpression newBooleanExpression = boolX(andX(booleanExpression(), 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()
- )
- );
+ BooleanExpression newBooleanExpression = boolX(orX(booleanExpression(), 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
index aebc2739393..0ecfef6cb0a 100644
--- 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
@@ -30,7 +30,7 @@ public class AssertionMap> implements Iterable internalMap;
public AssertionMap() {
- this.internalMap = new HashMap();
+ this.internalMap = new HashMap<>();
}
public void and(final MethodNode methodNode, final T assertion) {
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
index 538ef4b00bf..474b827f63a 100644
--- 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
@@ -19,15 +19,18 @@
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;
+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.constX;
+
/**
* A class-invariant assertion.
*/
public class ClassInvariant extends Assertion {
- public static final ClassInvariant DEFAULT = new ClassInvariant(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)));
+ public static final ClassInvariant DEFAULT = new ClassInvariant(block(), boolX(constX(true)));
public ClassInvariant() {
}
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
index b2701f9ce9e..68069f46e80 100644
--- 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
@@ -218,11 +218,11 @@ public void visitBlockStatement(BlockStatement block) {
if (statement == returnStatement) {
block.getStatements().remove(statement);
- final VariableExpression $_gc_result = localVarX("$_gc_result", ClassHelper.OBJECT_TYPE);
- block.addStatement(declS($_gc_result, returnStatement.getExpression()));
+ final VariableExpression gcResult = localVarX("$_gc_result", ClassHelper.OBJECT_TYPE);
+ block.addStatement(declS(gcResult, returnStatement.getExpression()));
block.addStatement(assertionCallStatement);
- final Statement gcResultReturn = returnS($_gc_result);
+ final Statement gcResultReturn = returnS(gcResult);
gcResultReturn.setSourcePosition(returnStatement);
block.addStatement(gcResultReturn);
return; // we found the return statement under target, let's cancel tree traversal
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
index 718d8d52717..0eb3b3eb80d 100644
--- 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
@@ -96,15 +96,15 @@ protected BlockStatement getInlineModeBlockStatement(BlockStatement blockStateme
protected BlockStatement wrapAssertionBooleanExpression(ClassNode type, MethodNode methodNode, BooleanExpression classInvariantExpression, String assertionType) {
ClassNode violationTrackerClassNode = ClassHelper.makeWithoutCaching(ViolationTracker.class);
- VariableExpression $_gc_result = varX("$_gc_result", ClassHelper.boolean_TYPE);
- $_gc_result.setAccessedVariable($_gc_result);
+ VariableExpression gcResult = varX("$_gc_result", ClassHelper.boolean_TYPE);
+ gcResult.setAccessedVariable(gcResult);
BlockStatement ifBlockStatement = block(
- declS($_gc_result, ConstantExpression.FALSE),
+ declS(gcResult, ConstantExpression.FALSE),
stmt(callX(classX(violationTrackerClassNode), "init")),
- assignS($_gc_result, classInvariantExpression),
+ assignS(gcResult, classInvariantExpression),
ifS(
- boolX(notX(callX($_gc_result, "booleanValue"))),
+ boolX(notX(callX(gcResult, "booleanValue"))),
ifS(
boolX(callX(classX(violationTrackerClassNode), "violationsOccurred")),
tryCatchS(
@@ -115,9 +115,9 @@ protected BlockStatement wrapAssertionBooleanExpression(ClassNode type, MethodNo
);
TryCatchStatement lockTryCatchStatement = tryCatchS(
- block(ifS(
+ 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)),
+ ifBlockStatement),
block(new VariableScope(), stmt(callX(
classX(ClassHelper.make(ContractExecutionTracker.class)),
"clear",
@@ -133,6 +133,7 @@ protected BooleanExpression addCallsToSuperMethodNodeAnnotationClosure(final Cla
if (contractElementAnnotations.isEmpty()) {
methodNode.putNodeMetaData(META_DATA_USE_INLINE_MODE, Boolean.TRUE);
} else {
+ BooleanExpression collectedPre = null;
for (AnnotationNode contractElementAnnotation : contractElementAnnotations) {
ArgumentListExpression argumentList = new ArgumentListExpression();
for (Parameter parameter : methodNode.getParameters()) {
@@ -151,9 +152,16 @@ protected BooleanExpression addCallsToSuperMethodNodeAnnotationClosure(final Cla
if (isPostcondition) {
booleanExpression = boolX(andX(booleanExpression, predicate));
} else {
- booleanExpression = boolX(orX(booleanExpression, predicate));
+ if (collectedPre == null) {
+ collectedPre = predicate;
+ } else {
+ collectedPre = boolX(andX(collectedPre, predicate));
+ }
}
}
+ if (collectedPre != null) {
+ booleanExpression = boolX(orX(booleanExpression, collectedPre));
+ }
}
return booleanExpression;
}
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
index 3bb7ebabb73..57919865fd8 100644
--- 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
@@ -35,11 +35,11 @@
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.ClassHelper.isPrimitiveVoid;
import static org.codehaus.groovy.ast.tools.GeneralUtils.andX;
+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.stmt;
@@ -51,6 +51,8 @@
*/
public class ClassInvariantGenerator extends BaseGenerator {
+ private static final ClassNode CLASS_INVARIANT_TYPE = ClassHelper.makeWithoutCaching(ClassInvariant.class);
+
public ClassInvariantGenerator(final ReaderSource source) {
super(source);
}
@@ -65,9 +67,9 @@ public ClassInvariantGenerator(final ReaderSource source) {
*/
public void generateInvariantAssertionStatement(final ClassNode type, final org.apache.groovy.contracts.domain.ClassInvariant classInvariant) {
- BooleanExpression classInvariantExpression = addCallsToSuperAnnotationClosure(type, ClassInvariant.class, classInvariant.booleanExpression());
+ BooleanExpression classInvariantExpression = addCallsToSuperAnnotationClosure(type, classInvariant.booleanExpression());
- final BlockStatement blockStatement = new BlockStatement();
+ final BlockStatement blockStatement = block();
// 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);
@@ -76,8 +78,8 @@ public void generateInvariantAssertionStatement(final ClassNode type, final org.
blockStatement.addStatements(wrapAssertionBooleanExpression(type, methodNode, classInvariantExpression, "invariant").getStatements());
}
- private BooleanExpression addCallsToSuperAnnotationClosure(final ClassNode type, final Class extends Annotation> annotationType, BooleanExpression booleanExpression) {
- List contractElementAnnotations = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), ClassHelper.makeWithoutCaching(annotationType));
+ private BooleanExpression addCallsToSuperAnnotationClosure(final ClassNode type, BooleanExpression booleanExpression) {
+ List contractElementAnnotations = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), CLASS_INVARIANT_TYPE);
for (AnnotationNode contractElementAnnotation : contractElementAnnotations) {
booleanExpression = boolX(andX(booleanExpression, BaseVisitor.asConditionExecution(contractElementAnnotation)));
}
@@ -113,9 +115,7 @@ public void addInvariantAssertionStatement(final ClassNode type, final MethodNod
final BlockStatement blockStatement = (BlockStatement) statement;
blockStatement.addStatement(invariantMethodCall);
} else {
- final BlockStatement assertionBlock = new BlockStatement();
- assertionBlock.addStatement(statement);
- assertionBlock.addStatement(invariantMethodCall);
+ final BlockStatement assertionBlock = block(statement, 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
index a3fdabf3d9c..f2af5f4328a 100644
--- 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
@@ -45,7 +45,7 @@ public final class Configurator {
private static void initAssertionConfiguration() {
- assertionConfiguration = new HashMap();
+ assertionConfiguration = new HashMap<>();
// per default assertion are enabled (Groovy like)
assertionConfiguration.put(null, Boolean.TRUE);
@@ -85,7 +85,7 @@ public static boolean checkAssertionsEnabled(final String className) {
}
private static boolean internalMethod(String className) {
- if (className == null || className.length() == 0) return false;
+ if (className == null || className.isEmpty()) return false;
if (assertionConfiguration.containsKey(className)) return assertionConfiguration.get(className);
if (className.lastIndexOf('.') < 0) 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
index 9549c14f351..6b20a262e16 100644
--- 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
@@ -20,6 +20,7 @@
import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
/**
* Keeps track of contract executions to avoid cyclic contract checks.
@@ -71,19 +72,19 @@ public int hashCode() {
}
- static class ContractExecutionThreadLocal extends ThreadLocal> {
+ static class ContractExecutionThreadLocal extends ThreadLocal> {
@Override
- protected HashSet initialValue() {
- return new HashSet();
+ protected Set initialValue() {
+ return new HashSet<>();
}
}
- private static ThreadLocal> executions = new ContractExecutionThreadLocal();
+ private static final ThreadLocal> 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 contractExecutions = executions.get();
+ final Set contractExecutions = executions.get();
if (!contractExecutions.contains(ce)) {
contractExecutions.add(ce);
@@ -94,7 +95,7 @@ public static boolean track(String className, String methodIdentifier, String as
}
public static void clear(String className, String methodIdentifier, String assertionType, boolean isStatic) {
- final HashSet contractExecutions = executions.get();
+ final Set 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
index 03b49439444..3a9917fc2ec 100644
--- 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
@@ -23,7 +23,6 @@
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;
@@ -34,12 +33,15 @@
import java.util.Map;
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
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.mapEntryX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.mapX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
/**
@@ -59,9 +61,9 @@ public class OldVariableGenerationUtility {
public static void addOldVariableMethodNode(final ClassNode classNode) {
if (classNode.getDeclaredMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null) return;
- final BlockStatement methodBlockStatement = new BlockStatement();
+ final BlockStatement methodBlockStatement = block();
- final MapExpression oldVariablesMap = new MapExpression();
+ final MapExpression oldVariablesMap = mapX();
// create variable assignments for old variables
for (final FieldNode fieldNode : classNode.getFields()) {
@@ -87,7 +89,7 @@ public static void addOldVariableMethodNode(final ClassNode classNode) {
Statement oldVariableAssignment = declS(oldVariable, cloneField);
methodBlockStatement.addStatement(oldVariableAssignment);
- oldVariablesMap.addMapEntryExpression(new MapEntryExpression(constX(oldVariable.getName().substring("$old$".length())), oldVariable));
+ oldVariablesMap.addMapEntryExpression(mapEntryX(constX(oldVariable.getName().substring("$old$".length())), oldVariable));
} else if (ClassHelper.isPrimitiveType(fieldType)
|| ClassHelper.isNumberType(fieldType)
@@ -99,7 +101,7 @@ public static void addOldVariableMethodNode(final ClassNode classNode) {
Statement oldVariableAssignment = declS(oldVariable, fieldX(fieldNode));
methodBlockStatement.addStatement(oldVariableAssignment);
- oldVariablesMap.addMapEntryExpression(new MapEntryExpression(constX(oldVariable.getName().substring("$old$".length())), oldVariable));
+ oldVariablesMap.addMapEntryExpression(mapEntryX(constX(oldVariable.getName().substring("$old$".length())), oldVariable));
}
}
}
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
index 334a0881fb6..e3b5ee4787f 100644
--- 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
@@ -53,6 +53,7 @@
*
*/
public class PostconditionGenerator extends BaseGenerator {
+ private static final String METHOD_PROCESSED = "org.apache.groovy.contracts.POSTCONDITION_PROCESSED";
public PostconditionGenerator(final ReaderSource source) {
super(source);
@@ -82,7 +83,7 @@ public void generatePostconditionAssertionStatement(MethodNode method, org.apach
BlockStatement blockStatement;
final BlockStatement originalBlockStatement = postcondition.originalBlockStatement();
- // if use execution tracker flag is found in the meta-data the annotation closure visitor discovered
+ // if useExecutionTracker 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));
@@ -103,14 +104,12 @@ public void generatePostconditionAssertionStatement(MethodNode method, org.apach
* @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;
+ boolean noPostconditionInHierarchy = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), method, ClassHelper.makeWithoutCaching(Postcondition.class)).isEmpty();
+ if (noPostconditionInHierarchy) 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);
+ final BooleanExpression postconditionBooleanExpression = addCallsToSuperMethodNodeAnnotationClosure(method.getDeclaringClass(), method, Postcondition.class, boolX(ConstantExpression.TRUE), true);
if (postconditionBooleanExpression.getExpression() == ConstantExpression.TRUE) return;
final BlockStatement blockStatement = wrapAssertionBooleanExpression(type, method, postconditionBooleanExpression, "postcondition");
@@ -118,14 +117,15 @@ public void generateDefaultPostconditionStatement(final ClassNode type, final Me
}
private void addPostcondition(MethodNode method, BlockStatement postconditionBlockStatement) {
+ if (Boolean.TRUE.equals(method.getNodeMetaData(METHOD_PROCESSED))) return;
final BlockStatement block = (BlockStatement) method.getCode();
- // if return type is not void, than a "result" variable is provided in the postcondition expression
final List statements = block.getStatements();
- if (statements.size() > 0) {
+ if (!statements.isEmpty()) {
Expression contractsEnabled = localVarX(BaseVisitor.GCONTRACTS_ENABLED_VAR, ClassHelper.boolean_TYPE);
if (!isPrimitiveVoid(method.getReturnType())) {
+ // if return type is not void, then a "result" variable is provided in the postcondition expression
List returnStatements = AssertStatementCreationUtility.getReturnStatements(method);
for (ReturnStatement returnStatement : returnStatements) {
@@ -143,6 +143,7 @@ private void addPostcondition(MethodNode method, BlockStatement postconditionBlo
setOldVariablesIfEnabled(block, contractsEnabled);
block.addStatements(postconditionBlockStatement.getStatements());
}
+ method.putNodeMetaData(METHOD_PROCESSED, true);
}
}
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
index 29b9db961eb..82c359069fe 100644
--- 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
@@ -34,6 +34,7 @@
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.io.ReaderSource;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
/**
@@ -58,7 +59,7 @@ public void generatePreconditionAssertionStatement(final MethodNode method, fina
BlockStatement blockStatement;
final BlockStatement originalBlockStatement = precondition.originalBlockStatement();
- // if use execution tracker flag is found in the meta-data the annotation closure visitor discovered
+ // if useExecutionTracker 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));
@@ -79,12 +80,10 @@ public void generatePreconditionAssertionStatement(final MethodNode method, fina
* @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) {
+ boolean noPreconditionInHierarchy = AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(), methodNode, ClassHelper.makeWithoutCaching(Precondition.class)).isEmpty();
+ if (noPreconditionInHierarchy) return;
- // 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
+ // if there is another precondition up the inheritance path, we need a default precondition with FALSE
// e.g. C1 : C2 - == false || item != null
BooleanExpression preconditionBooleanExpression = boolX(ConstantExpression.FALSE);
preconditionBooleanExpression = addCallsToSuperMethodNodeAnnotationClosure(type, methodNode, Precondition.class, preconditionBooleanExpression, false);
@@ -98,7 +97,7 @@ public void generateDefaultPreconditionStatement(final ClassNode type, final Met
}
private void addPrecondition(MethodNode method, BlockStatement blockStatement) {
- final BlockStatement modifiedMethodCode = new BlockStatement();
+ final BlockStatement modifiedMethodCode = block();
modifiedMethodCode.addStatements(blockStatement.getStatements());
if (method.getCode() instanceof BlockStatement) {
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
index b7685572674..84490205deb 100644
--- 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
@@ -88,8 +88,7 @@ public static BlockStatement generateTryCatchBlock(final ClassNode assertionErro
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 overallBlock = block(declS(variableExpression, ConstantExpression.FALSE));
final BlockStatement assertBlockStatement = block(
assertStatement,
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
index ef44b7cc81c..7743aa33f84 100644
--- 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
@@ -26,8 +26,9 @@
import org.codehaus.groovy.ast.MethodNode;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
/**
*
Helper methods for reading/getting {@link org.codehaus.groovy.ast.AnnotationNode} instances.
@@ -61,9 +62,9 @@ public static boolean hasAnnotationOfType(AnnotatedNode annotatedNode, String ty
* @return the next {@link org.codehaus.groovy.ast.AnnotationNode} in the inheritance line, or null
*/
public static List getAnnotationNodeInHierarchyWithMetaAnnotation(ClassNode type, ClassNode anno) {
- List result = new ArrayList();
+ List result = new ArrayList<>();
for (AnnotationNode annotation : type.getAnnotations()) {
- if (annotation.getClassNode().getAnnotations(anno).size() > 0) {
+ if (!annotation.getClassNode().getAnnotations(anno).isEmpty()) {
result.add(annotation);
}
}
@@ -85,18 +86,18 @@ public static List getAnnotationNodeInHierarchyWithMetaAnnotatio
* @return a list of {@link AnnotationNode} all annotated with metaAnnotationClassNode
*/
public static List getAnnotationNodeInHierarchyWithMetaAnnotation(ClassNode type, MethodNode originMethodNode, ClassNode metaAnnotationClassNode) {
- List result = new ArrayList();
+ List result = new ArrayList<>();
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) {
+ if (!annotation.getClassNode().getAnnotations(metaAnnotationClassNode).isEmpty()) {
result.add(annotation);
}
}
- if (result.size() > 0) return result;
+ if (!result.isEmpty()) return result;
}
type = type.getSuperClass();
@@ -115,7 +116,7 @@ public static List getAnnotationNodeInHierarchyWithMetaAnnotatio
*/
public static List hasMetaAnnotations(AnnotatedNode annotatedNode, String metaAnnotationClassName) {
List result = new ArrayList<>();
- Set seen = new java.util.HashSet<>();
+ Map seen = new HashMap<>();
ClassNode type = ClassHelper.makeWithoutCaching(metaAnnotationClassName);
for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) {
@@ -126,17 +127,23 @@ public static List hasMetaAnnotations(AnnotatedNode annotatedNod
return result;
}
- private static boolean hasMetaAnnotation(ClassNode annotationType, ClassNode metaAnnotationType, Set cycleCheck) {
- if (!CandidateChecks.isRuntimeClass(annotationType) && cycleCheck.add(annotationType)) {
+ private static boolean hasMetaAnnotation(ClassNode annotationType, ClassNode metaAnnotationType, Map cache) {
+ if (CandidateChecks.isRuntimeClass(annotationType)) return false;
+ if (!cache.containsKey(annotationType)) {
+ boolean result = false;
if (!annotationType.getAnnotations(metaAnnotationType).isEmpty()) {
- return true;
- }
- for (AnnotationNode annotationNode : annotationType.getAnnotations()) {
- if (hasMetaAnnotation(annotationNode.getClassNode(), metaAnnotationType, cycleCheck)) {
- return true;
+ result = true;
+ } else {
+ cache.put(annotationType, false); // preliminary value to avoid cycles
+ for (AnnotationNode annotationNode : annotationType.getAnnotations()) {
+ if (hasMetaAnnotation(annotationNode.getClassNode(), metaAnnotationType, cache)) {
+ result = true;
+ break;
+ }
}
}
+ cache.put(annotationType, result);
}
- return false;
+ return cache.get(annotationType);
}
}
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
index fafe349c4ce..749524e72a4 100644
--- 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
@@ -33,8 +33,7 @@
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.andX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
/**
@@ -43,7 +42,7 @@
* @see ClosureExpression
* @see BooleanExpression
*/
-public class ExpressionUtils {
+public final class ExpressionUtils {
private ExpressionUtils() { }
/**
@@ -115,7 +114,7 @@ public static BooleanExpression getBooleanExpression(List boo
if (result == null) {
result = booleanExpression;
} else {
- result = boolX(binX(result, AND, booleanExpression));
+ result = boolX(andX(result, booleanExpression));
}
}
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
index 8f2ca57288b..66ec5e7d6e1 100644
--- 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
@@ -214,6 +214,6 @@ public void remove() {
* loader.
*/
public static LifecycleImplementationLoader load(Class service, ClassLoader loader) {
- return new LifecycleImplementationLoader(service, loader);
+ return new LifecycleImplementationLoader<>(service, loader);
}
}
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
index 534ca2dd796..8bf8c08eecc 100644
--- 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
@@ -22,14 +22,14 @@ 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.codehaus.groovy.ast.tools.GeneralUtils.block
+import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
@@ -43,7 +43,6 @@ class ContractTests {
public void setUp() {
def source = '''
class Tester {
-
void some_method() {}
}
'''
@@ -60,7 +59,7 @@ class ContractTests {
void create_simple_contract() {
Contract contract = new Contract(classNode)
- Precondition precondition = new Precondition(new BlockStatement(), new BooleanExpression(new ConstantExpression(true)))
+ Precondition precondition = new Precondition(block(), boolX(constX(true)))
contract.preconditions().or(classNode.getMethod("some_method", [] as Parameter[]), precondition)
assertEquals(1, contract.preconditions().size())
@@ -71,8 +70,8 @@ class ContractTests {
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)))
+ Precondition precondition1 = new Precondition(block(), boolX(constX(true)))
+ Precondition precondition2 = new Precondition(block(), boolX(constX(true)))
contract.preconditions().or(methodNode, precondition1)
contract.preconditions().or(methodNode, precondition2)
@@ -86,8 +85,8 @@ class ContractTests {
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)
+ Postcondition postcondition = new Postcondition(block(), boolX(constX(true)), false)
+ Postcondition postcondition1 = new Postcondition(block(), boolX(constX(true)), false)
contract.postconditions().and(methodNode, postcondition)
contract.postconditions().and(methodNode, postcondition1)
@@ -101,8 +100,8 @@ class ContractTests {
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)))
+ Precondition precondition1 = new Precondition(block(), boolX(constX(true)))
+ Precondition precondition2 = new Precondition(block(), boolX(constX(true)))
contract.preconditions().join(methodNode, precondition1)
contract.preconditions().join(methodNode, precondition2)
diff --git a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/pre/SimplePreconditionTests.groovy b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/pre/SimplePreconditionTests.groovy
index ca4adeed3a1..7dd1c155c21 100644
--- a/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/pre/SimplePreconditionTests.groovy
+++ b/subprojects/groovy-contracts/src/test/groovy/org/apache/groovy/contracts/tests/pre/SimplePreconditionTests.groovy
@@ -18,6 +18,7 @@
*/
package org.apache.groovy.contracts.tests.pre
+import org.apache.groovy.contracts.ClassInvariantViolation
import org.apache.groovy.contracts.PreconditionViolation
import org.apache.groovy.contracts.tests.basic.BaseTestClass
import org.junit.Test
@@ -433,6 +434,52 @@ class Account
create_instance_of(source, ['test', 'test'])
}
+ @Test
+ void requires_on_constructor_with_params_instance_vars_same_name_and_this_expression_multiple_annotations() {
+
+ def source = """
+ import groovy.contracts.*
+
+ @Invariant({ a == b })
+ @Invariant({ a != 'foo' })
+ class A {
+ private final String a
+ private final String b
+
+ @Requires({ a != null })
+ @Requires({ b != null })
+ @Requires({ this.a == null })
+ @Requires({ this.b == null })
+ @Ensures({ this.a == a })
+ @Ensures({ this.b == b })
+ A(String a, String b) {
+ this.a = a
+ this.b = b
+ }
+ }
+ """
+
+ create_instance_of(source, ['test', 'test'])
+
+ shouldFail PreconditionViolation, {
+ create_instance_of(source, [null, null])
+ }
+ shouldFail PreconditionViolation, {
+ create_instance_of(source, ['a', null])
+ }
+ shouldFail PreconditionViolation, {
+ create_instance_of(source, [null, 'b'])
+ }
+
+ shouldFail ClassInvariantViolation, {
+ create_instance_of(source, ['foo', 'foo'])
+ }
+ shouldFail ClassInvariantViolation, {
+ create_instance_of(source, ['a', 'b'])
+ }
+
+ }
+
@Test
void requires_with_default_parameters() {
@@ -466,4 +513,4 @@ class Account
def a = create_instance_of(source)
a.m(null)
}
-}
\ No newline at end of file
+}