2

我正在使用 asm 在每个执行的函数中插入一个回调函数。我如何打印参数值?

我正在使用 MethodAdapter.visitCode 将我的函数注入到每个运行的函数中。

我想将函数参数插入一个数组并将我的回调函数发送到这个数组并将参数返回到堆栈,以便函数可以继续使用它们

以下代码将方法参数插入到数组中,并作为 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();
}                   
4

2 回答 2

4

在 for 循环之前,您将数组存储/创建到paramLength索引处的局部变量表中。但是在循环之后,您正在从第ith 索引访问它。为了安全起见:在倒数第三个源代码语句中i替换为;paramLength即这样做:this.visitVarInsn(Opcodes.ALOAD, paramLength);而不是 this this.visitVarInsn(Opcodes.ALOAD, i)

此外,您i的初始化为0. 对于实例方法,这意味着从局部变量表中加载this参数(阅读下面的一般说明)。确保这正是您打算做的。如果您不打算加载this变量,则初始化i1例如(非静态)方法。

一般说明:

鉴于您声称使用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,以防方法不是静态的,以说明this参数。
isMethodStatictrue当方法不是实例方法时,因此没有this参数,因此不需要增量;
isMethodStaticfalse当方法是实例方法时,因此存在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.
}
于 2013-08-05T22:00:49.667 回答
0

在 AMS 9.x 中

我有一个简单的建议。

  // org.objectweb.asm.commons.GeneratorAdapter
  public void loadArgArray() {
    push(argumentTypes.length);
    newArray(OBJECT_TYPE);
    for (int i = 0; i < argumentTypes.length; i++) {
      dup();
      push(i);
      loadArg(i);
      box(argumentTypes[i]);
      arrayStore(OBJECT_TYPE);
    }
  }

y 可以轻松获取 args 数组,并且 y 可以像loadArgArray/loadArgXXX访问每个 arg 的方法一样。

于 2021-12-24T08:45:23.580 回答