ASM中的Java方法参数值

时间:2012-12-17 09:09:19

标签: java jvm bytecode java-bytecode-asm bytecode-manipulation

我试图获取Java程序方法参数的值。 我正在使用ASM来检测字节码并获取这些值。 但是,我遇到了一些麻烦。

这是用于检测代码的visitCode()方法。 它正在做的是:

  1. 创建一个空数组以存储收集的参数。
  2. 对于每个参数,将其值加载到数组中。
  3. 将此数组发送到我的代理的OnMethodEntry方法(将使用其中的值)。
  4. @Override
    public void visitCode() {
        int paramLength = paramTypes.length;
    
        // 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) {
            mv.visitVarInsn(Opcodes.ALOAD, paramLength);
            mv.visitIntInsn(Opcodes.BIPUSH, i);
    
            if (tp.equals(Type.BOOLEAN_TYPE) || tp.equals(Type.BYTE_TYPE) || tp.equals(Type.CHAR_TYPE) || tp.equals(Type.SHORT_TYPE) || tp.equals(Type.INT_TYPE))
                mv.visitVarInsn(Opcodes.ILOAD, i);
            else if (tp.equals(Type.LONG_TYPE)) {
                mv.visitVarInsn(Opcodes.LLOAD, i);
                i++;
            }
            else if (tp.equals(Type.FLOAT_TYPE))
                mv.visitVarInsn(Opcodes.FLOAD, i);
            else if (tp.equals(Type.DOUBLE_TYPE)) {
                mv.visitVarInsn(Opcodes.DLOAD, i);
                i++;
            }
            else
                mv.visitVarInsn(Opcodes.ALOAD, i);
    
            mv.visitInsn(Opcodes.AASTORE);
            i++;
        }
    
        // Load id, class name and method name
        this.visitLdcInsn(new Integer(this.methodID));
        this.visitLdcInsn(this.className);
        this.visitLdcInsn(this.methodName);
    
        // Load the array of parameters that we created
        this.visitVarInsn(Opcodes.ALOAD, paramLength);
    
        mv.visitMethodInsn(Opcodes.INVOKESTATIC,
                "jalen/MethodStats",
                "onMethodEntry",
                "(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
        super.visitCode();
    }
    

    但是,当显然该方法有多个参数时,这不起作用。

    获得的类文件显示如下内容:

    static void moveDisk(char arg0, char arg1, PrintStream arg2) {
    Object[] arrayOfObject = new Object[3]; arrayOfObject[0] = ???; arrayOfObject[1] = ???;
    Object localObject;
    arrayOfObject[2] = localObject; MethodStats.onMethodEntry(5, "hanoi/TowersOfHanoi", "moveDisk", arrayOfObject);
    

    创建2个本地对象而不是加载参数。

    字节码没有显示任何奇怪的东西:

    static void moveDisk(char, char, java.io.PrintStream);
    Code:
       0: bipush        3
       2: anewarray     #4                  // class java/lang/Object
       5: astore_3      
       6: aload_3       
       7: bipush        0
       9: iload_0       
      10: aastore       
      11: aload_3       
      12: bipush        1
      14: iload_1       
      15: aastore       
      16: aload_3       
      17: bipush        2
      19: aload_2       
      20: aastore       
      21: ldc           #118                // int 5
      23: ldc           #12                 // String hanoi/TowersOfHanoi
      25: ldc           #119                // String moveDisk
      27: aload_3       
      28: invokestatic  #19                 // Method jalen/MethodStats.onMethodEntry:(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
    

    最后,显示的错误是(当使用-noverify时):

    param: [Ljava.lang.String;@420e54f3
    Exception in thread "Jalen Agent" java.lang.NullPointerException
    at hanoi.TowersOfHanoi.solveHanoi(TowersOfHanoi.java)
    at hanoi.TowersOfHanoi.main(TowersOfHanoi.java:29)
    

    否则,它是:

    Exception in thread "Jalen Agent" java.lang.VerifyError: (class: hanoi/TowersOfHanoi, method: moveDisk signature: (CCLjava/io/PrintStream;)V) Expecting to find object/array on stack
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
        at java.lang.Class.getMethod0(Class.java:2685)
        at java.lang.Class.getMethod(Class.java:1620)
        at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:492)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:484)
    

    通常,这应该工作,因为我只是从堆栈框架加载信息。 我还试着去检查静电和放大器非静态方法(截至此处解释的堆栈:http://www.artima.com/insidejvm/ed2/jvm8.html),但仍然没有成功。

    有关为什么会发生这种情况的想法,或者可能是解决方案的想法?

    谢谢:)

    修改

    现在正在装箱原始类型(感谢下面的int3的建议:))。以下是visitCode()方法的工作代码:

    @Override
    public void visitCode() {
        int paramLength = paramTypes.length;
    
        // 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) {
            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++;
        }
    
        // Load id, class name and method name
        this.visitLdcInsn(new Integer(this.methodID));
        this.visitLdcInsn(this.className);
        this.visitLdcInsn(this.methodName);
    
        // Load the array of parameters that we created
        this.visitVarInsn(Opcodes.ALOAD, paramLength);
    
        mv.visitMethodInsn(Opcodes.INVOKESTATIC,
                "jalen/MethodStats",
                "onMethodEntry",
                "(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
        super.visitCode();
    }
    

1 个答案:

答案 0 :(得分:6)

您正在使用aastorechar存储到对象数组中,这是类型错误。 aastore应仅用于存储对象和数组,这可能是错误说“堆栈上预期的对象/数组”的原因。应使用castore将字符存储在char数组中。但是,由于您希望这适用于任意签名,您可能希望将基元类型打包成对象,然后可以使用aastore - 例如char应该装在java.lang.Character对象中。