我使用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可以通过在封闭代码属性的开头附加一些指令来推断我,那将是很好的。
答案 0 :(得分:1)
最简单的方法是完全避免使用堆栈映射。仅当您要使用版本51.0+功能(即invokedynamic)时才需要堆栈映射。如果您不使用invokedynamic,则可以将classfile版本设置为50或更低,并且根本不需要堆栈映射。事实上,如果您没有明确指定版本,Krakatau会将版本默认为49.0,因此您无需在那里做任何事情。
如果您使用的是invokedynamic,那么事情就会变得越来越棘手,因为您必须生成堆栈映射。基本规则是,只要可以从前一条指令以外的任何地方访问指令,就需要堆栈映射条目。 (我想你还需要输入死代码的条目,但我没有检查过)。
至于实际生成条目,有几种不同类型的堆栈帧,但您不必担心。最简单的方法是每次只使用full
帧。这涉及列出局部变量(“寄存器”)槽和操作数堆栈中每个实时值的当前类型,因此您必须跟踪它们。
是的,计算和生成所有类型信息是一件痛苦的事情,但你不得不责怪甲骨文,而不是我。或者,您可以尝试使用ASM为您生成堆栈映射,如文档中所示。