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}
}
}