Access private inner classes in java ASM

The rule that inner and outer classes can access their private members is a pure Java programming language construct which isn’t reflected by the JVM’s access checks. When inner classes were introduced in Java 1.1, they were introduced in a way that didn’t require changes to the JVM. From the JVM’s point of view, nested classes are ordinary (top level) classes with some additional, ignorable meta information.

When an inner class is declared private, it’s ordinary class access level is “default” aka package-private. When it’s declared protected, it will be public on the JVM level.

When nested classes access each other’s private fields or methods, the compiler will generate synthetic helper methods with package-private access in the target class, providing the desired access.

So from the JVM’s point of view, you are trying to subclass a package-private class and the dollar in the name is just an ordinary name character. The generated class has a matching qualified name, but you are trying to define it in a different class loader, so the JVM considers these packages not identical at runtime, despite their identical name.

You can verify that the package level access works, if you define the class within the same class loader. Change the line

Class<?> genClass = myClassLoader.defineClass("Parent$OtherChild", bytes);

to

Method m=ClassLoader.class.getDeclaredMethod(
    "defineClass", String.class, byte[].class, int.class, int.class);
m.setAccessible(true);
Class<?> genClass=(Class<?>)m.invoke(
    Child.class.getClassLoader(), "Parent$OtherChild", bytes, 0, bytes.length);

Alternatively, you could declare Child as protected. Since it’s a public class on the low level then, it will be accessible by other class loaders.

Note that in both cases, you haven’t created a new inner class but just a class named Parent$OtherChild extending an inner class. The only difference is the meta information about the outer-inner class relationship but if you add that attribute to your generated class claiming that it was an inner class of Parent, it could happen that it gets rejected by the verifier because the meta information of Parent doesn’t mention the existence of an inner class OtherChild. That’s the only place where a JVM might ever look at this attribute.

But besides Reflection reporting inner class relationships, there is no functional difference between top level classes and nested classes anyway. As said, classes actually don’t have the access levels protected nor private and for all other member accesses, you’ll have to generate the necessary code yourself anyway. If you can’t modify the code of the existing classes Parent or Parent$Child, you can’t access those of their private members for which these synthetic accessor methods don’t already exist…


Starting with Java 9, there is a standard way to define a new class within an accessible context, which makes the “Reflection with access override” approach shown above obsolete for this use case, e.g. the following works:

public class Parent {
    public void generateClass() {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        String superType = Type.getInternalName(Child.class);
        cw.visit(49, Opcodes.ACC_PUBLIC, "Parent$OtherChild", null, superType, null);
        MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superType, "<init>", "()V", false);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(-1, -1);
        mv.visitEnd();
        // etc
        byte[] bytes = cw.toByteArray();
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        try {
            Class<?> genClass = lookup.defineClass(bytes);
            Child ch = (Child)
                lookup.findConstructor(genClass, MethodType.methodType(void.class))
                      .invoke();
            System.out.println(ch);
        } catch(Throwable ex) {
            Logger.getLogger(Parent.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    private static class Child {
        Child() {}
    }
}