Reputation: 3434
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 putfield
s 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:
Field
objects do lie in the constant pool, how can I retrieve it and push it to the stack in ASM?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
)?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?Upvotes: 3
Views: 1465
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 Field
s. 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