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