You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/10/09 06:09:15 UTC
[2/4] groovy git commit: GROOVY-8347: @AutoFinal: Added support to
auto-finalize closure parameters (support more targets and inner classes)
GROOVY-8347: @AutoFinal: Added support to auto-finalize closure parameters (support more targets and inner classes)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/bb8f0319
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/bb8f0319
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/bb8f0319
Branch: refs/heads/GROOVY_2_6_X
Commit: bb8f0319f6008f1704d2d1dee7203a6318420438
Parents: f33e5db
Author: paulk <pa...@asert.com.au>
Authored: Sun Oct 8 23:43:39 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Mon Oct 9 16:08:57 2017 +1000
----------------------------------------------------------------------
src/main/groovy/transform/AutoFinal.java | 18 +--
.../transform/AutoFinalASTTransformation.java | 140 ++++++++++++++-----
2 files changed, 114 insertions(+), 44 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/bb8f0319/src/main/groovy/transform/AutoFinal.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/AutoFinal.java b/src/main/groovy/transform/AutoFinal.java
index 8fb5310..1cc0ab6 100644
--- a/src/main/groovy/transform/AutoFinal.java
+++ b/src/main/groovy/transform/AutoFinal.java
@@ -30,14 +30,16 @@ import java.lang.annotation.Target;
* Annotation to automatically add the final qualifier to method, constructor,
* and closure parameters.
* <p>The annotation can be placed at the class level in which case it applies to
- * all methods, constructors, and closures within the class, or on individual
- * methods or constructors.
- * <p>In general it will make the most sense to automatically apply the
- * annotation to all classes of a project
- * (groovyc --configscript; google "Customising The Groovy Compiler", or see {@link CompilerConfiguration}
- * so that one can be sure that all arguments will automatically be final,
+ * all methods, constructors, and closures within the class. It can also be applied
+ * to an individual method, constructor, field with a Closure initial value
+ * or a Closure assigned to a local variable.
+ * <p>If you wish to automatically apply the
+ * annotation to all classes of a project, consider using
+ * {@code groovyc --configscript}. Google "Customising The Groovy Compiler",
+ * or see {@link CompilerConfiguration} for further details.
+ * This will ensure that all arguments will automatically be final,
* completely eliminating the need to clutter the code with final keywords
- * in any paramete list.
+ * in any parameter list.
* <p>
* <em>Example usage:</em>
* <pre class="groovyTestCase">
@@ -80,7 +82,7 @@ import java.lang.annotation.Target;
*/
@java.lang.annotation.Documented
@Retention(RetentionPolicy.SOURCE)
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE})
@GroovyASTTransformationClass("org.codehaus.groovy.transform.AutoFinalASTTransformation")
public @interface AutoFinal {
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/bb8f0319/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java b/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
index 20e8d51..4055f72 100644
--- a/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
@@ -19,12 +19,23 @@
package org.codehaus.groovy.transform;
import groovy.transform.AutoFinal;
-import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import java.lang.reflect.Modifier;
+import java.util.Iterator;
import static org.codehaus.groovy.ast.ClassHelper.make;
@@ -37,72 +48,129 @@ public class AutoFinalASTTransformation extends AbstractASTTransformation {
private static final Class MY_CLASS = AutoFinal.class;
private static final ClassNode MY_TYPE = make(MY_CLASS);
private static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
+ private AnnotatedNode candidate;
+
public void visit(ASTNode[] nodes, SourceUnit source) {
init(nodes, source);
- processClassesConstructorsMethods(nodes, source);
- processClosures(nodes, source);
+ final ClassCodeVisitorSupport visitor = createVisitor();
+ process(nodes, visitor);
}
- private void processClassesConstructorsMethods(ASTNode[] nodes, final SourceUnit unit) {
- AnnotatedNode candidate = (AnnotatedNode) nodes[1];
+ private ClassCodeVisitorSupport createVisitor() {
+ return new ClassCodeVisitorSupport() {
+ @Override
+ public void visitClosureExpression(ClosureExpression expression) {
+ if (expression.isSynthetic()) {
+ return;
+ }
+ Parameter[] origParams = expression.getParameters();
+ for (Parameter p : origParams) {
+ p.setModifiers(p.getModifiers() | Modifier.FINAL);
+ }
+ super.visitClosureExpression(expression);
+ }
+
+ @Override
+ protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+ if (hasNoExplicitAutoFinal(node) || candidate == node) {
+ super.visitConstructorOrMethod(node, isConstructor);
+ }
+ }
+
+ @Override
+ public void visitField(FieldNode node) {
+ if (hasNoExplicitAutoFinal(node) || candidate == node) {
+ super.visitField(node);
+ }
+ }
+
+ @Override
+ public void visitDeclarationExpression(DeclarationExpression expr) {
+ if (hasNoExplicitAutoFinal(expr) || candidate == expr) {
+ super.visitDeclarationExpression(expr);
+ }
+ }
+
+ protected SourceUnit getSourceUnit() {
+ return sourceUnit;
+ }
+ };
+ }
+
+ private void process(ASTNode[] nodes, final ClassCodeVisitorSupport visitor) {
+ candidate = (AnnotatedNode) nodes[1];
AnnotationNode node = (AnnotationNode) nodes[0];
if (!MY_TYPE.equals(node.getClassNode())) return;
if (candidate instanceof ClassNode) {
- processClass((ClassNode) candidate);
+ ClassNode cNode = (ClassNode) candidate;
+ processClass(cNode, visitor);
} else if (candidate instanceof MethodNode) {
// handles constructors and methods
- processConstructorOrMethod((MethodNode) candidate);
+ MethodNode mNode = (MethodNode) candidate;
+ processConstructorOrMethod(mNode, visitor);
+ } else if (candidate instanceof FieldNode) {
+ FieldNode fNode = (FieldNode) candidate;
+ processField(fNode, visitor);
+ } else if (candidate instanceof DeclarationExpression) {
+ DeclarationExpression de = (DeclarationExpression) candidate;
+ processLocalVariable(de, visitor);
}
}
- private void processClosures(ASTNode[] nodes, final SourceUnit source) {
- final ASTNode node = nodes[1];
- if(node instanceof ClassNode) {
- ClassNode annotatedClass = (ClassNode) node;
-
- final ClassCodeVisitorSupport visitor = new ClassCodeVisitorSupport() {
- @Override
- public void visitClosureExpression(ClosureExpression expression) {
- if (expression.isSynthetic()) { return; }
- Parameter[] origParams = expression.getParameters();
- for (Parameter p : origParams) {
- p.setModifiers(p.getModifiers() | Modifier.FINAL);
- }
- super.visitClosureExpression(expression);
- }
-
- protected SourceUnit getSourceUnit() {
- return source;
- }
- };
-
- visitor.visitClass(annotatedClass);
+ private void processLocalVariable(DeclarationExpression de, ClassCodeVisitorSupport visitor) {
+ if (de.getRightExpression() instanceof ClosureExpression) {
+ visitor.visitDeclarationExpression(de);
}
}
+ private void processField(FieldNode fNode, ClassCodeVisitorSupport visitor) {
+ if (fNode.hasInitialExpression() && fNode.getInitialExpression() instanceof ClosureExpression) {
+ visitor.visitField(fNode);
+ }
+ }
-
- private void processClass(ClassNode cNode) {
+ private void processClass(ClassNode cNode, final ClassCodeVisitorSupport visitor) {
if (cNode.isInterface()) {
addError("Error processing interface '" + cNode.getName() +
"'. " + MY_TYPE_NAME + " only allowed for classes.", cNode);
return;
}
+
for (ConstructorNode cn : cNode.getDeclaredConstructors()) {
- processConstructorOrMethod(cn);
+ if (hasNoExplicitAutoFinal(cn)) {
+ processConstructorOrMethod(cn, visitor);
+ }
}
+
for (MethodNode mn : cNode.getAllDeclaredMethods()) {
- processConstructorOrMethod(mn);
+ if (hasNoExplicitAutoFinal(mn)) {
+ processConstructorOrMethod(mn, visitor);
+ }
+ }
+
+ Iterator<InnerClassNode> it = cNode.getInnerClasses();
+ while (it.hasNext()) {
+ InnerClassNode in = it.next();
+ if (in.getAnnotations(MY_TYPE).isEmpty()) {
+ processClass(in, visitor);
+ }
}
+
+ visitor.visitClass(cNode);
+ }
+
+ private boolean hasNoExplicitAutoFinal(AnnotatedNode node) {
+ return node.getAnnotations(MY_TYPE).isEmpty();
}
- private void processConstructorOrMethod(MethodNode node) {
- if (node.isSynthetic()) return;
- Parameter[] origParams = node.getParameters();
+ private void processConstructorOrMethod(MethodNode mNode, ClassCodeVisitorSupport visitor) {
+ if (mNode.isSynthetic()) return;
+ Parameter[] origParams = mNode.getParameters();
for (Parameter p : origParams) {
p.setModifiers(p.getModifiers() | Modifier.FINAL);
}
+ visitor.visitMethod(mNode);
}
}