8

私はasmライブラリを使用して、Javaバイトコードの変更を実行しています。具体的には、クラスを変更して、新しいインターフェイスと関連するメソッドを実装しています。私の現在のアプローチは、javaagentを介してコアasmAPIを使用しています。.classファイルを静的に変更するのではなく、この動的なアプローチを維持したいと思います。

より高いレベルでは、私の問題は、Bから拡張するクラスAを変更することを選択した場合、Bも変更する必要があることです(クラスがJVMにロードされる方法を理解していると、クラスBは常にクラスAの前にトランスフォーマーに渡されます(間違っている場合は修正してください)。その仮定を前提として、Bに戻ってトランスフォームする必要があると考えています。私のアプローチは次のコードに含まれています。

public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) {
      throws IllegalClassFormatException {
    // **1**
    System.out.println("--->>> " + name);

    if (interestingClass(name)) {
        try {
            ClassReader cr = new ClassReader(b);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
            PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name);
            cr.accept(pv, 0);

            // **2** Retrieve the superclass and try to transform that
            if (! "Ljava/lang/Object;".equals(pv.getSuperName())) {
                String cName = classJvmToCanonical(pv.getSuperName());
                Class[] classes = inst.getAllLoadedClasses();
                for (Class c : classes) {
                    if (c.getName().equals(cName)) {
                        inst.retransformClasses(c);
                        break;
                    }
                }
            }

            // Dump the transformed class
            ClassReader cr2 = new ClassReader(cw.toByteArray());
            ClassWriter cw2 = new ClassWriter(cr2, 0);
            TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out));
            cr2.accept(tcv, 0);

            return cw2.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    } else {
        return b;
    }
}

(コンストラクターで渡されるinstハンドルです)Instrumentation

私が苦労しているのは、コメントでマークされているブロック**2**です。AがBを拡張し、Aの変換に「興味がある」ともう一度言いましょう。私が期待しているのは、スーパークラス(B)の名前が印刷されていること**1**です(ただし、私は考えていないため、変換されません)最初のパスで興味深いです)そして、**2**AのスーパークラスがBであることがわかったら、Bを再変換しようとする必要があります。この時点で、このメソッドが(を介してinst.retransformClasses())再度呼び出されることを期待しています。 Bがで印刷されるのを参照してください**1**。しかし、私はしません。(printステートメントを追加し、retransform呼び出しに到達していることを確認しました。これも確認しInstrumentation.isRetransformClassesSupported()Instrumentation.isModifiableClass(c)両方ともtrueを返します)。

エージェントを正しく設定したと思います。マニフェストでCan-Retransform-ClassesとCan-Redefine-Classesの両方をtrueに設定します。また、エージェントのpremainメソッドでインストルメンテーションにトランスフォーマーを追加すると、次のようになります。

public static void premain(String agentArgs, Instrumentation inst) {
    inst.addTransformer(new PyClassFileTransformer(inst), true);
}

私がここで間違っていることについての洞察はありますか?ありがとう。

4

1 に答える 1

1

バイトコード インストルメンテーション戦略を変更することもできます。そのため、クラス B がロードされたときに、そのすべてのサブクラスを見つけて、その時点でクラス B を変更する必要があるかどうかを判断します。これは、クラス メタデータ リポジトリまたはメモリ内のキャッシュ (つまり、クラス階層に関する情報) を維持することで最適化できるため、毎回メタデータをロードする必要はありません。

于 2012-03-02T18:57:25.907 に答える