我正在使用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”
。
一旦我将字节码NEW
和INVOKESPECIAL
分成两个不同的方法,那么验证者声称传递给INVOKESPECIAL
的参数已经初始化。
有什么建议可以解决这个问题吗? (请不要回答我,以避免中介并直接访问堆栈中的参数,因为复制和替换占据堆栈中任意位置的参数并不简单。)
答案 0 :(得分:1)
验证者拒绝您的代码是正确的(请参阅JVM规范)。没有办法绕过字节码验证器。
一种方法是在构造函数调用时内联调解器代码。您仍然可以在调用构造函数之前或之后调用mediator的一部分作为方法调用,但是构造函数调用必须与新指令的方法相同。
另一种方法是为每个实例化的类创建特殊的调解器,因此调解器自己调用new
指令以及构造函数调用。
如果他们能够正确完成所需的工作,您还可以查看现有的AOP库。