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 InvokeFinder
and 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?
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)