--- old/jdk/src/share/classes/java/lang/StackTraceElement.java 2010-11-04 20:47:52.000000000 -0700 +++ new/jdk/src/share/classes/java/lang/StackTraceElement.java 2010-11-04 20:47:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -25,6 +25,8 @@ package java.lang; +import java.util.Objects; + /** * An element in a stack trace, as returned by {@link * Throwable#getStackTrace()}. Each element represents a single stack frame. @@ -53,26 +55,21 @@ * @param methodName the name of the method containing the execution point * represented by the stack trace element * @param fileName the name of the file containing the execution point - * represented by the stack trace element, or null if + * represented by the stack trace element, or {@code null} if * this information is unavailable * @param lineNumber the line number of the source line containing the * execution point represented by this stack trace element, or * a negative number if this information is unavailable. A value * of -2 indicates that the method containing the execution point * is a native method - * @throws NullPointerException if declaringClass or - * methodName is null + * @throws NullPointerException if {@code declaringClass} or + * {@code methodName} is null * @since 1.5 */ public StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber) { - if (declaringClass == null) - throw new NullPointerException("Declaring class is null"); - if (methodName == null) - throw new NullPointerException("Method name is null"); - - this.declaringClass = declaringClass; - this.methodName = methodName; + this.declaringClass = Objects.nonNull(declaringClass, "Declaring class is null"); + this.methodName = Objects.nonNull(methodName, "Method name is null"); this.fileName = fileName; this.lineNumber = lineNumber; } @@ -80,13 +77,13 @@ /** * Returns the name of the source file containing the execution point * represented by this stack trace element. Generally, this corresponds - * to the SourceFile attribute of the relevant class + * to the {@code SourceFile} attribute of the relevant {@code class} * file (as per The Java Virtual Machine Specification, Section * 4.7.7). In some systems, the name may refer to some source code unit * other than a file, such as an entry in source repository. * * @return the name of the file containing the execution point - * represented by this stack trace element, or null if + * represented by this stack trace element, or {@code null} if * this information is unavailable. */ public String getFileName() { @@ -96,8 +93,8 @@ /** * Returns the line number of the source line containing the execution * point represented by this stack trace element. Generally, this is - * derived from the LineNumberTable attribute of the relevant - * class file (as per The Java Virtual Machine + * derived from the {@code LineNumberTable} attribute of the relevant + * {@code class} file (as per The Java Virtual Machine * Specification, Section 4.7.8). * * @return the line number of the source line containing the execution @@ -112,7 +109,7 @@ * Returns the fully qualified name of the class containing the * execution point represented by this stack trace element. * - * @return the fully qualified name of the Class containing + * @return the fully qualified name of the {@code Class} containing * the execution point represented by this stack trace element. */ public String getClassName() { @@ -123,8 +120,8 @@ * Returns the name of the method containing the execution point * represented by this stack trace element. If the execution point is * contained in an instance or class initializer, this method will return - * the appropriate special method name, <init> or - * <clinit>, as per Section 3.9 of The Java Virtual + * the appropriate special method name, {@code } or + * {@code }, as per Section 3.9 of The Java Virtual * Machine Specification. * * @return the name of the method containing the execution point @@ -138,7 +135,7 @@ * Returns true if the method containing the execution point * represented by this stack trace element is a native method. * - * @return true if the method containing the execution point + * @return {@code true} if the method containing the execution point * represented by this stack trace element is a native method. */ public boolean isNativeMethod() { @@ -151,21 +148,21 @@ * examples may be regarded as typical: *
    *
  • - * "MyClass.mash(MyClass.java:9)" - Here, "MyClass" + * {@code "MyClass.mash(MyClass.java:9)"} - Here, {@code "MyClass"} * is the fully-qualified name of the class containing the * execution point represented by this stack trace element, - * "mash" is the name of the method containing the execution - * point, "MyClass.java" is the source file containing the - * execution point, and "9" is the line number of the source + * {@code "mash"} is the name of the method containing the execution + * point, {@code "MyClass.java"} is the source file containing the + * execution point, and {@code "9"} is the line number of the source * line containing the execution point. *
  • - * "MyClass.mash(MyClass.java)" - As above, but the line + * {@code "MyClass.mash(MyClass.java)"} - As above, but the line * number is unavailable. *
  • - * "MyClass.mash(Unknown Source)" - As above, but neither + * {@code "MyClass.mash(Unknown Source)"} - As above, but neither * the file name nor the line number are available. *
  • - * "MyClass.mash(Native Method)" - As above, but neither + * {@code "MyClass.mash(Native Method)"} - As above, but neither * the file name nor the line number are available, and the method * containing the execution point is known to be a native method. *
@@ -181,25 +178,21 @@ /** * Returns true if the specified object is another - * StackTraceElement instance representing the same execution - * point as this instance. Two stack trace elements a and - * b are equal if and only if: + * {@code StackTraceElement} instance representing the same execution + * point as this instance. Two stack trace elements {@code a} and + * {@code b} are equal if and only if: *
      *     equals(a.getFileName(), b.getFileName()) &&
      *     a.getLineNumber() == b.getLineNumber()) &&
      *     equals(a.getClassName(), b.getClassName()) &&
      *     equals(a.getMethodName(), b.getMethodName())
      * 
- * where equals is defined as: - *
-     *     static boolean equals(Object a, Object b) {
-     *         return a==b || (a != null && a.equals(b));
-     *     }
-     * 
+ * where {@code equals} has the semantics of {@link + * java.util.Objects#equals(Object, Object) Objects.equals}. * * @param obj the object to be compared with this stack trace element. * @return true if the specified object is another - * StackTraceElement instance representing the same + * {@code StackTraceElement} instance representing the same * execution point as this instance. */ public boolean equals(Object obj) { @@ -208,12 +201,10 @@ if (!(obj instanceof StackTraceElement)) return false; StackTraceElement e = (StackTraceElement)obj; - return e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber - && eq(methodName, e.methodName) && eq(fileName, e.fileName); - } - - private static boolean eq(Object a, Object b) { - return a==b || (a != null && a.equals(b)); + return e.declaringClass.equals(declaringClass) && + e.lineNumber == lineNumber && + Objects.equals(methodName, e.methodName) && + Objects.equals(fileName, e.fileName); } /** @@ -221,7 +212,7 @@ */ public int hashCode() { int result = 31*declaringClass.hashCode() + methodName.hashCode(); - result = 31*result + (fileName == null ? 0 : fileName.hashCode()); + result = 31*result + Objects.hashCode(fileName); result = 31*result + lineNumber; return result; } --- old/jdk/src/share/classes/java/lang/Throwable.java 2010-11-04 20:47:54.000000000 -0700 +++ new/jdk/src/share/classes/java/lang/Throwable.java 2010-11-04 20:47:53.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 @@ -169,6 +169,61 @@ */ private String detailMessage; + + /** + * Holder class to defer initializing sentinel objects only used + * for serialization. + */ + private static class SentinelHolder { + /** + * {@linkplain #setStackTrace(StackTraceElement[]) Setting the + * stack trace} to a one-element array containing this sentinel + * value indicates future attempts to set the stack trace will be + * ignored. The sentinal is equal to the result of calling:
+ * {@code new StackTraceElement("", "", null, Integer.MIN_VALUE)} + */ + public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL = + new StackTraceElement("", "", null, Integer.MIN_VALUE); + + /** + * Sentinel value used in the serial form to indicate an immutable + * stack trace. + */ + public static final StackTraceElement[] STACK_TRACE_SENTINEL = + new StackTraceElement[] {STACK_TRACE_ELEMENT_SENTINEL}; + } + + /** + * A value indicating the stack trace field has not yet been initialized. + */ + private static final StackTraceElement[] EMPTY_STACK = new StackTraceElement[0]; + + /* + * To allow Throwable objects to be made immutable and safely + * reused by the JVM, such as OutOfMemoryErrors, the three fields + * of Throwable that are writable in response to user actions, + * cause, stackTrace, and suppressedExceptions obey the following + * protocol: + * + * 1) The fields are initialized to a non-null sentinel value + * which indicates the value has logically not been set. + * + * 2) Writing a null to the field indicates further writes + * are forbidden + * + * 3) The sentinel value may be replaced with another non-null + * value. + * + * For example, implementations of the HotSpot JVM have + * preallocated OutOfMemoryError objects to provide for better + * diagnosability of that situation. These objects are created + * without calling the constructor for that class and the fields + * in question are initialized to null. To support this + * capability, any new fields added to Throwable that require + * being initialized to a non-null value require a coordinated JVM + * change. + */ + /** * The throwable that caused this throwable to get thrown, or null if this * throwable was not caused by another throwable, or if the causative @@ -184,36 +239,38 @@ /** * The stack trace, as returned by {@link #getStackTrace()}. * + * The field is initialized to a zero-length array. A {@code + * null} value of this field indicates subsequent calls to {@link + * #setStackTrace()} will be be no-ops. + * * @serial * @since 1.4 */ - private StackTraceElement[] stackTrace; - /* - * This field is lazily initialized on first use or serialization and - * nulled out when fillInStackTrace is called. - */ + private StackTraceElement[] stackTrace = EMPTY_STACK; + + // Setting this static field introduces an acceptable + // initialization dependency on a few java.util classes. + private static final List suppressedSentinel = + Collections.unmodifiableList(new ArrayList(0)); /** - * The list of suppressed exceptions, as returned by - * {@link #getSuppressedExceptions()}. + * The list of suppressed exceptions, as returned by {@link + * #getSuppressed()}. The list is initialized to a zero-element + * unmodifiable sentinel list. When a serialized Throwable is + * read in, if the {@code suppressedExceptions} field points to a + * zero-element list, the field is reset to the sentinel value. * * @serial * @since 1.7 */ - private List suppressedExceptions = null; - /* - * This field is lazily initialized when the first suppressed - * exception is added. - * - * OutOfMemoryError is preallocated in the VM for better OOM - * diagnosability during VM initialization. Constructor can't - * be not invoked. If a new field to be added in the future must - * be initialized to non-null, it requires a synchronized VM change. - */ + private List suppressedExceptions = suppressedSentinel; /** 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 +629,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 +670,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); @@ -684,10 +741,21 @@ * {@code Throwable} object information about the current state of * the stack frames for the current thread. * + *

If the stack trace of this {@code Throwable} {@linkplain + * Throwable#setStackTrace(StackTraceElement[]) has been set} to + * {@code null}, calling this method has no effect. + * * @return a reference to this {@code Throwable} instance. * @see java.lang.Throwable#printStackTrace() */ - public synchronized native Throwable fillInStackTrace(); + public synchronized Throwable fillInStackTrace() { + if (stackTrace != null) + return fillInStackTrace0(); + else + return this; + } + + private native Throwable fillInStackTrace0(); /** * Provides programmatic access to the stack trace information printed by @@ -718,12 +786,15 @@ private synchronized StackTraceElement[] getOurStackTrace() { // Initialize stack trace if this is the first call to this method - if (stackTrace == null) { + if (stackTrace == EMPTY_STACK) { int depth = getStackTraceDepth(); stackTrace = new StackTraceElement[depth]; for (int i=0; i < depth; i++) stackTrace[i] = getStackTraceElement(i); + } else if (stackTrace == null) { + return EMPTY_STACK; } + return stackTrace; } @@ -738,23 +809,36 @@ * when a throwable is constructed or deserialized when a throwable is * read from a serialization stream. * + *

If the stack trace is set to {@code null}, then future calls + * to this method have no effect on this {@code Throwable}. + * * @param stackTrace the stack trace elements to be associated with * this {@code Throwable}. The specified array is copied by this * call; changes in the specified array after the method invocation * returns will have no affect on this {@code Throwable}'s stack * trace. * - * @throws NullPointerException if {@code stackTrace} is - * {@code null}, or if any of the elements of + * @throws NullPointerException if any of the elements of * {@code stackTrace} are {@code null} * * @since 1.4 */ public void setStackTrace(StackTraceElement[] stackTrace) { - StackTraceElement[] defensiveCopy = stackTrace.clone(); - for (int i = 0; i < defensiveCopy.length; i++) - if (defensiveCopy[i] == null) - throw new NullPointerException("stackTrace[" + i + "]"); + if (this.stackTrace == null) // Immutable stack + return; + + StackTraceElement[] defensiveCopy; + + if (stackTrace == null) { + defensiveCopy = stackTrace; + } else { + defensiveCopy = stackTrace.clone(); + + for (int i = 0; i < defensiveCopy.length; i++) { + if (defensiveCopy[i] == null) + throw new NullPointerException("stackTrace[" + i + "]"); + } + } synchronized (this) { this.stackTrace = defensiveCopy; @@ -780,27 +864,91 @@ */ native StackTraceElement getStackTraceElement(int index); + /** + * Read a {@code Throwable} from a stream, enforcing + * well-formedness constraints on fields. Null entries and + * self-pointers are not allowed in the list of {@code + * suppressedExceptions}. Null entries are not allowed for stack + * trace elements. A single-element stack trace whose entry is + * equal to {@code new StackTraceElement("", "", null, + * Integer.MIN_VALUE)} results in a {@code null} {@code + * stackTrace} field. + * + * Note that there are no constraints on the value the {@code + * cause} field can hold; both {@code null} and this are valid + * values for the field. + */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); // read in all fields List suppressed = null; if (suppressedExceptions != null && !suppressedExceptions.isEmpty()) { // Copy Throwables to new list - suppressed = new ArrayList(); + suppressed = new ArrayList(1); for (Throwable t : suppressedExceptions) { + // Enforce constraints on suppressed exceptions in + // case of corrupt or malicious stream. 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 is a zero-length list, use the sentinel + // value. + if (suppressed != null && suppressed.isEmpty()) + suppressedExceptions = suppressedSentinel; + else + suppressedExceptions = suppressed; + + // Check for the marker of an immutable stack trace + if (stackTrace != null) { + // Share zero-length stack traces + if (stackTrace.length == 0) { + stackTrace = EMPTY_STACK; + } else if (stackTrace.length == 1 && + SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace[0])) { + stackTrace = null; + } else { // Verify stack trace elements are non-null. + for(StackTraceElement ste : stackTrace) { + if (ste == null) + throw new NullPointerException("null StackTraceElement in serial stream. "); + } + } + } + + // A null stackTrace field in the serial form can result from + // an exception serialized without that field. Such exceptions + // are now treated as having immutable stack traces. } + /** + * Write a {@code Throwable} object to a stream. A {@code null} + * stack trace field is represented in the serial form as a + * one-element array whose element is equal to {@code new + * StackTraceElement("", "", null, Integer.MIN_VALUE)}. + */ private synchronized void writeObject(ObjectOutputStream s) - throws IOException - { - getOurStackTrace(); // Ensure that stackTrace field is initialized. - s.defaultWriteObject(); + throws IOException { + // Ensure that the stackTrace field is initialized to a + // non-null value, if appropriate. As of JDK 7, a null stack + // trace field is a valid value indicating the stack trace + // should not be set. + getOurStackTrace(); + ObjectOutputStream.PutField fields = s.putFields(); + + fields.put("detailMessage", detailMessage); + fields.put("cause", cause); + // Serialize a null stacktrace using the stack trace sentinel. + if (stackTrace == null) + fields.put("stackTrace", SentinelHolder.STACK_TRACE_SENTINEL); + else + fields.put("stackTrace", stackTrace); + fields.put("suppressedExceptions", suppressedExceptions); + + s.writeFields(); } /** @@ -808,6 +956,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 @@ -819,20 +975,35 @@ * * @param exception the exception to be added to the list of * suppressed exceptions - * @throws NullPointerException if {@code exception} is null * @throws IllegalArgumentException if {@code exception} is this * throwable; a throwable cannot suppress itself. + * @throws NullPointerException if {@code exception} is null and + * an exception has already been suppressed by this exception * @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 (suppressedExceptions == null) - suppressedExceptions = new ArrayList(); - suppressedExceptions.add(exception); + if (exception == null) { + if (suppressedExceptions == suppressedSentinel) { + 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 == suppressedSentinel) + suppressedExceptions = new ArrayList(1); + + assert suppressedExceptions != suppressedSentinel; + + suppressedExceptions.add(exception); + } } private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0]; @@ -842,12 +1013,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 == suppressedSentinel || + suppressedExceptions == null) return EMPTY_THROWABLE_ARRAY; else return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); --- old/jdk/src/share/native/java/lang/Throwable.c 2010-11-04 20:47:55.000000000 -0700 +++ new/jdk/src/share/native/java/lang/Throwable.c 2010-11-04 20:47:54.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2000, 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 @@ -23,12 +23,6 @@ * questions. */ -/* - * Implementation of class Throwable - * - * former classruntime.c, Wed Jun 26 18:43:20 1991 - */ - #include #include @@ -44,7 +38,7 @@ * `this' so you can write 'throw e.fillInStackTrace();' */ JNIEXPORT jobject JNICALL -Java_java_lang_Throwable_fillInStackTrace(JNIEnv *env, jobject throwable) +Java_java_lang_Throwable_fillInStackTrace0(JNIEnv *env, jobject throwable) { JVM_FillInStackTrace(env, throwable); return throwable; --- old/jdk/test/java/lang/Throwable/StackTraceSerialization.java 2010-11-04 20:47:57.000000000 -0700 +++ new/jdk/test/java/lang/Throwable/StackTraceSerialization.java 2010-11-04 20:47:57.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -26,13 +26,39 @@ /* * @test - * @bug 4202914 4363318 + * @bug 4202914 4363318 6991528 * @summary Basic test of serialization of stack trace information * @author Josh Bloch */ public class StackTraceSerialization { public static void main(String args[]) throws Exception { + testWithSetStackTrace(); + testWithFillInStackTrace(); + } + + private static void testWithSetStackTrace() { + Throwable t = new Throwable(); + t.setStackTrace(null); // Immutable and empty stack + assertEmptyStackTrace(t); + + t.fillInStackTrace(); + // Verify fillInStackTrace is now a no-op. + assertEmptyStackTrace(t); + + t.setStackTrace(new StackTrace[] {new StackTraceElement("foo", "bar", "baz", -1)}); + assertEmptyStackTrace(t); + + if (!equal(t, reconstitute(t))) + throw new Exception("Unequal Throwables with set stacktrace"); + } + + private static void assertEmptyStackTrace(Throwable t) { + if (t.getStackTrace.length != 0) + throw new AssertionError("Nonempty stacktrace."); + } + + private static void testWithFillInStackTrace() { Throwable original = null; try { a(); @@ -40,27 +66,38 @@ original = e; } + if (!equal(original, reconstitute(original))) + throw new Exception("Unequal Throwables with filled-in stacktrace"); + } + + + /** + * Serialize the argument and return the deserialized result. + */ + private static Throwable reconstitute(Throwable t) { + Throwable result = null; ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); - out.writeObject(original); + out.writeObject(t); out.flush(); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); - ObjectInputStream in = new ObjectInputStream(bin); - Throwable clone = (Throwable) in.readObject(); - - if (!equal(original, clone)) - throw new Exception(); +o ObjectInputStream in = new ObjectInputStream(bin); + result = (Throwable) in.readObject(); + return result; } /** - * Returns true if e1 and e2 have equal stack traces and their causes - * are recursively equal (by the same definition). Returns false - * or throws NullPointerExeption otherwise. + * Returns true if e1 and e2 have equal stack traces and their + * causes are recursively equal (by the same definition) and their + * suppressed exception information is equals. Returns false or + * throws NullPointerExeption otherwise. */ private static boolean equal(Throwable t1, Throwable t2) { - return t1==t2 || (Arrays.equals(t1.getStackTrace(), t2.getStackTrace()) - && equal(t1.getCause(), t2.getCause())); + return t1==t2 || + (Arrays.equals(t1.getStackTrace(), t2.getStackTrace()) && + equal(t1.getCause(), t2.getCause()) && + Objects.equals(t1.getSuppressed(), t2.getSuppressed())); } static void a() throws HighLevelException { --- old/jdk/test/java/lang/Throwable/SuppressedExceptions.java 2010-11-04 20:47:59.000000000 -0700 +++ new/jdk/test/java/lang/Throwable/SuppressedExceptions.java 2010-11-04 20:47:58.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-11-04 20:48:00.000000000 -0700 +++ new/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-11-04 20:48:00.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-11-04 20:48:01.000000000 -0700 +++ new/langtools/test/tools/javac/TryWithResources/TwrSuppression.java 2010-11-04 20:48:01.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-11-04 20:48:02.000000000 -0700 +++ new/langtools/test/tools/javac/TryWithResources/TwrTests.java 2010-11-04 20:48:01.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); }