假設我想在調用某個感興趣的方法之前調用(日誌記錄)方法之前。這意味着在偵聽visitMethodInsn時,堆棧已經填充了感興趣方法的參數。ASM ByteCode:在另一個調用中預先訪問visitMethodInsn
是否有可能將當前堆棧存儲在某處,調用日誌並重新填充堆棧?我是否缺少任何明顯的堆棧變異操作符?還是我真的需要:
- 存儲在變量堆棧暫時
- 緩衝區中的所有堆棧突變呼叫,直到一個方法調用到我的(可能不適用)記錄調用之後重播緩衝區?
例:給予原代碼
public static void main()
{
doSomethingUnrelated();
methodOfInterest(); // line 5 for example
doSomethingUnrelated();
}
生成的代碼應該是這樣的:
public static void main()
{
doSomethingUnrelated();
Logger.log("methodOfInterest", "main", 5);
methodOfInterest();
doSomethingUnrelated();
}
public Logger
{
public static void log(String method, String callee, int line)
{ ... }
}
語境:我的實際ASM MethodVisitor中看起來是這樣的:
class UsageClassMethodVisitor extends MethodVisitor implements Opcodes
{
private final String fileName;
private final String visitedClass;
private final String visitedMethod;
private int lineNumber;
UsageClassMethodVisitor(MethodVisitor mv, String fileName, String visitedClass, String visitedMethod, boolean isStatic)
{
super(Opcodes.ASM5, mv);
this.fileName = fileName;
this.visitedClass = visitedClass;
this.visitedMethod = visitedMethod;
}
@Override
public void visitLineNumber(int i, Label label) {
lineNumber = i;
super.visitLineNumber(i, label);
}
@Override
public void visitMethodInsn(int access, String ownerClass, String method, String signature, boolean isInterface) {
if(ownerClass.contains("org/example/package/")) {
System.out.printf("prepending to visitMethodInsn(%s, %s, %s, %b) @ %s.%s:%d\n",
ownerClass, method, signature, isInterface,
visitedClass, visitedMethod, lineNumber);
super.visitLdcInsn(fileName);
super.visitLdcInsn(visitedClass);
super.visitLdcInsn(visitedMethod);
super.visitLdcInsn(lineNumber);
super.visitMethodInsn(Opcodes.INVOKESTATIC,
Hook.ACCESS_OWNER_NAME,
Hook.ACCESS_METHOD_NAME,
Hook.ACCESS_METHOD_DESC, false);
}
super.visitMethodInsn(access, ownerClass, method, signature, isInterface);
}
}
,但顯然這是拋出一個錯誤堆棧8長,而不是4在調用時間:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
org.using.package.Main.main([Ljava/lang/String;)V @13: invokestatic
Reason:
Type integer (current frame, stack[8]) is not assignable to 'java/lang/String'
Current Frame:
bci: @13
flags: { }
locals: { '[Ljava/lang/String;' }
stack: { long, long_2nd, long, long_2nd, 'java/util/concurrent/TimeUnit', 'java/lang/String', 'java/lang/String', 'java/lang/String', integer }
不知道你爲什麼推送文件名。注意:'long'需要2個插槽,所以你有7個值,最後一個位置是8號插槽(這是從0開始的第9個插槽) –
什麼是ACCESS_METHOD_NAME/DESC? –
這些文件位於暴露鉤子的文件中。參見http://www.slideshare.net/Takipi/advanced-production-debugging#33來自Takipi的例子。 – Herman