11

私は ASM を使用しており、次のように書き直したいと考えています。

someMethod().targetMethod(args...)

に:

someMethod().injectedMethod(arg).targetMethod(args...)

問題は、以前のメソッドが何であるかがわからず、ターゲット メソッドしか知らないことです (そのため、後で見つけsomeMethod()て注入することはできません)。

また、ターゲット メソッドの多くのバージョンがあり、これを操作するさまざまなパラメーターのセットがあります。

ASM を使用すると、目的のメソッド呼び出しを簡単に見つけることができますが、残念ながらその時点でのオペランド スタックは次のようになります。

[ argN, ..., arg1, instance, ... ]

そして、インスタンスがどれくらい下にあるのかを理解することはできますが、それを読み取るために挿入できるバイトコードはありません。dup コマンドのトリックを使用して最大 4 つのパラメーターに対して実行できることはわかっていますが、一般的な解決策が必要です。

たくさんのローカル変数を追加してスタックからすべてをコピーし、指定されたインスタンスを複製してすべてを元に戻すことができますが、それは実行時の非効率性であり、私は本当に望んでいません。

どの命令がインスタンス ポインターをスタックに置くかを追跡でき、ターゲット メソッド呼び出しではなくそこにメソッド呼び出しを挿入できれば、うまくいくと思います。しかし、これを行うのに役立つものを見つけることができませんでした。

AspectJ のようなものがこれを可能にしていることは知っていますが、多くのクラスがロードされ、AspectJ が遅すぎるため、これを行う必要があります。

これを可能にするASM上に構築された分析ツールを教えてもらえますか、またはあるメソッド呼び出しを別のメソッド呼び出しの前に挿入するためのより良いアプローチを誰か考えてもらえますか?

4

3 に答える 3

2

私があなたの質問を正しく理解していれば、あなたがやりたいことと同じことを別の方法で達成したことになります。

ASM イベント ドリブン バイト コード変更を使用して、まず someMethod( arg, arg, arg ) を copyOf_someMethod( arg, arg, arg ) に名前変更しました。次に、 someMethod( arg, arg, arg ) という新しいメソッドを作成しました。このメソッドは、いくつかの処理を実行してから、 copyOf_someMethod( arg, arg, arg ) を呼び出しました。

実装した ClassVisitor の visitMethod(..) メソッドでメソッドの名前を変更しました。

MethodVisitor methodVisitor =
    super.visitMethod(
        methodAccess, "copyOf_" + methodName, methodDesc,
            methodSignature, methodExceptions );

return methodVisitor;

また、visitMethod(..) では、すべてのメソッド シグネチャの詳細を、visitEnd() メソッドで使用できるようにクラス変数に格納しました。

実際にメソッドの詳細を MethodDetail オブジェクトに格納し、キューに配置しました。

private Queue<MethodDetail> methodDetails = new LinkedList<MethodDetail>();

実装した ClassVisitor の visitEnd() メソッドを使用して、 someMethod( arg, arg, arg ) の新しい実装を作成しました。ASMFier を使用して、visitEnd() メソッドに入れるコードを生成しました。この実装では、以前に visitMethod(..) に保存した詳細を利用しました。新しい実装は、いくつかの処理を行ってから、copyOf_someMethod() を呼び出しました。visitEnd() メソッドでは、キューのすべての MethodDetail をポップし、以前に ASMFier によって生成した ASM コードと呼ばれる MethodDetail ごとにポップしました。

この設計を使用して、何らかの処理を行ってから元のメソッドを呼び出すメソッドのプロキシを作成しました。元のメソッドの名前が copyOf_someMethod(..) に変更されていることに注意してください。また、プロキシとして機能する元のメソッドの新しい実装を提供したことにも注意してください。

複数の引数をサポートするために、ASMFier を使用して 1 つの引数、2 つの引数、3 つの引数などの異なるコードを生成しました。最大 7 つの引数をサポートし、プロキシされるメソッドに 7 つを超える引数がある場合はサポートされていない例外をスローします。visitEnd(..) メソッドでは、元のメソッドが持つメソッド引数の数に応じて、異なるコード (ASMFier によって生成されたもの) を呼び出しました。

javaagent を使用して、クラスのロードをインターセプトし、バイトを変更しました。

私は ASM を初めて使用するので、あなたの質問を正しく理解していなかったかもしれませんが、何らかの処理を行ってから元のメソッドを呼び出すプロキシの作成に関する質問であれば、私のソリューションは機能します。遅いようには見えませんでした。メソッドがプロキシされているクラスのロードアップ時間は、バイトコードを変更しない場合よりもそれほど遅くはありませんでした。導入されたコードの実行速度は遅くはなく、ASMFier によって生成された ASM コードは非常に高速のようです。

乾杯

于 2012-12-19T09:07:29.503 に答える
1

一般に、値をスタックから一時的なローカル変数にオフロードできます。ASM commons パッケージのLocalVariableSorterアダプターを使用すると、非常に簡単になります。しかし実際には、4 つ以上の引数を持つメソッドはまれなケースです。とにかく、実行時に本格的なデータフロー分析を行うよりもはるかにシンプルで堅実です。

ASM のorg.objectweb.asm.tree.analysisは、データ フロー分析の機能を提供します。たとえば、SourceInterpreterを使用して、各変数とスタック スロットでどの命令が値を生成したかを追跡できます。詳細については、 ASM ユーザー ガイドを参照してください。

于 2012-12-19T04:54:52.427 に答える
0

org.objectweb.asm.tree.analysis を調べてください。SourceIterpreter は、値をスタックに置く命令を提供する必要があります。

于 2012-12-19T16:21:59.430 に答える