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。