如何创建一个ASM LdcInsnNode,将当前类静态添加到堆栈中



我正在使用ASM库来修改其他人创建的字节码。对于任意类中的任意方法,我想创建一个将当前类添加到堆栈的LdcInsnNode

例如,假设我正在转换一个名为 com.example.ExampleClass 的类。我想创建等效于System.out.println(ExampleClass.class.getName());的字节码。

这似乎是一项相对简单的任务。当我使用 Eclipse Bytecode Outline 插件时,它说以下字节码是等效的:

GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC Lcom/example/ExampleClass;.class
INVOKEVIRTUAL java/lang/Class.getName ()Ljava/lang/String;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V

我尝试了以下代码:

private InsnList printClass() {
    InsnList result = new InsnList();
    result.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
    result.add(new LdcInsnNode("L" + name + ";.class"));
    result.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false));
    result.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
    return result;
}

这是在ClassNode的扩展中运行的,所以name指的是ClassNode.name字段。此方法返回的InsnList是使用 InsnList.insertBefore(AbstractInsnNode, printClass()) 插入到现有AbstractInsnNode之前。当字节码中达到这一点时,我收到一个错误,原因如下:

Type 'java/lang/String' (current frame, stack[1]) is not assignable to 'java/lang/Class'

这显然是因为LDC指令正在添加字符串"Lcom/example/ExampleClass;.class"而不是实际的类Lcom/example/ExampleClass;.class

有什么解决方法吗?似乎不可能直接将Class对象添加到LdcInsnNode,因为该类尚不存在。但是有没有办法添加加载Class对象的指令?

在我的特定情况下,调用 Object.getClass() 方法不是一种选择,因为它需要从静态上下文中工作。

你不需要引用Class对象,事实上,它甚至不受支持(直接(。如果要通过 ASM 将Class推送到操作数堆栈,则必须将其称为Type实例。

例如

new LdcInsnNode(Type.getObjectType(name))

使用Type.getObjectType(…)工厂方法,该方法期望name表示内部名称形式,例如 com/example/ExampleClass周围没有L … ;。它等效于非数组类型的Type.getType('L'+name+';')。工厂方法的选择很重要,因为Type对象也可以表示基元类型或方法类型。由于 ldcType.getObjectType 都只支持引用类型,因此这是自然组合。

似乎LdcInsnNode构造函数文档有点过时了,因为它只提到了数字类型和String(自Java 5以来Class支持ldc,但文档包含指向1.4.2的链接(。您可以改为引用MethodVisitor.visitLdcInsn(…),在生成字节码时,对象最终会传递到该对象。

相关内容

  • 没有找到相关文章

最新更新