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 04e8f44132 GROOVY-10687: populate nest members attribute for inner 
class w/ closure
04e8f44132 is described below

commit 04e8f441326f5fecc1882735134f632a5291eaca
Author: Eric Milles <[email protected]>
AuthorDate: Mon Sep 23 12:07:47 2024 -0500

    GROOVY-10687: populate nest members attribute for inner class w/ closure
---
 src/main/java/groovy/lang/GroovyClassLoader.java   | 58 +++++++++++++++------
 .../codehaus/groovy/control/CompilationUnit.java   | 59 +++++++---------------
 src/test/gls/enums/EnumTest.groovy                 | 16 +++---
 .../classgen/asm/AbstractBytecodeTestCase.groovy   |  6 ++-
 .../groovy/classgen/asm/NestHostTests.groovy       | 28 +++++++---
 5 files changed, 95 insertions(+), 72 deletions(-)

diff --git a/src/main/java/groovy/lang/GroovyClassLoader.java 
b/src/main/java/groovy/lang/GroovyClassLoader.java
index 4f95301c45..3b734ced40 100644
--- a/src/main/java/groovy/lang/GroovyClassLoader.java
+++ b/src/main/java/groovy/lang/GroovyClassLoader.java
@@ -64,10 +64,11 @@ import java.security.Permissions;
 import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.Enumeration;
+import java.util.LinkedHashMap;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.lang.System;
 
 /*
  * TODO: multi-threaded compiling of the same class but with different roots 
for
@@ -215,11 +216,13 @@ public class GroovyClassLoader extends URLClassLoader {
         }
 
         CompilationUnit unit = createCompilationUnit(config, codeSource);
+        var generatedClasses = new LinkedHashMap<ClassNode, ClassVisitor>();
         ClassCollector collector = createCollector(unit, 
classNode.getModule().getContext());
         try {
             unit.addClassNode(classNode);
-            unit.setClassgenCallback(collector);
+            unit.setClassgenCallback((cv, cn) -> generatedClasses.put(cn, cv));
             unit.compile(Phases.CLASS_GENERATION);
+            collect(collector, generatedClasses);
             definePackageInternal(collector.generatedClass.getName());
             return collector.generatedClass;
         } catch (CompilationFailedException e) {
@@ -227,8 +230,33 @@ public class GroovyClassLoader extends URLClassLoader {
         }
     }
 
+    private static void collect(final ClassCollector collector, final 
LinkedHashMap<ClassNode, ClassVisitor> generatedClasses) {
+        // GROOVY-10687: drive ClassCollector after classgen -- interfaces 
first
+        var classes = new ArrayList<ClassNode>(generatedClasses.keySet());
+        classes.sort(Comparator.comparingInt(cn -> {
+            int n;
+            if (cn.isInterface()) {
+                var interfaces = cn.getInterfaces();
+                n = interfaces.length;
+                for (var in : interfaces) {
+                    n += in.getInterfaces().length;
+                }
+            } else {
+                n = 999;
+                while (cn != null) { n += 1;
+                    cn = cn.getSuperClass();
+                }
+            }
+            return n;
+        }));
+
+        for (ClassNode cn : classes) {
+            collector.call(generatedClasses.get(cn), cn);
+        }
+    }
+
     /**
-     * Check if this class loader has compatible {@link CompilerConfiguration}
+     * Checks if this class loader has compatible {@link CompilerConfiguration}
      * with the provided one.
      * @param config the compiler configuration to test for compatibility
      * @return {@code true} if the provided config is exactly the same instance
@@ -333,7 +361,7 @@ public class GroovyClassLoader extends URLClassLoader {
 
     private Class<?> doParseClass(final GroovyCodeSource codeSource) {
         validate(codeSource);
-        Class<?> answer;  // Was neither already loaded nor compiling, so 
compile and add to cache.
+        // Was neither already loaded nor compiling, so compile and add to 
cache.
         CompilationUnit unit = createCompilationUnit(config, 
codeSource.getCodeSource());
         if (recompile != null ? recompile : config.getRecompileGroovySource()) 
{
             unit.addFirstPhaseOperation(TimestampAdder.INSTANCE, 
CompilePhase.CLASS_GENERATION.getPhaseNumber());
@@ -352,19 +380,19 @@ public class GroovyClassLoader extends URLClassLoader {
         }
 
         ClassCollector collector = createCollector(unit, su);
-        unit.setClassgenCallback(collector);
-        int goalPhase = Phases.CLASS_GENERATION;
-        if (config != null && config.getTargetDirectory() != null) goalPhase = 
Phases.OUTPUT;
-        unit.compile(goalPhase);
+        var generatedClasses = new LinkedHashMap<ClassNode,ClassVisitor>();
+        unit.setClassgenCallback((cv, cn) -> generatedClasses.put(cn, cv));
+        unit.compile(config == null || config.getTargetDirectory() == null ? 
Phases.CLASS_GENERATION : Phases.OUTPUT);
+
+        collect(collector, generatedClasses);
 
-        answer = collector.generatedClass;
-        String mainClass = su.getAST().getMainClassName();
+        Class<?> answer = collector.generatedClass;
+        String mainName = su.getAST().getMainClassName();
         for (Object o : collector.getLoadedClasses()) {
-            Class<?> clazz = (Class<?>) o;
-            String clazzName = clazz.getName();
-            definePackageInternal(clazzName);
-            setClassCacheEntry(clazz);
-            if (clazzName.equals(mainClass)) answer = clazz;
+            var c = (Class<?>) o;
+            setClassCacheEntry(c);
+            definePackageInternal(c.getName());
+            if (c.getName().equals(mainName)) answer = c;
         }
         return answer;
     }
diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java 
b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
index 071f6e3a0c..23d97ca6ce 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -62,6 +62,7 @@ import java.io.InputStream;
 import java.net.URL;
 import java.security.CodeSource;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -73,7 +74,6 @@ import java.util.Optional;
 import java.util.Queue;
 import java.util.Set;
 
-import static java.util.stream.Collectors.toList;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static 
org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK;
@@ -1036,58 +1036,33 @@ public class CompilationUnit extends ProcessingUnit {
         return count;
     }
 
-    private static int getSuperInterfaceCount(final ClassNode classNode) {
+    private static int getInterfacesCount(final ClassNode classNode) {
         int count = 1;
         for (ClassNode face : classNode.getInterfaces()) {
-            count = Math.max(count, getSuperInterfaceCount(face) + 1);
+            count = Math.max(count, getInterfacesCount(face) + 1);
         }
         return count;
     }
 
     private List<ClassNode> getPrimaryClassNodes(final boolean sort) {
-        List<ClassNode> unsorted = getAST().getModules().stream()
-            .flatMap(module -> module.getClasses().stream()).collect(toList());
-
-        if (!sort) return unsorted;
-
-        int n = unsorted.size();
-        int[] indexClass = new int[n];
-        int[] indexInterface = new int[n];
-        {
-            int i = 0;
-            for (ClassNode element : unsorted) {
-                if (element.isInterface()) {
-                    indexInterface[i] = getSuperInterfaceCount(element);
-                    indexClass[i] = -1;
+        List<ClassNode> classes = getAST().getClasses();
+
+        if (sort && classes.size() > 1) {
+            classes.sort(Comparator.comparingInt(cn -> {
+                int count;
+                if (cn.isInterface()) {
+                    count = getInterfacesCount(cn);
                 } else {
-                    indexClass[i] = getSuperClassCount(element);
-                    indexInterface[i] = -1;
+                    count = getSuperClassCount(cn) + 1000;
                 }
-                i += 1;
-            }
-        }
-
-        List<ClassNode> sorted = getSorted(indexInterface, unsorted);
-        sorted.addAll(getSorted(indexClass, unsorted));
-        return sorted;
-    }
-
-    private static List<ClassNode> getSorted(final int[] index, final 
List<ClassNode> unsorted) {
-        int unsortedSize = unsorted.size();
-        List<ClassNode> sorted = new ArrayList<>(unsortedSize);
-        for (int i = 0; i < unsortedSize; i += 1) {
-            int min = -1;
-            for (int j = 0; j < unsortedSize; j += 1) {
-                if (index[j] == -1) continue;
-                if (min == -1 || index[j] < index[min]) {
-                    min = j;
+                if (cn.getOuterClass() == null && 
cn.getInnerClasses().hasNext()) {
+                    count += 2000; // GROOVY-10687: nest host must follow 
members (with closures)
                 }
-            }
-            if (min == -1) break;
-            sorted.add(unsorted.get(min));
-            index[min] = -1;
+                return count;
+            }));
         }
-        return sorted;
+
+        return classes;
     }
 
     private void changeBugText(final GroovyBugError e, final SourceUnit 
context) {
diff --git a/src/test/gls/enums/EnumTest.groovy 
b/src/test/gls/enums/EnumTest.groovy
index 5c1ba15cb3..d41f9010a0 100644
--- a/src/test/gls/enums/EnumTest.groovy
+++ b/src/test/gls/enums/EnumTest.groovy
@@ -552,18 +552,22 @@ class EnumTest extends CompilableTestSupport {
     // GROOVY-5756
     void testInnerClosureDefinitions() {
         assertScript '''
-            enum MyEnum {
+            enum E {
+                CONTROL,
                 INSTANCE {
+                    @Override
                     String foo() {
-                        def clos1 = { "foo1" }; clos1.call()
+                        def c1 = { -> 'foo1' }
+                        c1.call()
                     }
-                }, CONTROL
+                };
                 String foo() {
-                    def clos2 = { "foo2" }; clos2.call()
+                    def c2 = { -> 'foo2' }
+                    c2.call()
                 }
             }
-            assert "foo2" == MyEnum.CONTROL.foo()
-            assert "foo1" == MyEnum.INSTANCE.foo()
+            assert E.CONTROL .foo() == 'foo2'
+            assert E.INSTANCE.foo() == 'foo1'
         '''
     }
 
diff --git 
a/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy 
b/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy
index 7a1522e646..c21245bbad 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy
@@ -60,7 +60,8 @@ abstract class AbstractBytecodeTestCase extends 
GroovyTestCase {
         } finally {
             if (unit != null) {
                 try {
-                    sequence = extractSequence(unit.classes[0].bytes, 
extractionOptions)
+                    def firstClass = unit.classes.find { it.name == 
unit.firstClassNode.name }
+                    sequence = extractSequence(firstClass.bytes, 
extractionOptions)
                     if (extractionOptions.print) println(sequence)
                 } catch (e) {
                     // probably an error in the script
@@ -93,7 +94,8 @@ abstract class AbstractBytecodeTestCase extends 
GroovyTestCase {
             }
         }
         if (sequence == null && cu.classes) {
-            sequence = extractSequence(cu.classes[0].bytes, options)
+            def gc = cu.classes.find { it.name == cu.firstClassNode.name }
+            sequence = extractSequence(gc.bytes, options)
         }
         for (gc in cu.classes) {
             try {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy 
b/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy
index 340c4fc544..3ccf2a2f96 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy
@@ -88,21 +88,35 @@ final class NestHostTests {
     void testNestHost4() {
         def types = compileScript '''
             class C {
-                def closure = { -> }
                 static class D {
                     def closure = { -> }
                 }
+                def another_closure = { -> }
             }
         '''
 
-        types.init().each { type ->
+        types.each { type ->
             assert type.nestHost.name == 'C'
-            if (Runtime.version().feature() >= 15)
-            assert type.nestMembers*.name.sort() == ['C', 'C$D', 
'C$_closure1', /* TODO: 'C$D$_closure1'*/]
+            assert type.nestMembers*.name.sort() == ['C', 'C$D', 
'C$D$_closure1', 'C$_closure1']
         }
-        types.last().with { type -> // TODO
-            assert type.nestHost.name == 'C$D$_closure1'
-            if (Runtime.version().feature() >= 15) assert 
type.nestMembers*.name.sort() == ['C$D$_closure1']
+    }
+
+    @Test
+    void testNestHost5() {
+        def types = compileScript '''
+            class C {
+                class D {
+                    @groovy.transform.CompileStatic
+                    void m() {
+                        [].forEach(item -> Optional.of(item).orElseGet(() -> 
2))
+                    }
+                }
+            }
+        '''
+
+        types.each { type ->
+            assert type.nestHost.name == 'C'
+            assert type.nestMembers*.name.sort() == ['C', 'C$D', 
'C$D$_m_lambda1', 'C$D$_m_lambda1$_lambda2']
         }
     }
 }

Reply via email to