我正在编写一个编译器的代码生成器,我在我正在教授的编译器类中使用它作为示例。我们使用ASM 5.0.3生成JVM代码。我能够评估大多数直接表达式和语句,有些调用运行时方法,就好了。对于示例参考语言,我们只有boolean和ints,以及一些带有类型推断的简单控制结构。所有“程序”都会在结果类中编译为静态main方法。这是我使用ASM的第一年。我们以前用过茉莉花。
我遇到了stackmap帧的问题。这是我正在编译的示例程序,它将显示问题:
i <- 5
if
i < 0 :: j <- 0
i = 0 :: j <- 1
i > 0 :: j <- 2
fi
相当于Java程序:
int i = 5, j;
if (i < 0) j = 0;
else if (i == 0)j = 1;
else if (i > 0) j = 2;
如果我只是用一个替代方案编写程序,它会生成正常的。但是当我有多个时,就像在这种情况下,我得到这样的痕迹:
java.lang.VerifyError: Inconsistent stackmap frames at branch target 39
Exception Details:
Location:
djkcode/Test.main([Ljava/lang/String;)V @12: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @12
flags: { }
locals: { '[Ljava/lang/String;', integer, integer }
stack: { integer }
Stackmap Frame:
bci: @39
flags: { }
locals: { '[Ljava/lang/String;', integer }
stack: { integer, integer, integer }
Bytecode:
0x0000000: 120b 3c1b 120c 9900 0912 0c3d a700 1b1b
0x0000010: 120c 9900 0912 0d3d a700 0f1b 120c 9900
0x0000020: 0912 0e3d a700 03b1
Stackmap Table:
full_frame(@15,{Object[#16],Integer},{Integer})
full_frame(@27,{Object[#16],Integer},{Integer,Integer})
full_frame(@39,{Object[#16],Integer},{Integer,Integer,Integer})
我可以在此调试输出中看到我做的ASM调用:
DBG> cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
DBG> cw.visit(V1_8, ACC_PUBLIC+ACC_STATIC, "djkcode/Test", null, "java/lang/Object", null
DBG> Generate the default constructor..this works in all cases
DBG>
Start the main method
DBG> mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null
DBG> mv.visitCode()
DBG> mv.visitLdcInsn(5);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> Enter Alternative
DBG> Label endLabel = new Label(); // value = L692342133
DBG> Enter Guard
DBG> Label failLabel = new Label(); // failLabel = L578866604
DBG> mv.visitVarInsn(ILOAD, 1);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitJumpInsn(IFEQ, failLabel);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> mv.visitJumpInsn(GOTO, guardLabelStack.peek()); // label value = L692342133
DBG> mv.visitLabel(failLabel); // failLabel = L578866604
DBG> L578866604:
DBG> Exit Guard
DBG> Enter Guard
DBG> Label failLabel = new Label(); // failLabel = L1156060786
DBG> mv.visitVarInsn(ILOAD, 1);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitJumpInsn(IFEQ, failLabel);
DBG> mv.visitLdcInsn(1);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> mv.visitJumpInsn(GOTO, guardLabelStack.peek()); // label value = L692342133
DBG> mv.visitLabel(failLabel); // failLabel = L1156060786
DBG> L1156060786:
DBG> Exit Guard
DBG> Enter Guard
DBG> Label failLabel = new Label(); // failLabel = L1612799726
DBG> mv.visitVarInsn(ILOAD, 1);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitJumpInsn(IFEQ, failLabel);
DBG> mv.visitLdcInsn(2);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> mv.visitJumpInsn(GOTO, guardLabelStack.peek()); // label value = L692342133
DBG> mv.visitLabel(failLabel); // failLabel = L1612799726
DBG> L1612799726:
DBG> Exit Guard
DBG> mv.visitLabel(endLabel); // endLabel = L692342133
DBG> L692342133:
DBG> Exit Alternative
DBG> mv.visitInsn(RETURN)
DBG> mv.visitMaxs(0, 0);
DBG> mv.visitEnd();
DBG> cw.visitEnd();
如果我在Eclips ASM字节码浏览器中查看该类,我会得到:
package asm.djkcode;
...
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(52, ACC_PUBLIC + ACC_STATIC, "djkcode/Test", null, "java/lang/Object", null);
...
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
...
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l0);
mv.visitFrame(Opcodes.F_FULL, 2, new Object[] {"[Ljava/lang/String;", Opcodes.INTEGER}, 1, new Object[] {Opcodes.INTEGER});
...
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_FULL, 2, new Object[] {"[Ljava/lang/String;", Opcodes.INTEGER}, 2, new Object[] {Opcodes.INTEGER, Opcodes.INTEGER});
...
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_FULL, 2, new Object[] {"[Ljava/lang/String;", Opcodes.INTEGER}, 3, new Object[] {Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER});
mv.visitInsn(RETURN);
mv.visitMaxs(4, 3);
mv.visitEnd();
}
cw.visitEnd();
似乎有一些visitFrame语句不正确,但文档(我使用的是ASM 5.0.3)说如果使用COMPUTE_FRAMES,则不必调用visitFrame。我错过了什么?我花了很多时间试图弄清楚这一点,并且可以想象我的学生必须得到多么沮丧。我开始讽刺茉莉花了。
答案 0 :(得分:2)
问题是您正在使用ifeq
指令错误。 ifeq
将一个参数与零进行比较,并相应地进行分支。您正在将两个值推入堆栈,因为您似乎将其与if_icmpeq
混淆,后者将测试两个操作数是否相等。因此,在每个条件块之后,悬空int
保留在堆栈上,因此块之前的代码具有与其后面的代码不同的堆栈深度,这意味着您不能分支它,因为如果不允许分支源和目标具有不同的堆栈深度(这正是VerifierError
告诉您的,您可以看到每个显式帧在堆栈上还有一个Integer
。
因此,您可以将ifeq
指令更改为if_icmpeq
以反映您的初衷,但保留ifeq
指令并移除零常数的推送会更有效。
请注意,所有三个指令执行相同的ifeq
测试似乎是另一个错误。我想,您希望将< 0
和> 0
代码编译为相应的iflt
和ifgt
指令。再次,请注意iflt
/ ifgt
和ificmplt
/ ificmpgt
说明之间的差异......