RFR: jsr166 jdk9 integration wave 2

Martin Buchholz martinrb at google.com
Wed Dec 9 23:18:16 UTC 2015


I finally studied ForkJoinTask.getThrowableException - I was not aware
that we were doing reflection based exception cloning.  In practice it
probably muddles through, but it just seems too hacky to use in a java
core library.  We're relying on common conventions, but in principle
we are invoking random code with an unknown spec.  The stack traces
created are fake in the sense that they are not created naturally, and
the thrown exception may differ from the original in significant ways,
most notably missing the message string.  With heroic efforts we can
make it safer, e.g. examine the bytecode for the constructors we are
invoking, and check whether they call the super constructors in the
right way, but that will be a major engineering project by itself.

For CompletableFuture.whenComplete: I also keep moving in the
direction of less magic.  It is perfectly possible for a well-written
whenComplete action to catch all Throwables, add the source exception
as a suppressed exception, and rethrow.  In fact, perhaps people have
already tried this and discovered we incorrectly throw the source
exception.  I think we should really accept our mistake and switch to
throwing the action exception rather than the source exception.
Perhaps we should only call addSuppressed if the source exception is
not already in the list of suppressed exceptions.


On Thu, Dec 3, 2015 at 9:54 AM, Jason Mehrens <jason_mehrens at hotmail.com> wrote:
> Hi Peter,
>
> I've done this trick before to perform Throwable cloning.  You have to hunt for the constructors in this order:
> 1. Walk the type of the cause and super types by calling getConstructor(String, type of cause). (java.io.WriteAbortedException and javax.mail.MessagingException)
> 2. Walk the type of the cause and super types by calling getConstructor(type of cause, String)  (I've seen this but, I can't seem to find an example)
> 3. getConstructor(String) + initCause (java.io.InvalidObjectException)
> 4. Walk the type of the cause and super types by calling getConstructor(type of cause). (java.awt.print.PrinterIOException)
> 5. getConstructor() + initCause.  (java.nio.BufferOverflowException)
> 6. Look at the return type of methods declared in the cause type and assume there is a constructor matching the type or no repeating types. (java.nio.charset.MalformedInputException, java.lang.EnumConstantNotPresentException, java.util.regex.PatternSyntaxException)
>
> In the end all of that can still fail.  Exceptions can be private subclasses of public classes so that gets interesting with reflection too.
>
> Jason
>
>
> ===========
> What about the 4th option (keep current behavior, but try the
> best-effort with reflection to make new exception of the same type):
>
> ...
>              } catch (Throwable ex) {
>                  if (x == null) {
>                      x = ex;
>                  } else {
>                      // try to create new exception with same:
>                      // type, cause, suppressed exceptions and
> stack-trace...
>                      Throwable nx;
>                      Class<?> xClass = x.getClass();
>                      try {
>                          Constructor<?> xConstr =
> xClass.getConstructor(Throwable.class);
>                          nx = (Throwable) xConstr.newInstance(x.getCause());
>                      } catch (Exception e1) {
>                          try {
>                              nx = (Throwable) xClass.newInstance();
>                              nx.initCause(x.getCause());
>                          } catch (Exception e2) {
>                              // no luck
>                              nx = null;
>                          }
>                      }
>                      if (nx != null) {
>                          // inherit stack-trace of original exception
>                          nx.setStackTrace(x.getStackTrace());
>                          // inherit suppressed exceptions
>                          for (Throwable sx : x.getSuppressed()) {
>                              nx.addSuppressed(sx);
>                          }
>                          // add 'ex' as a final suppressed exception
>                          nx.addSuppressed(ex);
>                          x = nx;
>                      }
>                  }
>              }
>              completeThrowable(x, r);
>
> ...
>
>
>
> Note that such code and similar code in
> ForkJoinTask.getThrowableException will probably have to be modified for
> jigsaw to include dynamic addition of read-edge to the module of
> exception class...
>
> Regards, Peter
>



More information about the core-libs-dev mailing list