如何在每次返回之前将消息添加到消息中?

nan*_*ack 9 java java-bytecode-asm

我目前正在尝试通过精心设计的java-asm库(版本4)生成代码.更具体地说,我想在每次返回调用之前将代码附加到方法的末尾.我成功地能够在方法代码之前添加代码.但是目前我不知道如何进行上述转换.我真的很感激指出如何实现这一目标.

MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 42);
return mv;
Run Code Online (Sandbox Code Playgroud)

vij*_*jay 10

你有两个解决方案:

1.使用visitInsn(int opcode)方法MethodVisitor:

//this is the custom method visitor
private class InsertInitCodeBeforeReturnMethodVisitor extends MethodVisitor{

    public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM4, mv);
    }

    @Override
    public void visitInsn(int opcode) {
        //whenever we find a RETURN, we instert the code, here only crazy example code
        switch(opcode) {
          case Opcodes.IRETURN:
      case Opcodes.FRETURN:
      case Opcodes.ARETURN:
      case Opcodes.LRETURN:
      case Opcodes.DRETURN:
      case Opcodes.RETURN:
              mv.visitVarInsn(Opcodes.ALOAD, 42);
              break;
          default: // do nothing
        }
        super.visitInsn(opcode);
    }
}
Run Code Online (Sandbox Code Playgroud)

2.使用onMethodExit(int opcode)in AdviceAdapterin中的方法org.objectweb.asm.commons:

//this is the custom method visitor
private class InsertInitCodeBeforeReturnMethodVisitor extends AdviceAdapter{

    public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv, int access, String name, String desc) {
        super(Opcodes.ASM4, mv, access, name, desc);
    }

    @Override
    protected void onMethodExit(int opcode) {
        if(opcode != Opcdoes.ATHROW) {
            mv.visitVarInsn(Opcodes.ALOAD, 42);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我个人更喜欢AdviceAdapter,因为它消除了实际调用原始返回指令的麻烦,就像你必须使用第一个解决方案一样(例如super.visitInsn(opcode);).其次,它为访问RETURN指令提供了一个很好的抽象(和ATHORW); visitInsn(int opcode)对于香草中的方法而言,这是不正确的MethodVisitor,在那里你必须检测访问RETURN许多其他如DUPs ICONST_0,等等的指令,这些指令可能与手头的问题有关,也可能与之无关.

但这又取决于手头的问题.如果这是唯一正在执行的仪器,我会坚持AdviceAdapter.如果您还希望结合访问RETURN说明做其他事情,我可能会保持简单MethodVisitor,因为它可能会给我更大的灵活性.话虽这么说,我已经使用AdviceAdapter了一年多一点的时间用于重型仪表驱动项目,到目前为止它已经很好了!


编辑:

应用方法访问者

通常不清楚如何使用或应用方法访问者/方法适配器(至少对我来说),所以我在这里汇总了一个快速代码示例:gist.github.com/VijayKrishna/1ca807c952187a7d8c4d,显示如何通过相应的class-visitor/class-adapter使用方法适配器.在示例代码段中,我已经从我在此答案中使用的方法更改了方法适配器的名称,但它们执行相同的操作.此外,代码段显示了一个扩展的方法适配器AdviceAdapter.

总之,您首先"调用"类适配器,如下所示:

ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(ClassReader.EXPAND_FRAMES);
ReturnAdapter returnAdapter = new ReturnAdapter(cw, className);
cr.accept(returnAdapter, 0);
Run Code Online (Sandbox Code Playgroud)

然后,您可以在类适配器的visitMethod方法中按照以下方法调整方法:

MethodVisitor mv;
mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv = new MethodReturnAdapter(Opcodes.ASM4, className, access, name, desc, mv);
return mv;
Run Code Online (Sandbox Code Playgroud)


nan*_*ack 5

其实结果很容易,但我在完全错误的地方搜索。为了在任意位置转换方法体,您必须创建一个自定义 MethodVisitor 子类并通过它管道现有的 MethodVisitor 以执行您希望它执行的转换。在我的示例中,每当自定义 MethodVisitor 找到操作码 RETURN 时,它都会添加代码,如下所示:

public class ActorInterfaceTransformer extends ClassVisitor {
    public ActorInterfaceTransformer(ClassVisitor cv) {
        super(Opcodes.ASM4, cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        //if the method is the one we want to transform
        if(name.equals("<init>")){
            //... then we pipe the method visitor 
            MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
            return new InsertInitCodeBeforeReturnMethodVisitor(mv);
        }
        return super.visitMethod(access, name, desc, signature, exceptions);
    }
}

//this is the custom method visitor
private class InsertInitCodeBeforeReturnMethodVisitor extends MethodVisitor{

    public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM4, mv);
    }

    @Override
    public void visitInsn(int opcode) {
        //whenever we find a RETURN, we instert the code, here only crazy example code
        if(opcode==Opcodes.RETURN){
            mv.visitVarInsn(Opcodes.ALOAD, 42);
        }
        super.visitInsn(opcode);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您忽略了非 void 返回类型,例如 `ARET`、`LRET`、`FRET` 等。 (2认同)
  • 是的,确实如此,但由于我想将代码插入到构造函数中,因此这对我来说并不重要。尽管如此,这就是为什么我接受维杰的答案作为解决方案 (2认同)