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