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