在try-finally块中嵌入方法的现有代码(2) [英] Embed the existing code of a method in a try-finally block (2)

查看:167
本文介绍了在try-finally块中嵌入方法的现有代码(2)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前段时间,我在在try-finally块中嵌入方法的现有代码如何使用ASM将方法体包装在try-finally块中。
解决方案是在 visitCode()中访问方法体开头的try块的标签,并在访问时完成try-finally块带有返回操作码的指令 visitInsn()。我知道如果方法没有返回指令,如果方法总是以异常方式离开,那么解决方案将不起作用。

Some time ago, I asked in Embed the existing code of a method in a try-finally block how to wrap the body of a method in a try-finally block using ASM. The solution was to visit a label for the try block at the beginning of the method body in visitCode() and to complete the try-finally block when visiting an instruction with a return opcode in visitInsn(). I was aware that the solution won't be working if a method has no return instruction which applies if the method is always leaving with an exception.

虽然,我发现了前一种解决方案有时也不适用于带有返回指令的方法。如果方法有多个返回指令,它将无法工作。原因是它生成了无效的字节码,因为在方法的开头添加了一个try-finally块,但是完成了多个try-finally块。

Though, I discovered that the former solution is sometimes inappropriate for methods with return instructions, too. It won't be working if a method has more than one return instruction. The reason is that it generates invalid bytecode because one try-finally block is added at the beginning of the method but more than one try-finally block is completed.

通常(但可能取决于javac编译器),字节码方法包含单个返回指令,并且所有返回路径都通过跳转在该指令处结束。但是,使用Eclipse编译以下代码将导致字节代码带有两个返回指令:

Usually (but probably depending on the javac compiler), a bytecode method contains a single return instruction and all return paths end at that instruction by jumping there. However, the compilation of the following code with Eclipse will lead to byte code with two return instructions:

public boolean isEven(int x) {
  return x % 2 == 0;
}

用Eclipse编译的字节代码:

Byte code compiled with Eclipse:

   0: iload_1
   1: iconst_2
   2: irem
   3: ifne          8
   6: iconst_1
   7: ireturn       // javac compilation: goto 9
   8: iconst_0
   9: ireturn

因此,我想知道包装方法代码的整个代码的正确方法是什么。

推荐答案

在编译 try ... finally ... 时,你必须回顾Java编译器的作用,这意味着复制你的 finally 对受保护(源)代码块的每个点(即返回指令)执行操作并安装多个受保护(生成字节代码)区域(因为它们不应覆盖 finally action)但它们都可能指向同一个异常处理程序。或者,您可以转换代码,将分支的所有返回指令替换为after操作的一个实例,然后单独返回指令。

You have to retrace what a Java compiler does when compiling try … finally … which implies copying your finally action to every point where the protected (source) code block will be left (i.e. return instruction) and install multiple protected (resulting byte code) regions (as they shouldn’t cover your finally action) but they may all point to the same exception handler. Alternatively you can transform the code, replacing all return instruction by a branch to one instance of your "after" action followed by a sole return instruction.

这不是微不足道的。因此,如果您不需要通常不支持向已加载类添加方法的热代码替换,则避免所有这些的最简单方法是将原始方法重命名为不与其他方冲突的名称(您可能使用不允许的字符)在普通源代码中)并使用旧名称和签名创建一个新方法,该方法由一个简单的 try ... finally ... 构造组成,其中包含对重命名方法的调用。

That’s not trivial. So if you don’t need Hot Code Replace which usually doesn’t support adding methods to a loaded class, the easiest way to avoid all this is to rename the original method to a name not clashing with others (you may use characters not allowed in ordinary source code) and create a new method using the old name and signature which consists of a simple try … finally … construct containing an invocation of the renamed method.

例如更改 public void desired() to private void desired $ instrumented()并添加新的

E.g. change public void desired() to private void desired$instrumented() and add a new

public void desired() {
    //some logging X

    try {
        desired$instrumented();
    }
    finally {
        //some logging Y
    }
}

请注意,由于调试信息保留在重命名的方法中,因此如果在重命名的方法中抛出异常,堆栈跟踪将继续报告正确的行号。如果你通过添加一个不可见的字符来重命名它(请记住你在字节代码级别有更多的自由),它会非常流畅。

Note that since the debug information remains at the renamed method, stack traces will continue to report the correct line numbers if an exception is thrown in the renamed method. If you rename it by just adding an invisible character (keep in mind that you have more freedom at byte code level), it will like quite smooth.

这篇关于在try-finally块中嵌入方法的现有代码(2)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆