【问题标题】:Is there an analogue of visitLdcInsn for loading objects (not constant)?是否有类似 visitLdcInsn 用于加载对象(不是常量)?
【发布时间】:2014-09-09 07:05:05
【问题描述】:

我们用 Java 编写了一个简单的 PostScript 解释器,并希望通过直接为源代码的特定部分生成字节码来优化它。为此,我们需要从 Java 字节码上下文的上下文中加载对象。在生成的字节码方法的签名中指定这样的对象是不好的,因为在我们的例子中它们可能是大量的。

在 Java Asm 中我们有方法

public void visitLdcInsn(Object cst)

它访问一条 LDC 指令。参数 cst - 要加载到堆栈上的常量。

有没有办法加载非常量对象?

谢谢

【问题讨论】:

  • 有 Unsafe.defineAnonymousClass(),但它是黑魔法。最后一个参数允许您使用任意对象“修补”常量池并通过 LDC 加载它们
  • 你想要完成什么?根据定义,LDC 只能用于常量。

标签: java bytecode java-bytecode-asm bytecode-manipulation


【解决方案1】:

从 Java 11 开始,可以使用 LDC 指令加载任意常量。这些可能是任意类型的对象,但具有不变的语义,因此它们最好是不可变的。

为此,引用的常量池条目必须是 CONSTANT_Dynamic_info,其结构与 CONSTANT_InvokeDynamic_info 相似,同样描述了引导方法。

一个区别是动态信息结构的name_and_type_index 条目将指向一个字段描述符。此外,引导方法具有(MethodHandles.Lookup,String,Class[,static arguments]) 的签名,其中Class 参数表示常量的预期类型,而不是MethodType 对象。 bootstrap 方法必须直接返回常量值而不是调用站点。

invokedynamic 指令的共同点是第一个引导过程的结果将与LDC 指令相关联,并用于所有后续执行(因为它应该是一个常量)。

这些动态常量的一个有趣特性是它们是另一个动态常量或invokedynamic 指令的引导方法的有效静态参数(只要动态常量之间没有循环依赖关系)。

请注意,已经有一个 convenience class 包含一些现成的用于动态常量的引导方法。

【讨论】:

    【解决方案2】:

    ldc可用于加载intfloatStringClassMethodTypeMethodHandle类型的值; ldc2_w 支持 longdouble 类型的值。 1

    As said,在 Oracle 的 JVM 实现中,有一个内部使用的 Unsafe API,它允许在运行时对象中修补作为常量的替换,但这有几个缺点。首先,它显然不是官方 API 的一部分,不是每个 JVM 中都存在,甚至可能在未来的 Oracle JVM 中消失(或更改方法签名)。此外,ASM 框架不会知道您将要做什么,并且难以为以后的补丁生成适当的字节码。

    毕竟,在你的项目中为运行时对象滥用ldc 有什么好处还不清楚。使用 ASM 生成代码以将实例作为方法或构造函数参数传递并将对象存储在字段中并不是很复杂。对于程序逻辑,无论您使用 ldc 还是,例如getstatic,在使用该值之前。

    【讨论】:

      【解决方案3】:

      指出使用Unsafe不好的方式(这也不是一个真正的选择,因为它需要您匿名加载类):

      我假设您在构建期间创建了一个类,但您想将某种运行时上下文注入到这些类中,这是运行您的检测所必需的。您至少可以通过为您的应用程序编写一个专门的ClassLoader 来模拟这一点,该应用程序知道此上下文并通过例如注释显式初始化一个类。

      这意味着你检测一个类,例如:

      @Enhanced
      class Foo {
      
         static EnhancementDelegate delegate;
      
         void instrumentedMethod() {
           // do something with delegate
         }
      }
      

      在构建时,您在加载时显式初始化:

      class EnhancementClassLoader extends ClassLoader {
        @Override
        protected Class<?> loadClass(String name) {
          Class<?> clazz = super.loadClass(name);
          if(clazz.isAnnotationPresent(Enhanced.class)) {
             // do initialization stuff
          }
          return clazz;
        }
      }
      

      这对你有帮助吗?这有点猜测您要达到的目标,但我认为这可能是一个很好的解决方案。查看我的项目Byte Buddy,它通过引入LoadedTypeInitializer 解决了代理类的类似问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-04
        • 1970-01-01
        • 2012-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-01
        相关资源
        最近更新 更多