Search code examples
javanetbeansbytecodestatic-analysisjava-bytecode-asm

ASM: MethodVisitor's methods are not called by the ClassReader


I am writing a small tool to perform static analysis of bytecode using ASM 5. I give my input Java class to a ClassReader which triggers events of a custom ClassVisitor. My ClassVisitor overrides visitMethod to return my custom MethodVisitor if certain conditions are met, and null if they are not.

The problem is, the ClassReader never invokes the MethodVisitor's methods; I debugged it with NetBeans and i saw that the visitor is correctly returned (it is not null) but still the execution never enters the visitMethodInsn even though there are method instruction in the classes I'm using for testing.

I have used ASM before, but this is the first time i encounter this problem.

Here is my code for MyClassVisitor.visitMethod:

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    InvokeFinder visitor;
    if(condition.isMet()) {
        visitor = new InvokeFinder(api);
        visitor.doStuff();
    } else {
        visitor = null;
    }
    return visitor;
}

Here the code of InvokeFinder, which is never entered during execution:

public class InvokeFinder extends MethodVisitor {

    public InvokeFinder(int api) {
        super(api);
    }

    void unrelatedMethod(String parameter) {
        doStuff(parameter);
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
        switch (opcode) {
            case Opcodes.INVOKEVIRTUAL:
            case Opcodes.INVOKESPECIAL:
            case Opcodes.INVOKEDYNAMIC:
                doSomething();
                break;
            case Opcodes.INVOKEINTERFACE:
                doSomethingElse();
                break;
            case Opcodes.INVOKESTATIC:
                doSomethingDifferentAgain();
                break;
            default: break;
        }
    }
}

And finally this is the class I'm testing on (this is not meant to be ever executed, its sole purpose is to be compiled and then analyzed by my tool):

public class MyTestClass{

  public MyTestClass(String s) {
    System.out.println( "Function Name" );
  }

  public static void main(String args[]) {
    CGraph g = new CGraph("entry");

    g.enterBF(new CGVisitor() {
        @Override
        public void nodeOperations(CGraph graph, CGraphNode currentNode) {
            System.out.println("20");
        }

        @Override
        public String getString() {
            throw new UnsupportedOperationException("No string can be returned from this anonymous implementation of CGVisitor.");
        }
    });

    a( 11 );
  }

  public static void a(int n) {
    b( n );
  }

  public static void b(int m) {
    int n;

    if (m >= 10)
      a( m-1 );
    else
      System.out.println(m);
  }
}

I double checked that the visitMethod method above is always executed, returns a non-null instance of InvokeFinderand that this instance never happens to be executed; how is it possible that under these conditions InvokeFinder.visitMethodInsn is never called by the ClassReader, even if there are invocation instruction in some methods of MyTestClass (e.g. inside method a(int))?

EDIT: Going further with the debugging, i found out that ClassReader calls the default MethodVisitor.visitMethodInsn instead of calling the overridden version InvokeFinder.visitMethodInsn. Why does this happen?


Solution

  • Turned out that i changed API version of ASM and the signature of MethodVisitor.visitMethodInsn is different between versions 4 and 5. The dynamic binding no longer recognized it as an overriding method.

    I could fix this by adding the missing parameters to InvokeFinder.visitMethodInsn, whose signature now is

    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)