Question

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:

  • If the Field objects do lie in the constant pool, how can I retrieve it and push it to the stack in ASM?
  • If I can't find the Field objects, is that possible to Use FieldInsnNode.name and FieldInsoNode.owner to instantiate an instance of Field (Since there are no public constructors for Field)?
  • If all of the above won't work, I think I can still just printing out FieldInsnNode.name so at least I can know which field does the instruction write to. But it looks like all Strings also lie in the constant pool as well, so how can I build up some instructions to instantiate a String at runtime?
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top