Próbuję uzyskać wartości parametrów metody programu Java. Używam ASM do instrument kod bajtowy i uzyskiwanie tych wartości. Jednak mam pewne problemy.Wartości parametrów metody Java w ASM
Oto metoda visitCode() używana do instrumentowania kodu. Co to jest:
- Utwórz pustą tablicę do przechowywania zebranych parametrów.
- Dla każdego parametru, załaduj jego wartość do tablicy.
- Wyślij tę tablicę do metody OnMethodEntry mojego agenta (w której zostaną użyte wartości).
.
@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();
}
Jednak to nie działa, gdy podobno metoda ma więcej niż jeden parametr.
pokazypliku klasy uzyskuje takie rzeczy:
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);
Gdzie 2 lokalne obiekty są tworzone zamiast ładowania parametrów.
Kod bajtowy nie pokazuje nic dziwnego:
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
i wreszcie pokazał się błąd (przy użyciu -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)
Inaczej, to jest:
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)
Zwykle powinno to działać, ponieważ właśnie ładuję informacje z ramki stosu. Próbowałem również sprawdzić statyczne metody statyczne & (od wyjaśnienia tutaj: http://www.artima.com/insidejvm/ed2/jvm8.html), ale wciąż bez powodzenia.
Każdy pomysł, dlaczego tak się dzieje, a może pomysł rozwiązania?
Dzięki :)
EDIT:
Obecnie pracuje gdy boks się prymitywne typy (dzięki sugestii przez INT3 poniżej :)). Oto kod działa metody 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();
}
Jak mogę przywrócić argumenty do stosu, aby funkcja mogła nadal z nich korzystać? – Shay