Community
Participate
Working Groups
Created attachment 270013 [details] example code ECJ tries to optimize bytecode generated for try catch finally blocks (It tries to optimize "Exception table:" entries). In certain situations this results in unexpected execution paths in the ECJ compiled code. For example in the following code (note that z() throws InterruptedException when called) try(C c = new C()) { x(); return 0; } catch(InterruptedException e) { y(); throw new CancellationException(); } finally { z(); -> Throws InterruptedException } Output for the ECJ compiled code is: new C() x() C.close() z() C.close() y() z() Exception in thread "main" java.util.concurrent.CancellationException output for the javac compiled code is: new C() x() C.close() z() Exception in thread "main" java.lang.InterruptedException: interrupt I think the execution path for bytecode generated javac is correct. Workaround * return after finally clause, * Use nested try-with resources block
This looks wrong, indeed. Once we reached the finally block, the handlers should never branch back into a catch block of the same try, should it? Let's see: //--- 0: aconst_null 1: astore_1 2: aconst_null 3: astore_2 4: new #25 // class Test$C 7: dup 8: invokespecial #27 // Method Test$C."<init>":()V 11: astore_3 12: aload_0 13: invokevirtual #28 // Method x:()V 16: aload_3 17: ifnull 24 20: aload_3 21: invokevirtual #31 // Method Test$C.close:()V 24: aload_0 25: invokevirtual #34 // Method z:()V 28: iconst_0 29: ireturn 30: astore_1 31: aload_3 32: ifnull 39 35: aload_3 36: invokevirtual #31 // Method Test$C.close:()V 39: aload_1 40: athrow 41: astore_2 42: aload_1 43: ifnonnull 51 46: aload_2 47: astore_1 48: goto 61 51: aload_1 52: aload_2 53: if_acmpeq 61 56: aload_1 57: aload_2 58: invokevirtual #37 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 61: aload_1 62: athrow 63: pop 64: aload_0 65: invokevirtual #43 // Method y:()V 68: new #46 // class java/util/concurrent/CancellationException 71: dup 72: invokespecial #48 // Method java/util/concurrent/CancellationException."<init>":()V 75: athrow 76: astore 4 78: aload_0 79: invokevirtual #34 // Method z:()V 82: aload 4 84: athrow Exception table: from to target type 12 16 30 any 24 30 30 any 4 41 41 any 0 24 63 Class java/lang/InterruptedException 30 63 63 Class java/lang/InterruptedException 0 24 76 any 30 76 76 any //----- Both z() call (at bci 25 and bci 79) are excluded from the ranges for InterruptedException, so this exception should not branch back into catch. There are handlers, however, that catch any exception at 25, one targeting 30, and another one targeting 41. At 30 we have a "close(); rethrow" pattern. At 41 we have some conditional addSuppressed() logic and then rethrow. Both rethrows are within the range of the second InterruptedException-handler. This looks wrong to me. In fact, all of that handler (30-63) looks wrong to me. That code section contains no user code that could branch into the catch block. Yet, even binary editing the class file to disable that handler only fixes part of the problem (we still double-trigger the finally): new C() x() C.close() z() C.close() z() Exception in thread "main" java.lang.InterruptedException: interrupt Disclaimer: all this is said without another look into JLS. The problem exists since introduction of t-w-r in ecj (3.7.1).
Bulk move out of 4.11
Bulk move out of 4.13
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant. -- The automated Eclipse Genie.