我目前正在尝试使我的自定义编译器允许try/catch
用作表达式,即在堆栈上留下一个值。类型检查器和后端已经支持这一点,但问题似乎是 ASM 的COMPUTE_FRAMES
. 使用以下代码进行检测:
private void write(MethodWriter writer, boolean expression)
{
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();
boolean hasFinally = this.finallyBlock != null;
writer.writeLabel(tryStart);
if (this.action != null)
{
if (expression && !hasFinally)
{
this.action.writeExpression(writer);
}
else
{
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();
// 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.
int localCount = writer.registerLocal();
writer.writeLabel(handlerLabel);
writer.writeVarInsn(Opcodes.ASTORE, localCount);
block.variable.index = localCount;
if (expression && !hasFinally)
{
block.action.writeExpression(writer);
}
else
{
block.action.writeStatement(writer);
}
writer.resetLocals(localCount);
}
// Otherwise pop the exception from the stack
else
{
writer.writeLabel(handlerLabel);
writer.writeInsn(Opcodes.POP);
if (expression && !hasFinally)
{
block.action.writeExpression(writer);
}
else
{
block.action.writeStatement(writer);
}
}
writer.writeTryCatchBlock(tryStart, tryEnd, handlerLabel, block.type.getInternalName());
writer.writeJumpInsn(Opcodes.GOTO, endLabel);
}
if (hasFinally)
{
org.objectweb.asm.Label finallyLabel = new org.objectweb.asm.Label();
writer.writeLabel(finallyLabel);
writer.writeInsn(Opcodes.POP);
writer.writeLabel(endLabel);
if (expression)
{
this.finallyBlock.writeExpression(writer);
}
else
{
this.finallyBlock.writeStatement(writer);
}
writer.writeFinallyBlock(tryStart, tryEnd, finallyLabel);
}
else
{
writer.writeLabel(endLabel);
}
}
编译这段代码:
System.out.println(try Integer.parseInt("10") catch (Throwable t) 10)
VerifyError
我在类加载时得到以下信息:
java.lang.VerifyError: Inconsistent stackmap frames at branch target 17
Exception Details:
Location:
dyvil/test/Main.main([Ljava/lang/String;)V @14: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @14
flags: { }
locals: { '[Ljava/lang/String;' }
stack: { integer }
Stackmap Frame:
bci: @17
flags: { }
locals: { '[Ljava/lang/String;' }
stack: { top, integer }
Bytecode:
0000000: b200 1412 16b8 001c a700 0957 100a a700
0000010: 03b6 0024 b1
Exception Handler Table:
bci [3, 11] => handler: 11
Stackmap Table:
same_locals_1_stack_item_frame(@11,Object[#30])
full_frame(@17,{Object[#38]},{Top,Integer})
由于我认为 ASM 在计算try/catch
具有输出值的块的堆栈帧时没有问题,所以我的检测代码有问题吗?(请注意ClassWriter.getCommonSuperclass
,虽然这里不需要,但已正确实现。)