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

我是格拉巴酒的维护者。此包在运行时从 Java 代码生成解析器,方法是使用 ASM 生成扩展解析器类的类。

我已经从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_6Opcodes.V1_7

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

  • 我有几个调用s,内容如下:MethodVisitor

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

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

  • 起初,我只尝试将参数添加到构造函数中,但是对于我的一个测试,它一度失败了,我声明:COMPUTE_FRAMESClassWriter

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

    错误是:

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

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

无论如何,我所有的测试现在都有效,我正在尝试更重的测试等......但是,由于我的目标是走得更远,我想至少了解一下为什么我的修改有效......


答案 1

首先,关于和COMPUTE_FRAMESCOMPUTE_MAXS

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

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

当然,编译器必须生成这些帧。这也很困难,所以ASM包含允许这样做 - 然后它会为你计算它们。ClassWriter.COMPUTE_FRAMES

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

所以,实际上,你两者都想要!但是,正如你所说,当你试图添加它们时,它失败了。可能的答案在于文档(这是你对它感到困惑时应该看的第一个地方):它说“computeFrames意味着computeMaxs”。因此,您只需指定.ClassWriter.COMPUTE_FRAMESClassWriter.COMPUTE_FRAMES

第一个问题

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

第二个问题

我在上面解释了为什么只需使用就足够了,这是这里的关键部分。我不确定为什么指定这两个标志会破坏你的测试。如果你能为你在那里所做的事情提供确切的代码,它可能会提供帮助。我对/source进行了一些源代码挖掘,似乎没有任何理由说明为什么指定两者会破坏您的代码。COMPUTE_FRAMESClassWriterMethodWriter


答案 2

推荐