3

Using ASM 5.0.4, I'm trying to find a class' fields and methods having a specific annotation. I like to avoid having to load the class to not worry about dependencies. So far, I can't figure out how to get a hold of the field/method annotations with this approach:

class AnnotationScanner extends ClassVisitor{

    public AnnotationVisitor visitAnnotation(String desc, boolean visible){
        System.out.println("visitAnnotation: desc="+desc+" visible="+visible);
        return super.visitAnnotation(desc,visible);
    }

    public void visitAttribute(Attribute attr){
        System.out.println("visitAttribute: attr="+attr);
        super.visitAttribute(attr);
    }

    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value){
        System.out.println("visitField: access="+access+" name="+name+" desc="+desc+" signature="+signature+" value="+value);
        return super.visitField(access,name,desc,signature,value);
    }

    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions){
        System.out.println("visitMethod: access="+access+" name="+name+" desc="+desc+" signature="+signature+" exceptions="+exceptions);
        return super.visitMethod(access,name,desc,signature,exceptions);
    }

    public static void main(String[] args) throws Exception{
        for (String arg : args){
            FileInputStream in = new FileInputStream(new File(arg));
            ClassReader cr = new ClassReader(in);
            cr.accept(new AnnotationScanner(Opcodes.ASM4),0);
        }
    }
}

The entire sample project is on GitHub.

Running it against a simple class

@MyClass(name="annotation scanner")
public class Scannee{
    @MyField(name="a string field") public String aStringField;
    @MyMethod(name="a method")      public void aMethod(){}
}

gives

visitAnnotation: desc=Lorg/springdot/sandbox/asm/MyClass; visible=true
visitField: access=1 name=aStringField desc=Ljava/lang/String; signature=null value=null
visitMethod: access=1 name=<init> desc=()V signature=null exceptions=null
visitMethod: access=1 name=aMethod desc=()V signature=null exceptions=null

but not the annotations of the field and the method.

How can I also get the field and method annotations?

Max Spring
  • 1,111
  • 2
  • 11
  • 18

2 Answers2

6

You will need to subclass FieldVisitor and MethodVisitor and override the method visitAnnotation, similar to your ClassVisitor subclass. For example,

class FieldAnnotationScanner extends FieldVisitor {
    public FieldAnnotationScanner() {
        super(Opcodes.ASM5);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        System.out.println("visitAnnotation: desc="+desc+" visible="+visible);
        return super.visitAnnotation(desc, visible);
    }
}

class MethodAnnotationScanner extends MethodVisitor {
    public MethodAnnotationScanner() {
        super(Opcodes.ASM5);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        System.out.println("visitAnnotation: desc="+desc+" visible="+visible);
        return super.visitAnnotation(desc, visible);
    }
}

Then connect them to your AnnotationScanner in visitField and visitMethod. For example, change your code to

@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value){
    System.out.println("visitField: access="+access+" name="+name+" desc="+desc+" signature="+signature+" value="+value);
    return new FieldAnnotationScanner();
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions){
    System.out.println("visitMethod: access="+access+" name="+name+" desc="+desc+" signature="+signature+" exceptions="+exceptions);
    return new MethodAnnotationScanner();
}
dejvuth
  • 6,986
  • 3
  • 33
  • 36
  • Incorporated the solution snippets into the [sample code](https://github.com/m2spring/asm-eval/commit/b31666b208db33cfaa8b5d285e085799d7d3da2a). It works. – Max Spring Dec 26 '15 at 00:59
1

The example doesn't show how tho access the name value of the @MyMethod annotation (name="a method"). To also read the a method value, you have to add two more Visitors:

static class MethodAnnotationScanner extends MethodVisitor {
        MethodAnnotationScanner() { 
          super(Opcodes.ASM8); 
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        System.out.println("visitMethodAnnotation: type= " + desc);
        return new MyAnnotationVisitor();
    }
}
static class MethodAnnotationValueScanner extends AnnotationVisitor {
    MethodAnnotationValueScanner() { 
        super(Opcodes.ASM8); 
    }

    @Override
    public void visit(String name, Object value) {
        System.out.println("visitMethodAnnotationValue: " + name + " = " + value);
        super.visit(name, value);
    }
}

And insert them by changing retruning the MethodAnnotationScanner int the visitMethod:


@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    System.out.println("visitMethod: access="+access+" name="+name+" desc="+desc+" signature="+signature+" exceptions="+exceptions);
    return new MethodAnnotationScanner();
}

You can see the full updated example here: how-to-read-a-java-class-method-annotation-value-with-asm

Tobse
  • 1,176
  • 1
  • 9
  • 22