在无限深度上访问JVM方法堆栈

时间:2014-07-08 11:18:22

标签: jvm java-bytecode-asm

我想使用ASM MethodVisitor进行字节码转换。 我的目标是使用以下序列替换调用某些方法(它们标有注释)的所有INVOKEINTERFACE指令:

// instructions that put instance object on stack
INVOKESTATIC package/name/Wrapper wrap (Ljava/lang/Object;)Lpackage/name/Wrapper;
LDC <method_name>
// instructions that put first parameter on stack
INVOKESTATIC package/name/Wrapper wrap (Ljava/lang/Object;)Lpackage/name/Wrapper;
// and so forth for all parameters
INVOKESTATIC package/name/Wrapper invoke (Lpackage/name/Wrapper;<repeat n times>)Lpackage/name/Wrapper;
INVOKESTATIC package/name/Wrapper unwrap (Lpackage/name/Wrapper)Ljava/lang/Object;

使用单参数方法,这是非常容易的任务。但是,由于无法在任何深度上检索堆栈,因此使用更多参数会变得非常简单。什么是可能的解决方案?好吧,首先是在本地备份最后N堆栈值,但在这种情况下,变换器必须知道使用的局部变量的数量,因此需要第二次传递。此外,我不喜欢这种方法如何破坏所有这些堆栈备份的字节代码。

第二个解决方案是构建def-use链并在堆栈出现后直接使用此信息包装堆栈值。这种方法不会破坏字节代码,但存在一些缺点:

  1. 首先,很难实施。
  2. 其次,需要额外的通行证。
  3. 第三,使用链条的搜索和保存范围很广。
  4. 第四,有时在包装后处理DUP指令很困难,而且还没有包装掉的值。
  5. 有人会注意到我可以在我的案例中使用代理。不幸的是,我不能,因为转换的字节代码将由另一个依赖于Wrapper信息的变换器/分析器处理。

1 个答案:

答案 0 :(得分:0)

我建议你使用ASM的树API。此API允许您在操作数堆栈中来回导航。这样,您实现将保持相当简单,因为您可以在发现要替换的方法之前简单地添加指令。

否则,在将值存储在局部变量数组中时,可以使用ASM的LocalVariableSorter。与此同时,您不再需要担心覆盖以后的插槽。此实现将执行得更快,因为它可以节省您从树API的开销。

我不会太担心表现。 JVM的JIT编译器非常智能,可以在代码变热时对其进行优化。两种解决方案都应该没问题。