2

I am using asm for inserting a callback function inside each function which is executed. How can I print the arguents values which?

I am using MethodAdapter.visitCode to inject my function into each function ran.

I want to insert the function argument into an array and send my callbackk function this array and return the arguments back to the stack so that the function can continue using them

The following code inserts the method argument into an array and is sent to a callback function as an Object array. I am having trouble returning the arguments back to the original function

@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();
}                   
Shay
  • 633
  • 2
  • 11
  • 27
  • provide more details as to what you want to do with the argument. do you plan to print the arguments? Or do you want to simply send the values of these arguments to another function? – vijay Aug 05 '13 at 21:31
  • I want to send these values to another function, probably in an array – Shay Aug 06 '13 at 07:16

2 Answers2

4

Before the for loop, you are storing/creating your array onto the local variable table at the paramLength index. But after the loop, you are accessing it from the ith index. Just to be safe: replace i with paramLength in your third-to-last source-code statement; i.e. do this: this.visitVarInsn(Opcodes.ALOAD, paramLength); instead of this this.visitVarInsn(Opcodes.ALOAD, i).

Also, your i is initialized to 0. For instance methods this means loading the this argument from the local variable table (read general explanation below). Make sure that it is exactly what you intend to do. If you do not intend to load the this variable, then initialize i to 1 for instance (non-static) methods.

General Explanation:

Given that you claim to use MethodAdapter.visitCode, i will assume that you want access the values of the method arguments, after the method's invocation.

The method arguments are stored in the local variable table for each method invocation. Thus, you can access the values of the method arguments by simply loading, on to the operand stack, the first N variables from the local variable table; where N is simply the number of arguments to the method. Keep in mind that the local variable table is 0-indexed; and thus the indices start from 0. Also note that "this" is also treated as an argument in the case of instance methods. In case of instance methods, the local variable at index-0 indicates the this "variable".

The number of arguments to the method can be computed from the method description using the code in the following gist: https://gist.github.com/VijayKrishna/6160036. Using the parseMethodArguments(String desc) method you can easily compute the number of arguments to the method. Then somewhere in the visitCode() method, ideally as soon as possible, do this:

@Override
public void visitCode() {
    ...
    char[] methodDescParsed = parseMethodArguments(methodDescription);
    int N = methodDescParsed.length + (isMethodStatic ? 0 : 1);
    ...
}

Most of the computation in the parseMethodArguments simply parses the method description which is a string. It replaces the type descriptors for arrays and objects with a capital L. and leaves the type descriptors for primitives as is. It returns a char[] with each element in the array roughly indicating the type of argument passed.

Because the type of the this argument is not reflected in the method description, the (isMethodStatic ? 1 : 0) ternary expression is used to increment the argument count by 1, in case the method is not static, to account for the this argument.
isMethodStatic is true when the method is not an instance method, thus, no this argument and thus, no increment required;
isMethodStatic is false when the method is an instance method, thus, the this argument is present, thus the argument count is incremented.

once you get the argument count, the loose Java code using ASM is as follows to access the first N local variables of the method soon after its method invocation:

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.
}
vijay
  • 2,646
  • 2
  • 23
  • 37
  • How do I return the arguments back to the stack so that the method can continue using them? – Shay Aug 06 '13 at 07:18
  • you bring the arguments **from** the **local-variable-table** and **to** the **operand-stack**. you can access them from the local-variable-table at any time, as long as the arguments do not change during the method's execution. – vijay Aug 06 '13 at 07:54
  • You bring the arguments from the local var table to the operand stack by simply using the xLOAD bytecode operations. In the answer i have provided the preliminary code for using the ASM API to instrument the method with those xLOAD instructions depending on the type of the method arguments. If you can provide the code that you are working on, perhaps then, i can be more specific with the code example. – vijay Aug 06 '13 at 08:32
  • I edited my post to include my visitCode function. You can see that the method arguments are inserted into an array and sent to my callback function. Now I need to return the arguments back to the stack as it was before my change so that the function can use them and continue running – Shay Aug 06 '13 at 08:45
  • just updated my answer based on the code that you provided. I am guessing that the issue is with your counter: `i` – vijay Aug 06 '13 at 09:09
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/34901/discussion-between-shay-and-vijay) – Shay Aug 06 '13 at 11:18
0

In AMS 9.x

I have a simple suggest.

  // 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 can easy get the args array and y can do like the loadArgArray/loadArgXXX methods to visit each arg.

haoxiqiang
  • 359
  • 2
  • 4