Exception self suppression error in try-with-resources not occuring from Eclipse
Asked Answered
J

1

6

I have come across a troubling situation where I expect Java to complain (via an IllegalArgumentException from Throwable.addSuppressed) about throwing the same exception twice, once from within a try-with-resources block and once from the AutoCloseable class's close() routine. I have created a simple test case below which highlights the problem.

I am running JDK 1.7.0_65 with the following code:

public class TestDoubleThrow {
    public static void main(String[] args) {
        class TestA implements AutoCloseable {
            RuntimeException e;
            public TestA(RuntimeException e) { this.e = e; }
            @Override public void close() { throw e; }
        }

        RuntimeException e = new RuntimeException("My Exception");
        try (TestA A = new TestA(e)) {
            throw e;
        }
    }
}

When I compile and run the code above via the command line I get the expected result, an error indicating I have tried to self-suppress and exception:

[coreys terminal]$ java TestDoubleThrow.java ; java TestDoubleThrow
Exception in thread "main" java.lang.IllegalArgumentException: Self-suppression not permitted
    at java.lang.Throwable.addSuppressed(Throwable.java:1043)
    at TestDoubleThrow.main(TestDoubleThrow.java:12)
Caused by: java.lang.RuntimeException: My Exception
    at TestDoubleThrow.main(TestDoubleThrow.java:9)

However, when I build and run the same code from Eclipse I do not get the same result, I get the following:

Exception in thread "main" java.lang.RuntimeException: My Exception
    at TestDoubleThrow.main(TestDoubleThrow.java:9)

I removed the .class path after building from the command line to ensure that Eclipse rebuilt it. Running from a debugger I noticed that from Eclipse the code never enters java.lang.Throwable.addSuppressed().

Interestingly, if I build the class from Eclipse, then run it from the command line I DO NOT SEE the self suppression error. Similarly if I build the class from the command line and run it from Eclipse (without building from Eclipse) then I DO SEE the error. This suggests that it is something funny about how eclipse is building the class.

I would like to know how to ensure Eclipse can build the class in such a way that I do get the error, because for my purposes it is an error and I want to be able to detect it when I build and run from Eclipse.

Josiejosler answered 1/9, 2014 at 2:50 Comment(0)
A
4

Eclipse has it's own compiler and is producing different output. Code compiled by Eclipse checks to see if a suppressed exception equals itself before invoking Throwable.addSuppressed. You can see this using the javap tool.

See the if_acmpeq line in the Eclipse compiler output.

The JDK behaviour more closely adheres to the example in the specification. You could raise a defect with the Eclipse team.

JDK/javac output:

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/RuntimeException
       3: dup           
       4: ldc           #3                  // String My Exception
       6: invokespecial #4                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
       9: astore_1      
      10: new           #5                  // class TestDoubleThrow$1TestA
      13: dup           
      14: aload_1       
      15: invokespecial #6                  // Method TestDoubleThrow$1TestA."<init>":(Ljava/lang/RuntimeException;)V
      18: astore_2      
      19: aconst_null   
      20: astore_3      
      21: aload_1       
      22: athrow        
      23: astore        4
      25: aload         4
      27: astore_3      
      28: aload         4
      30: athrow        
      31: astore        5
      33: aload_2       
      34: ifnull        63
      37: aload_3       
      38: ifnull        59
      41: aload_2       
      42: invokevirtual #8                  // Method TestDoubleThrow$1TestA.close:()V
      45: goto          63
      48: astore        6
      50: aload_3       
      51: aload         6
      53: invokevirtual #9                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      56: goto          63
      59: aload_2       
      60: invokevirtual #8                  // Method TestDoubleThrow$1TestA.close:()V
      63: aload         5
      65: athrow        
    Exception table:
       from    to  target type
          21    23    23   Class java/lang/Throwable
          41    45    48   Class java/lang/Throwable
          21    33    31   any

Eclipse output:

  public static void main(java.lang.String[]);
    Code:
       0: new           #16                 // class java/lang/RuntimeException
       3: dup           
       4: ldc           #18                 // String My Exception
       6: invokespecial #20                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
       9: astore_1      
      10: aconst_null   
      11: astore_2      
      12: aconst_null   
      13: astore_3      
      14: new           #23                 // class TestDoubleThrow$1TestA
      17: dup           
      18: aload_1       
      19: invokespecial #25                 // Method TestDoubleThrow$1TestA."<init>":(Ljava/lang/RuntimeException;)V
      22: astore        4
      24: aload_1       
      25: athrow        
      26: astore_2      
      27: aload         4
      29: ifnull        37
      32: aload         4
      34: invokevirtual #28                 // Method TestDoubleThrow$1TestA.close:()V
      37: aload_2       
      38: athrow        
      39: astore_3      
      40: aload_2       
      41: ifnonnull     49
      44: aload_3       
      45: astore_2      
      46: goto          59
      49: aload_2       
      50: aload_3       
      51: if_acmpeq     59
      54: aload_2       
      55: aload_3       
      56: invokevirtual #31                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      59: aload_2       
      60: athrow        
    Exception table:
       from    to  target type
          24    26    26   any
          14    39    39   any

I used JDK 8 and Eclipse 4.4.

Arabella answered 1/9, 2014 at 8:13 Comment(1)
I noticed a similar thing when looking at the byte code in both cases. I found the bug discussed as a JDK bug here. Whether it is a JDK bug or an Eclipse bug is not for me to say :) Having consistent behaviour between the two would be good, and the fact Eclipse is actually inserting code which bypasses that in Throwable is not that great.Josiejosler

© 2022 - 2024 — McMap. All rights reserved.