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 8684a3506c GROOVY-11846: enclosing method is `doCall` for anon. inner
in closure
8684a3506c is described below
commit 8684a3506cf6f69536d167e7a79a2f4ae6e85b5e
Author: Eric Milles <[email protected]>
AuthorDate: Sun Jan 25 19:36:24 2026 -0600
GROOVY-11846: enclosing method is `doCall` for anon. inner in closure
---
.../groovy/classgen/AsmClassGenerator.java | 8 +--
.../groovy/classgen/asm/ClosureWriter.java | 24 ++++---
src/test/groovy/bugs/Groovy10840.groovy | 39 +++++++++++
.../groovy/gls/innerClass/InnerClassTest.groovy | 79 +++++++++++++---------
4 files changed, 104 insertions(+), 46 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 6150764f25..ef413fc294 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -345,7 +345,7 @@ public class AsmClassGenerator extends ClassGenerator {
MethodNode enclosingMethod = classNode.getEnclosingMethod();
if (enclosingMethod != null) {
classVisitor.visitOuterClass(
-
BytecodeHelper.getClassInternalName(classNode.getOuterClass()),
+
BytecodeHelper.getClassInternalName(enclosingMethod.getDeclaringClass()),
enclosingMethod.getName(),
BytecodeHelper.getMethodDescriptor(enclosingMethod));
}
}
@@ -441,9 +441,6 @@ public class AsmClassGenerator extends ClassGenerator {
}
private void makeInnerClassEntry(final ClassNode innerClass) {
- ClassNode outerClass = innerClass.getOuterClass();
- maybeInnerClassEntry(outerClass); // GROOVY-9842
-
String innerClassName = innerClass.getName();
String innerClassInternalName =
BytecodeHelper.getClassInternalName(innerClassName);
{
@@ -452,7 +449,8 @@ public class AsmClassGenerator extends ClassGenerator {
}
String outerClassInternalName;
if (innerClass.getEnclosingMethod() == null) {
- outerClassInternalName =
BytecodeHelper.getClassInternalName(outerClass.getName());
+ maybeInnerClassEntry(innerClass.getOuterClass()); // GROOVY-9842
+ outerClassInternalName =
BytecodeHelper.getClassInternalName(innerClass.getOuterClass().getName());
} else {
outerClassInternalName = null; // local inner classes don't
specify the outer class name
if (innerClass instanceof InnerClassNode && ((InnerClassNode)
innerClass).isAnonymous()) innerClassName = null;
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 6dc4eb0104..4326ef4a4a 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -50,7 +50,6 @@ import java.util.List;
import java.util.Map;
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
-import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
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.constX;
@@ -171,7 +170,6 @@ public class ClosureWriter {
closureClass = createClosureClass(expression, modifiers);
closureClasses.put(expression, closureClass);
controller.getAcg().addInnerClass(closureClass);
- closureClass.addInterface(ClassHelper.GENERATED_CLOSURE_Type);
closureClass.putNodeMetaData(WriterControllerFactory.class,
(WriterControllerFactory) x -> controller);
}
return closureClass;
@@ -217,13 +215,13 @@ public class ClosureWriter {
else if (ClassHelper.isPrimitiveType(returnType)) returnType =
ClassHelper.getWrapper(returnType);
else if (GenericsUtils.hasUnresolvedGenerics(returnType)) returnType =
GenericsUtils.nonGeneric(returnType);
- InnerClassNode answer = new InnerClassNode(classNode, name, modifiers,
ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
+ var answer = new InnerClassNode(classNode, name, modifiers,
ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
+
answer.addInterface(ClassHelper.GENERATED_CLOSURE_Type.getPlainNodeReference());
answer.setEnclosingMethod(enclosingMethod);
answer.setScriptBody(controller.isInScriptBody());
answer.setSourcePosition(expression);
answer.setStaticClass(controller.isStaticMethod() ||
classNode.isStaticClass());
answer.setSynthetic(true);
- answer.setUsingGenerics(rootClass.isUsingGenerics());
{
MethodNode doCall = answer.addMethod("doCall", ACC_PUBLIC,
returnType, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
@@ -231,14 +229,24 @@ public class ClosureWriter {
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());
}
+ doCall.setVariableScope(varScope.copy());
+
+ new CodeVisitorSupport() {
+ @Override
+ public void visitConstructorCallExpression(final
ConstructorCallExpression cce) {
+ if (cce.isUsingAnonymousInnerClass()) { // GROOVY-11846
+ cce.getType().setEnclosingMethod(doCall);
+ }
+ super.visitConstructorCallExpression(cce);
+ }
+ }
+ .visit(expression.getCode());
}
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(
+ var call = new MethodNode(
"call",
ACC_PUBLIC,
returnType,
@@ -266,7 +274,7 @@ public class ClosureWriter {
//
https://docs.oracle.com/en/java/javase/21/docs/specs/serialization/class.html#stream-unique-identifiers
// As we could see, it's too complex for closures.
long serialVersionUID = hash(classNode.getName());
- classNode.addFieldFirst("serialVersionUID", ACC_PRIVATE | ACC_STATIC |
ACC_FINAL, long_TYPE, constX(serialVersionUID, true));
+ classNode.addFieldFirst("serialVersionUID", ACC_PRIVATE | ACC_STATIC |
ACC_FINAL, ClassHelper.long_TYPE, constX(serialVersionUID, true));
}
private static long hash(String str) {
diff --git a/src/test/groovy/bugs/Groovy10840.groovy
b/src/test/groovy/bugs/Groovy10840.groovy
new file mode 100644
index 0000000000..d7079200b4
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy10840.groovy
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package bugs
+
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy10840 {
+
+ @Test
+ void testSpecialConstructorCallWithArray() {
+ assertScript '''
+ class BAIS extends ByteArrayInputStream {
+ BAIS(String input) {
+ super(input.bytes)
+ }
+ }
+
+ assert new BAIS('input').available() >= 5
+ '''
+ }
+}
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index 2aac220e3f..ed46f15eda 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -50,10 +50,10 @@ final class InnerClassTest {
assertScript '''import Foo as Bar
class Foo {}
- def regular = new Bar()
- def anonymous = new Bar() {}
- assert regular.class.name == 'Foo'
- assert anonymous.class.superclass.name == 'Foo'
+ def obj = new Bar()
+ def aic = new Bar() {}
+ assert obj.class.name == 'Foo'
+ assert aic.class.superclass.name == 'Foo'
'''
assertScript '''import static Baz.Foo as Bar
@@ -61,24 +61,31 @@ final class InnerClassTest {
static class Foo {}
}
- def regular = new Bar()
- def anonymous = new Bar() {}
- assert regular.class.name == 'Baz$Foo'
- assert anonymous.class.superclass.name == 'Baz$Foo'
+ def obj = new Bar()
+ def aic = new Bar() {}
+ assert obj.class.name == 'Baz$Foo'
+ assert aic.class.superclass.name == 'Baz$Foo'
'''
}
- // GROOVY-10840
+ // GROOVY-11846
@Test
- void testArrayAIC() {
+ void testInnerAIC() {
assertScript '''
- class BAIS extends ByteArrayInputStream {
- BAIS(String input) {
- super(input.bytes)
+ class C {
+ def m() {
+ return { ->
+ new Object() {
+ }
+ }
}
}
- assert new BAIS('input').available() >= 5
+ def obj = new C().m() ()
+ def aic = obj.getClass()
+ assert aic.getName() == 'C$1'
+ assert aic.getEnclosingClass().getName() == 'C$_m_closure1'
+ assert aic.getEnclosingMethod().getName() == 'doCall' : 'not m()'
'''
}
@@ -252,14 +259,14 @@ final class InnerClassTest {
}
class Two extends One {
- Two() {
- super(new Object() { // AIC before special ctor call completes
- int hashCode() {
- hash() // should be able to call static method safely
- }
- })
- }
- static int hash() { 42 }
+ Two() {
+ super(new Object() { // AIC before special ctor call
completes
+ int hashCode() {
+ hash() // should be able to call static method
safely
+ }
+ })
+ }
+ static int hash() { 42 }
}
def obj = new Two()
@@ -408,6 +415,7 @@ final class InnerClassTest {
class A {
static class B {}
}
+
assert A.declaredClasses.length == 1
assert A.declaredClasses[0] == A.B
'''
@@ -440,6 +448,7 @@ final class InnerClassTest {
final String foo = 'foo'
}
}
+
def b = new A.B(new A())
assert b.foo == 'foo'
'''
@@ -483,6 +492,7 @@ final class InnerClassTest {
class A {
class B {}
}
+
def x = new A.B() // requires reference to A
'''
}
@@ -1464,23 +1474,26 @@ final class InnerClassTest {
@Test
void testInnerClassDotThisUsage2() {
assertScript '''
- interface X {
+ interface A {
def m()
}
-
- class A {
+ class B {
def foo() {
- def c = {
- return new X(){def m(){
- A.this
- } }
+ def x = { ->
+ return new A() {
+ @Override
+ def m() {
+ B.this
+ }
+ }
}
- return c().m()
+ return x().m()
}
}
- class B extends A {}
- def b = new B()
- assert b.foo() instanceof B
+ class C extends B {
+ }
+ def c = new C()
+ assert c.foo() instanceof C
'''
}