I am using Java ASM (4.0) to write a simple compiler. I use the classWriter(COMPUTE_FRAMES) to write a class.
It all works well with simple programs, but when I start to nest jumps (e.g. a while statement in an IfThenElse statement) and after that I call methodVisitor.visitMaxs(0,0) it gives me the following error:
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)
The code for my while statements:
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();
And my IfThenElse :
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();
The condition.accept(this) will insert the right condition and jump to the last label pushed on the stack (e.g. IFEQ jumplabels.peek()).
I hope anyone can tell me what I'm doing wrong. And sorry for the perhaps unclear code.
You should rethink the way you are compiling these statements. If statement usually don't have two GOTO
instructions. Also, you should get rid of the jumplabels
stack. In my compiler, this is the way I compile an if
statement:
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
simply writes the AST node (this chunk of code is itself in writeStatement
). writeInvJump
writes a jump instruction that jumps to the specified label if the expression evaluates to false. Both are members and implemented in all subtypes of IValue
. (Note that I am using a custom MethodWriter
that delegates calls to an ASM MethodWriter
and uses a slightly different naming format)
(IfStatement Source) (IValue Source)
For the sake of completeness, here is the code for 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);