自动用Krakatau填充StackMapTable

时间:2017-11-12 04:47:43

标签: java compiler-construction jvm bytecode

我使用Krakatau从Jasmin语法生成字节码。我的Jasmin代码是通过三地址代码(TAC)形式的中间代码的直接翻译创建的。我的问题是,我无法确定,只是 查看TAC,在翻译跳转语句时我应该在stack指令中定位。

Krakatau关于汇编程序的文档说明如下:

  

自动填写StackMapTable属性的内容   基于封闭代码属性中的堆栈指令。如果这   属性的内容是非空的,未指定属性   明确地,将隐含地添加一个。

但它也说:

  

Krakatau不会从没有任何堆栈信息的字节码中为您计算新的堆栈映射。如果你想这样做,你应该尝试使用ASM。

我对哪种指令感到困惑,我应该在哪里将它们添加到我的翻译中,以便汇编程序知道如何隐式添加属性。任何有关这方面的帮助将不胜感激。

例如,我的代码用类似于Java的语法编写(但不一样,所以我需要使用另一个编译器):

我从编译器的前端获得的是左侧的TAC,我的翻译器在右侧生成Jasmin代码(我删除页眉和页脚,只留下字节码本身,错过了返回指令):

当我尝试运行它时,我会得到类似的结果:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 24
Exception Details:
  Location:
    Main.main([Ljava/lang/String;)V @16: iflt
  Reason:
    Expected stackmap frame at this location.
  Bytecode:
    0x0000000: 1103 e8bc 073a 050f 4814 000a 4a27 2997
    0x0000010: 9b00 0819 0503 2752 b1

    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)

我知道发生这种情况是因为它在标有 L23 的行之后期待.stack append Double Double Object [D,但正如我之前所说,这个例子很简单,我不能总是推断这些指令的位置。如果Krakatau可以通过在封闭代码属性的开头附加一些指令来推断我,那将是很好的。

1 个答案:

答案 0 :(得分:1)

最简单的方法是完全避免使用堆栈映射。仅当您要使用版本51.0+功能(即invokedynamic)时才需要堆栈映射。如果您不使用invokedynamic,则可以将classfile版本设置为50或更低,并且根本不需要堆栈映射。事实上,如果您没有明确指定版本,Krakatau会将版本默认为49.0,因此您无需在那里做任何事情。

如果您使用的是invokedynamic,那么事情就会变得越来越棘手,因为您必须生成堆栈映射。基本规则是,只要可以从前一条指令以外的任何地方访问指令,就需要堆栈映射条目。 (我想你还需要输入死代码的条目,但我没有检查过)。

至于实际生成条目,有几种不同类型的堆栈帧,但您不必担心。最简单的方法是每次只使用full帧。这涉及列出局部变量(“寄存器”)槽和操作数堆栈中每个实时值的当前类型,因此您必须跟踪它们。

是的,计算和生成所有类型信息是一件痛苦的事情,但你不得不责怪甲骨文,而不是我。或者,您可以尝试使用ASM为您生成堆栈映射,如文档中所示。

相关问题