Java 我可以在ASM中获取putfield或putstatic指令的字段实例吗?
我正在使用并希望管理类文件以跟踪一些字段写入。我已经了解了Java 我可以在ASM中获取putfield或putstatic指令的字段实例吗?,java,java-bytecode-asm,Java,Java Bytecode Asm,我正在使用并希望管理类文件以跟踪一些字段写入。我已经了解了putfield和putstatic指令是ASM中的FieldInsnNode类的实例,我想在运行时注入一些代码来构造一个对象,并调用以该Field对象为参数的其他方法 我通过编译一个简单的Java源代码做了一些实验: package com.test.simple; public class Simple { public int a,b; public void foo() { a = 20;
putfield
和putstatic
指令是ASM中的FieldInsnNode
类的实例,我想在运行时注入一些代码来构造一个对象,并调用以该Field
对象为参数的其他方法
我通过编译一个简单的Java源代码做了一些实验:
package com.test.simple;
public class Simple {
public int a,b;
public void foo() {
a = 20;
b = 10;
}
}
然后使用javap
检查类文件:
$ javap -c -l Simple.class
Compiled from "Simple.java"
public class com.test.simple.Simple {
public int a;
public int b;
public com.test.simple.Simple();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public void foo();
Code:
0: aload_0
1: bipush 20
3: putfield #2 // Field a:I
6: aload_0
7: bipush 10
9: putfield #3 // Field b:I
12: return
LineNumberTable:
line 7: 0
line 8: 6
line 9: 12
}
$javap-c-l Simple.class
从“Simple.java”编译而来
公共类com.test.simple.simple{
公共INTA;
公共int b;
public com.test.simple.simple();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
LineNumberTable:
第3行:0
公开无效foo();
代码:
0:aload_0
1:bipush 20
3:putfield#2//字段a:I
6:aload_0
7:bipush 10
9:putfield#3//字段b:I
12:返回
LineNumberTable:
第7行:0
第8行:6
第9行:12
}
在这里,我可以找到putfield
s后面实际上跟有类似#2
的东西,我猜这是对常量池的引用。(我更大胆地猜测,常量实际上是字段的一个实例)
然而,在ASM中,FieldInsnNode
只有3个字段(即owner
,name
,desc
)隐藏了常数字段的所有细节,因此我不知道如何验证我的猜测
我的问题是:
- 如果
字段
对象确实位于常量池中,如何检索它并将其推送到ASM中的堆栈中
- 如果找不到
字段
对象,是否可以使用FieldInsnNode.name
和fieldinsnode.owner
来实例化字段
的实例(因为字段
没有公共构造函数)
- 如果以上所有操作都不起作用,我想我仍然可以打印出
FieldInsnNode.name
,这样至少我可以知道指令写入哪个字段。但是看起来所有的字符串也都位于常量池中,那么我如何构建一些指令来在运行时实例化字符串呢
您可以简单地使用ldc
指令将字符串
常量从常量池加载到操作数堆栈。但是,您不能使用字段
s执行此操作。与您的假设相反,常量池中没有字段
实例。JVM的正常操作不使用反射
putfield
(等)指令的相关常量池条目指的是一个描述符,简单地说,它确切地包含ASM提供的信息、所有者类、字段名及其类型签名
要从这些信息中获取字段
实例,可以使用
ldc owner // a Class instance from constant pool
ldc name // a String instance from constant pool
invokevirtual java/lang/Class getDeclaredField (Ljava/lang/String;)Ljava/lang/reflect/Field; // returns the Field
这里可以忽略desc
,因为反射只会返回唯一命名的字段,而不管其类型如何
ASM库提供了构建上述指令序列的方法。请注意,visitFieldInsn
为所有者类提供了一个String
,在传递给之前,您希望生成一个ldc
指令,生成关联的class
实例,而不是包含类名称的String
实例
为了完整性,从Java 7开始,可以从常量池包装字段操作中获取MethodHandle
,例如编译时已知字段的putfield
,但结果句柄只允许使用其invoke
方法执行封装字段操作,而不检查封装字段的属性。这是Java 8或更新版本保留的,它引入了一个,但这并不比上面的三条指令序列简单,这三条指令序列适用于从Java 5开始的所有JVM。我的链接指向ASM的visitor API,但当然,ASM的tree API遵循相同的概念,因此我的答案信息应该很容易适应该API。