Bug 521544 - ECJ bytecode execution paths can be different form javac
Summary: ECJ bytecode execution paths can be different form javac
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.6   Edit
Hardware: PC Windows 7
: P3 normal with 1 vote (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug bulk move
Keywords:
Depends on:
Blocks:
 
Reported: 2017-08-29 11:21 EDT by Wiraj Bibile CLA
Modified: 2023-08-09 17:55 EDT (History)
3 users (show)

See Also:


Attachments
example code (1.04 KB, application/octet-stream)
2017-08-29 11:21 EDT, Wiraj Bibile CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Wiraj Bibile CLA 2017-08-29 11:21:17 EDT
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
Comment 1 Stephan Herrmann CLA 2017-08-29 12:35:28 EDT
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).
Comment 2 Manoj N Palat CLA 2019-02-11 04:05:49 EST
Bulk move out of 4.11
Comment 3 Manoj N Palat CLA 2019-08-27 02:07:33 EDT
Bulk move out of 4.13
Comment 4 Eclipse Genie CLA 2021-08-18 05:55:55 EDT
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.
Comment 5 Eclipse Genie CLA 2023-08-09 17:55:50 EDT
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.