我正在使用asm在每个执行的函数中插入一个回调函数。 如何打印arguents值?
我正在使用MethodAdapter.visitCode将我的函数注入到每个运行的函数中。
我想将函数参数插入一个数组并发送我的callbackk函数这个数组并将参数返回给堆栈,以便函数可以继续使用它们
以下代码将方法参数插入到数组中,并作为Object数组发送到回调函数。 我无法将参数返回到原始函数
@Override public void visitCode()
{
int paramLength = paramTypes.length;
System.out.println(className + "." + methodName + ": paramLength = " + paramLength);
// Create array with length equal to number of parameters
mv.visitIntInsn(Opcodes.BIPUSH, paramLength);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
mv.visitVarInsn(Opcodes.ASTORE, paramLength);
// Fill the created array with method parameters
int i = 0;
for (Type tp : paramTypes)
{
System.out.println("tp.getClassName() = " + tp.getClassName());
mv.visitVarInsn(Opcodes.ALOAD, paramLength);
mv.visitIntInsn(Opcodes.BIPUSH, i);
if (tp.equals(Type.BOOLEAN_TYPE))
{
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
}
else if (tp.equals(Type.BYTE_TYPE))
{
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
}
else if (tp.equals(Type.CHAR_TYPE))
{
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
}
else if (tp.equals(Type.SHORT_TYPE))
{
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
}
else if (tp.equals(Type.INT_TYPE))
{
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
}
else if (tp.equals(Type.LONG_TYPE))
{
mv.visitVarInsn(Opcodes.LLOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
i++;
}
else if (tp.equals(Type.FLOAT_TYPE))
{
mv.visitVarInsn(Opcodes.FLOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
}
else if (tp.equals(Type.DOUBLE_TYPE))
{
mv.visitVarInsn(Opcodes.DLOAD, i);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
i++;
}
else
mv.visitVarInsn(Opcodes.ALOAD, i);
mv.visitInsn(Opcodes.AASTORE);
i++;
}
//System.out.println("end for");
// Load class name and method name
this.visitLdcInsn(className);
this.visitLdcInsn(methodName);
// Load the array of parameters that we created
this.visitVarInsn(Opcodes.ALOAD, i);
this.visitMethodInsn(Opcodes.INVOKESTATIC, "callbackpackage/CallBack", "callbackfunc", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
super.visitCode();
}
答案 0 :(得分:3)
在for循环之前,您将数组存储/创建到paramLength
索引的局部变量表中。但是在循环之后,您将从i
索引访问它。为了安全起见:在第三个到最后一个源代码语句中用i
替换paramLength
;即执行此操作:this.visitVarInsn(Opcodes.ALOAD, paramLength);
而不是此this.visitVarInsn(Opcodes.ALOAD, i)
。
此外,您的i
已初始化为0
。例如,这意味着从局部变量表中加载 this 参数(请参阅下面的一般说明)。确保它正是您打算做的。如果您不打算加载此变量,请将i
初始化为1
,例如(非静态)方法。
一般说明:
鉴于您声称使用MethodAdapter.visitCode
,我将假设您要访问方法参数的值, 方法的调用。
方法参数存储在本地变量表中,用于每个方法调用。因此,您可以通过简单地将操作数堆栈中的第一个 N 变量加载到局部变量表中来访问方法参数的值。其中N只是方法的参数个数。请记住,局部变量表是0索引的;因此索引从0开始。还要注意,在实例方法的情况下,“ this ”也被视为参数。在实例方法的情况下,索引为0的局部变量表示此“变量”。
可以使用以下要点中的代码从方法描述计算方法的参数数量:https://gist.github.com/VijayKrishna/6160036。使用parseMethodArguments(String desc)
方法,您可以轻松计算方法的参数数量。然后在visitCode()
方法的某个地方,理想情况下尽快执行此操作:
@Override
public void visitCode() {
...
char[] methodDescParsed = parseMethodArguments(methodDescription);
int N = methodDescParsed.length + (isMethodStatic ? 0 : 1);
...
}
parseMethodArguments中的大多数计算只是解析方法描述,这是一个字符串。它用大写字母L替换数组和对象的类型描述符,并保留原语的类型描述符。它返回一个char[]
,数组中的每个元素大致表示传递的参数类型。
由于 this 参数的类型未反映在方法描述中,因此(isMethodStatic ? 1 : 0)
三元表达式用于将参数计数递增1,以防方法不是静态的,以解释此参数
当方法不是实例方法时,isMethodStatic
为true
,因此,没有此参数,因此不需要增量;
当方法是实例方法时,isMethodStatic
为false
,因此, this 参数存在,因此参数计数会递增。
一旦得到参数计数,使用ASM的松散Java代码如下所示,在方法调用后很快访问方法的第一个 N 局部变量:
for(int i = 0; i < N; i ++) {
int opcode = 0;
switch() {
case 'L': opcode = Opcodes.ALOAD; break;
case 'I': opocde = Opcodes.ILOAD; break;
case 'J': opcode = Opocdes.LLOAD; break;
case 'F': ...
case 'D': ...
// TODO: complete all the cases.
}
mv.visitVarInsn(opcode, i);
// ith argument is now loaded on the operand stack.
// add more ASM code to do what ever it is that you want to do with the argument.
}