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

Reply via email to