Search code examples
javajava-bytecode-asm

Why does methodvistor.visitMaxs(0,0) crash in Java asm?


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.


Solution

  • 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);
    

    (WhileStatement Source)