JDK-8030198 : Nashorn: finally-block may execute twice
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 9
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: linux_ubuntu
  • CPU: generic
  • Submitted: 2013-12-13
  • Updated: 2015-03-05
  • Resolved: 2015-01-28
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Related Reports
Duplicate :  
Duplicate :  
Relates :  

Incorrect byte code is generated if the try-block ends with a terminal node (except for throw). Combined with a throw statement in the finally block, the finally block will be executed twice.

jjs> function f() { try { print("try"); return } finally { print("finally"); throw 0 } } f();

Expected: output is "try finally"
Actual: output is "try finally finally"

This bug can be reproduced always.
This is a P4 bug. There is no need to request deferral. Please remove the 8u20-defer-request and update the FixVersion (indicate what version you do plan to fix, ex: 9, 8u40)

8u20-defer-request: * Executing finally twice is somewhat higher impact (twice closing a resource for eg). Impact: medium to high * but this code shape is very highly unlikely - extreme corner case. Finally code being executed because of exception thrown or normal path, it is very unlikely that finally throws another exception. Likelihood: very low * There is a simple, clear workaround. Exception can be thrown outside finally block or code can be suitably adjusted easily. Workaround: easy.

The catch_all block introduced by Lower has a rethrow. The Lower replaces rethrow by finally block code (Lower.spliceFinally -> leaveThrowNode). But, in this example, "try" block has a "return" -- and so "finally" block is "inlined there". Since the "finally" block ends with a "throw", return from try is replaced with print("finally"); throw 0; That "throw 0" is caught by "catch_all" -- which again has print("finally"); throw 0; (as rethrow has also been inlined with finally block code) So, we have "finally" once printed by "return" statement in "try" block. And "catch_all" block also prints "finally" again and throws 0!! Equivalent Java code and compiled code looks as follows: public class Main { public static void main(String[] ar) { f(); } public static void f() { try { System.out.println("try"); return; } finally { System.out.println("finally"); throw new RuntimeException("wrong!"); } } } would be compiled to have "any" type handler as below: public static void main(java.lang.String[]); Code: 0: invokestatic #2 // Method f:()V 3: return public static void f(); Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #4 // String try 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 11: ldc #6 // String finally 13: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: new #7 // class java/lang/RuntimeException 19: dup 20: ldc #8 // String wrong! 22: invokespecial #9 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V 25: athrow 26: astore_0 27: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 30: ldc #6 // String finally 32: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 35: new #7 // class java/lang/RuntimeException 38: dup 39: ldc #8 // String wrong! 41: invokespecial #9 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V 44: athrow Exception table: from to target type 0 8 26 any <---- any type hander }

yes - finally block executed twice as described.