ASM ByteCode:将visitMethodInsn与另一个调用

时间:2016-09-14 11:08:32

标签: java bytecode java-bytecode-asm

假设我想在调用某些感兴趣的方法之前调用(强)> 之前的方法。这意味着在侦听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 }

1 个答案:

答案 0 :(得分:2)

没有理由将堆栈存储在任何地方,因为堆栈很好,是堆栈。您只需按下日志参数,调用日志记录功能,弹出结果,您就可以保留原始堆栈。