This is an automated email from the ASF dual-hosted git repository.
emilles 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 95f36bc52c `groovy-contracts`: refactor attribute replacement
95f36bc52c is described below
commit 95f36bc52cdca47ca7722e45aabb57152dcc86f7
Author: Eric Milles <[email protected]>
AuthorDate: Mon Aug 14 15:33:45 2023 -0500
`groovy-contracts`: refactor attribute replacement
---
.../groovy/classgen/AsmClassGenerator.java | 22 +--
.../classgen/asm/WriterControllerFactory.java | 1 +
.../ast/visitor/AnnotationClosureVisitor.java | 117 +++++++---------
.../ast/visitor/AnnotationProcessorVisitor.java | 154 ++++++++-------------
.../groovy/contracts/ast/visitor/BaseVisitor.java | 51 ++++++-
.../classgen/asm/ContractClosureWriter.java | 49 +++----
.../groovy/contracts/generation/BaseGenerator.java | 95 +++++--------
.../generation/ClassInvariantGenerator.java | 32 +----
.../main/resources/dsld/contract_transform.dsld | 4 +-
9 files changed, 221 insertions(+), 304 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index d56efbfd25..dcba562e5f 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -1093,28 +1093,28 @@ public class AsmClassGenerator extends ClassGenerator {
MethodVisitor mv = controller.getMethodVisitor();
mv.visitVarInsn(ALOAD, 0);
- ClassNode iterType = classNode;
- while (!iterType.equals(type)) {
- String ownerName =
BytecodeHelper.getClassInternalName(iterType);
- if (iterType.getOuterClass() == null) break;
- FieldNode thisField = iterType.getField("this$0");
- iterType = iterType.getOuterClass();
+ ClassNode thisType = classNode;
+ while (!thisType.equals(type)) {
+ if (thisType.getOuterClass() == null) break;
+ String ownerName =
BytecodeHelper.getClassInternalName(thisType);
+ FieldNode thisField = thisType.getField("this$0");
+ thisType = thisType.getOuterClass();
if (thisField == null) {
// closure within inner class
- while (ClassHelper.isGeneratedFunction(iterType)) {
+ while (ClassHelper.isGeneratedFunction(thisType)) {
// GROOVY-8881: cater for closures within closures -
getThisObject is already outer class of all closures
- iterType = iterType.getOuterClass();
+ thisType = thisType.getOuterClass();
}
mv.visitMethodInsn(INVOKEVIRTUAL,
BytecodeHelper.getClassInternalName(ClassHelper.CLOSURE_TYPE), "getThisObject",
"()Ljava/lang/Object;", false);
- mv.visitTypeInsn(CHECKCAST,
BytecodeHelper.getClassInternalName(iterType));
+ mv.visitTypeInsn(CHECKCAST,
BytecodeHelper.getClassInternalName(thisType));
} else {
ClassNode thisFieldType = thisField.getType();
if (ClassHelper.CLOSURE_TYPE.equals(thisFieldType)) {
mv.visitFieldInsn(GETFIELD, ownerName, "this$0",
BytecodeHelper.getTypeDescription(ClassHelper.CLOSURE_TYPE));
mv.visitMethodInsn(INVOKEVIRTUAL,
BytecodeHelper.getClassInternalName(ClassHelper.CLOSURE_TYPE), "getThisObject",
"()Ljava/lang/Object;", false);
- mv.visitTypeInsn(CHECKCAST,
BytecodeHelper.getClassInternalName(iterType));
+ mv.visitTypeInsn(CHECKCAST,
BytecodeHelper.getClassInternalName(thisType));
} else {
- String typeName =
BytecodeHelper.getTypeDescription(iterType);
+ String typeName =
BytecodeHelper.getTypeDescription(thisType);
mv.visitFieldInsn(GETFIELD, ownerName, "this$0",
typeName);
}
}
diff --git
a/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
b/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
index 9d683edc07..eddc1fb1b5 100644
---
a/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
+++
b/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.classgen.asm;
/**
* A non-static factory to get alternative writer controller to be stored in
the metadata
*/
+@FunctionalInterface
public interface WriterControllerFactory {
WriterController makeController(WriterController normalController);
}
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 4f6bcb05c2..5ff5d506f9 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
@@ -39,7 +39,6 @@ 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;
@@ -49,7 +48,6 @@ 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;
@@ -105,74 +103,63 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
super(sourceUnit, source);
}
+
//--------------------------------------------------------------------------
+
@Override
- public void visitClass(ClassNode node) {
- if (node == null) return;
- if (!(CandidateChecks.isInterfaceContractsCandidate(node) ||
CandidateChecks.isContractsCandidate(node)))
- return;
+ public void visitClass(ClassNode classNode) {
+ if (classNode == null ||
!(CandidateChecks.isInterfaceContractsCandidate(classNode) ||
CandidateChecks.isContractsCandidate(classNode))) return;
- classNode = node;
+ this.classNode = classNode;
- if (classNode.getNodeMetaData(PROCESSED) == null &&
CandidateChecks.isContractsCandidate(node)) {
- final List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(node, ContractElement.class.getName());
+ if (classNode.getNodeMetaData(PROCESSED) == null &&
CandidateChecks.isContractsCandidate(classNode)) {
+ List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(classNode, 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;
+ ClosureExpression closureExpression =
getOriginalCondition(annotationNode);
+ if (closureExpression == null) continue;
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);
+ 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(closureExpression.getCode());
-
newClosureBlockStatement.setSourcePosition(closureBlockStatement);
-
- ClosureExpression rewrittenClosureExpression = new
ClosureExpression(parameters.toArray(Parameter.EMPTY_ARRAY),
newClosureBlockStatement);
-
rewrittenClosureExpression.setSourcePosition(closureExpression);
+ ClosureExpression rewrittenClosureExpression = new
ClosureExpression(closureExpression.getParameters(), newClosureBlockStatement);
rewrittenClosureExpression.setDeclaringClass(closureExpression.getDeclaringClass());
+
rewrittenClosureExpression.setSourcePosition(closureExpression);
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(
+ BlockStatement block =
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);
+ block.setNodeMetaData(META_DATA_USE_EXECUTION_TRACKER,
validator.isMethodCalls());
+ Expression value =
classX(closureClassNode.getPlainNodeReference());
+ value.setNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK,
block);
+ value.setSourcePosition(annotationNode);
+ replaceCondition(annotationNode, value);
markClosureReplaced(classNode);
}
}
- super.visitClass(node);
+ super.visitClass(classNode);
// generate closure classes for the super class and all implemented
interfaces
- visitClass(node.getSuperClass());
- for (ClassNode i : node.getInterfaces()) {
+ visitClass(classNode.getSuperClass());
+ for (ClassNode i : classNode.getInterfaces()) {
visitClass(i);
}
@@ -181,11 +168,9 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
@Override
public void visitConstructorOrMethod(MethodNode methodNode, boolean
isConstructor) {
- if (!CandidateChecks.couldBeContractElementMethodNode(classNode,
methodNode) && !(CandidateChecks.isPreconditionCandidate(classNode,
methodNode)))
- return;
- if (methodNode.getNodeMetaData(PROCESSED) != null) return;
+ if (methodNode.getNodeMetaData(PROCESSED) != null ||
!(CandidateChecks.couldBeContractElementMethodNode(classNode, methodNode) ||
CandidateChecks.isPreconditionCandidate(classNode, methodNode))) return;
- final List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(methodNode, ContractElement.class.getName());
+ List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(methodNode, ContractElement.class.getName());
for (AnnotationNode annotationNode : annotationNodes) {
replaceWithClosureClassReference(annotationNode, methodNode);
}
@@ -199,13 +184,9 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
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());
+ ClosureExpression closureExpression =
getOriginalCondition(annotationNode);
+ if (closureExpression == null) return;
- 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);
@@ -215,52 +196,46 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
validator.secondPass(closureExpression);
List<Parameter> parameters = new
ArrayList<>(Arrays.asList(closureExpression.getParameters()));
+ parameters.addAll(Arrays.asList(methodNode.getParameters()));
- parameters.addAll(new
ArrayList<>(Arrays.asList(methodNode.getParameters())));
-
- final List<BooleanExpression> booleanExpressions =
ExpressionUtils.getBooleanExpression(closureExpression);
+ List<BooleanExpression> booleanExpressions =
ExpressionUtils.getBooleanExpression(closureExpression);
if (booleanExpressions == null || booleanExpressions.isEmpty()) return;
- BlockStatement closureBlockStatement = (BlockStatement)
closureExpression.getCode();
+ boolean isConstructor = methodNode.isConstructor();
+ boolean isPostcondition =
AnnotationUtils.hasAnnotationOfType(annotationNode.getClassNode(),
POSTCONDITION_TYPE_NAME);
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);
+
newClosureBlockStatement.setSourcePosition(closureExpression.getCode());
ClosureExpression rewrittenClosureExpression = new
ClosureExpression(parameters.toArray(Parameter.EMPTY_ARRAY),
newClosureBlockStatement);
- rewrittenClosureExpression.setSourcePosition(closureExpression);
rewrittenClosureExpression.setDeclaringClass(closureExpression.getDeclaringClass());
+ rewrittenClosureExpression.setSourcePosition(closureExpression);
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(
+ BlockStatement block =
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);
+ block.setNodeMetaData(META_DATA_USE_EXECUTION_TRACKER,
validator.isMethodCalls());
+ Expression value = classX(closureClassNode.getPlainNodeReference());
+ value.setNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK, block);
+ value.setSourcePosition(annotationNode);
+ replaceCondition(annotationNode, 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)
+ if (variableScope == null || methodNode == null ||
methodNode.getParameters() == null || methodNode.getParameters().length == 0)
return variableScope;
VariableScope copy = copy(variableScope);
@@ -291,15 +266,17 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
return copy;
}
+ private void markClosureReplaced(ASTNode someNode) {
+ if (someNode.getNodeMetaData(CLOSURE_REPLACED) == null)
+ someNode.setNodeMetaData(CLOSURE_REPLACED, Boolean.TRUE);
+ }
+
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 {
@@ -490,7 +467,7 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
private static class OldPropertyExpressionTransformer extends
ClassCodeExpressionTransformer {
private final MethodNode methodNode;
- private CastExpression currentCast = null;
+ private CastExpression currentCast;
public OldPropertyExpressionTransformer(MethodNode methodNode) {
this.methodNode = methodNode;
@@ -531,4 +508,4 @@ public class AnnotationClosureVisitor extends BaseVisitor
implements ASTNodeMeta
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/AnnotationProcessorVisitor.java
b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java
index b09df6191c..b23e38ab96 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
@@ -19,6 +19,7 @@
package org.apache.groovy.contracts.ast.visitor;
import org.apache.groovy.contracts.annotations.meta.ContractElement;
+import org.apache.groovy.contracts.annotations.meta.Postcondition;
import org.apache.groovy.contracts.common.spi.AnnotationProcessor;
import org.apache.groovy.contracts.common.spi.ProcessingContextInformation;
import org.apache.groovy.contracts.generation.CandidateChecks;
@@ -26,31 +27,25 @@ 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.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.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.apache.groovy.contracts.ast.visitor.AnnotationClosureVisitor.META_DATA_ORIGINAL_TRY_CATCH_BLOCK;
import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
-import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
/**
* Visits annotations of meta-type {@link ContractElement} and applies the AST
transformations of the underlying
@@ -60,13 +55,11 @@ import static
org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
*/
public class AnnotationProcessorVisitor extends BaseVisitor {
- private static final String DO_CALL = "doCall";
private ProcessingContextInformation pci;
public AnnotationProcessorVisitor(final SourceUnit sourceUnit, final
ReaderSource source, final ProcessingContextInformation pci) {
super(sourceUnit, source);
Validate.notNull(pci);
-
this.pci = pci;
}
@@ -79,10 +72,9 @@ public class AnnotationProcessorVisitor extends BaseVisitor {
methodNodes.addAll(type.getDeclaredConstructors());
for (MethodNode methodNode : methodNodes) {
- if (!CandidateChecks.isClassInvariantCandidate(type, methodNode)
&& !CandidateChecks.isPreOrPostconditionCandidate(type, methodNode))
- continue;
-
- handleMethodAnnotations(methodNode,
AnnotationUtils.hasMetaAnnotations(methodNode,
ContractElement.class.getName()));
+ if (CandidateChecks.isClassInvariantCandidate(type, methodNode) ||
CandidateChecks.isPreOrPostconditionCandidate(type, methodNode)) {
+ handleMethodAnnotations(methodNode,
AnnotationUtils.hasMetaAnnotations(methodNode,
ContractElement.class.getName()));
+ }
}
// visit all interfaces of this class
@@ -91,23 +83,18 @@ public class AnnotationProcessorVisitor extends BaseVisitor
{
}
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<>(interfaceClassNode.getMethods());
-
- for (MethodNode interfaceMethodNode : methodNodes) {
- final List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(interfaceMethodNode,
ContractElement.class.getName());
+ if (superClass == null || !superClass.isAbstract()) return;
+ for (ClassNode interfaceNode : superClass.getInterfaces()) {
+ List<MethodNode> interfaceMethods = new
ArrayList<>(interfaceNode.getMethods());
+ for (MethodNode interfaceMethod : interfaceMethods) {
+ List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(interfaceMethod,
ContractElement.class.getName());
if (annotationNodes == null || annotationNodes.isEmpty())
continue;
- MethodNode implementingMethodNode =
superClass.getMethod(interfaceMethodNode.getName(),
interfaceMethodNode.getParameters());
+ MethodNode implementingMethod =
superClass.getMethod(interfaceMethod.getName(),
interfaceMethod.getParameters());
+ // if implementingMethodNode == null, then superClass is
abstract and does not implement the interface methodNode
+ if (implementingMethod != null) continue;
- // 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());
+ MethodNode implementationInOriginClassNode =
origin.getMethod(interfaceMethod.getName(), interfaceMethod.getParameters());
if (implementationInOriginClassNode == null) continue;
handleMethodAnnotations(implementationInOriginClassNode,
annotationNodes);
@@ -116,41 +103,29 @@ public class AnnotationProcessorVisitor extends
BaseVisitor {
}
private void visitInterfaces(final ClassNode classNode, final ClassNode[]
interfaces) {
- for (ClassNode interfaceClassNode : interfaces) {
- List<MethodNode> methodNodes = new
ArrayList<>(interfaceClassNode.getMethods());
-
+ for (ClassNode interfaceNode : interfaces) {
+ List<MethodNode> interfaceMethods = new
ArrayList<>(interfaceNode.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;
+ for (MethodNode interfaceMethod : interfaceMethods) {
+ MethodNode implementingMethod =
classNode.getMethod(interfaceMethod.getName(), interfaceMethod.getParameters());
+ if (implementingMethod == null) continue;
- final List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(interfaceMethodNode,
ContractElement.class.getName());
- handleInterfaceMethodNode(classNode, implementingMethodNode,
annotationNodes);
+ List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(interfaceMethod,
ContractElement.class.getName());
+ handleInterfaceMethodNode(classNode, implementingMethod,
annotationNodes);
}
- visitInterfaces(classNode, interfaceClassNode.getInterfaces());
+ visitInterfaces(classNode, interfaceNode.getInterfaces());
}
}
private void handleClassNode(final ClassNode classNode) {
- final List<AnnotationNode> annotationNodes =
AnnotationUtils.hasMetaAnnotations(classNode, ContractElement.class.getName());
-
+ 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)),
- DO_CALL
- );
-
doCall.setMethodTarget(closureClassExpression.getType().getMethods(DO_CALL).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);
+ AnnotationProcessor annotationProcessor =
createAnnotationProcessor(annotationNode);
+ if (annotationProcessor != null) {
+ Expression valueExpression =
getReplacedCondition(annotationNode);
+ BlockStatement blockStatement =
valueExpression.getNodeMetaData(META_DATA_ORIGINAL_TRY_CATCH_BLOCK);
+ annotationProcessor.process(pci, pci.contract(), classNode,
blockStatement, asConditionExecution(annotationNode));
}
}
}
@@ -162,73 +137,58 @@ public class AnnotationProcessorVisitor extends
BaseVisitor {
private void handleMethodAnnotations(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) {
+ AnnotationProcessor annotationProcessor =
createAnnotationProcessor(annotationNode);
+ if (annotationProcessor != null &&
getReplacedCondition(annotationNode) != null) {
handleMethodAnnotation(methodNode, annotationNode,
annotationProcessor);
}
}
}
private void handleMethodAnnotation(MethodNode methodNode, AnnotationNode
annotationNode, AnnotationProcessor annotationProcessor) {
- 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();
+ boolean isPostcondition =
AnnotationUtils.hasAnnotationOfType(annotationNode.getClassNode(),
Postcondition.class.getName());
+ ArgumentListExpression argumentList = new ArgumentListExpression();
for (Parameter parameter : methodNode.getParameters()) {
- closureArgumentList.addExpression(varX(parameter));
+ argumentList.addExpression(varX(parameter));
}
-
- if (!isPrimitiveVoid(methodNode.getReturnType()) && isPostcondition &&
!(methodNode instanceof ConstructorNode)) {
- closureArgumentList.addExpression(localVarX("result",
methodNode.getReturnType()));
+ if (isPostcondition && !methodNode.isVoidMethod()) {
+ argumentList.addExpression(localVarX("result",
methodNode.getReturnType()));
}
-
- if (isPostcondition && !(methodNode instanceof ConstructorNode)) {
- closureArgumentList.addExpression(localVarX("old", new
ClassNode(Map.class)));
+ if (isPostcondition && !methodNode.isConstructor()) {
+ argumentList.addExpression(localVarX("old",
ClassHelper.MAP_TYPE.getPlainNodeReference()));
}
- MethodCallExpression doCall = callX(
-
ctorX(annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME).getType(),
args(VariableExpression.THIS_EXPRESSION, VariableExpression.THIS_EXPRESSION)),
- DO_CALL,
- closureArgumentList
- );
- ClassNode type =
annotationNode.getMember(CLOSURE_ATTRIBUTE_NAME).getType();
- doCall.setMethodTarget(type.getMethods(DO_CALL).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);
+ Expression valueExpression = getReplacedCondition(annotationNode);
+ 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 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);
+ AnnotationNode markerAnnotation = new
AnnotationNode(annotationNode.getClassNode());
+ replaceCondition(markerAnnotation, valueExpression);
+ markerAnnotation.setRuntimeRetention(true);
+ markerAnnotation.setSourceRetention(false);
+ methodNode.addAnnotation(markerAnnotation);
}
}
private AnnotationProcessor createAnnotationProcessor(AnnotationNode
annotationNode) {
- ClassExpression annotationProcessingAnno = null;
+ Expression annotationProcessor = null;
- List<AnnotationNode> annotations =
annotationNode.getClassNode().redirect().getAnnotations();
+ List<AnnotationNode> annotations =
annotationNode.getClassNode().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");
+ if
(anno.getClassNode().getName().equals("org.apache.groovy.contracts.annotations.meta.AnnotationProcessorImplementation"))
{
+ annotationProcessor = anno.getMember("value");
break;
}
}
- if (annotationProcessingAnno != null) {
+ if (annotationProcessor != null) {
try {
- final Class<?> clz =
Class.forName(annotationProcessingAnno.getType().getTypeClass().getName());
- return (AnnotationProcessor)
clz.getDeclaredConstructor().newInstance();
+ var apt =
Class.forName(annotationProcessor.getType().getName());
+ return (AnnotationProcessor)
apt.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException |
ClassNotFoundException | NoSuchMethodException | InvocationTargetException
ignore) {
}
}
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 f620378cb3..2337cdd1ef 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
@@ -18,10 +18,23 @@
*/
package org.apache.groovy.contracts.ast.visitor;
+import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+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.control.SourceUnit;
import org.codehaus.groovy.control.io.ReaderSource;
+import java.util.Objects;
+
+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;
+
/**
* <p>
* Base class for {@link org.codehaus.groovy.ast.ClassCodeVisitorSupport}
descendants. This class is used in groovy-contracts
@@ -34,18 +47,44 @@ public abstract class BaseVisitor extends
ClassCodeVisitorSupport {
public static final String GCONTRACTS_ENABLED_VAR = "$GCONTRACTS_ENABLED";
- public static final String CLOSURE_ATTRIBUTE_NAME = "value";
+ protected final SourceUnit sourceUnit;
- protected SourceUnit sourceUnit;
- protected ReaderSource source;
+ @Override
+ protected SourceUnit getSourceUnit() {
+ return sourceUnit;
+ }
public BaseVisitor(final SourceUnit sourceUnit, final ReaderSource source)
{
this.sourceUnit = sourceUnit;
- this.source = source;
}
- @Override
- protected SourceUnit getSourceUnit() {
+ 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");
+ doCall.setMethodTarget(conditionClass.getMethods("doCall").get(0));
+ BooleanExpression asBoolean = boolX(doCall);
+ asBoolean.setSourcePosition(annotation);
+ return asBoolean;
+ }
+
+ protected static ClosureExpression getOriginalCondition(final
AnnotationNode annotation) {
+ Expression value = annotation.getMember("value");
+ if (value instanceof ClosureExpression) {
+ return (ClosureExpression) value;
+ }
return null;
}
+
+ protected static /*???*/Expression getReplacedCondition(final
AnnotationNode annotation) {
+ Expression value = annotation.getMember("value");
+ if (!(value instanceof ClosureExpression)) {
+ return value;
+ }
+ return null;
+ }
+
+ protected static void replaceCondition(final AnnotationNode node, final
Expression expr) {
+ node.setMember("value", Objects.requireNonNull(expr));
+ }
}
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 1b32e0281c..9bc6ba4e6c 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
@@ -18,7 +18,6 @@
*/
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;
@@ -30,7 +29,6 @@ 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;
@@ -39,14 +37,13 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
+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.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.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -78,26 +75,23 @@ public class ContractClosureWriter {
}
// contains all params of the original method
- ArrayList<Parameter> closureParameters = new ArrayList<Parameter>();
+ List<Parameter> closureParameters = new ArrayList<>();
for (Parameter param : parametersTemp) {
- Parameter closureParameter = new
Parameter(param.getType().getPlainNodeReference(), param.getName());
- closureParameters.add(closureParameter);
+ closureParameters.add(new
Parameter(param.getType().getPlainNodeReference(), param.getName()));
}
ClassNode answer = new ClassNode(name, mods | ACC_FINAL,
ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
- answer.setSynthetic(true);
answer.setSourcePosition(expression);
+ answer.setSynthetic(true);
- MethodNode method =
- answer.addMethod("doCall", ACC_PUBLIC,
ClassHelper.Boolean_TYPE, closureParameters.toArray(new
Parameter[closureParameters.size()]), ClassNode.EMPTY_ARRAY,
expression.getCode());
- method.setSourcePosition(expression);
+ MethodNode doCall = answer.addMethod("doCall", ACC_PUBLIC,
ClassHelper.Boolean_TYPE, closureParameters.toArray(Parameter.EMPTY_ARRAY),
ClassNode.EMPTY_ARRAY, expression.getCode());
+ doCall.setSourcePosition(expression);
VariableScope varScope = expression.getVariableScope();
if (varScope == null) {
- throw new RuntimeException(
- "Must have a VariableScope by now! for expression: " +
expression + " class: " + name);
+ throw new RuntimeException("Must have a VariableScope by now! for
expression: " + expression + " class: " + name);
} else {
- method.setVariableScope(varScope.copy());
+ doCall.setVariableScope(varScope.copy());
}
// let's add a typesafe call method
@@ -110,10 +104,10 @@ public class ContractClosureWriter {
"call",
ACC_PUBLIC,
ClassHelper.Boolean_TYPE,
- closureParameters.toArray(new
Parameter[closureParameters.size()]),
+ closureParameters.toArray(Parameter.EMPTY_ARRAY),
ClassNode.EMPTY_ARRAY,
- returnS(callThisX("doCall", arguments)));
-
+ returnS(callThisX("doCall", arguments))
+ );
call.setSourcePosition(expression);
call.setSynthetic(true);
@@ -127,16 +121,15 @@ public class ContractClosureWriter {
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"));
+ block.addStatement(stmt(ctorSuperX(args(outer, thisObject))));
- ASTNode sn = answer.addConstructor(ACC_PUBLIC, consParams,
ClassNode.EMPTY_ARRAY, block);
- sn.setSourcePosition(expression);
- correctAccessedVariable(method, expression);
+ Parameter[] ctorParams = {
+ new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance"),
+ new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject")
+ };
+ var ctor = answer.addConstructor(ACC_PUBLIC, ctorParams,
ClassNode.EMPTY_ARRAY, block);
+ ctor.setSourcePosition(expression);
+ correctAccessedVariable(doCall, expression);
return answer;
}
@@ -151,7 +144,7 @@ public class ContractClosureWriter {
return outermostClass;
}
- private void correctAccessedVariable(final MethodNode methodNode,
ClosureExpression ce) {
+ private void correctAccessedVariable(MethodNode methodNode,
ClosureExpression ce) {
CodeVisitorSupport visitor = new CodeVisitorSupport() {
@Override
public void visitVariableExpression(VariableExpression expression)
{
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 84bb65a458..718d8d5271 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
@@ -25,33 +25,24 @@ 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.andX;
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;
@@ -61,10 +52,10 @@ 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.orX;
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;
-import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
/**
* Base class for groovy-contracts code generators.
@@ -97,19 +88,18 @@ public abstract class BaseGenerator {
}
protected BlockStatement getInlineModeBlockStatement(BlockStatement
blockStatement) {
- final BooleanExpression combinedBooleanExpression =
ExpressionUtils.getBooleanExpression(ExpressionUtils.getBooleanExpressionsFromAssertionStatements(blockStatement));
+ 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);
+ ClassNode violationTrackerClassNode =
ClassHelper.makeWithoutCaching(ViolationTracker.class);
+ VariableExpression $_gc_result = varX("$_gc_result",
ClassHelper.boolean_TYPE);
$_gc_result.setAccessedVariable($_gc_result);
- final BlockStatement ifBlockStatement = block(
+ BlockStatement ifBlockStatement = block(
declS($_gc_result, ConstantExpression.FALSE),
stmt(callX(classX(violationTrackerClassNode), "init")),
assignS($_gc_result, classInvariantExpression),
@@ -124,7 +114,7 @@ public abstract class BaseGenerator {
)
);
- final TryCatchStatement lockTryCatchStatement = tryCatchS(
+ 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)),
@@ -139,55 +129,32 @@ public abstract class BaseGenerator {
// 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));
+ List<AnnotationNode> contractElementAnnotations =
AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(),
methodNode, ClassHelper.makeWithoutCaching(annotationType));
+ if (contractElementAnnotations.isEmpty()) {
+ methodNode.putNodeMetaData(META_DATA_USE_INLINE_MODE,
Boolean.TRUE);
+ } else {
+ for (AnnotationNode contractElementAnnotation :
contractElementAnnotations) {
+ ArgumentListExpression argumentList = new
ArgumentListExpression();
+ for (Parameter parameter : methodNode.getParameters()) {
+ argumentList.addExpression(varX(parameter));
+ }
+ if (isPostcondition && !methodNode.isVoidMethod()) {
+ argumentList.addExpression(localVarX("result",
methodNode.getReturnType()));
+ }
+ if (isPostcondition && !methodNode.isConstructor()) {
+ argumentList.addExpression(localVarX("old",
ClassHelper.MAP_TYPE.getPlainNodeReference()));
+ }
+
+ BooleanExpression predicate =
BaseVisitor.asConditionExecution(contractElementAnnotation);
+ ((MethodCallExpression)
predicate.getExpression()).setArguments(argumentList);
+
+ if (isPostcondition) {
+ booleanExpression = boolX(andX(booleanExpression,
predicate));
+ } else {
+ booleanExpression = boolX(orX(booleanExpression,
predicate));
+ }
}
-
- if (isPostcondition &&
!isPrimitiveVoid(methodNode.getReturnType()) && !(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.OBJECT_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/ClassInvariantGenerator.java
b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java
index a2d8f145b7..3bb7ebabb7 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
@@ -29,9 +29,6 @@ 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;
@@ -41,15 +38,11 @@ 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.ClassHelper.isPrimitiveVoid;
+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.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;
-import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
/**
* <p>
@@ -84,25 +77,10 @@ public class ClassInvariantGenerator extends BaseGenerator {
}
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));
+ List<AnnotationNode> contractElementAnnotations =
AnnotationUtils.getAnnotationNodeInHierarchyWithMetaAnnotation(type.getSuperClass(),
ClassHelper.makeWithoutCaching(annotationType));
+ for (AnnotationNode contractElementAnnotation :
contractElementAnnotations) {
+ booleanExpression = boolX(andX(booleanExpression,
BaseVisitor.asConditionExecution(contractElementAnnotation)));
}
-
return booleanExpression;
}
diff --git
a/subprojects/groovy-contracts/src/main/resources/dsld/contract_transform.dsld
b/subprojects/groovy-contracts/src/main/resources/dsld/contract_transform.dsld
index d219ff5f75..7948ca6201 100644
---
a/subprojects/groovy-contracts/src/main/resources/dsld/contract_transform.dsld
+++
b/subprojects/groovy-contracts/src/main/resources/dsld/contract_transform.dsld
@@ -73,7 +73,9 @@ contribute(bind(closure: inClosure()) & isThisType() &
bind(methods: enclosingMe
for (Parameter parameter : methods[0].parameters) {
property name: parameter.name, type: parameter.type, readOnly: true
}
- property name: 'old', type: Map, readOnly: true, doc: 'Property values
before the method call.'
+ if (!methods[0].isConstructor()) {
+ property name: 'old', type: Map, readOnly: true, doc: 'Property values
before the method call.'
+ }
if (!methods[0].isVoidMethod()) {
property name: 'result', type: methods[0].returnType, readOnly: true,
doc: 'Return value of the method call.'
}