ASM 树 API:使用 LDC 加载一个 Class<?> 常量

ASM Tree API: Using LDC to load a Class<?> constant

我正在用 ASM 编写一个程序,它使用树 API 将字节码添加到某些方法。我已经使用 ASMifier 生成创建特定方法所需的代码,但我在使用以下行时遇到了一些问题:

mv.visitLdcInsn(Type.getType('L' + targetClassName + ';'));

我只是将 mv 初始化为 new MethodNode,但没有加载 Class,上面的行在字节码中显示为:

ldc Lsome/test/TestClass; (org.objectweb.asm.Type)

如何让 ASM 加载 java/lang/Class 常量而不是 org.objectweb.asm.Type 常量?

如果相关,下一行字节码将是invokevirtual java/lang/Class getClassLoader(()Ljava/lang/ClassLoader;);

我现在想出了一个临时解决方法。

mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false);

您的代码是正确的,但不是规范的解决方案。您可以将其简化为

mv.visitLdcInsn(Type.getObjectType(targetClassName));

(在 ASM 中,“对象类型”表示引用类型,另请参见 Type.getObjectType(…) and the Type.OBJECT sort)。然而,结果是一样的。原因,为什么反汇编输出看起来像

ldc Lsome/test/TestClass; (org.objectweb.asm.Type)

在于反汇编程序。如果你看 it’s code for converting an LDC instruction to a String:

protected String printLdcInsnNode(LdcInsnNode ldc, ListIterator<?> it) {
    if (ldc.cst instanceof String)
        return nameOpcode(ldc.opcode()) + " \"" + StringEscapeUtils.escapeJava(ldc.cst.toString()) + "\" (" + ldc.cst.getClass().getCanonicalName() + ")";

   return nameOpcode(ldc.opcode()) + " " + StringEscapeUtils.escapeJava(ldc.cst.toString()) + " (" + ldc.cst.getClass().getCanonicalName() + ")";
}

您会看到它将打印存储在 LdcInsnNode 中的对象实例的类型,而不是代码最终在运行时生成的类型。对于 String,这就足够了,因为它是同一类型,对于原始类型,它将打印相应的包装器类型,但是对于 TypeHandle 对象,此反汇编程序将向您显示这些 ASM具体 类 而不是相应的运行时 类 java.lang.Classjava.lang.invoke.MethodTypejava.lang.invoke.MethodHandleldc 指令实际上会将其压入操作数堆栈。