使用ASM字节代码修改库验证实现方法代理的错误

时间:2012-11-06 14:44:36

标签: java java-bytecode-asm

我在使用ASM字节代码库创建代理方法时遇到了困难。

我希望转换以下代码:

public ReturnType doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

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

为:

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 );
}

要创建copyOff_doSomething()方法,我使用以下代码:

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 );
    }
}

上面的代码有效地删除了原始的doSomething()方法,并将其复制到copyOff_doSomething()及其代码体。

当我生成替换doSomething()方法时出现问题:

@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();
}

问题是我收到以下错误:

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

我不知道错误的含义以及如何修复错误。如上所述,我使用ASMfier生成替换doSomething()方法的代码。

我的策略是使用visitMethod方法将doSomething()重命名为copyOff_doSomething(),然后使用visitEnd()方法从头开始生成一个新的替换doSomething()方法,调用copyOff_doSomething()方法。

也许ASM不能满足我的策略,我应该以不同的方式做到这一点?

也许我需要修改ASMfier为替换doSomething()方法生成的代码。我对生成的代码的理解并不是那么好。

我正在使用JDK 1.7和ASM 4.0。我使用ASMfier来查看用于生成替换doSomething()方法的ASM字节代码指令。

如果有人知道ASM,我们将非常感谢您的帮助吗?

干杯

约翰

1 个答案:

答案 0 :(得分:0)

owner参数(在您的示例中硬编码为com/javaspeak/classloader/tests/proxymethod/FinalMethod)很可能与this的实际类型不匹配。因此,您的适配器应使用name调用的ClassVisitor.visit(..)值。

另请参阅我的AOSD'07论文中的“替换方法主体”部分“使用ASM框架实现常见的字节码转换模式”。