Proposal to enhance the basic try-with-resources translation

Anthony Vanelverdinghe anthony.vanelverdinghe at gmail.com
Sun Jan 10 10:34:58 UTC 2016


Hi

I would like to propose an enhanced translation for the basic 
try-with-resources statement ( 
https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20.3.1 
), such that:

- there's a possibility that #suppressedExc suppresses #primaryExc, 
governed by the following precedence rule: Error > RuntimeException > 
checked Exception
So e.g. if #suppressedExc is an Error and #primaryExc is an IOException, 
then it's the Error that will be thrown with the IOException as its 
suppressed Throwable. The motivation for this, is allowing Errors and 
RuntimeExceptions to "blow up" the program. In my experience, it's rare 
for exception-handling code to check the suppressed Throwables and act 
accordingly. So this effectively means that suppressed Errors / 
RuntimeExceptions are typically handled the same way as their 
suppressing (checked) exceptions.

- any Throwables that may occur during an invocation of addSuppressed 
are ignored
The motivation here, is guaranteeing that exception information is never 
lost. Currently it's possible (though very unlikely, I admit) that an 
OutOfMemoryError occurs at that point (e.g. due to the creation of the 
ArrayList). In this case, both #primaryExc and #suppressedExc are lost, 
and it might be very hard to find out what went wrong.

In order to implement this translation, I'd propose a new instance 
method on Throwable: public void withSuppressed(AutoCloseable resource)
The motivation for this method, is that it's useful in itself. Moreover, 
it helps to simplify the translation & results in a smaller bytecode 
footprint than the current translation. In short, the method closes the 
resource argument and behaves as described above. A possible 
implementation is:

     public void withSuppressed(AutoCloseable resource) {
         try {
             resource.close();
         } catch (RuntimeException e) {
             try {
                 if (this instanceof Error || this instanceof 
RuntimeException) {
                     addSuppressed(e);
                 } else {
                     e.addSuppressed(this);
                     throw e;
                 }
             } catch (Throwable ignored) {
             }
         } catch (Exception e) {
             try {
                 addSuppressed(e);
             } catch (Throwable ignored) {
             }
         } catch (Error e) {
             try {
                 if (this instanceof Error) {
                     addSuppressed(e);
                 } else {
                     e.addSuppressed(this);
                     throw e;
                 }
             } catch (Throwable ignored) {
             }
         }
     }

The updated translation for the basic try-with-resources statement would 
then become as follows:

{
     final {VariableModifierNoFinal} R Identifier = Expression;
     Throwable #primaryExc = null;

     try ResourceSpecification_tail
         Block
     catch (Throwable #t) {
         #primaryExc = #t;
         throw #t;
     } finally {
         if (Identifier != null) {
             if (#primaryExc != null) {
                 #primaryExc.withSuppressed(Identifier);
             } else {
                 Identifier.close();
             }
         }
     }
}

What are your thoughts on this proposal?

Kind regards,
Anthony



More information about the compiler-dev mailing list