Question

I'm writing a bytecode transformer using the ClassAdapter of the asm framework. If a custom annotation is present on the class I want to add some methods and make the class implement an interface. Adding the methods is working fine, but I'm wondering what the best way is to make the class implement an interface. Since visitAnnotation is only called after visit, I need to somehow delay calling the super visit method and buffer all needed information until then.

Has anyone implemented something similar? Should I use the tree api of asm for this although the package documentation recommends avoiding it if possible?

Here is the general structure of the transformation:

public class MyClassAdapter extends ClassAdapter {
    private String  classname;
    private boolean instrument;

    public PropertyChangeSupportAdapter(ClassVisitor cv) {
        super(cv);
    }

    @Override
    public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this.classname = name;
    }

    @Override
    public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
        if (desc.equals("Lmypackage/MyAnnotation;")) {
            instrument = true;
            System.out.println("Instrumenting " + classname);
        }
        return super.visitAnnotation(desc, visible);
    }

    @Override
    public void visitEnd() {
        if (instrument) {
            // add methods
        }
    }
}
Was it helpful?

Solution

I ended up using a combination of the ClassNode and ClassAdapter apis. First the class file is parsed into a ClassNode:

ClassReader cr = new ClassReader(inputStream);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);

Then it is possible to check wheter cn.visibleAnnotation or cn.invisibleAnnotations contains my annotation and also if the class already implements the interface I want to add. In that case the second step can be skipped. The ClassNode can then be transformed using the ClassAdapter api as in the question:

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
CheckClassAdapter ca = new CheckClassAdapter(cw);
ClassVisitor cv = new PropertyChangeSupportAdapter(ca);
cn.accept(cv);

The note from the package documentation, which was the reason for my question, mentioned a big performance difference between these apis:

It is almost two times faster to read, "modify" and write a class with a ClassAdapter than with a ClassNode. ... This is also why it is recommended not to use this class adapter when it is possible.

After rereading that again the difference seems to come from using the ClassNode to manipulate the bytecodes. However, I did not benchmark this hybrid solution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top