現在、カスタム コンパイラで式として使用できるようにしようと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})
try/catch
ASM が出力値を持つブロックのスタック フレームを計算する際に問題があるとは思わないので、インストルメンテーション コードに問題がありますか? (ClassWriter.getCommonSuperclass
はここでは必要ありませんが、正しく実装されていることに注意してください。)