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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With