如何创建一个将当前 class 静态添加到堆栈的 ASM LdcInsnNode?
How does one create an ASM LdcInsnNode that statically adds the current class to the stack?
我正在使用 ASM library 修改其他人创建的字节码。对于任意 class 中的任意方法,我想创建一个 LdcInsnNode
将当前 class 添加到堆栈。
例如,假设我正在转换一个名为 com.example.ExampleClass
的 class。我想创建等同于 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.insertBefore(AbstractInsnNode, printClass())
将此方法返回的 InsnList
插入到现有 AbstractInsnNode
之前。当字节码到达这一点时,我得到一个错误,原因如下:
Type 'java/lang/String' (current frame, stack[1]) is not assignable to 'java/lang/Class'
这显然是因为 LDC 指令添加了字符串 "Lcom/example/ExampleClass;.class"
而不是实际的 class Lcom/example/ExampleClass;.class
.
有什么解决方法吗?直接将 Class
对象添加到 LdcInsnNode
似乎是不可能的,因为 class 还不存在。但是有没有办法添加加载Class
对象的指令?
在我的特殊情况下,调用 Object.getClass()
方法不是一个选项,因为它需要在静态上下文中工作。
您不需要引用 Class
对象,事实上,它甚至不受支持(直接)。如果要通过 ASM 将 Class
压入操作数堆栈,则必须将其称为 Type
实例。
例如
new LdcInsnNode(Type.getObjectType(name))
使用 Type.getObjectType(…)
工厂方法,它期望 name
表示内部名称形式,例如com/example/ExampleClass
周围没有 L … ;
。对于非数组类型,它等同于 Type.getType('L'+name+';')
。工厂方法的选择很重要,因为 Type
对象也可以表示基本类型或方法类型。因为 ldc
和 Type.getObjectType
都只支持引用类型,所以这是自然的组合。
似乎 LdcInsnNode
constructor documentation is a bit outdated, as it only mentions number types and String
(Class
support with ldc
exists since Java 5, but the documentation contains links to 1.4.2). You may refer to MethodVisitor.visitLdcInsn(…)
而不是,在生成字节码时最终将对象传递给它。
我正在使用 ASM library 修改其他人创建的字节码。对于任意 class 中的任意方法,我想创建一个 LdcInsnNode
将当前 class 添加到堆栈。
例如,假设我正在转换一个名为 com.example.ExampleClass
的 class。我想创建等同于 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.insertBefore(AbstractInsnNode, printClass())
将此方法返回的 InsnList
插入到现有 AbstractInsnNode
之前。当字节码到达这一点时,我得到一个错误,原因如下:
Type 'java/lang/String' (current frame, stack[1]) is not assignable to 'java/lang/Class'
这显然是因为 LDC 指令添加了字符串 "Lcom/example/ExampleClass;.class"
而不是实际的 class Lcom/example/ExampleClass;.class
.
有什么解决方法吗?直接将 Class
对象添加到 LdcInsnNode
似乎是不可能的,因为 class 还不存在。但是有没有办法添加加载Class
对象的指令?
在我的特殊情况下,调用 Object.getClass()
方法不是一个选项,因为它需要在静态上下文中工作。
您不需要引用 Class
对象,事实上,它甚至不受支持(直接)。如果要通过 ASM 将 Class
压入操作数堆栈,则必须将其称为 Type
实例。
例如
new LdcInsnNode(Type.getObjectType(name))
使用 Type.getObjectType(…)
工厂方法,它期望 name
表示内部名称形式,例如com/example/ExampleClass
周围没有 L … ;
。对于非数组类型,它等同于 Type.getType('L'+name+';')
。工厂方法的选择很重要,因为 Type
对象也可以表示基本类型或方法类型。因为 ldc
和 Type.getObjectType
都只支持引用类型,所以这是自然的组合。
似乎 LdcInsnNode
constructor documentation is a bit outdated, as it only mentions number types and String
(Class
support with ldc
exists since Java 5, but the documentation contains links to 1.4.2). You may refer to MethodVisitor.visitLdcInsn(…)
而不是,在生成字节码时最终将对象传递给它。