如何使用ASM修改Java BootstrapMethods中的指令?

时间:2017-11-29 21:03:23

标签: java java-bytecode-asm

使用Java 8编译并使用javap转储生成的类文件后,我看到了这一点,其中我只展示了前两个项目:

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的标准类文件格式文档,我没有看到有关引导方法的指令的直接描述,但我确实看到了我将描述为引导方法的元数据。

感谢,

-David

1 个答案:

答案 0 :(得分:1)

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

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

使用访客API时,您必须覆盖visitInvokeDynamicInsn。在这个地方,ASM已经解码了BootstrapMethods属性的引用条目,为访问方法提供了这些值,并且当您传递这些可能已更改的值时,将(重新)创建相应的BootstrapMethods属性到visitInvokeDynamicInsn方法访问者的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参数(bootstrap方法的invokedName name参数)提供。有关详细信息,请参阅LambdaMetafactory’s comprehensive documentation