如何在另一个生成字节码类中使用匿名类实例

时间:2015-07-10 20:26:26

标签: java reflection unsafe anonymous-class java-bytecode-asm

我很难使用由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&#39;并希望用它创建新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&#39的字段成员),所以是否有任何解决方法来传递新的新callee的构造函数的callee2实例?

1 个答案:

答案 0 :(得分:0)

请查看the signature and documentation comment,这在标准API文档中不可用,因为它不是官方API的一部分:

  

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

     
      
  • Integer,Long,Float,Double:来自java.lang
  • 的相应包装器对象类型   
  • Utf8:一个字符串(如果用作签名或名称,必须具有合适的语法)   类:任何java.lang.Class对象
  •   
  • String:任何对象(不只是java.lang.String)
  •   
  • InterfaceMethodRef:(NYI)一个调用该调用站点参数的方法句柄
  •   
     

...(params:)

     

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”意味着“尚未实施”)...