Asm生成动态调用指令
import org.objectweb.asm.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.invoke.*;
// 假设的用户类
class UserClass {
public String test(int param) {
System.out.println("Test method is called with param: " + param);
return "Result from test method";
}
}
public class InvokeTestWithParamsAndReturnGenerator {
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, String descIdent, Object objVal) throws Throwable {
Class<?> clazz = Class.forName(descIdent);
// 根据实际的参数和返回值类型构建 MethodType
MethodType methodType = MethodType.methodType(String.class, int.class);
MethodHandle mh = lookup.findVirtual(clazz, name, methodType);
return new ConstantCallSite(mh.bindTo(objVal));
}
public static void main(String[] args) throws IOException {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "InvokeTestWithParamsAndReturn", null, "java/lang/Object", null);
// 生成 main 方法
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
mv.visitCode();
// 创建 FObj 实例并存储 UserClass 实例
mv.visitTypeInsn(Opcodes.NEW, "com/faros/lib/typeobj/FObj");
mv.visitInsn(Opcodes.DUP);
// 创建 UserClass 实例
mv.visitTypeInsn(Opcodes.NEW, "UserClass");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "UserClass", "<init>", "()V", false);
// 调用 FObj 的 fUObj 静态方法创建 FObj 实例
mv.visitLdcInsn("UserClass");
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/faros/lib/typeobj/FObj", "fUObj", "(Ljava/lang/Object;Ljava/lang/String;)Lcom/faros/lib/typeobj/FObj;", false);
// 现在栈顶是 FObj 实例,通过 invokedynamic 调用其存储对象的 test 方法
// 获取 FObj 中的 objVal
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "com/faros/lib/typeobj/FObj", "objVal", "Ljava/lang/Object;");
// 获取 FObj 中的 descIdent
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "com/faros/lib/typeobj/FObj", "descIdent", "Ljava/lang/String;");
// 准备 test 方法的参数
mv.visitLdcInsn(10); // 假设参数为 10
Handle bootstrapMethod = new Handle(
Opcodes.H_INVOKESTATIC,
"InvokeTestWithParamsAndReturnGenerator",
"bootstrap",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/CallSite;",
false
);
// 指定 invokedynamic 指令的方法类型,与 test 方法的参数和返回值匹配
mv.visitInvokeDynamicInsn(
"test",
"(Ljava/lang/Object;Ljava/lang/String;I)Ljava/lang/String;",
bootstrapMethod,
"descIdent",
"objVal"
);
// 处理返回值,这里简单打印
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "toString", "()Ljava/lang/String;", false);
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
cw.visitEnd();
// 输出字节码到文件
try (FileOutputStream fos = new FileOutputStream("InvokeTestWithParamsAndReturn.class")) {
fos.write(cw.toByteArray());
}
}
}
详细解释
- mv.visitInvokeDynamicInsn(
mv 是 MethodVisitor 的实例,visitInvokeDynamicInsn 是 MethodVisitor 类中的方法,用于在生成的字节码中插入 invokedynamic 指令。该指令的作用是在运行时动态解析要调用的方法。 - "test"
这是调用点(call site)的名称,通常与要调用的方法名一致。在这个例子中,我们要调用 UserClass 类的 test 方法,所以这里写 "test"。这个名称主要用于引导方法(bootstrap method)内部逻辑,帮助引导方法识别要调用的目标方法。 - Ljava/lang/Object;Ljava/lang/String;I)Ljava/lang/String;
这是 invokedynamic 指令的方法类型(method type)描述符。它定义了调用点的参数类型和返回值类型,遵循 Java 字节码的方法描述符规则:
(Ljava/lang/Object;Ljava/lang/String;I) 表示参数列表,依次为一个 Object 类型、一个 String 类型和一个 int 类型(I 是 int 的字节码表示)。这里的 Object 对应 FObj 中的 objVal,String 对应 descIdent,int 是 test 方法的参数。
Ljava/lang/String; 表示返回值类型,即 test 方法返回一个 String 类型的值。 - bootstrapMethod
bootstrapMethod 是一个 Handle 对象,它指向引导方法。引导方法是一个静态方法,负责在运行时动态地确定要调用的方法,并返回一个 CallSite 对象。CallSite 对象封装了目标方法的调用逻辑。在这个例子中,引导方法是 InvokeTestWithParamsAndReturnGenerator 类中的 bootstrap 方法。 - "descIdent" 和 "objVal"
这两个是动态参数(dynamic arguments),它们会被传递给引导方法。引导方法可以根据这些参数在运行时动态地确定要调用的方法。在这个例子中,"descIdent" 是 FObj 中存储的类的限定符,"objVal" 是 FObj 中存储的实际对象实例。引导方法会根据 descIdent 找到对应的类,然后使用 objVal 作为调用 test 方法的对象。
总结
这行代码的作用是在生成的字节码中插入一个 invokedynamic 指令,该指令在运行时会触发引导方法,引导方法根据传入的动态参数动态地确定要调用的 test 方法,并返回一个 CallSite 对象,最终实现对 test 方法的动态调用。
Rust编程语言群 1036955113
java新手自学群 626070845
java/springboot/hadoop/JVM 群 4915800
Hadoop/mongodb(搭建/开发/运维)Q群481975850
GOLang Q1群:6848027
GOLang Q2群:450509103
GOLang Q3群:436173132
GOLang Q4群:141984758
GOLang Q5群:215535604
C/C++/QT群 1414577
单片机嵌入式/电子电路入门群群 306312845
MUD/LIB/交流群 391486684
Electron/koa/Nodejs/express 214737701
大前端群vue/js/ts 165150391
操作系统研发群:15375777
汇编/辅助/破解新手群:755783453
大数据 elasticsearch 群 481975850
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
java新手自学群 626070845
java/springboot/hadoop/JVM 群 4915800
Hadoop/mongodb(搭建/开发/运维)Q群481975850
GOLang Q1群:6848027
GOLang Q2群:450509103
GOLang Q3群:436173132
GOLang Q4群:141984758
GOLang Q5群:215535604
C/C++/QT群 1414577
单片机嵌入式/电子电路入门群群 306312845
MUD/LIB/交流群 391486684
Electron/koa/Nodejs/express 214737701
大前端群vue/js/ts 165150391
操作系统研发群:15375777
汇编/辅助/破解新手群:755783453
大数据 elasticsearch 群 481975850
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。