如何避免VerifyError:“期望在堆栈上找到未初始化的对象”已经初始化的对象

时间:2012-11-05 15:17:11

标签: java instrumentation java-bytecode-asm

我正在使用ASM开发一个检测引擎,我需要拦截方法的调用,这些方法接收数组类型的参数。为此,我实现了MethodVisitor并在其visitMethodInsn中检查desc参数是否指定了数组类型的任何参数。如果目标方法没有数组类型的参数,那么我无事可做,我插入原始方法调用:super.visitMethodInsn(opcode, owner, name, desc);

另一方面,如果目标方法具有数组类型的参数,那么我必须为其参数执行特定的操作。我访问每个参数的最简单的解决方案是调用一个mediator方法,使用相同的目标方法描述符,在这个中介中我可以轻松访问它的参数(对应于传递给目标方法的参数)。

然而,当目标方法是实例构造函数(<init>)时会出现问题。在Java中,new XXX()被转换为字节码,如下所示:

  NEW XXX 
  DUP 
  INVOKESPECIAL XXXX.<init>()

根据我上面解释的方法,我将INVOKESPECIAL调用移动到中介方法中,新实例化的对象是此中介的第一个参数。然而,验证程序为此介体引发了一个错误,报告介体的第一个参数(它将是目标方法的第一个参数 - <init>)不是“单元化对象”。更确切地说,我收到了错误:Exception in thread "main" java.lang.VerifyError: Expecting to find unitialized object on stack”

一旦我将字节码NEWINVOKESPECIAL分成两个不同的方法,那么验证者声称传递给INVOKESPECIAL的参数已经初始化。

有什么建议可以解决这个问题吗? (请不要回答我,以避免中介并直接访问堆栈中的参数,因为复制和替换占据堆栈中任意位置的参数并不简单。)

1 个答案:

答案 0 :(得分:1)

验证者拒绝您的代码是正确的(请参阅JVM规范)。没有办法绕过字节码验证器。

一种方法是在构造函数调用时内联调解器代码。您仍然可以在调用构造函数之前或之后调用mediator的一部分作为方法调用,但是构造函数调用必须与新指令的方法相同。

另一种方法是为每个实例化的类创建特殊的调解器,因此调解器自己调用new指令以及构造函数调用。

如果他们能够正确完成所需的工作,您还可以查看现有的AOP库。