--- old/jdk/src/share/classes/java/lang/Throwable.java 2010-10-12 20:17:41.000000000 -0700 +++ new/jdk/src/share/classes/java/lang/Throwable.java 2010-10-12 20:17:40.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,18 +189,21 @@ */ private StackTraceElement[] stackTrace; /* - * This field is lazily initialized on first use or serialization and - * nulled out when fillInStackTrace is called. + * This field above is lazily initialized on first use or + * serialization and nulled out when fillInStackTrace is called. */ + private static final List suppressedSentinal = + Collections.unmodifiableList(new ArrayList(0)); + /** * The list of suppressed exceptions, as returned by - * {@link #getSuppressedExceptions()}. + * {@link #getSuppressed()}. * * @serial * @since 1.7 */ - private List suppressedExceptions = null; + private List suppressedExceptions = suppressedSentinal; /* * This field is lazily initialized when the first suppressed * exception is added. @@ -214,6 +217,9 @@ /** Message for trying to suppress a null exception. */ private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; + /** Message for trying to suppress oneself. */ + private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted"; + /** Caption for labeling causative exception stack traces */ private static final String CAUSE_CAPTION = "Caused by: "; @@ -572,7 +578,7 @@ s.println("\tat " + traceElement); // Print suppressed exceptions, if any - for (Throwable se : getSuppressedExceptions()) + for (Throwable se : getSuppressed()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); // Print cause, if any @@ -613,7 +619,7 @@ s.println(prefix + "\t... " + framesInCommon + " more"); // Print suppressed exceptions, if any - for (Throwable se : getSuppressedExceptions()) + for (Throwable se : getSuppressed()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, prefix +"\t", dejaVu); @@ -786,14 +792,20 @@ List suppressed = null; if (suppressedExceptions != null && !suppressedExceptions.isEmpty()) { // Copy Throwables to new list - suppressed = new ArrayList(); + suppressed = new ArrayList(1); for (Throwable t : suppressedExceptions) { if (t == null) throw new NullPointerException(NULL_CAUSE_MESSAGE); + if (t == this) + throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); suppressed.add(t); } } - suppressedExceptions = suppressed; + + if (suppressed != null && suppressed.isEmpty()) + suppressedExceptions = suppressedSentinal; + else + suppressedExceptions = suppressed; } private synchronized void writeObject(ObjectOutputStream s) @@ -808,6 +820,14 @@ * were suppressed, typically by the {@code try}-with-resources * statement, in order to deliver this exception. * + * If the first exception to be suppressed is {@code null}, that + * indicates suppressed exception information will not be + * recorded for this exception. Subsequent calls to this method + * will not record any suppressed exceptions. Otherwise, + * attempting to suppress {@code null} after an exception has + * already been successfully suppressed results in a {@code + * NullPointerException}. + * *

Note that when one exception {@linkplain * #initCause(Throwable) causes} another exception, the first * exception is usually caught and then the second exception is @@ -824,15 +844,30 @@ * throwable; a throwable cannot suppress itself. * @since 1.7 */ - public synchronized void addSuppressedException(Throwable exception) { - if (exception == null) - throw new NullPointerException(NULL_CAUSE_MESSAGE); + public synchronized void addSuppressed(Throwable exception) { if (exception == this) - throw new IllegalArgumentException("Self-suppression not permitted"); + throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); + + if (exception == null) { + if (suppressedExceptions == suppressedSentinal) { + suppressedExceptions = null; // No suppression information recorded + return; + } else + throw new NullPointerException(NULL_CAUSE_MESSAGE); + } else { + assert exception != null && exception != this; + + if (suppressedExceptions == null) // Suppressed exceptions not recorded + return; + + if (suppressedExceptions == suppressedSentinal) + suppressedExceptions = new ArrayList(1); - if (suppressedExceptions == null) - suppressedExceptions = new ArrayList(); - suppressedExceptions.add(exception); + assert suppressedExceptions != suppressedSentinal; + + suppressedExceptions.add(exception); + return; + } } private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0]; @@ -842,12 +877,15 @@ * suppressed, typically by the {@code try}-with-resources * statement, in order to deliver this exception. * + * If no exceptions were suppressed, an empty array is returned. + * * @return an array containing all of the exceptions that were * suppressed to deliver this exception. * @since 1.7 */ - public synchronized Throwable[] getSuppressedExceptions() { - if (suppressedExceptions == null) + public synchronized Throwable[] getSuppressed() { + if (suppressedExceptions == suppressedSentinal || + suppressedExceptions == null) return EMPTY_THROWABLE_ARRAY; else return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); --- old/jdk/test/java/lang/Throwable/SuppressedExceptions.java 2010-10-12 20:17:41.000000000 -0700 +++ new/jdk/test/java/lang/Throwable/SuppressedExceptions.java 2010-10-12 20:17:41.000000000 -0700 @@ -26,7 +26,7 @@ /* * @test - * @bug 6911258 6962571 6963622 + * @bug 6911258 6962571 6963622 6991528 * @summary Basic tests of suppressed exceptions * @author Joseph D. Darcy */ @@ -39,12 +39,21 @@ basicSupressionTest(); serializationTest(); selfReference(); + noModification(); } private static void noSelfSuppression() { Throwable throwable = new Throwable(); try { - throwable.addSuppressedException(throwable); + throwable.addSuppressed(throwable); + throw new RuntimeException("IllegalArgumentException for self-suppresion not thrown."); + } catch (IllegalArgumentException iae) { + ; // Expected + } + + throwable.addSuppressed(null); // Immutable suppression list + try { + throwable.addSuppressed(throwable); throw new RuntimeException("IllegalArgumentException for self-suppresion not thrown."); } catch (IllegalArgumentException iae) { ; // Expected @@ -56,21 +65,21 @@ RuntimeException suppressed = new RuntimeException("A suppressed exception."); AssertionError repressed = new AssertionError("A repressed error."); - Throwable[] t0 = throwable.getSuppressedExceptions(); + Throwable[] t0 = throwable.getSuppressed(); if (t0.length != 0) { throw new RuntimeException(message); } throwable.printStackTrace(); - throwable.addSuppressedException(suppressed); - Throwable[] t1 = throwable.getSuppressedExceptions(); + throwable.addSuppressed(suppressed); + Throwable[] t1 = throwable.getSuppressed(); if (t1.length != 1 || t1[0] != suppressed) {throw new RuntimeException(message); } throwable.printStackTrace(); - throwable.addSuppressedException(repressed); - Throwable[] t2 = throwable.getSuppressedExceptions(); + throwable.addSuppressed(repressed); + Throwable[] t2 = throwable.getSuppressed(); if (t2.length != 2 || t2[0] != suppressed || t2[1] != repressed) { @@ -152,7 +161,7 @@ System.err.println("TESTING SERIALIZED EXCEPTION"); - Throwable[] t0 = throwable.getSuppressedExceptions(); + Throwable[] t0 = throwable.getSuppressed(); if (t0.length != 0) { // Will fail if t0 is null. throw new RuntimeException(message); } @@ -167,9 +176,25 @@ throwable1.printStackTrace(); - throwable1.addSuppressedException(throwable2); - throwable2.addSuppressedException(throwable1); + throwable1.addSuppressed(throwable2); + throwable2.addSuppressed(throwable1); throwable1.printStackTrace(); } + + private static void noModification() { + Throwable t = new Throwable(); + t.addSuppressed(null); + + Throwable[] t0 = t.getSuppressed(); + if (t0.length != 0) + throw new RuntimeException("Bad nonzero length of suppressed exceptions."); + + t.addSuppressed(new ArithmeticException()); + + // Make sure a suppressed exception did *not* get added. + t0 = t.getSuppressed(); + if (t0.length != 0) + throw new RuntimeException("Bad nonzero length of suppressed exceptions."); + } } --- old/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-10-12 20:17:42.000000000 -0700 +++ new/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-10-12 20:17:42.000000000 -0700 @@ -1509,17 +1509,17 @@ } private JCBlock makeArmFinallyClause(Symbol primaryException, JCExpression resource) { - // primaryException.addSuppressedException(catchException); + // primaryException.addSuppressed(catchException); VarSymbol catchException = new VarSymbol(0, make.paramName(2), syms.throwableType, currentMethodSym); JCStatement addSuppressionStatement = make.Exec(makeCall(make.Ident(primaryException), - names.fromString("addSuppressedException"), + names.fromString("addSuppressed"), List.of(make.Ident(catchException)))); - // try { resource.close(); } catch (e) { primaryException.addSuppressedException(e); } + // try { resource.close(); } catch (e) { primaryException.addSuppressed(e); } JCBlock tryBlock = make.Block(0L, List.of(makeResourceCloseInvocation(resource))); JCVariableDecl catchExceptionDecl = make.VarDef(catchException, null); --- old/langtools/test/tools/javac/TryWithResources/TwrSuppression.java 2010-10-12 20:17:43.000000000 -0700 +++ new/langtools/test/tools/javac/TryWithResources/TwrSuppression.java 2010-10-12 20:17:42.000000000 -0700 @@ -36,7 +36,7 @@ throw new RuntimeException(); } } catch(RuntimeException e) { - Throwable[] suppressedExceptions = e.getSuppressedExceptions(); + Throwable[] suppressedExceptions = e.getSuppressed(); int length = suppressedExceptions.length; if (length != 2) throw new RuntimeException("Unexpected length " + length); --- old/langtools/test/tools/javac/TryWithResources/TwrTests.java 2010-10-12 20:17:43.000000000 -0700 +++ new/langtools/test/tools/javac/TryWithResources/TwrTests.java 2010-10-12 20:17:43.000000000 -0700 @@ -90,7 +90,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed"); } @@ -112,7 +112,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed"); } @@ -134,7 +134,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed:" + e); } @@ -158,7 +158,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed:" + e); } @@ -181,7 +181,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed:" + e); } @@ -207,7 +207,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed:" + e); } @@ -231,7 +231,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed:" + e); } @@ -259,7 +259,7 @@ } catch (Resource.CreateFailException e) { creationFailuresDetected++; checkCreateFailureId(e.resourceId(), createFailureId); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { throw new AssertionError("Secondary exception suppression failed:" + e); } @@ -310,7 +310,7 @@ * Check for proper suppressed exceptions in proper order. * * @param suppressedExceptions the suppressed exceptions array returned by - * getSuppressedExceptions() + * getSuppressed() * @bitmap a bitmap indicating which suppressed exceptions are expected. * Bit i is set iff id should throw a CloseFailException. */ @@ -376,7 +376,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -388,7 +388,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 1); } @@ -409,7 +409,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -421,7 +421,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 2); } @@ -443,7 +443,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -455,7 +455,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 2); } @@ -477,7 +477,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -489,7 +489,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 3); } @@ -513,7 +513,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -525,7 +525,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 3); } @@ -548,7 +548,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -560,7 +560,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 4); } @@ -586,7 +586,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -598,7 +598,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 4); } @@ -621,7 +621,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -633,7 +633,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 5); } @@ -660,7 +660,7 @@ } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + checkSuppressedExceptions(e.getSuppressed(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); @@ -672,7 +672,7 @@ throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } - checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + checkSuppressedExceptions(e.getSuppressed(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 5); }