だから私は最近Javassistを少し使っていて、答えを見つけることができなかった質問に出くわしました。CtMethod の insertAt メソッドを使用すると、特定の行番号にコードを挿入できますが、その行は上書きされるのでしょうか、それとも保持されるのでしょうか? また、デフォルトの動作とは逆の動作をさせるにはどうすればよいでしょうか? XML ファイルの「フック」に基づいて、Javassist を使用してランタイムの直前にソースを変更するアプリケーションがあります。線を上書きできるようにしたい、または線を上書きする代わりに線の上に配置できるようにしたい。明らかにそれを行うためのハックな方法がありますが、私はむしろ適切な方法を使用したいと思います。
1 に答える
簡単な部分
CtMethod オブジェクトに存在するメソッドinsertAt(int lineNumber, String src)を使用すると、特定の行にあったコードの前にsrc に記述されたコードを挿入できます。
たとえば、次の (単純な) プログラム例を見てみましょう。
public class TestSubject {
public static void main(String[] args) {
TestSubject testSubject = new TestSubject();
testSubject.print();
}
private void print() {
System.out.println("One"); // line 9
System.out.println("Two"); // line 10
System.out.println("Three"); // line 11
}
}
単純にコーディングします (メソッド変数は、 printメソッドの CtMethod 表現でなければならないことに注意してください):
// notice that I said line 10, which is where the sysout of "two" is
method.insertAt(10, true, "System.out.println(\"one and an half\");");
クラスに新しい sysout 命令を挿入します。新しいクラスの出力は次のようになります。
one
one and an half
two
three
難しい部分
Javassist は、コード行を簡単に削除する方法を提供していません。そのため、本当にそれを置き換えたい場合は、ハッキングする以外に選択肢はありません。
どうやってするの?さて、あなたの新しい友人 (まだご存じない場合) であるCodeAttributeオブジェクトを紹介しましょう。
CodeAttribute オブジェクトは、コード属性に加えて、行番号をバイトコード配列にマップするのに役立つLineNumberAttributeと呼ばれる別の属性も持つメソッド フローを表すバイトコードを保持する役割を果たします。このオブジェクトを要約すると、必要なものがすべて揃っています。
次の例の考え方は非常に単純です。バイトコード配列内のバイトを削除する必要がある行に関連付け、そのバイトをノーオペレーション コードで置き換えます。
ここでも、method はメソッドprintの CtMethod 表現です。
// let's erase the sysout "Two"
int lineNumberToReplace = 10;
// Access the code attribute
CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
// Access the LineNumberAttribute
LineNumberAttribute lineNumberAttribute = (LineNumberAttribute) codeAttribute.getAttribute(LineNumberAttribute.tag);
// Index in bytecode array where the instruction starts
int startPc = lineNumberAttribute.toStartPc(lineNumberToReplace);
// Index in the bytecode array where the following instruction starts
int endPc = lineNumberAttribute.toStartPc(lineNumberToReplace+1);
System.out.println("Modifying from " + startPc + " to " + endPc);
// Let's now get the bytecode array
byte[] code = codeAttribute.getCode();
for (int i = startPc; i < endPc; i++) {
// change byte to a no operation code
code[i] = CodeAttribute.NOP;
}
元のTestSubject クラスでこの変更を実行すると、次の出力を持つクラスが挿入されます。
one
three
まとめ
行を追加する必要があり、既存の行を保持する必要がある場合、行を置き換えたい場合は、簡単な部分で指定された例を使用する必要があります。最初に、最初の例を使用して新しい行を挿入します。
また、上記の例では、javassist の基本にすでに慣れていると想定していたことに注意してください。そのため、たとえば、例には ctClass.writeFile がありません...それでも実行する必要があります。実行する必要があることを知っているはずなので、省略しました。
コード例で追加のヘルプが必要な場合は、質問してください。喜んでお手伝いします。