我想要做的是记录这些参数并与之前输入的参数进行一些比较。我需要为每个调用的方法记录参数,因此参数列表的长度是不确定的。
我想我可以解析方法描述符来知道这个方法有多少个参数。但问题是,如何从操作数堆栈中记录任意数量的值?
现在我只能写一些示例代码。在我的 MethodVisitorAdapter
类(class):
public void visitMethodInsn(int opc, String owner, String name, String desc, boolean isInterface){
int n = getParameterCount(desc);
// How to duplicate arbitrary number of values at operand stack?
// ...
mv.visitMethodInsn(INVOKESTATIC, MyClass, "recordParameters",
/* should be what kind of descriptor (may have arbitrary number of parameters)? */,
false);
mv.visitMethodInsn(opc, owner, name, desc, isInterface);
}
最佳答案
您可以制作 recordParameters
可变数量方法(varargs)。它将接受所有参数作为单个 Object[]
争论。
因此,您需要创建一个数组并用(可能是装箱的)参数填充它。以下构建器样式的类将有助于从堆栈中收集参数。它包含 push
所有可能类型的方法,还处理自动装箱。
记录参数后,helper 可以通过调用对应的 pop
将参数放回栈中。方法顺序相反。
public class ArgCollector {
private final Object[] args;
private int index;
public ArgCollector(int length) {
this.args = new Object[length];
this.index = length;
}
public ArgCollector push(Object o) {
args[--index] = o;
return this;
}
public Object pop() {
return args[index++];
}
public static ArgCollector push(boolean a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(byte a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(char a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(short a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(int a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(long a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(float a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(double a, ArgCollector c) { return c.push(a); }
public static ArgCollector push(Object a, ArgCollector c) { return c.push(a); }
public boolean popZ() { return (boolean) pop(); }
public byte popB() { return (byte) pop(); }
public char popC() { return (char) pop(); }
public short popS() { return (short) pop(); }
public int popI() { return (int) pop(); }
public long popJ() { return (long) pop(); }
public float popF() { return (float) pop(); }
public double popD() { return (double) pop(); }
public Object[] toArray() {
return args;
}
}
现在,任务是为以下 Java 代码生成等效的字节码: ArgCollector collector = new ArgCollector(N);
recordParameters(
ArgCollector.push(arg1,
ArgCollector.push(arg2,
ArgCollector.push(argN, collector)))
.toArray()
);
originalMethod(
collector.popI(),
collector.popJ(),
(String) collector.pop()
);
以下是使用 ASM 执行此操作的方法: Type[] args = Type.getArgumentTypes(desc);
String collector = Type.getInternalName(ArgCollector.class);
// new ArgCollector(argCount)
mv.visitTypeInsn(NEW, collector);
mv.visitInsn(DUP);
mv.visitIntInsn(SIPUSH, args.length);
mv.visitMethodInsn(INVOKESPECIAL, collector, "<init>", "(I)V", false);
// For each argument call ArgCollector.push(arg, collector)
for (int i = args.length; --i >= 0; ) {
Type arg = args[i];
String argDesc = arg.getDescriptor().length() == 1 ? arg.getDescriptor() : "Ljava/lang/Object;";
mv.visitMethodInsn(INVOKESTATIC, collector, "push",
"(" + argDesc + "L" + collector + ";)L" + collector + ";", false);
}
// Call recordParameters(collector.toArray())
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEVIRTUAL, collector, "toArray", "()[Ljava/lang/Object;", false);
mv.visitMethodInsn(INVOKESTATIC, MyClass, "recordParameters", "([Ljava/lang/Object;)V", false);
// Push original arguments back on stack
for (Type arg : args) {
String argDesc = arg.getDescriptor().length() == 1 ? arg.getDescriptor() : "Ljava/lang/Object;";
mv.visitInsn(DUP);
if (argDesc.length() == 1) {
mv.visitMethodInsn(INVOKEVIRTUAL, collector, "pop" + argDesc, "()" + argDesc, false);
} else {
mv.visitMethodInsn(INVOKEVIRTUAL, collector, "pop", "()Ljava/lang/Object;", false);
if (!arg.getDescriptor().equals("Ljava/lang/Object;")) {
// Need to cast object arguments to the original type
mv.visitTypeInsn(CHECKCAST, arg.getDescriptor());
}
}
// Swap the last argument with ArgCollector, so that ArgCollector is on top again
if (arg.getSize() == 1) {
mv.visitInsn(SWAP);
} else {
mv.visitInsn(DUP2_X1);
mv.visitInsn(POP2);
}
}
// Pop off the remaining ArgCollector, and call the original method
mv.visitInsn(POP);
mv.visitMethodInsn(opc, owner, name, desc, isInterface);
关于java - 如何使用java代理和ASM动态记录任何调用的java方法的所有参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62816194/