【问题标题】:How to modify instructions in Java BootstrapMethods using ASM?如何使用 ASM 修改 Java BootstrapMethods 中的指令?
【发布时间】:2017-11-29 21:03:23
【问题描述】:

使用 Java 8 编译并使用 javap 转储生成的类文件后,我看到了这个,我只显示了前 2 项:

BootstrapMethods:

  0: #174 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

    Method arguments:

      #175 (Ljava/lang/Object;)Z

      #179 invokestatic llllll/lallll.lambda$printPersons$0:(Lllllll/lallll;)Z

      #180 (Lllllll/lallll;)Z

  1: #174 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

    Method arguments:

      #175 (Ljava/lang/Object;)Z

      #191 invokestatic llllll/lallll.lambda$printPersons$1:(Lllllll/lallll;)Z

      #180 (Lllllll/lallll;)Z

  2: #174 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/inv

我可以使用 ASM 访问这些引导方法中的指令并更改例如使用上述 invokestatic 指令调用的方法的名称吗?

显然,这些方法不是类的普通方法的一部分,而且我没有任何运气使用标准 ASM 类和方法技术访问它们。

如果不使用 ASM,是否可以使用另一个 Java 字节码/类文件操作框架来查找和修改这些指令?

我一直在阅读 Java 的标准类文件格式文档,但没有看到对引导方法指令的直接描述,但我确实看到了我将描述为引导方法元数据的内容。

谢谢,

-大卫

【问题讨论】:

    标签: java java-bytecode-asm


    【解决方案1】:

    BootstrapMethods 属性包含对其他方法的引用。您的类文件中没有引导方法(通常),您实际上不想更改引导方法,即示例中 java.lang.invoke.LambdaMetafactory 类中的方法 metafactory

    您实际上想要做的(显然)是更改 invokedynamic instruction 的属性,这将为 lambda 表达式或方法引用创建实例。对于这项任务,ASM 已经为您提供了帮助。

    使用访问者 API 时,您必须覆盖 visitInvokeDynamicInsn。在这个地方,ASM 已经解码了 BootstrapMethods 属性的引用条目,将这些值提供给 visit 方法,并在您将这些可能更改的值传递给 @987654337 时(重新)创建适当的 BootstrapMethods 属性ClassWriter的方法访问者的@方法。

    在被覆盖的visitInvokeDynamicInsn 中,您首先必须验证此invokedynamic 指令确实是一个lambda 创建站点。 bsm 参数必须是 Handle,其所有者是 java/lang/invoke/LambdaMetafactory,方法必须是 metafactoryaltMetafactory。如果没有,只需将所有内容传递给 super.visitInvokeDynamicInsn(未更改地委托给作者),因为它是 invokedynamic feature 的不同用法(例如,Java 9 将使用它进行字符串连接)。

    当它是一个 lambda 创建站点时,您可以根据the documentation of LambdaMetafactory 中指定的约定来解释参数。 bsmArgs 数组对应于您在问题中发布的属性。索引1 处的数组元素将是目标方法,在ASM 中再次表示为Handle。您可以将其更改为不同的句柄,这似乎是您的预期操作。目标函数式接口是在desc 参数中编码的返回类型,函数式接口方​​法的名称作为name 参数提供(引导方法的invokedName 名称参数)。更多详情请参考LambdaMetafactory’s comprehensive documentation

    【讨论】:

    • 作为 lambda 创建站点的 Handle 的 getOwner 方法不返回 java/lang/invoke/LambdaMetafactory。您还有其他方法可以检查所有者吗?
    • 你在看正确的把手吗? bsm 参数必须指向 LambdaMetafactorybsmArgs args 数组中的句柄指向方法引用/lambda 表达式的目标方法。
    猜你喜欢
    • 1970-01-01
    • 2017-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-16
    • 1970-01-01
    • 2013-10-22
    • 2011-02-10
    相关资源
    最近更新 更多