GROOVY-10687: JEP 181: nest host and members attributes
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 5de2ad3..ee10580 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -330,10 +330,13 @@
if (classNode instanceof InnerClassNode && !(classNode instanceof InterfaceHelperClassNode)) {
makeInnerClassEntry(classNode); // GROOVY-4649, et al.
+ ClassNode nestHost = controller.getOutermostClass(); // GROOVY-10687
+ classVisitor.visitNestHost(BytecodeHelper.getClassInternalName(nestHost));
+
MethodNode enclosingMethod = classNode.getEnclosingMethod();
if (enclosingMethod != null) {
classVisitor.visitOuterClass(
- BytecodeHelper.getClassInternalName(classNode.getOuterClass().getName()),
+ BytecodeHelper.getClassInternalName(classNode.getOuterClass()),
enclosingMethod.getName(), BytecodeHelper.getMethodDescriptor(enclosingMethod));
}
}
@@ -375,7 +378,10 @@
createSyntheticStaticFields();
}
}
-
+ // GROOVY-10687
+ if (classNode.getOuterClass() == null && classNode.getInnerClasses().hasNext()) {
+ makeNestMatesEntries(classNode);
+ }
// GROOVY-4649, GROOVY-6750, GROOVY-6808
for (Iterator<InnerClassNode> it = classNode.getInnerClasses(); it.hasNext(); ) {
makeInnerClassEntry(it.next());
@@ -446,6 +452,14 @@
classVisitor.visitInnerClass(innerClassInternalName, outerClassInternalName, innerClassName, modifiers);
}
+ private void makeNestMatesEntries(final ClassNode classNode) {
+ for (Iterator<InnerClassNode> it = classNode.getInnerClasses(); it.hasNext(); ) {
+ ClassNode innerClass = it.next();
+ classVisitor.visitNestMember(BytecodeHelper.getClassInternalName(innerClass));
+ makeNestMatesEntries(innerClass);
+ }
+ }
+
/*
* See http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.6-300-D.2-5
* for what flags are allowed depending on the fact we are writing the inner class table
diff --git a/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy b/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy
new file mode 100644
index 0000000..33103e4
--- /dev/null
+++ b/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy
@@ -0,0 +1,107 @@
+/*
+ * 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 org.codehaus.groovy.classgen.asm
+
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.Phases
+import org.junit.Test
+
+final class NestHostTests {
+
+ private List<Class> compileScript(String script) {
+ new CompilationUnit().with {
+ addSource('script', script)
+ compile(Phases.CLASS_GENERATION)
+ classes.collect { classLoader.defineClass(it.name, it.bytes) }
+ }
+ }
+
+ @Test
+ void testNestHost0() {
+ def types = compileScript('print "hello world"')
+
+ types.each { type ->
+ assert type.nestHost.name == 'script'
+ assert type.nestMembers*.name == ['script']
+ }
+ }
+
+ @Test
+ void testNestHost1() {
+ def types = compileScript('class C { }')
+
+ types.each { type ->
+ assert type.nestHost.name == 'C'
+ assert type.nestMembers*.name == ['C']
+ }
+ }
+
+ @Test
+ void testNestHost2() {
+ def types = compileScript('class C { class D { } }')
+
+ types.each { type ->
+ assert type.nestHost.name == 'C'
+ assert type.nestMembers*.name.sort() == ['C', 'C$D']
+ }
+ }
+
+ @Test
+ void testNestHost3() {
+ def types = compileScript '''
+ class C {
+ static class D {
+ def aic = new Object() {}
+ }
+ interface I {
+ interface J {
+ }
+ }
+ enum E {
+ }
+ }
+ '''
+
+ types.each { type ->
+ assert type.nestHost.name == 'C'
+ assert type.nestMembers*.name.sort() == ['C', 'C$D', 'C$D$1', 'C$E', 'C$I', 'C$I$J']
+ }
+ }
+
+ @Test
+ void testNestHost4() {
+ def types = compileScript '''
+ class C {
+ def closure = { -> }
+ static class D {
+ def closure = { -> }
+ }
+ }
+ '''
+//X
+ types.init().each { type ->
+ assert type.nestHost.name == 'C'
+ assert type.nestMembers*.name.sort() == ['C', 'C$D', 'C$_closure1', /* TODO: 'C$D$_closure1'*/]
+ }
+ types.last().with { type -> // TODO
+ assert type.nestHost.name == 'C$D$_closure1'
+ assert type.nestMembers*.name.sort() == ['C$D$_closure1']
+ }
+ }
+}