2012-12-17 16 views
8

Ich versuche, die Werte der Methodenparameter eines Java-Programms zu ermitteln. Ich verwende ASM, um den Bytecode zu instrumentieren und diese Werte zu erhalten. Allerdings laufe ich in einige Probleme.Werte für Java-Methodenparameter in ASM

Hier ist die Methode visitCode(), mit der der Code instrumentiert wird. Was es macht ist:

  1. Erstellen Sie ein leeres Array, um die gesammelten Parameter zu speichern.
  2. Laden Sie für jeden Parameter seinen Wert in das Array.
  3. Senden Sie dieses Array an die OnMethodEntry-Methode meines Agenten (wo die Werte verwendet werden).

.

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

Dies ist jedoch nicht möglich, wenn offensichtlich das Verfahren mehr als einen Parameter haben.

Die Klassendatei erhalten wurde, zeigt, Dinge wie diese:

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); 

Wo 2 lokale Objekte erstellt werden, anstatt die Parameter zu laden.

Der Bytecode zeigt nichts seltsam:

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 

Und schließlich zeigte der Fehler (bei Verwendung von -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) 

Ansonsten ist es:

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) 

Normalerweise sollte dies eher funktionieren, da ich gerade Informationen aus dem Stapelrahmen lade. Ich habe auch versucht, statische & nicht statische Methoden zu überprüfen (wie der Stapel hier erklärt: http://www.artima.com/insidejvm/ed2/jvm8.html), aber immer noch mit keinem Erfolg.

Jede Idee, warum dies geschieht, oder möglicherweise eine Idee einer Lösung?

Thanks :)

EDIT:

Es arbeitet jetzt als Urtyp Boxen bis (dank Vorschläge von int3 unten :)). Hier ist der Arbeitscode visitCode() Methode:

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

Wie kann ich die Argumente an den Stapel zurückgeben, damit die Funktion sie weiterhin verwenden kann? – Shay

Antwort

6

aastore Sie werden unter Verwendung eines char in ein Objekt Array zu speichern, die eine Art Fehler. aastore sollte nur verwendet werden, um Objekte und Arrays zu speichern, was wahrscheinlich der Grund ist, warum der Fehler "erwartetes Objekt/Array auf Stack" lautet. Zeichen sollten in einem Zeichenfeld unter Verwendung castore gespeichert werden. Da Sie jedoch möchten, dass dies für beliebige Signaturen funktioniert, möchten Sie wahrscheinlich die primitiven Typen in Objekte einbetten, die Sie dann verwenden können, z. B. aastore. char sollte in einem java.lang.Character Objekt eingerahmt werden.