1

好奇心に駆られて、GeneratedMethodAccessor1 (リフレクションの使用時に JVM によって生成された) のバイトコードをエクスポートしようとしました。

次の方法でクラスのバイトコードを取得しようとします。

public class MethodExtractor {

    public static void main(String[] args) throws Exception {

        ExampleClass example = new ExampleClass();

        Method exampleMethod = ExampleClass.class
                .getDeclaredMethod("exampleMethod");
        exampleMethod.setAccessible(true);

        int rndSum = 0;
        for (int i = 0; i < 20; i++) {
            rndSum += (Integer) exampleMethod.invoke(example);
        }

        Field field = Method.class.getDeclaredField("methodAccessor");
        field.setAccessible(true);
        Object methodAccessor = field.get(exampleMethod);
        Field delegate = methodAccessor.getClass().getDeclaredField("delegate");
        delegate.setAccessible(true);
        Object gma = delegate.get(methodAccessor);

        ByteBuddyAgent.installOnOpenJDK();
        try {
            ClassFileLocator classFileLocator = ClassFileLocator.AgentBased
                    .fromInstalledAgent(gma.getClass().getClassLoader());
            Unloaded<? extends Object> unloaded = new ByteBuddy().redefine(
                    gma.getClass(), classFileLocator).make();
            Map<TypeDescription, File> saved = unloaded.saveIn(Files
                    .createTempDirectory("javaproxy").toFile());
            saved.forEach((t, u) -> System.out.println(u.getAbsolutePath()));
        } catch (IOException e) {
            throw new RuntimeException("Failed to save class to file");
        }
    }
}

ただし、このクラスを実行すると、次のエラーが発生します。

Exception in thread "main" java.lang.NullPointerException
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Engine$ForRedefinition.create(TypeWriter.java:172)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1182)
    at net.bytebuddy.dynamic.scaffold.inline.InlineDynamicTypeBuilder.make(InlineDynamicTypeBuilder.java:244)
    at reegnz.dyna.proxy.extractor.MethodExtractor.main(MethodExtractor.java:48)

基本的には、最初に JVM がメソッドを拡張する (GeneratedMethodAccessor を生成する) のに十分な回数だけメソッド呼び出しを繰り返し、次にバイトコードを取得するためにクラスを再定義しようとします。

生成されたプロキシ クラスをエクスポートするために同じ方法を試しましたが、問題なく動作しました。それが私がこれを試すようになった理由です。

GeneratedMethodAccessor1 クラスの DelegatingClassLoader は、loadClass メソッドでクラスをロードしようとすると、クラスをリロードすることさえできないようです。

GeneratedMethodAccessor クラスのバイトコードを取得する方法はありますか?

4

1 に答える 1

1

まず第一に、これNullPointerExceptionはバグです。私はそれを修正しました。ローダーはIllegalArgumentException代わりにスローする必要がありましたが、そこまで到達しませんでした。これを知らせてくれてありがとう。

要するに、Byte Buddy が直面している問題は、

gma.getClass().getClassLoader().findClass(gma.getClass().getName());

をスローしClassNotFoundExceptionます。これはDelegatingClassLoader、アクセサ クラスに を使用した結果です。経験に基づいた推測として、このクラスローダーは、クラスを簡単にガベージコレクションできるようにするために、クラスを外部から保護することを意図していると思います。ただし、クラスのルックアップを許可しないと、ClassLoader. それとは別に、このロード ルーチンは、将来のある時点で (ラムダ式を表すクラスと同様に) JDK の匿名クラス ローダーを使用するようにリファクタリングされると想定しています。不思議なことに、のソース コードDelegatingClassLoaderは JDK では利用できないようですが、ディストリビューションでは見つけることができます。おそらく、VM はこれらのローダーをどこかで特別に扱います。

現時点ではClassFileTransformer、クラス ローダーでリフレクション マジックを使用する次のコードを使用して、ロードされたクラスを特定し、バイト配列を抽出できます。(ClassFileLocatorインターフェイスは、ロードされたクラスの代わりに名前のみを使用して、通常は常にアンロードされた型を操作できるようにします。この場合、これが機能しない理由はわかりません。)

class DelegateExtractor extends ClassFileLocator.AgentBased {

  private final ClassLoader classLoader;
  private final Instrumentation instrumentation;

  public DelegateExtractor(ClassLoader classLoader, Instrumentation instrumentation) {
    super(classLoader, instrumentation);
    this.classLoader = classLoader;
    this.instrumentation = instrumentation;
  }

  @Override
  public Resolution locate(String typeName) {
    try {
      ExtractionClassFileTransformer classFileTransformer = 
          new ExtractionClassFileTransformer(classLoader, typeName);
      try {
        instrumentation.addTransformer(classFileTransformer, true);
        // Start nasty hack
        Field field = ClassLoader.class.getDeclaredField("classes");
        field.setAccessible(true);
        instrumentation.retransformClasses(
            (Class<?>) ((Vector<?>) field.get(classLoader)).get(0));
        // End nasty hack
        byte[] binaryRepresentation = classFileTransformer.getBinaryRepresentation();
        return binaryRepresentation == null
          ? Resolution.Illegal.INSTANCE
          : new Resolution.Explicit(binaryRepresentation);
      } finally {
        instrumentation.removeTransformer(classFileTransformer);
      }
    } catch (Exception ignored) {
      return Resolution.Illegal.INSTANCE;
    }
  }
}

コードをさらに単純化するためにClassFileLocator、書き換えを適用する代わりに s を直接使用できます。これにより、クラスに変更を適用しなくても、実際にはクラス ファイルがわずかに変更される可能性があります。

于 2015-03-17T07:42:26.307 に答える