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 2021/03/31 22:26:12 UTC

[groovy] branch master updated: GROOVY-10004: @Lazy transform should check for explicit getters/setters (closes #1537)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new a93b782  GROOVY-10004: @Lazy transform should check for explicit getters/setters (closes #1537)
a93b782 is described below

commit a93b782639246b864304b0e5383ca160cdd7dc60
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Mar 29 16:44:14 2021 +1000

    GROOVY-10004: @Lazy transform should check for explicit getters/setters (closes #1537)
---
 .../groovy/transform/LazyASTTransformation.java    | 39 +++++++++++++++-------
 .../groovy/transform/LazyTransformTest.groovy      | 24 +++++++++++++
 2 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
index e95b8b2..9251d0b 100644
--- a/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
@@ -25,6 +25,7 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 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.PropertyNode;
 import org.codehaus.groovy.ast.expr.ConstantExpression;
@@ -37,6 +38,7 @@ import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.Opcodes;
 
 import java.lang.ref.SoftReference;
 
@@ -102,9 +104,9 @@ public class LazyASTTransformation extends AbstractASTTransformation {
         }
 
         if (soft instanceof ConstantExpression && ((ConstantExpression) soft).getValue().equals(true)) {
-            createSoft(fieldNode, init);
+            createSoft(fieldNode, init, xform);
         } else {
-            create(fieldNode, init);
+            create(fieldNode, init, xform);
             // @Lazy not meaningful with primitive so convert to wrapper if needed
             if (ClassHelper.isPrimitiveType(fieldNode.getType())) {
                 fieldNode.setType(ClassHelper.getWrapper(fieldNode.getType()));
@@ -112,7 +114,7 @@ public class LazyASTTransformation extends AbstractASTTransformation {
         }
     }
 
-    private static void create(FieldNode fieldNode, final Expression initExpr) {
+    private static void create(FieldNode fieldNode, final Expression initExpr, ErrorCollecting xform) {
         final BlockStatement body = new BlockStatement();
         if (fieldNode.isStatic()) {
             addHolderClassIdiomBody(body, fieldNode, initExpr);
@@ -121,7 +123,7 @@ public class LazyASTTransformation extends AbstractASTTransformation {
         } else {
             addNonThreadSafeBody(body, fieldNode, initExpr);
         }
-        addMethod(fieldNode, body, fieldNode.getType());
+        addMethod(fieldNode, body, fieldNode.getType(), xform);
     }
 
     private static void addHolderClassIdiomBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
@@ -170,26 +172,39 @@ public class LazyASTTransformation extends AbstractASTTransformation {
         body.addStatement(ifElseS(notNullX(fieldExpr), stmt(fieldExpr), assignS(fieldExpr, initExpr)));
     }
 
-    private static void addMethod(FieldNode fieldNode, BlockStatement body, ClassNode type) {
+    private static void addMethod(FieldNode fieldNode, BlockStatement body, ClassNode type, ErrorCollecting xform) {
         int visibility = ACC_PUBLIC;
         if (fieldNode.isStatic()) visibility |= ACC_STATIC;
         String propName = capitalize(fieldNode.getName().substring(1));
         ClassNode declaringClass = fieldNode.getDeclaringClass();
-        addGeneratedMethod(declaringClass, "get" + propName, visibility, type, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethodOrError(declaringClass, "get" + propName, visibility, type, body, xform, fieldNode);
         if (ClassHelper.boolean_TYPE.equals(type)) {
-            addGeneratedMethod(declaringClass, "is" + propName, visibility, type,
-                    Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, stmt(callThisX("get" + propName)));
+            addGeneratedMethodOrError(declaringClass, "is" + propName, visibility, type, stmt(callThisX("get" + propName)), xform, fieldNode);
         }
+        // expect no setter
+        MethodNode existing = declaringClass.getDeclaredMethod("set" + propName, params(param(fieldNode.getType(), "value")));
+        if (existing != null && (existing.getModifiers() & Opcodes.ACC_SYNTHETIC) == 0) {
+            xform.addError("Error during @Lazy processing: invalid explicit setter 'set" + propName + "' found", fieldNode);
+        }
+    }
+
+    private static void addGeneratedMethodOrError(ClassNode declaringClass, String name, int visibility, ClassNode type, Statement body, ErrorCollecting xform, FieldNode fieldNode) {
+        Parameter[] params = Parameter.EMPTY_ARRAY;
+        MethodNode existing = declaringClass.getDeclaredMethod(name, params);
+        if (existing != null && (existing.getModifiers() & Opcodes.ACC_SYNTHETIC) == 0) {
+            xform.addError("Error during @Lazy processing: invalid explicit getter '" + name + "' found", fieldNode);
+        }
+        addGeneratedMethod(declaringClass, name, visibility, type, params, ClassNode.EMPTY_ARRAY, body);
     }
 
-    private static void createSoft(FieldNode fieldNode, Expression initExpr) {
+    private static void createSoft(FieldNode fieldNode, Expression initExpr, ErrorCollecting xform) {
         final ClassNode type = fieldNode.getType();
         fieldNode.setType(SOFT_REF);
-        createSoftGetter(fieldNode, initExpr, type);
+        createSoftGetter(fieldNode, initExpr, type, xform);
         createSoftSetter(fieldNode, type);
     }
 
-    private static void createSoftGetter(FieldNode fieldNode, Expression initExpr, ClassNode type) {
+    private static void createSoftGetter(FieldNode fieldNode, Expression initExpr, ClassNode type, ErrorCollecting xform) {
         final BlockStatement body = new BlockStatement();
         final Expression fieldExpr = varX(fieldNode);
         final Expression resExpr = localVarX("_result", type);
@@ -214,7 +229,7 @@ public class LazyASTTransformation extends AbstractASTTransformation {
         } else {
             body.addStatement(mainIf);
         }
-        addMethod(fieldNode, body, type);
+        addMethod(fieldNode, body, type, xform);
     }
 
     private static void createSoftSetter(FieldNode fieldNode, ClassNode type) {
diff --git a/src/test/org/codehaus/groovy/transform/LazyTransformTest.groovy b/src/test/org/codehaus/groovy/transform/LazyTransformTest.groovy
index 1c6fb96..32f9faa 100644
--- a/src/test/org/codehaus/groovy/transform/LazyTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/LazyTransformTest.groovy
@@ -164,6 +164,30 @@ class LazyTransformTest extends GroovyShellTestCase {
         assert message.contains("You cannot lazily initialize 'foo' from the abstract class 'Foo'")
     }
 
+    void testClassWithExplicitGetterForLazyFieldShouldNotCompile() {
+        def message = shouldFail {
+            new GroovyShell().run('''
+            class Animal {
+                @Lazy String name = { 'sloth' }()
+                String getName() {}
+            }
+            ''', 'dummyFileName', [])
+        }
+        assert message.contains("Error during @Lazy processing: invalid explicit getter 'getName' found")
+    }
+
+    void testClassWithExplicitSetterForLazyFieldShouldNotCompile() {
+        def message = shouldFail {
+            new GroovyShell().run('''
+            class Animal {
+                @Lazy String name = { 'sloth' }()
+                void setName(String name) {}
+            }
+            ''', 'dummyFileName', [])
+        }
+        assert message.contains("Error during @Lazy processing: invalid explicit setter 'setName' found")
+    }
+
     void testSoft() {
         def res = evaluate("""
               class X {