为什么methodvistor.visitMaxs(0,0)在JavaASM中崩溃?
我正在使用JavaASM(4.0)编写一个简单的编译器。我使用classWriter(COMPUTE_FRAMES)来编写一个类 对于简单的程序来说,这一切都很好,但是当我开始嵌套跳转(例如,IfThenElse语句中的while语句)之后,我调用methodVisitor.visitMaxs(0,0)时,会出现以下错误:为什么methodvistor.visitMaxs(0,0)在JavaASM中崩溃?,java,java-bytecode-asm,Java,Java Bytecode Asm,我正在使用JavaASM(4.0)编写一个简单的编译器。我使用classWriter(COMPUTE_FRAMES)来编写一个类 对于简单的程序来说,这一切都很好,但是当我开始嵌套跳转(例如,IfThenElse语句中的while语句)之后,我调用methodVisitor.visitMaxs(0,0)时,会出现以下错误: java.lang.NullPointerException at org.objectweb.asm.Frame.a(Unknown Source) at org.obje
java.lang.NullPointerException
at org.objectweb.asm.Frame.a(Unknown Source)
at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
at front.ir.visitors.ClassWriterVisitor.visitAstProcedure(ClassWriterVisitor.java:182)
at front.ir.visitors.ClassWriterVisitor.visitAstProcedure(ClassWriterVisitor.java:1)
at front.ir.ASTProcedure.accept(ASTProcedure.java:27)
at front.ir.visitors.ClassWriterVisitor.visitProgram(ClassWriterVisitor.java:235)
at front.ir.visitors.ClassWriterVisitor.visitProgram(ClassWriterVisitor.java:1)
at front.ir.ASTProgram.accept(ASTProgram.java:37)
at front.FrontEnd.main(FrontEnd.java:122)
我的while语句的代码:
jumplabels.push(new Label());
L2 = new Label();
mv.visitJumpInsn(Opcodes.GOTO, L2);
mv.visitLabel(jumplabels.peek());
whi.stmt.accept(this);
mv.visitLabel(L2);
whi.condition.accept(this);
jumplabels.pop();
还有我的朋友:
jumplabels.push(new Label());
L2 = new Label();
L1 = new Label();
ifthenelse.condition.accept(this);
mv.visitJumpInsn(Opcodes.GOTO, L2);
mv.visitLabel(jumplabels.peek());
ifthenelse.thenStmt.accept(this);
mv.visitJumpInsn(Opcodes.GOTO, L1);
mv.visitLabel(L2);
if (ifthenelse.elseStmt != null) {
ifthenelse.elseStmt.accept(this);
}
mv.visitLabel(L1);
jumplabels.pop();
condition.accept(此)将插入正确的条件并跳转到堆栈上推送的最后一个标签(例如IFEQ jumplabels.peek())
我希望任何人都能告诉我我做错了什么。很抱歉代码可能不清楚。您应该重新考虑编译这些语句的方式。If语句通常没有两条
GOTO
指令。另外,您应该去掉jumplabels
堆栈。在我的编译器中,这是编译if
语句的方式:
org.objectweb.asm.Label elseEnd = new org.objectweb.asm.Label();
// Condition
this.condition.writeInvJump(writer, elseStart);
this.then.writeStatement(writer);
writer.writeJumpInsn(Opcodes.GOTO, elseEnd);
writer.writeFrameLabel(elseStart);
this.elseThen.writeStatement(writer);
writer.writeFrameLabel(elseEnd);
writeStatement
只写AST节点(这段代码本身在writeStatement
中)writeInvJump
写入一条跳转指令,如果表达式的计算结果为false,则跳转到指定的标签。两者都是成员,并在IValue
的所有子类型中实现。(请注意,我使用的是自定义的MethodWriter
,它将调用委托给ASMMethodWriter
,并使用稍微不同的命名格式)
()()
为完整起见,以下是WhileStatement.writeStatement的代码:
writer.writeFrameLabel(this.startLabel.target);
this.condition.writeInvJump(writer, this.endLabel.target);
this.action.writeStatement(writer);
writer.writeJumpInsn(Opcodes.GOTO, this.startLabel.target);
writer.writeFrameLabel(this.endLabel.target);
()我不知道您到底做错了什么,但此时的崩溃总是表明您编写的字节码在某种程度上是不正确的。当我们点击这个按钮时,我们在启用调试开关的情况下再次运行它,其中调试开关触发对CheckClassAdapter.verify()的调用,该调用生成字节码列表,通过实践,您可以对该列表进行解释以找出错误所在 在回答你的问题时,这里是我们所做工作的更多细节 实际上我们可以设置两个选项,分别是
displayByteCode
和debugByteCode
。display选项无条件地将字节码打印到文件中,debug选项只有在出现问题时才会启动
首先,我们创建一个ClassWriter cw
,用于创建类。如果启用了displayByteCode
选项,则我们立即将其包装在TraceClassVisitor
中,为其提供一个PrintWriter
,将生成的字节码写入其中
完成时(调用cw.visitEnd()
后),如果启用了debugByteCode
选项,我们将执行以下操作:
StringWriter sw = new StringWriter();
CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, new PrintWriter(sw));
if (sw.toString().length() != 0) {
System.err.println("Verify Output for " + objectName + ":");
try {
BufferedWriter out = new BufferedWriter(new FileWriter("ByteCodeOutput.txt"));
out.write(sw.toString());
out.close();
} catch (IOException e) {
System.out.println("Exception " + e);
}
System.err.println(sw);
throw new IllegalStateException("Bytecode failed verification");
}
这段代码几乎肯定可以改进,但因为它只在紧急情况下才需要,所以对于我们的目的来说已经足够好了 非常感谢你!我的方法真是太草率了。现在它就像一个符咒。嗨,你能解释一下吗?我非常渴望知道如何调试这个,或者至少能够看到字节码。我已经添加了更多信息。