changeset 3504:3197e9b4d80a

Initial push for nestmate support * Add support for nestmate classfile attributes: NestMembers, MemberOfNest * Internal flag -XDdisableAccessors can be used to inhibit static accessor generation
author mcimadamore
date Mon, 11 Apr 2016 15:08:12 +0100
parents 99f2a8052f60
children f18dc3fd0496
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java src/jdk.jdeps/share/classes/com/sun/tools/classfile/MemberOfNest_attribute.java src/jdk.jdeps/share/classes/com/sun/tools/classfile/NestMembers_attribute.java src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java test/lib/annotations/annotations/classfile/ClassfileInspector.java test/tools/javac/MethodParameters/AttributeVisitor.java test/tools/javac/valhalla/nestmate/CheckNestmateAttrs.java
diffstat 11 files changed, 396 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Mon Apr 11 15:08:12 2016 +0100
@@ -92,6 +92,7 @@
     private final Name classDollar;
     private final Types types;
     private final boolean debugLower;
+    private final boolean disableAccessors;
     private final PkgInfo pkginfoOpt;
 
     protected Lower(Context context) {
@@ -119,6 +120,7 @@
         Options options = Options.instance(context);
         debugLower = options.isSet("debuglower");
         pkginfoOpt = PkgInfo.get(options);
+        disableAccessors = options.isSet("disableAccessors");
     }
 
     /** The currently enclosing class.
@@ -1085,6 +1087,7 @@
     /** Do we need an access method to reference private symbol?
      */
     boolean needsPrivateAccess(Symbol sym, JCTree tree) {
+        if (disableAccessors) return false;
         if ((sym.flags() & PRIVATE) == 0 ||
                 (sym.owner == currentClass && !crossSpecializationBoundaries(sym, tree))) {
             return false;
@@ -1100,6 +1103,7 @@
     /** Do we need an access method to reference symbol in other package?
      */
     boolean needsProtectedAccess(Symbol sym, JCTree tree) {
+        if (disableAccessors) return false;
         if ((sym.flags() & PROTECTED) == 0 ||
             (sym.owner.owner == currentClass.owner && !crossSpecializationBoundaries(sym, tree)) || // fast special case
             sym.packge() == currentClass.packge())
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Mon Apr 11 15:08:12 2016 +0100
@@ -909,6 +909,55 @@
         endAttr(alenIdx);
     }
 
+    /**
+     * Write NestMembers attribute (if needed)
+     */
+    int writeNestMembersIfNeeded(ClassSymbol csym) {
+        ListBuffer<Symbol> nested = new ListBuffer<>();
+        listNested(csym, nested);
+        if (csym.owner.kind == PCK && !nested.isEmpty()) {
+            int alenIdx = writeAttr(names.NestMembers);
+            databuf.appendChar(nested.size());
+            for (Symbol s : nested) {
+                databuf.appendChar(poolwriter.putSymbol(s));
+            }
+            endAttr(alenIdx);
+            return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * Write MemberOfNest attribute (if needed)
+     */
+    int writeMemberOfNestIfNeeded(ClassSymbol csym) {
+        if (csym.owner.kind != PCK) {
+            int alenIdx = writeAttr(names.MemberOfNest);
+            databuf.appendChar(poolwriter.putSymbol(csym.outermostClass()));
+            endAttr(alenIdx);
+            return 1;
+        }
+        return 0;
+    }
+
+    private void listNested(Symbol sym, ListBuffer<Symbol> seen) {
+        if (sym.kind != TYP) return;
+        ClassSymbol csym = (ClassSymbol)sym;
+        if (csym.owner.kind != PCK) {
+            seen.add(csym);
+        }
+        if (csym.members() != null) {
+            for (Symbol s : sym.members().getSymbols()) {
+                listNested(s, seen);
+            }
+        }
+        if (csym.trans_local != null) {
+            for (Symbol s : csym.trans_local) {
+                listNested(s, seen);
+            }
+        }
+    }
+
     /** Write "bootstrapMethods" attribute.
      */
     void writeBootstrapMethods() {
@@ -1740,6 +1789,9 @@
         poolbuf.appendChar(target.minorVersion);
         poolbuf.appendChar(target.majorVersion);
 
+        acount += writeNestMembersIfNeeded(c);
+        acount += writeMemberOfNestIfNeeded(c);
+
         if (!poolwriter.innerClasses.isEmpty()) {
             writeInnerClasses();
             acount++;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Mon Apr 11 15:08:12 2016 +0100
@@ -152,6 +152,8 @@
     public final Name StackMap;
     public final Name StackMapTable;
     public final Name Synthetic;
+    public final Name MemberOfNest;
+    public final Name NestMembers;
     public final Name TypeVariablesMap;
     public final Name Value;
     public final Name ValueFactory;
@@ -304,6 +306,8 @@
         StackMap = fromString("StackMap");
         StackMapTable = fromString("StackMapTable");
         Synthetic = fromString("Synthetic");
+        MemberOfNest = fromString("MemberOfNest");
+        NestMembers = fromString("NestMembers");
         TypeVariablesMap = fromString("TypeVariablesMap");
         Value = fromString("Value");
         ValueFactory = fromString("ValueFactory");
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java	Mon Apr 11 15:08:12 2016 +0100
@@ -52,6 +52,8 @@
     public static final String LineNumberTable          = "LineNumberTable";
     public static final String LocalVariableTable       = "LocalVariableTable";
     public static final String LocalVariableTypeTable   = "LocalVariableTypeTable";
+    public static final String MemberOfNest             = "MemberOfNest";
+    public static final String NestMembers              = "NestMembers";
     public static final String BytecodeMapping          = "BytecodeMapping";
     public static final String TypeVariablesMap         = "TypeVariablesMap";
     public static final String Where                    = "Where";
@@ -124,6 +126,8 @@
             standardAttributes.put(LineNumberTable,   LineNumberTable_attribute.class);
             standardAttributes.put(LocalVariableTable, LocalVariableTable_attribute.class);
             standardAttributes.put(LocalVariableTypeTable, LocalVariableTypeTable_attribute.class);
+            standardAttributes.put(MemberOfNest, MemberOfNest_attribute.class);
+            standardAttributes.put(NestMembers, NestMembers_attribute.class);
             standardAttributes.put(BytecodeMapping, BytecodeMapping_attribute.class);
             standardAttributes.put(TypeVariablesMap, TypeVariablesMap_attribute.class);
             standardAttributes.put(Where,             Where_attribute.class);
@@ -187,6 +191,8 @@
         R visitLineNumberTable(LineNumberTable_attribute attr, P p);
         R visitLocalVariableTable(LocalVariableTable_attribute attr, P p);
         R visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, P p);
+        R visitMemberOfNest(MemberOfNest_attribute attr, P p);
+        R visitNestMembers(NestMembers_attribute attr, P p);
         R visitBytecodeMapping(BytecodeMapping_attribute attr, P p);
         R visitTypeVariablesMap(TypeVariablesMap_attribute attr, P p);
         R visitWhere(Where_attribute attr, P p);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java	Mon Apr 11 15:08:12 2016 +0100
@@ -33,7 +33,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.stream.IntStream;
-import java.util.stream.Stream;
 
 import static com.sun.tools.classfile.Annotation.*;
 import static com.sun.tools.classfile.ConstantPool.*;
@@ -513,6 +512,21 @@
             out.writeShort(entry.index);
         }
 
+        @Override
+        public Void visitMemberOfNest(MemberOfNest_attribute attr, ClassOutputStream out) {
+            out.writeShort(attr.top_index);
+            return null;
+        }
+
+        @Override
+        public Void visitNestMembers(NestMembers_attribute attr, ClassOutputStream out) {
+            out.writeShort(attr.members_indexes.length);
+            for (int i : attr.members_indexes) {
+                out.writeShort(i);
+            }
+            return null;
+        }
+
         public Void visitBytecodeMapping(BytecodeMapping_attribute attr, ClassOutputStream out) {
             out.writeShort(attr.bytecode_mapping_length);
             for (BytecodeMapping_attribute.Entry e: attr.bytecode_mapping_table) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/MemberOfNest_attribute.java	Mon Apr 11 15:08:12 2016 +0100
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.classfile;
+
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+
+import java.io.IOException;
+
+/**
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class MemberOfNest_attribute extends Attribute {
+    MemberOfNest_attribute(ClassReader cr, int name_index, int length) throws IOException {
+        super(name_index, length);
+        top_index = cr.readUnsignedShort();
+    }
+
+    public MemberOfNest_attribute(ConstantPool constant_pool, int signature_index)
+            throws ConstantPoolException {
+        this(constant_pool.getUTF8Index(Attribute.Signature), signature_index);
+    }
+
+    public MemberOfNest_attribute(int name_index, int top_index) {
+        super(name_index, 2);
+        this.top_index = top_index;
+    }
+
+    public CONSTANT_Class_info getNestTop(ConstantPool constant_pool) throws ConstantPoolException {
+        return constant_pool.getClassInfo(top_index);
+    }
+
+    public <R, D> R accept(Visitor<R, D> visitor, D data) {
+        return visitor.visitMemberOfNest(this, data);
+    }
+
+    public final int top_index;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/NestMembers_attribute.java	Mon Apr 11 15:08:12 2016 +0100
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.classfile;
+
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+
+import java.io.IOException;
+import java.util.stream.IntStream;
+
+/**
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class NestMembers_attribute extends Attribute {
+    NestMembers_attribute(ClassReader cr, int name_index, int length) throws IOException {
+        super(name_index, length);
+        int len = cr.readUnsignedShort();
+        members_indexes = new int[len];
+        for (int i = 0 ; i < len ; i++) {
+            members_indexes[i] = cr.readUnsignedShort();
+        }
+    }
+
+    public NestMembers_attribute(int name_index, int[] members_indexes) {
+        super(name_index, 2);
+        this.members_indexes = members_indexes;
+    }
+
+    public CONSTANT_Class_info[] getChildren(ConstantPool constant_pool) throws ConstantPoolException {
+        return IntStream.of(members_indexes)
+                .mapToObj(i -> {
+                    try {
+                        return constant_pool.getClassInfo(i);
+                    } catch (ConstantPoolException ex) {
+                        throw new AssertionError(ex);
+                    }
+                }).toArray(CONSTANT_Class_info[]::new);
+    }
+
+    public <R, D> R accept(Visitor<R, D> visitor, D data) {
+        return visitor.visitNestMembers(this, data);
+    }
+
+    public final int[] members_indexes;
+}
--- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java	Mon Apr 11 15:08:12 2016 +0100
@@ -36,6 +36,7 @@
 import com.sun.tools.classfile.Code_attribute;
 import com.sun.tools.classfile.CompilationID_attribute;
 import com.sun.tools.classfile.ConstantPool;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
 import com.sun.tools.classfile.ConstantPoolException;
 import com.sun.tools.classfile.ConstantValue_attribute;
 import com.sun.tools.classfile.DefaultAttribute;
@@ -47,6 +48,8 @@
 import com.sun.tools.classfile.LocalVariableTable_attribute;
 import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
 import com.sun.tools.classfile.MethodParameters_attribute;
+import com.sun.tools.classfile.MemberOfNest_attribute;
+import com.sun.tools.classfile.NestMembers_attribute;
 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
 import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
 import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
@@ -386,6 +389,30 @@
         return null;
     }
 
+    @Override
+    public Void visitMemberOfNest(MemberOfNest_attribute attr, Void aVoid) {
+        print("MemberOfNest: ");
+        constantWriter.write(attr.top_index);
+        println();
+        return null;
+    }
+
+    @Override
+    public Void visitNestMembers(NestMembers_attribute attr, Void aVoid) {
+        println("NestMembers:");
+        indent(+1);
+        try {
+            CONSTANT_Class_info[] children = attr.getChildren(constant_pool);
+            for (int i = 0; i < attr.members_indexes.length; i++) {
+                println(constantWriter.stringValue(children[i]));
+            }
+            indent(-1);
+        } catch (ConstantPoolException ex) {
+            throw new AssertionError(ex);
+        }
+        return null;
+    }
+
     public Void visitBytecodeMapping(BytecodeMapping_attribute attr, Void ignore) {
         println("BytecodeMapping:");
         indent(+1);
--- a/test/lib/annotations/annotations/classfile/ClassfileInspector.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/test/lib/annotations/annotations/classfile/ClassfileInspector.java	Mon Apr 11 15:08:12 2016 +0100
@@ -1365,6 +1365,18 @@
         }
 
         @Override
+        public Void visitMemberOfNest(MemberOfNest_attribute attr,
+                                      ExpectedTypeAnnotation expectedParameterAnnotation) {
+            return null;
+        }
+
+        @Override
+        public Void visitNestMembers(NestMembers_attribute attr,
+                                     ExpectedTypeAnnotation expectedParameterAnnotation) {
+            return null;
+        }
+
+        @Override
         public Void visitBytecodeMapping(BytecodeMapping_attribute attr,
                                          ExpectedTypeAnnotation expectedParameterAnnotation) {
             return null;
@@ -1584,6 +1596,18 @@
             }
 
             @Override
+            public Void visitMemberOfNest(MemberOfNest_attribute attr,
+                                          ExpectedAnnotation expectedParameterAnnotation) {
+                return null;
+            }
+
+            @Override
+            public Void visitNestMembers(NestMembers_attribute attr,
+                                         ExpectedAnnotation expectedParameterAnnotation) {
+                return null;
+            }
+
+            @Override
             public Void visitBytecodeMapping(BytecodeMapping_attribute attr,
                                              ExpectedAnnotation expectedParameterAnnotation) {
                 return null;
@@ -1810,6 +1834,18 @@
             }
 
             @Override
+            public Void visitMemberOfNest(MemberOfNest_attribute attr,
+                                          ExpectedParameterAnnotation expectedParameterAnnotation) {
+                return null;
+            }
+
+            @Override
+            public Void visitNestMembers(NestMembers_attribute attr,
+                                         ExpectedParameterAnnotation expectedParameterAnnotation) {
+                return null;
+            }
+
+            @Override
             public Void visitBytecodeMapping(BytecodeMapping_attribute attr,
                                              ExpectedParameterAnnotation expectedParameterAnnotation) {
                 return null;
--- a/test/tools/javac/MethodParameters/AttributeVisitor.java	Wed Mar 16 12:20:17 2016 +0000
+++ b/test/tools/javac/MethodParameters/AttributeVisitor.java	Mon Apr 11 15:08:12 2016 +0100
@@ -43,6 +43,8 @@
     public R visitLineNumberTable(LineNumberTable_attribute attr, P p) { return null; }
     public R visitLocalVariableTable(LocalVariableTable_attribute attr, P p) { return null; }
     public R visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, P p) { return null; }
+    public R visitMemberOfNest(MemberOfNest_attribute attr, P p) { return null; }
+    public R visitNestMembers(NestMembers_attribute attr, P p) { return null; }
     public R visitBytecodeMapping(BytecodeMapping_attribute attr, P p) { return null; }
     public R visitTypeVariablesMap(TypeVariablesMap_attribute attr, P p) { return null; }
     public R visitWhere(Where_attribute attr, P p){ return null; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/valhalla/nestmate/CheckNestmateAttrs.java	Mon Apr 11 15:08:12 2016 +0100
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Smoke test for nestmate classfile support
+ * @compile -XDdisableAccessors CheckNestmateAttrs.java
+ * @run main CheckNestmateAttrs
+ * @modules jdk.compiler
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Paths;
+
+public class CheckNestmateAttrs {
+
+    private void test() { }
+
+    class Inner {
+        void m() {
+            class LocalInner {
+                void testInner() {
+                    test();
+                }
+            }
+        }
+    }
+
+    static class Nested {
+        void s() {
+            class LocalNested { }
+        }
+    }
+
+    public static void main(String[] args) {
+        new CheckNestmateAttrs().run();
+    }
+
+    void run() {
+        String [] params = new String [] { "-v",
+                                            Paths.get(System.getProperty("test.classes"),
+                                                "CheckNestmateAttrs.class").toString() };
+        runCheck(params, new String [] {
+                        "NestMembers:" +
+                        "  CheckNestmateAttrs$Nested" +
+                        "  CheckNestmateAttrs$Nested$1LocalNested" +
+                        "  CheckNestmateAttrs$Inner" +
+                        "  CheckNestmateAttrs$Inner$1LocalInner"
+                        });
+
+        params = new String [] { "-v",
+                                 Paths.get(System.getProperty("test.classes"),
+                                     "CheckNestmateAttrs$Inner.class").toString() };
+
+        runCheck(params, new String [] { "MemberOfNest: class CheckNestmateAttrs" });
+
+        params = new String [] { "-v",
+                                 Paths.get(System.getProperty("test.classes"),
+                                     "CheckNestmateAttrs$Nested.class").toString() };
+
+        runCheck(params, new String [] { "MemberOfNest: class CheckNestmateAttrs" });
+
+        params = new String [] { "-v",
+                                 Paths.get(System.getProperty("test.classes"),
+                                     "CheckNestmateAttrs$Inner$1LocalInner.class").toString() };
+
+        runCheck(params, new String [] {
+                        "MemberOfNest: class CheckNestmateAttrs",
+                        "0: aload_0",
+                        "1: getfield      #12                 // Field this$1:LCheckNestmateAttrs$Inner;",
+                        "4: getfield      #22                 // Field CheckNestmateAttrs$Inner.this$0:LCheckNestmateAttrs;",
+                        "7: invokespecial #25                 // Method CheckNestmateAttrs.test:()V",
+                        "10: return"
+        });
+
+        params = new String [] { "-v",
+                                 Paths.get(System.getProperty("test.classes"),
+                                     "CheckNestmateAttrs$Nested$1LocalNested.class").toString() };
+
+        runCheck(params, new String [] { "MemberOfNest: class CheckNestmateAttrs" });
+     }
+
+     void runCheck(String [] params, String [] expectedOut) {
+        StringWriter s;
+        String out;
+
+        try (PrintWriter pw = new PrintWriter(s = new StringWriter())) {
+            com.sun.tools.javap.Main.run(params, pw);
+            out = s.toString();
+        }
+        for (String eo: expectedOut) {
+            if (!out.contains(eo))
+                throw new AssertionError("Unexpected output: " + out);
+        }
+    }
+}