Javran
Javran

Reputation: 3434

Can I get the Field instance of putfield or putstatic instruction in ASM?

I'm working with ASM and want to manipluate the class file to keep track of some field writes. I've learned putfield and putstatic instructions are instance of FieldInsnNode class in ASM, and I want to inject some code to construct a Field object at runtime and call other methods taking this Field object as an argument.

I did some experiment by compiling a simple Java source code:

package com.test.simple;

public class Simple {
    public int a,b;

    public void foo() {
        a = 20;
        b = 10;
    }
}

And then use javap to examine the class file:

$ 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
}

Here I can find putfields are actually followed by something like #2, which I guess are references to the constant pool. (And I have a wilder guess that constant is actually an instance of Field)

However, in ASM, FieldInsnNode just has 3 fields(namely owner, name, desc) hiding all the details about the constant field so I don't know how to verfy my guesses.

Here my question is:

Upvotes: 3

Views: 1465

Answers (1)

Holger
Holger

Reputation: 298153

You can simply use an ldc instruction to load a String constant from the constant pool to the operand stack. However you can’t do that with Fields. Contrary to your assumption, there is no Field instance on the constant pool. The JVM doesn’t use Reflection for its normal operations.

The associated constant pool entry of a putfield (etc.) instruction refers to a descriptor which, simply said, contains exactly the information which ASM provided, the owner class, the field name and its type signature.

To get a Field instance from these information you can use

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

The desc can be ignored here as Reflection will simply return the uniquely named field regardless of its type.

The ASM library provides the methods for constructing the instruction sequence described above. Be aware that visitFieldInsn provides you a String for the owner class which you have to convert to a Type instance before passing to visitLdcInsn as you want to generate an ldc instruction producing the associated Class instance rather than a String instance containing the name of the class.


For completeness, starting with Java 7 there is a possibility to get a MethodHandle from the constant pool wrapping field operations like putfield for a field known at compile-time, but the resulting handle only allows performing the encapsulated field operation using its invoke method but not inspecting the properties of the encapsulated field. That is reserved to Java 8 or newer which introduces an API method for converting such a MethodHandle to a Field but that is not simpler than the three instruction sequence above which works with all JVMs starting with Java 5.

Upvotes: 3

Related Questions