Java ASM 生成捕获块

Java ASM Generate Catch Block

我正在尝试使用我的编译器中的 ASM 框架编译一个 Try/Catch 块(这意味着 try 块、处理程序块和变量是动态的).这是我的代码目前的样子:

    org.objectweb.asm.Label tryStart = new org.objectweb.asm.Label();
    org.objectweb.asm.Label tryEnd = new org.objectweb.asm.Label();
    org.objectweb.asm.Label endLabel = new org.objectweb.asm.Label();

    writer.writeLabel(tryStart);
    if (this.action != null)
    {
        this.action.writeStatement(writer);
        writer.writeJumpInsn(Opcodes.GOTO, endLabel);
    }
    writer.writeLabel(tryEnd);

    for (int i = 0; i < this.catchBlockCount; i++)
    {
        CatchBlock block = this.catchBlocks[i];
        org.objectweb.asm.Label handlerLabel = new org.objectweb.asm.Label();
        int varIndex;

        writer.push(block.type);
        // Check if the block's variable is actually used
        if (block.variable != null)
        {
            // If yes register a new local variable for the exception and
            // store it.
            varIndex = block.variable.index = writer.registerLocal(block.type);
            writer.writeFrameLabel(handlerLabel);
            writer.writeVarInsn(Opcodes.ASTORE, varIndex);
            block.action.writeStatement(writer);
            writer.removeLocals(1);
        }
        // Otherwise pop the exception from the stack
        else
        {
            varIndex = -1;
            writer.writeFrameLabel(handlerLabel);
            writer.writeInsn(Opcodes.POP);
            block.action.writeStatement(writer);
        }

        writer.writeTryCatchBlock(tryStart, tryEnd, handlerLabel, block.type);
        writer.writeJumpInsn(Opcodes.GOTO, endLabel);
    }
    writer.writeFrameLabel(endLabel);

但是,此实现生成以下 VerifyError

java.lang.VerifyError: Stack map does not match the one at exception handler 20
Exception Details:
  Location:
    dyvil/test/Main.main([Ljava/lang/String;)V @20: astore_1
  Reason:
    Type top (current frame, locals[1]) is not assignable to 'Ljava/lang/Exception;' (stack map, locals[1])
  Current Frame:
    bci: @0
    flags: { }
    locals: { '[Ljava/lang/String;' }
    stack: { 'java/lang/Exception' }
  Stackmap Frame:
    bci: @20
    flags: { }
    locals: { '[Ljava/lang/String;', 'Ljava/lang/Exception;' }
    stack: { 'Ljava/lang/Exception;' }
  Bytecode:
    0000000: b200 1312 15b6 001b b200 1304 036c b600
    0000010: 1ea7 0013 4cb2 0013 1224 b600 1b2b b600
    0000020: 29a7 0003 b1                           
  Exception Handler Table:
    bci [0, 20] => handler: 20
  Stackmap Table:
    full_frame(@20,{Object[#32],Object[#34]},{Object[#34]})
    chop_frame(@36,1)

(请注意,writerwriteX 开头的方法只是对 MethodVisitor.visitX 的委托。writeFrameLabel 还告诉 MethodWriter 它应该生成写入下一条指令之前的堆栈帧)

我通过替换这部分设法自己找到了解决方案:

varIndex = block.variable.index = writer.registerLocal(block.type);
writer.writeFrameLabel(handlerLabel);
writer.writeVarInsn(Opcodes.ASTORE, varIndex);

有了这个:

writer.writeLabel(handlerLabel);
writer.writeFrame();
varIndex = block.variable.index = writer.registerLocal(block.type);
writer.writeVarInsn(Opcodes.ASTORE, varIndex);

基本上,ASTORE处的堆栈映射帧不应该包含局部变量,而是应该在帧之后初始化局部变量。