尝试重新定义sun.reflect.GeneratedMethodAccessor1时,ByteBuddy失败 [英] ByteBuddy fails when trying to redefine sun.reflect.GeneratedMethodAccessor1

查看:364
本文介绍了尝试重新定义sun.reflect.GeneratedMethodAccessor1时,ByteBuddy失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在好奇心的驱使下,我试图导出GeneratedMethodAccessor1的字节码(在使用反射时由JVM生成)。



我试图获取类的字节码以下方式:

  public class MethodExtractor {

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

ExampleClass example = new ExampleClass();

方法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());
已卸载<? 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){
抛出新的RuntimeException(无法将类保存到文件中);
}
}
}

但是我收到以下错误执行此类时:

 线程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),然后尝试重新定义类以获取字节码。



我尝试使用相同的方法导出生成的Proxy类,并且它运行完美。这就是驱使我尝试这个的原因。



当我尝试使用loadClass方法加载类时,似乎GeneratedMethodAccessor1类的DelegatingClassLoader甚至无法重新加载类。



我是如何检索GeneratedMethodAccessor类的字节码的?

解决方案

首先, NullPointerException 是一个错误,我刚刚解决了这个问题。加载器应该抛出一个 IllegalArgumentException ,但它从来没有这么远。感谢您引起我的注意。



简而言之,Byte Buddy面临的问题是

  gma.getClass()getClassLoader()的findClass(gma.getClass()的getName())。。 

抛出 ClassNotFoundException 。这是使用 DelegatingClassLoader 作为访问者类的结果。作为一个有根据的猜测,我认为这个类加载器打算从外部屏蔽它的类,以使它们容易被垃圾收集。但是,不允许查找类有点违反 ClassLoader 的合同。除此之外,我假设这个加载例程将被重构为在将来的某个时候使用JDK的匿名类加载器(类似于表示lambda表达式的类)。奇怪的是,它似乎是 JDK 中没有 DelegatingClassLoader 的源代码,即使我可以在发行版中找到它。可能VM特别在某个地方处理这些加载器。



现在,您可以使用以下 ClassFileTransformer 在类加载器上使用一些反射魔法来定位加载的类,然后提取字节数组。 ( ClassFileLocator 接口只接受一个名称而不是一个加载的类,以便允许使用卸载的类型,这通常是这种情况。不知道为什么这不起作用case。)

  class DelegateExtractor扩展ClassFileLocator.AgentBased {

private final ClassLoader classLoader;
私人最终仪器仪表;

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);
//开始讨厌的黑客
Field field = ClassLoader.class.getDeclaredField(classes);
field.setAccessible(true);
instrumentation.retransformClasses(
(Class<?>)((Vector<?>)field.get(classLoader))。get(0));
//结束令人讨厌的黑客
byte [] binaryRepresentation = classFileTransformer.getBinaryRepresentation();
返回binaryRepresentation == null
? Resolution.Illegal.INSTANCE
:new Resolution.Explicit(binaryRepresentation);
} finally {
instrumentation.removeTransformer(classFileTransformer);
}
} catch(忽略异常){
返回Resolution.Illegal.INSTANCE;
}
}
}

为了进一步简化代码,您可以直接使用 ClassFileLocator ,而不是应用重写,即使您没有对类应用任何更改,也可能稍微修改类文件。 / p>

Driven by curiosity, I tried to export the bytecode of GeneratedMethodAccessor1 (generated by the JVM when using reflection).

I try to get the bytecode of the class the following way:

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");
        }
    }
}

I however get the following error when executing this class:

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)

Basically I first iterate on the method call enough times for the JVM to inflate the method (generate the GeneratedMethodAccessor) and then try to redefine the class to get the bytecode.

I tried the same method to export a generated Proxy class, and it worked flawlessly. That's what drove me to try this.

It seems that the DelegatingClassLoader of the GeneratedMethodAccessor1 class can't even reload the class when I try to load the class with the loadClass method.

Any ideas how I could retrieve the bytecode for GeneratedMethodAccessor classes?

解决方案

First of all, the NullPointerException is a bug, I just fixed that. The loader should have thrown an IllegalArgumentException instead but it never got that far. Thanks for bringing this to my attention.

Boiled down, the problem Byte Buddy is facing is that

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

throws a ClassNotFoundException. This is a consequence of using a DelegatingClassLoader for the accessor classes. As an educated guess, I think that this class loader intends to shield its classes from the outside in order to make them easily garbage collectable. However, not allowing the lookup of a class what somewhat breaks the contract for a ClassLoader. Apart from that, I assume that this loading routine will be refactored to use the JDK's anonymous class loaders at some point in the future (similar to classes representing lambda expressions). Strangely enough, it seems like the source code for the DelegatingClassLoader is not available in the JDK even though I can find it in the distribution. Probably, the VM treats these loader specially at some place.

For now, you can use the following ClassFileTransformer which uses some reflection magic on the class loader to locate the loaded class and to then extract the byte array. (The ClassFileLocator interface only takes a name instead of a loaded class in order to allow working with unloaded types which is normally always the case. No idea why this does not work in this case.)

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;
    }
  }
}

To further simplify your code, you can use the ClassFileLocators directly instead of applying a rewrite which as a matter of fact might slightly modify the class file even if you do not apply any changes to a class.

这篇关于尝试重新定义sun.reflect.GeneratedMethodAccessor1时,ByteBuddy失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆