10

Javaプログラムのメソッドのパラメータの値を取得しようとしています。ASMを使用してバイトコードを計測し、これらの値を取得しています。しかし、私はいくつかの問題に直面しています。

コードをインストルメント化するために使用されるvisitCode()メソッドは次のとおりです。それがしていることは:

  1. 収集されたパラメータを格納するための空の配列を作成します。
  2. パラメータごとに、その値を配列にロードします。
  3. この配列をエージェントのOnMethodEntryメソッド(値が使用される場所)に送信します。

@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();
}
4

1 に答える 1

6

をオブジェクト配列にaastore格納するために使用していますが、これはタイプエラーです。オブジェクトと配列を格納するためにのみ使用する必要があります。これが、エラーが「スタック上の予期されたオブジェクト/配列」と表示される理由である可能性があります。文字は、を使用してchar配列に格納する必要があります。ただし、これを任意の署名に対して機能させたいので、プリミティブ型をオブジェクトにボックス化して、使用できるようにする必要があります。たとえば、オブジェクトにボックス化する必要があります。charaastorecastoreaastorecharjava.lang.Character

于 2012-12-17T10:01:01.973 に答える