Search code examples
javajava-bytecode-asm

Verify Errors implementing method proxy using ASM byte code modification library


I am having difficulties using the ASM byte code library to create proxy methods.

I wish to convert the following code:

public ReturnType doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

    ReturnType returnType = new ReturnType();
    returnType.setDataOne( parameterOne.getDataOne() );
    returnType.setDataTwo( parameterTwo.getDataTwo() );
    return returnType;
}

to:

public ReturnType copyOff_doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

    ReturnType returnType = new ReturnType();
    returnType.setDataOne( parameterOne.getDataOne() );
    returnType.setDataTwo( parameterTwo.getDataTwo() );
    return returnType;
}

public ReturnType doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

    return copyOff_doSomething( parameterOne, parameterTwo );
}

To create the copyOff_doSomething() method I am using the following code:

public MethodVisitor visitMethod( int access, String name, String desc, 
    String signature, String[] exceptions ) {

    System.out.println(
        "access= " + access + ", name = " + name + ", desc = " +
                  desc + ", signature = " + signature );

    if ( name.equals( "doSomething" ) ){

        MethodVisitor methodVisitor =
            super.visitMethod( access, "copyOff_" + name, desc, 
                signature, exceptions );

        return methodVisitor;
    }
    else {
        return super.visitMethod( access, name, desc, signature, exceptions );
    }
}

The above code effectively removes the original doSomething() method and copies it to copyOff_doSomething() along with its body of code.

My problem occurs when I am generating the replacement doSomething() method:

@Override
public void visitEnd() {

    MethodVisitor mv = super.visitMethod( ACC_PUBLIC, "doSomething", 
        "(Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;" + 
        "Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;)" + 
        "Lcom/javaspeak/classloader/tests/proxymethod/ReturnType;", 
            null, null );

    mv.visitCode();
    Label l0 = new Label();
    mv.visitLabel( l0 );
    mv.visitLineNumber( 18, l0 );
    mv.visitVarInsn( ALOAD, 0 );
    mv.visitVarInsn( ALOAD, 1 );
    mv.visitVarInsn( ALOAD, 2 );

    mv.visitMethodInsn( INVOKEVIRTUAL, 
        "com/javaspeak/classloader/tests/proxymethod/FinalMethod", 
           "copyOff_doSomething", 
               "(Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;" +        
               "Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;)" + 
               "Lcom/javaspeak/classloader/tests/proxymethod/ReturnType;");

    mv.visitInsn( ARETURN );
    Label l1 = new Label();
    mv.visitLabel( l1 );

    mv.visitLocalVariable( "this", 
       "Lcom/javaspeak/classloader/tests/proxymethod/FinalMethod;", 
           null, l0, l1, 0 );

    mv.visitLocalVariable( "parameterOne", 
        "Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;", 
            null, l0, l1, 1 );

    mv.visitLocalVariable( "parameterTwo", 
        "Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;", 
            null, l0, l1, 2 );

    mv.visitMaxs( 3, 3 );
    mv.visitEnd();
    super.visitEnd();
}

The problem is I get the following error:

java.lang.VerifyError: Bad type on operand stack in method 
com.javaspeak.classloader.tests.proxymethod.ProxyMethod.doSomething(
Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;
Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;)
Lcom/javaspeak/classloader/tests/proxymethod/ReturnType; at offset 3

I do not know what the error means and how to fix the error. As mentioned above I used ASMfier to generate the code for the replacement doSomething() method.

My strategy was to use the visitMethod method to rename doSomething() to copyOff_doSomething() and then use the visitEnd() method to generate a new replacement doSomething() method from scratch that calls the copyOff_doSomething() method.

Perhaps ASM does not cater for my strategy and I should do it a different way?

Perhaps I need to modify the code that the ASMfier generated for the replacement doSomething() method. My understanding of the generated code is not that good.

I am using JDK 1.7 and ASM 4.0. I used ASMfier to see what ASM byte code instructions to use to generate the replacement doSomething() method.

If anyone knows ASM your help will be much appreciated?

Cheers

John


Solution

  • Most likely the owner parameter (hardcoded to com/javaspeak/classloader/tests/proxymethod/FinalMethod in your example) don't match the actual type for this. So, your adapter should be using the name value from ClassVisitor.visit(..) call.

    Also see "Replace Method Body" section in my AOSD'07 paper "Using ASM framework to implement common bytecode transformation patterns".