ASM を使用してメソッドを try/finally ブロックにラップしようとしています。具体的には、「3.2.3 メソッドの終了前にコードを挿入する」の「ASM フレームワークを使用して一般的なバイトコード変換パターンを実装する」org.objectweb.asm.commons.AdviceAdapter
で説明されている手法を拡張し、従っています。
また、LocalVariablesSorter
(のスーパークラスAdviceAdapter
)を使用して、finally ブロックで使用するメソッドの開始時にローカル変数を追加しています。
ASM で変更しようとしているメソッドがあります。このメソッドは 3 つの引数を取り、独自のローカルはありません。
class Example {
public static int f4(int i, long l, int f) {
return (int) (i + l + f);
}
}
私のfinally
ブロックは例外ハンドラのターゲットである可能性があるため、呼び出し so を追加してsuper.visitFrame(F_NEW, ...)
、メソッド パラメータを渡します。これはスーパークラスを呼び出すため、 newLocal が によってこのフレームに追加されることを期待していLocalVariablesSorter
ます。しかし、これを ASM パイプラインで実行すると、Java 7 でエラーが発生しますjava.lang.VerifyError: Bad local variable type in method Example.f4(IJI)I at offset 32
。
メソッドののソースをLocalVariablesSorter
見るvisitFrame
と、ブール値がchanged
あり、変更されていない場合visitFrame
、作成される可能性のある新しいローカルの考慮がスキップされることがわかります。このchanged
ブール値は、アップストリームのバイトコードが変数に対して特定の操作を実行する場合にのみ設定されるように見えますが、LocalVariablesSorter
新しいローカルを作成するために使用するために再マップする必要がありました (f4()
上記の方法ではそのような操作は発生しません)。
この回避策を適用すると:
Field field = LocalVariablesSorter.class.getDeclaredField("changed");
field.setAccessible(true);
field.setBoolean(this, true);
Java 7 のベリファイアを通過できます。また、TraceClassVisitor
ASM ユーティリティを使用すると、finally ブロックの開始直前のスタック マップ フレームに違いが見られます。
前:
FRAME FULL [I J I] [java/lang/Throwable]
リフレクション ハックの後 (追加する新しいローカルは int です):
FRAME FULL [I J I I] [java/lang/Throwable]
周囲のコードを共有していないことはわかっていますが、私の質問はより一般的です。このchanged
ブール値は による誤った最適化LocalVariablesSorter
ですか? それとも、どういうわけかそれを悪用しているのですか?