ASM 5:初始化ClassWriter时,COMPUTE_MAXS和COMPUTE_FRAMES之间有什么区别?

时间:2015-03-29 17:01:36

标签: java jvm java-bytecode-asm bytecode-manipulation

我是grappa的维护者。该包通过使用ASM生成一个扩展解析器类的类,从Java代码在运行时生成解析器。

我已经从ASM 4迁移到ASM 5,从生成JVM 1.5字节码到生成JVM 1.6字节码,现在我已经成功生成JVM 1.7字节码而不是......除了我不知道为什么这是有效的。

基本上,我做了以下事情:

  • 将参数更改为ClassWriter构造函数;之前它是new ClassWriter(ClassWriter.COMPUTE_MAXS),现在是new ClassWriter(ClassWriter.COMPUTE_FRAMES)
  • .visit()方法的每次调用的第一个参数从Opcodes.V1_6更改为Opcodes.V1_7

现在,为什么我不明白为什么它有效有两个原因:

  • 我有几次打电话给MethodVisitor,内容如下:

    mv.visitMaxs(0, 0); // trigger automatic computing
    

    这是否意味着可以删除这些说明?

  • 起初我只尝试COMPUTE_FRAMES构造函数添加 ClassWriter参数但是我的一次测试失败了一次声明:

    static class TestJoinParser
        extends EventBusParser<Object>
    {
        protected final JoinMatcherBuilder builder
            = join('a').using('b');
    }
    

    错误是:

    java.lang.ClassFormatError: Arguments can't fit into locals
    

    鉴于它是一个实例字段,我想它与在构造函数中初始化的特定参数有关吗?

无论如何,我的所有测试现在都有效,我正在尝试更重的测试......但是我的目标是进一步,我想至少了解为什么我的修改工作在所有...

1 个答案:

答案 0 :(得分:8)

首先,COMPUTE_FRAMESCOMPUTE_MAXS

的背景知识

ClassWriter.COMPUTE_MAXS的功能与ClassWriter.COMPUTE_FRAMES不同。

在最新版本的JVM中,类包含堆栈映射以及方法代码。该映射描述了在方法执行期间关键点(跳转目标)处的堆栈布局。在以前的版本中,JVM必须计算此信息,这在计算上是昂贵的。通过要求这些信息,JVM可以只验证帧的工作,这比重新计算所有内容要容易得多。

当然,编译器必须生成这些帧。这也很困难,因此ASM包含ClassWriter.COMPUTE_FRAMES以允许此操作 - 然后它将为您计算它们。

现在,ClassWriter.COMPUTE_MAXS做了类似的事情:JVM要求类文件指定每个方法使用的最大堆栈大小和变量数,这样它就可以验证这一点而不必自己计算。这对于堆叠帧的类似原因很有用:它的计算成本较低。

所以,实际上,你想要两个!但是,正如您所说,当您尝试添加它时失败了。可能的答案在于ClassWriter.COMPUTE_FRAMES的文档(这是第一个在混淆时应该看的地方):它说&#34; computeFrames意味着computeMaxs&#34; 。因此,您只需指定ClassWriter.COMPUTE_FRAMES

第一个问题

使用MethodVisitor时,仍需要visitMaxs次来电。此时ASM将重新计算帧和最大值。它只会忽略你给它的论据。所以,不,你不能删除它们。 (另请注意,它实际上并不是指令。)

第二个问题

我在上面解释了为什么只使用COMPUTE_FRAMES就足够了,这是这里的关键部分。我不确定为什么指定两个标志会破坏你的测试。如果您可以提供您在那里所做的确切代码,那么可能会有所帮助。我在ClassWriter / MethodWriter来源上做了一些消息来源,并且似乎没有任何理由说明为什么两者都会破坏您的代码。