我很难使用Unsafe.defineAnonymousClass()加载的生成的字节码类。我想知道如何使用匿名类的对象初始化另一个类(或匿名类)。

以下面的示例类Callee为例,其构造函数接受Callee2作为参数。

Class Callee{
    Callee2 _call2;
    public Callee(Callee2 callee2){
        ...
    }
}

在运行时,我为Callee2和Callee生成了新类,并且这两个新类都由Unsafe.defineAnonymousClass()加载。对于新的Callee,构造函数也更改为:
 public test.code.jit.asm.simple.Callee(test.code.jit.asm.simple.Callee2.1506553666);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=2
         0: aload_0
         1: invokespecial #65                 // Method java/lang/Object."<init>":()V
           ....
         8: return

而Callee2的生成类名称为:
      class test.code.jit.asm.simple.Callee2/1506553666

我创建了`Callee2 / 1506553666'的实例,并想用它创建新Callee的实例,但是失败了:
        newCls.getConstructor(args).newInstance(objs);

哪里
args = [class test.code.jit.asm.simple.Callee2/1506553666]
objs= [test.code.jit.asm.simple.Callee2/1506553666@39b0a038]
args [0]毫无意义,因为此类是由匿名加载器加载的(任何类加载器均未引用匿名类)。因此,这真的使我感到困惑,如何将objs数组中的对象传递给方法调用。

使用消息调用getConstructor(args)时会发生异常:
java.lang.NoClassDefFoundError: test/code/jit/asm/simple/Callee2/1506553666
    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2483)
    at java.lang.Class.getConstructor0(Class.java:2793)
    at java.lang.Class.getConstructor(Class.java:1708)
    at code.jit.asm.util.ReflectionUtil.adapt2GeneratedObject(ReflectionUtil.java:36)
    at code.jit.asm.services.BytecodeGenerator.generator(BytecodeGenerator.java:164)

对我来说显然是个例外,因为匿名类对于任何类加载器都是临时的。但就我而言,我确实需要通过新的Callee2实例初始化新的Callee(也是匿名类)(Callee的构造函数中的字节码将读取Callee2的字段成员),因此,是否有任何变通办法将新的Callee2实例传递给新的Callee2的构造函数?

最佳答案

看看the signature and documentation comment,它不是标准API的一部分,因此在标准API文档中不可用:

定义一个类,但不要让类加载器或系统字典知道它。
对于每个CP条目,相应的CP修补程序必须为null或具有与其标签匹配的格式:


  • 整数,Long,Float,Double:java.lang中的相应包装对象类型
  • Utf8:字符串(如果用作签名或名称,则必须具有适当的语法)
    类:任何java.lang.Class对象
  • 字符串:任何对象(不仅是java.lang.String)
  • InterfaceMethodRef:(NYI)在该调用站点的参数
  • 上调用的方法句柄


    …(参数:)

    存在非空条目的cpPatches,它们替换数据中相应的CP条目
    public native Class<?> defineAnonymousClass(
                           Class<?> hostClass, byte[] data, Object[] cpPatches);
    

    换句话说,您可以提供与要定义的类的常量池相同大小的数组。将null保留在您不想对其进行修改的位置。在常量池中有一个表示匿名类的CONSTANT_Class_info的地方,您只需在数组中传递关联的Class对象。这样一来,无需查找该类,您甚至不必在类字节中提供正确的类名称。

    有一些明显的局限性:
  • 如果您具有循环依赖项,则会出现问题,因为您需要一个已经存在的Class对象来修补另一个类的池。好吧,对于已知会延迟解决的类用法,它可能会起作用
  • 您只能将CONSTANT_Class_info修补为足以满足要求的Class,例如访问该类的成员或创建该类的新实例。但是,当匿名类成为签名的一部分时,即您要声明该类型的字段或使用将其作为参数或返回类型的方法时,这无济于事。但是您可以使用通过CONSTANT_InterfaceMethodref_info修补MethodHandle条目的选项来访问此类方法(哎呀,一旦实现,因为我猜“NYI”的意思是“尚未实现”)…
  • 10-06 13:30