2

我正在使用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方法返回的 被插入到现有AbstractInsnNodeusing之前InsnList.insertBefore(AbstractInsnNode, printClass())。在字节码中达到这一点时,我收到一个错误,原因如下:

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

这显然是因为 LDC 指令添加的是 String"Lcom/example/ExampleClass;.class"而不是实际的 class Lcom/example/ExampleClass;.class

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

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

4

1 回答 1

2

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

例如

new LdcInsnNode(Type.getObjectType(name))

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

LdcInsnNode构造函数文档似乎有点过时了,因为它只提到了数字类型和StringClass支持ldc自 Java 5 起就存在,但文档包含指向 1.4.2 的链接)。相反,您可以参考MethodVisitor.visitLdcInsn(…)在生成字节码时最终将对象传递给哪个对象。

于 2016-11-15T15:41:09.393 回答