我想使用ASM Java框架对一些耗时的方法(例如 org/json/JSONObject.toString()
进行一些检测工作。
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?
}
}
}
答案 0 :(得分:9)
您无需“操纵”方法调用。关键点在于,您的访问者通过将每个传入的访问者呼叫转发给作者来生成代码,并且您可以方便地继承执行此操作的1:1。
因此,您未覆盖的每个visit…
方法都会将每次调用委托给编写器,从而生成完全相同的指令。当它们委托给传递相同参数的原始super
实现时,这同样适用于重写方法。当您重写方法并且不中继调用时,不会再现,读取和删除相应的指令。当您调用其他visit…
方法(而不是)时,您将生成其他指令。
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
}
因此上面的代码拦截了您感兴趣的指令,并且不会重现它,而是生成所需的invokestatic
指令。这没有额外的调整,因为静态方法调用将从堆栈中消耗JSONObject
并生成String
,就像原始调用一样,因此对周围的代码没有影响。