You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2024/02/26 18:49:54 UTC

(groovy) branch master updated: GROOVY-11311: generate `call` method if parameter is annotated

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 c5b5f81907 GROOVY-11311: generate `call` method if parameter is annotated
c5b5f81907 is described below

commit c5b5f81907a15393f3d32edbdc6bbf72d95e579a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Feb 26 12:28:07 2024 -0600

    GROOVY-11311: generate `call` method if parameter is annotated
---
 .../groovy/classgen/asm/ClosureWriter.java         | 37 +++++++-------
 src/test/groovy/ClosureTest.groovy                 | 14 +++--
 .../groovy/generated/ClosureGeneratedTest.groovy   | 15 +++---
 .../ClosureWriterGeneratedAnnotationTest.groovy    | 59 +++++++++++++++-------
 ...ticCompileClosureGeneratedAnnotationTest.groovy | 37 +++++++-------
 5 files changed, 87 insertions(+), 75 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index 3864d3309d..73f1234fe3 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -187,6 +187,10 @@ public class ClosureWriter {
         return false;
     }
 
+    private static boolean isNotObjectOrObjectArray(final ClassNode classNode) {
+        return !ClassHelper.isObjectType(classNode) && !ClassHelper.isObjectType(classNode.getComponentType());
+    }
+
     protected ClassNode createClosureClass(final ClosureExpression expression, final int modifiers) {
         ClassNode classNode = controller.getClassNode();
         ClassNode rootClass = controller.getOutermostClass();
@@ -222,22 +226,19 @@ public class ClosureWriter {
         answer.setSynthetic(true);
         answer.setUsingGenerics(rootClass.isUsingGenerics());
 
-        MethodNode method = answer.addMethod("doCall", ACC_PUBLIC, returnType, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
-        method.setSourcePosition(expression);
-
-        VariableScope varScope = expression.getVariableScope();
-        if (varScope == null) {
-            throw new RuntimeException(
-                    "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
-        } else {
-            method.setVariableScope(varScope.copy());
+        {
+            MethodNode doCall = answer.addMethod("doCall", ACC_PUBLIC, returnType, parameters, 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);
+            } else {
+                doCall.setVariableScope(varScope.copy());
+            }
         }
-        if (parameters.length > 1
-                || (parameters.length == 1
-                    && parameters[0].getType() != null
-                    && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType())
-                    && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) {
-            // let's add a typesafe call method
+
+        if (parameters.length > 1 || (parameters.length == 1 && (isNotObjectOrObjectArray(parameters[0].getType())
+                || !parameters[0].getAnnotations().isEmpty() || !parameters[0].getType().getTypeAnnotations().isEmpty()))) { // GROOVY-11311
             MethodNode call = new MethodNode(
                     "call",
                     ACC_PUBLIC,
@@ -246,17 +247,13 @@ public class ClosureWriter {
                     ClassNode.EMPTY_ARRAY,
                     returnS(callThisX("doCall", args(parameters))));
             addGeneratedMethod(answer, call, true);
-            call.setSourcePosition(expression);
         }
 
-        // let's make the constructor
         BlockStatement block = createBlockStatementForConstructor(expression, rootClass, classNode);
+        addConstructor(expression, localVariableParams, answer, block);
 
-        // let's make fields for variables from outer context
         addFieldsForLocalVariables(answer, localVariableParams);
 
-        addConstructor(expression, localVariableParams, answer, block);
-
         correctAccessedVariable(answer, expression);
 
         addSerialVersionUIDField(answer);
diff --git a/src/test/groovy/ClosureTest.groovy b/src/test/groovy/ClosureTest.groovy
index 0f37fd269e..829645151d 100644
--- a/src/test/groovy/ClosureTest.groovy
+++ b/src/test/groovy/ClosureTest.groovy
@@ -136,17 +136,15 @@ final class ClosureTest {
     @Test
     void testEach() {
         assertScript '''
-            Object[] arr = new Object[1]
-            arr[0] = "1"
-            List list = new ArrayList()
-            list.add(arr)
+            Object[] array = new Object[]{"1"}
+            List<Object[]> list = [array]
 
-            list.each { def obj ->
-                assert obj[0] == "1"
+            list.each { item ->
+                assert item[0] == "1"
             }
 
-            list.each { Object[] obj ->
-                assert obj[0] == "1"
+            list.each { Object[] item ->
+                assert item[0] == "1"
             }
         '''
     }
diff --git a/src/test/groovy/generated/ClosureGeneratedTest.groovy b/src/test/groovy/generated/ClosureGeneratedTest.groovy
index bc64994556..f69fa2b014 100644
--- a/src/test/groovy/generated/ClosureGeneratedTest.groovy
+++ b/src/test/groovy/generated/ClosureGeneratedTest.groovy
@@ -22,20 +22,19 @@ import org.junit.Test
 
 final class ClosureGeneratedTest extends AbstractGeneratedAstTestCase {
 
-    // GROOVY-11313
+    // GROOVY-11311
     @Test
-    void 'no accessor for captured argument with reserved name'() {
+    void 'call method annotated'() {
         def classUnderTest = parseClass '''
             class C {
-                Closure<String> m(String owner) {
-                    return { -> owner }
+                Closure m() {
+                    return { @Deprecated p -> }
                 }
             }
         '''
-        def objectUnderTest = classUnderTest.newInstance()
-        Closure<String> cls = classUnderTest.getMethod('m', String).invoke(objectUnderTest, 'value')
+        Object instance = classUnderTest.newInstance()
+        Closure closure = classUnderTest.getMethod('m').invoke(instance)
 
-        def getter = cls.getClass().getMethod('getOwner')
-        assert getter.declaringClass == Closure.class
+        assertMethodIsAnnotated/*AsGenerated*/(closure.getClass(), 'call', Object)
     }
 }
diff --git a/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy
index 9fbcabe848..72aec07ef2 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy
@@ -23,6 +23,8 @@ import org.codehaus.groovy.control.CompilationUnit
 import org.codehaus.groovy.control.Phases
 import org.junit.Test
 
+import java.lang.reflect.Method
+
 /**
  * Verifies if {@link Generated} annotations are added on {@code call} methods of generated closure classes.
  */
@@ -44,10 +46,10 @@ final class ClosureWriterGeneratedAnnotationTest {
     }
 
     /**
-     * For closure without params, no {@code call} methods should be generated.
+     * For closure with implicit param, no {@code call} methods should be generated.
      */
     @Test
-    void testClosureWithNoParameters() {
+    void testClosureWithSingleParameter1() {
         String scriptText = '''
             class MyClass {
                 void myMethod() {
@@ -57,18 +59,39 @@ final class ClosureWriterGeneratedAnnotationTest {
                 }
             }
         '''
-        CompilationUnit compilationUnit = compileScript(scriptText)
-        Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0]
-        Collection callMethods = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' }
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
 
         assert callMethods.size() == 0
     }
 
     /**
-     * For closure with single param, a single annotated {@code call} method with corresponding parameter should be generated.
+     * For closure with annotated param, a single annotated {@code call} method with corresponding parameter should be generated.
+     */
+    @Test
+    void testClosureWithSingleParameter2() {
+        String scriptText = '''
+            class MyClass {
+                void myMethod() {
+                    [1..3].each { @Deprecated i ->
+                        println i
+                    }
+                }
+            }
+        '''
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
+
+        assert callMethods.size() == 1
+        assert callMethods[0].getAnnotation(Generated)
+        assert callMethods[0].getParameterTypes() == new Class[] {Object}
+    }
+
+    /**
+     * For closure with non-object param, a single annotated {@code call} method with corresponding parameter should be generated.
      */
     @Test
-    void testClosureWithSingleParameter() {
+    void testClosureWithSingleParameter3() {
         String scriptText = '''
             class MyClass {
                 void myMethod() {
@@ -78,13 +101,12 @@ final class ClosureWriterGeneratedAnnotationTest {
                 }
             }
         '''
-        CompilationUnit compilationUnit = compileScript(scriptText)
-        Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0]
-        Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' }
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
 
-        assert callMethodCollection.size() == 1
-        assert callMethodCollection[0].getAnnotation(Generated)
-        assert callMethodCollection[0].getParameterTypes() == new Class[] {Integer}
+        assert callMethods.size() == 1
+        assert callMethods[0].getAnnotation(Generated)
+        assert callMethods[0].getParameterTypes() == new Class[] {Integer}
     }
 
     /**
@@ -101,12 +123,11 @@ final class ClosureWriterGeneratedAnnotationTest {
                 }
             }
         '''
-        CompilationUnit compilationUnit = compileScript(scriptText)
-        Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0]
-        Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' }
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
 
-        assert callMethodCollection.size() == 1
-        assert callMethodCollection[0].getAnnotation(Generated)
-        assert callMethodCollection[0].getParameterTypes() == new Class[] {IntRange, Integer}
+        assert callMethods.size() == 1
+        assert callMethods[0].getAnnotation(Generated)
+        assert callMethods[0].getParameterTypes() == new Class[] {IntRange, Integer}
     }
 }
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy
index 634a6b10dc..6295a578ed 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy
@@ -46,7 +46,7 @@ final class StaticCompileClosureGeneratedAnnotationTest {
     }
 
     /**
-     * For closure without params, two annotated {@code call} methods should be generated.
+     * For closure with implicit param, two annotated {@code call} methods should be generated.
      */
     @Test
     void testClosureWithNoParameters() {
@@ -60,16 +60,15 @@ final class StaticCompileClosureGeneratedAnnotationTest {
                 }
             }
         '''
-        CompilationUnit compilationUnit = compileScript(scriptText)
-        Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0]
-        Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' }
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
 
-        assert callMethodCollection.size() == 2
-        callMethodCollection.each { Method method ->
+        assert callMethods.size() == 2
+        callMethods.each { Method method ->
             assert method.getAnnotation(Generated)
         }
-        assert callMethodCollection.find { it.getParameterTypes() == new Class[] {} }
-        assert callMethodCollection.find { it.getParameterTypes() == new Class[] {Object} }
+        assert callMethods.find { it.getParameterTypes() == new Class[] {} }
+        assert callMethods.find { it.getParameterTypes() == new Class[] {Object} }
     }
 
     /**
@@ -87,13 +86,12 @@ final class StaticCompileClosureGeneratedAnnotationTest {
                 }
             }
         '''
-        CompilationUnit compilationUnit = compileScript(scriptText)
-        Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0]
-        Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' }
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
 
-        assert callMethodCollection.size() == 1
-        assert callMethodCollection[0].getAnnotation(Generated)
-        assert callMethodCollection[0].getParameterTypes() == new Class[] {IntRange}
+        assert callMethods.size() == 1
+        assert callMethods[0].getAnnotation(Generated)
+        assert callMethods[0].getParameterTypes() == new Class[] {IntRange}
     }
 
     /**
@@ -111,12 +109,11 @@ final class StaticCompileClosureGeneratedAnnotationTest {
                 }
             }
         '''
-        CompilationUnit compilationUnit = compileScript(scriptText)
-        Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0]
-        Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' }
+        Class<?> generatedClosureClass = findGeneratedClosureClasses('MyClass',compileScript(scriptText))[0]
+        Collection<Method> callMethods = generatedClosureClass.declaredMethods.findAll { it.name == 'call' }
 
-        assert callMethodCollection.size() == 1
-        assert callMethodCollection[0].getAnnotation(Generated)
-        assert callMethodCollection[0].getParameterTypes() == new Class[] {IntRange, Integer}
+        assert callMethods.size() == 1
+        assert callMethods[0].getAnnotation(Generated)
+        assert callMethods[0].getParameterTypes() == new Class[] {IntRange, Integer}
     }
 }