Search code examples
anonymous-classjava-bytecode-asm

How do I know if I am visiting an anonymous class in ASM?


I have extended the ASM ClassVisitor and want to know if the visited class in an anonymous class.

Since I do have the class file I figured that if the class file name ended with $[1-9][0-9]*.class it would be an anonymous class. But currency characters are valid in class names so for example a class named MyClass$1 would match (classfile is named MyClass$1.class) even though it is not an anonymous class. I know most people would probably not name things like this but it is allowed.

So I thought about overriding the visitOuterClass method to see if it had an outer class that would eliminate outer classes with names ending in $# but still you could have an inner non anonymous class name ending with, for example, $1.

So is there some way in ASM to know that it is visiting an anonymous class? Or a better trick than mine?

# means any number (regexp: [1-9][0-9]*)


Solution

  • The relevant criteria is whether the class’ InnerClasses attribute declares the class to be an anonymous inner class. ASM report the contents by calling visitInnerClass for each entry of the attribute, if present.

    So you can check it like

    public class CheckForInnerClass extends ClassVisitor {
        public static void main(String[] args) throws IOException {
            Class<?>[] test={ Object.class, Map.Entry.class, new Object(){}.getClass() };
            for(Class<?> c: test) {
                new ClassReader(c.getName())
                    .accept(new CheckForInnerClass(Opcodes.ASM5), ClassReader.SKIP_CODE);
            }
        }
    
        private String actualName;
        private Boolean anonymous;
    
        public CheckForInnerClass(int api) {
            super(Opcodes.ASM5);
        }
        @Override
        public void visit(int version, int access,
            String name, String signature, String superName, String[] interfaces) {
            actualName=name;
        }
        @Override
        public void visitInnerClass(String name, String outer, String innerName, int access) {
            if(name.equals(actualName)) {
                anonymous = innerName==null;
            }
        }
        @Override
        public void visitEnd() {
            System.out.println(actualName+": is "
                + (anonymous==null? "not an": anonymous? "an anonymous": "a named")
                + " inner class");
        }
    }
    

    which prints

    java/lang/Object: is not an inner class
    java/util/Map$Entry: is a named inner class
    CheckForInnerClass$1: is an anonymous inner class