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 方法的动态调用。
posted @ 2025-04-06 08:08  方东信  阅读(31)  评论(0)    收藏  举报