I want to do some instrumentation work on some time consuming method such as org/json/JSONObject.toString()
, using ASM Java framework.
public class JSONUsage {
public void callToString() {
JSONObject jsonObject = new JSONObject();
String a = jsonObject.toString();//original call
System.out.println(a);
}
}
public class JSONUsage {
public void callToString() {
JSONObject jsonObject = new JSONObject();
// **important!**
//pass the instance as an param, replace the call to a static method
String a = JSONReplacement.jsonToString(jsonObject);
System.out.println(a);
}
}
public class JSONReplacement {
public static String jsonToString(JSONObject jsonObject) {
//do the time caculation
long before = System.currentTimeMillis();
String ret = jsonObject.toString();
long elapsed = System.currentTimeMillis() - before;
return ret;
}
}
ClassReader cr = new ClassReader("JSONUsage");
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ReplaceClassVisitor replaceClassVisitor = new ReplaceClassVisitor(cw);
cr.accept(replaceClassVisitor, ClassReader.EXPAND_FRAMES);
public class ReplaceClassVisitor extends ClassAdapter {
public ReplaceClassVisitor(ClassVisitor cv) {
super(cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MethodReplaceMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc);
}
private static final class MethodReplaceMethodVisitor extends GeneratorAdapter {
public MethodReplaceMethodVisitor(MethodVisitor mv, int access, String name, String desc) {
super(mv, access, name, desc);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
//org/json/JSONObject.toString() here is a example,
//i want a general instruction
if (owner.equals("org/json/JSONObject") && name.equals("toString")) {
replaceCall(opcode, owner, name, desc);
}
}
private void replaceCall(int opcode, String owner, String name, String desc) {
//how can i have a general asm instruction to manipulate this method call?
}
}
}
You don’t need to “manipulate” a method call. The key point is that your visitor is producing the code by relaying every incoming visitor call to the writer and you’re conveniently inheriting an implementation which does this 1:1.
So every visit…
method you’re not overriding will delegate every invocation to the writer, producing exactly the same instruction. The same applies to overridden methods when they delegate to their original super
implementation passing the same arguments. When you override a method and do not relay an invocation, the corresponding instruction is not reproduced, read, effectively removed. When you invoke other visit…
methods (instead) you will produce other instructions.
private static final class MethodReplaceMethodVisitor extends GeneratorAdapter {
public MethodReplaceMethodVisitor(
MethodVisitor mv, int access, String name, String desc) {
super(mv, access, name, desc);
}
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String desc, boolean itf) {
if(opcode==Opcodes.INVOKEVIRTUAL && owner.equals("org/json/JSONObject")
&& name.equals("toString") && desc.equals("()Ljava/lang/String;")) {
// not relaying the original instruction to super effectively removes the original
// instruction, instead we're producing a different instruction:
super.visitMethodInsn(Opcodes.INVOKESTATIC, "whatever/package/JSONReplacement",
"jsonToString", "(Lorg/json/JSONObject;)Ljava/lang/String;", false);
}
else // relaying to super will reproduce the same instruction
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
// all other, not overridden visit methods reproduce the original instructions
}
So the code above intercepts the instruction you’re interested in and will not reproduce it, but produce the desired invokestatic
instruction instead. This works without additional adaptations as your static method invocation will consume a JSONObject
from the stack and produce a String
, just like the original invocation, so there is no effect on the surrounding code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With