6998871: Support making the Throwable.stackTrace field immutable
authordarcy
Thu, 21 Apr 2011 15:55:59 -0700
changeset 9513 1079ae7ada52
parent 9512 78a4f1f6b88d
child 9514 bdb24db75fe8
6998871: Support making the Throwable.stackTrace field immutable Reviewed-by: dholmes, mchung, forax
jdk/src/share/classes/java/lang/ArithmeticException.java
jdk/src/share/classes/java/lang/Error.java
jdk/src/share/classes/java/lang/Exception.java
jdk/src/share/classes/java/lang/NullPointerException.java
jdk/src/share/classes/java/lang/OutOfMemoryError.java
jdk/src/share/classes/java/lang/RuntimeException.java
jdk/src/share/classes/java/lang/Throwable.java
jdk/src/share/native/java/lang/Throwable.c
jdk/test/java/lang/Throwable/ChainedExceptions.java
jdk/test/java/lang/Throwable/StackTraceSerialization.java
jdk/test/java/lang/Throwable/SuppressedExceptions.java
--- a/jdk/src/share/classes/java/lang/ArithmeticException.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/ArithmeticException.java	Thu Apr 21 15:55:59 2011 -0700
@@ -32,7 +32,8 @@
  *
  * {@code ArithmeticException} objects may be constructed by the
  * virtual machine as if {@linkplain Throwable#Throwable(String,
- * Throwable, boolean) suppression were disabled}.
+ * Throwable, boolean, boolean) suppression were disabled and/or the
+ * stack trace was not writable}.
  *
  * @author  unascribed
  * @since   JDK1.0
--- a/jdk/src/share/classes/java/lang/Error.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/Error.java	Thu Apr 21 15:55:59 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2011, 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
@@ -79,7 +79,7 @@
      * @param  message the detail message (which is saved for later retrieval
      *         by the {@link #getMessage()} method).
      * @param  cause the cause (which is saved for later retrieval by the
-     *         {@link #getCause()} method).  (A <tt>null</tt> value is
+     *         {@link #getCause()} method).  (A {@code null} value is
      *         permitted, and indicates that the cause is nonexistent or
      *         unknown.)
      * @since  1.4
@@ -90,13 +90,13 @@
 
     /**
      * Constructs a new error with the specified cause and a detail
-     * message of <tt>(cause==null ? null : cause.toString())</tt> (which
-     * typically contains the class and detail message of <tt>cause</tt>).
+     * message of {@code (cause==null ? null : cause.toString())} (which
+     * typically contains the class and detail message of {@code cause}).
      * This constructor is useful for errors that are little more than
      * wrappers for other throwables.
      *
      * @param  cause the cause (which is saved for later retrieval by the
-     *         {@link #getCause()} method).  (A <tt>null</tt> value is
+     *         {@link #getCause()} method).  (A {@code null} value is
      *         permitted, and indicates that the cause is nonexistent or
      *         unknown.)
      * @since  1.4
@@ -104,4 +104,25 @@
     public Error(Throwable cause) {
         super(cause);
     }
+
+    /**
+     * Constructs a new error with the specified detail message,
+     * cause, suppression enabled or disabled, and writable stack
+     * trace enabled or disabled.
+     *
+     * @param  message the detail message.
+     * @param cause the cause.  (A {@code null} value is permitted,
+     * and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression whether or not suppression is enabled
+     *                          or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     *
+     * @since 1.7
+     */
+    protected Error(String message, Throwable cause,
+                    boolean enableSuppression,
+                    boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
 }
--- a/jdk/src/share/classes/java/lang/Exception.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/Exception.java	Thu Apr 21 15:55:59 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2011, 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
@@ -101,4 +101,24 @@
     public Exception(Throwable cause) {
         super(cause);
     }
+
+    /**
+     * Constructs a new exception with the specified detail message,
+     * cause, suppression enabled or disabled, and writable stack
+     * trace enabled or disabled.
+     *
+     * @param  message the detail message.
+     * @param cause the cause.  (A {@code null} value is permitted,
+     * and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression whether or not suppression is enabled
+     *                          or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     * @since 1.7
+     */
+    protected Exception(String message, Throwable cause,
+                        boolean enableSuppression,
+                        boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
 }
--- a/jdk/src/share/classes/java/lang/NullPointerException.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/NullPointerException.java	Thu Apr 21 15:55:59 2011 -0700
@@ -43,7 +43,8 @@
  *
  * {@code NullPointerException} objects may be constructed by the
  * virtual machine as if {@linkplain Throwable#Throwable(String,
- * Throwable, boolean) suppression were disabled}.
+ * Throwable, boolean, boolean) suppression were disabled and/or the
+ * stack trace was not writable}.
  *
  * @author  unascribed
  * @since   JDK1.0
--- a/jdk/src/share/classes/java/lang/OutOfMemoryError.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/OutOfMemoryError.java	Thu Apr 21 15:55:59 2011 -0700
@@ -32,7 +32,8 @@
  *
  * {@code OutOfMemoryError} objects may be constructed by the virtual
  * machine as if {@linkplain Throwable#Throwable(String, Throwable,
- * boolean) suppression were disabled}.
+ * boolean, boolean) suppression were disabled and/or the stack trace was not
+ * writable}.
  *
  * @author  unascribed
  * @since   JDK1.0
--- a/jdk/src/share/classes/java/lang/RuntimeException.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/RuntimeException.java	Thu Apr 21 15:55:59 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2011, 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
@@ -95,4 +95,25 @@
     public RuntimeException(Throwable cause) {
         super(cause);
     }
+
+    /**
+     * Constructs a new runtime exception with the specified detail
+     * message, cause, suppression enabled or disabled, and writable
+     * stack trace enabled or disabled.
+     *
+     * @param  message the detail message.
+     * @param cause the cause.  (A {@code null} value is permitted,
+     * and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression whether or not suppression is enabled
+     *                          or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     *
+     * @since 1.7
+     */
+    protected RuntimeException(String message, Throwable cause,
+                               boolean enableSuppression,
+                               boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
 }
--- a/jdk/src/share/classes/java/lang/Throwable.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/classes/java/lang/Throwable.java	Thu Apr 21 15:55:59 2011 -0700
@@ -129,16 +129,41 @@
      */
     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:<br>
+         * {@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 shared value for an empty stack.
      */
-    private static final StackTraceElement[] EMPTY_STACK = new StackTraceElement[0];
+    private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
 
     /*
      * To allow Throwable objects to be made immutable and safely
      * reused by the JVM, such as OutOfMemoryErrors, fields of
-     * Throwable that are writable in response to user actions, cause
-     * and suppressedExceptions obey the following protocol:
+     * 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.
@@ -174,10 +199,15 @@
     /**
      * 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(StackTraceElement[])} and {@link
+     * #fillInStackTrace()} will be be no-ops.
+     *
      * @serial
      * @since 1.4
      */
-    private StackTraceElement[] stackTrace;
+    private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
 
     // Setting this static field introduces an acceptable
     // initialization dependency on a few java.util classes.
@@ -284,24 +314,36 @@
 
     /**
      * Constructs a new throwable with the specified detail message,
-     * cause, and {@linkplain #addSuppressed suppression} enabled or
-     * disabled.  If suppression is disabled, {@link #getSuppressed}
-     * for this object will return a zero-length array and calls to
-     * {@link #addSuppressed} that would otherwise append an exception
-     * to the suppressed list will have no effect.
+     * cause, {@linkplain #addSuppressed suppression} enabled or
+     * disabled, and writable stack trace enabled or disabled.  If
+     * suppression is disabled, {@link #getSuppressed} for this object
+     * will return a zero-length array and calls to {@link
+     * #addSuppressed} that would otherwise append an exception to the
+     * suppressed list will have no effect.  If the writable stack
+     * trace is false, this constructor will not call {@link
+     * #fillInStackTrace()}, a {@code null} will be written to the
+     * {@code stackTrace} field, and subsequent calls to {@code
+     * fillInStackTrace} and {@link
+     * #setStackTrace(StackTraceElement[])} will not set the stack
+     * trace.  If the writable stack trace is false, {@link
+     * #getStackTrace} will return a zero length array.
      *
      * <p>Note that the other constructors of {@code Throwable} treat
-     * suppression as being enabled.  Subclasses of {@code Throwable}
-     * should document any conditions under which suppression is
-     * disabled.  Disabling of suppression should only occur in
-     * exceptional circumstances where special requirements exist,
-     * such as a virtual machine reusing exception objects under
-     * low-memory situations.
+     * suppression as being enabled and the stack trace as being
+     * writable.  Subclasses of {@code Throwable} should document any
+     * conditions under which suppression is disabled and document
+     * conditions under which the stack trace is not writable.
+     * Disabling of suppression should only occur in exceptional
+     * circumstances where special requirements exist, such as a
+     * virtual machine reusing exception objects under low-memory
+     * situations.
      *
      * @param  message the detail message.
      * @param cause the cause.  (A {@code null} value is permitted,
      * and indicates that the cause is nonexistent or unknown.)
      * @param enableSuppression whether or not suppression is enabled or disabled
+     * @param writableStackTrace whether or not the stack trace should be
+     *                           writable
      *
      * @see OutOfMemoryError
      * @see NullPointerException
@@ -309,8 +351,13 @@
      * @since 1.7
      */
     protected Throwable(String message, Throwable cause,
-                        boolean enableSuppression) {
-        fillInStackTrace();
+                        boolean enableSuppression,
+                        boolean writableStackTrace) {
+        if (writableStackTrace) {
+            fillInStackTrace();
+        } else {
+            stackTrace = null;
+        }
         detailMessage = message;
         this.cause = cause;
         if (!enableSuppression)
@@ -707,10 +754,22 @@
      * {@code Throwable} object information about the current state of
      * the stack frames for the current thread.
      *
+     * <p>If the stack trace of this {@code Throwable} {@linkplain
+     * Throwable#Throwable(String, Throwable, boolean, boolean) is not
+     * writable}, 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) {
+            fillInStackTrace(0);
+            stackTrace = UNASSIGNED_STACK;
+        }
+        return this;
+    }
+
+    private native Throwable fillInStackTrace(int dummy);
 
     /**
      * Provides programmatic access to the stack trace information printed by
@@ -740,12 +799,15 @@
     }
 
     private synchronized StackTraceElement[] getOurStackTrace() {
-        // Initialize stack trace if this is the first call to this method
-        if (stackTrace == null) {
+        // Initialize stack trace field with information from
+        // backtrace if this is the first call to this method
+        if (stackTrace == UNASSIGNED_STACK) {
             int depth = getStackTraceDepth();
             stackTrace = new StackTraceElement[depth];
             for (int i=0; i < depth; i++)
                 stackTrace[i] = getStackTraceElement(i);
+        } else if (stackTrace == null) {
+            return UNASSIGNED_STACK;
         }
         return stackTrace;
     }
@@ -761,6 +823,11 @@
      * when a throwable is constructed or deserialized when a throwable is
      * read from a serialization stream.
      *
+     * <p>If the stack trace of this {@code Throwable} {@linkplain
+     * Throwable#Throwable(String, Throwable, boolean, boolean) is not
+     * writable}, calling this method has no effect other than
+     * validating its argument.
+     *
      * @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
@@ -768,16 +835,21 @@
      * trace.
      *
      * @throws NullPointerException if {@code stackTrace} is
-     *         {@code null}, or if any of the elements of
+     *         {@code null} or if any of the elements of
      *         {@code stackTrace} are {@code null}
      *
      * @since  1.4
      */
     public void setStackTrace(StackTraceElement[] stackTrace) {
+        // Validate argument
         StackTraceElement[] defensiveCopy = stackTrace.clone();
-        for (int i = 0; i < defensiveCopy.length; i++)
+        for (int i = 0; i < defensiveCopy.length; i++) {
             if (defensiveCopy[i] == null)
                 throw new NullPointerException("stackTrace[" + i + "]");
+        }
+
+        if (this.stackTrace == null) // Immutable stack
+            return;
 
         synchronized (this) {
             this.stackTrace = defensiveCopy;
@@ -808,7 +880,11 @@
      * 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.
+     * trace elements.  A null stack trace in the serial form results
+     * in a zero-length stack element array. 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 {@code this} are
@@ -837,26 +913,63 @@
             suppressedExceptions = suppressed;
         } // else a null suppressedExceptions field remains null
 
+        /*
+         * For zero-length stack traces, use a clone of
+         * UNASSIGNED_STACK rather than UNASSIGNED_STACK itself to
+         * allow identity comparison against UNASSIGNED_STACK in
+         * getOurStackTrace.  The identity of UNASSIGNED_STACK in
+         * stackTrace indicates to the getOurStackTrace method that
+         * the stackTrace needs to be constructed from the information
+         * in backtrace.
+         */
         if (stackTrace != null) {
-            for (StackTraceElement ste : stackTrace) {
-                if (ste == null)
-                    throw new NullPointerException("null StackTraceElement in serial stream. ");
+            if (stackTrace.length == 0) {
+                stackTrace = UNASSIGNED_STACK.clone();
+            }  else if (stackTrace.length == 1 &&
+                        // Check for the marker of an immutable stack trace
+                        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. ");
+                }
             }
         } else {
-            // A null stackTrace field in the serial form can result from
-            // an exception serialized without that field in older JDK releases.
-            stackTrace = EMPTY_STACK;
+            // A null stackTrace field in the serial form can result
+            // from an exception serialized without that field in
+            // older JDK releases; treat such exceptions as having
+            // empty stack traces.
+            stackTrace = UNASSIGNED_STACK.clone();
         }
-
     }
 
     /**
      * 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();
+        // 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();
     }
 
     /**
@@ -866,8 +979,8 @@
      * try}-with-resources statement.
      *
      * <p>The suppression behavior is enabled <em>unless</em> disabled
-     * {@linkplain #Throwable(String, Throwable, boolean) via a
-     * constructor}.  When suppression is disabled, this method does
+     * {@linkplain #Throwable(String, Throwable, boolean, boolean) via
+     * a constructor}.  When suppression is disabled, this method does
      * nothing other than to validate its argument.
      *
      * <p>Note that when one exception {@linkplain
@@ -933,8 +1046,8 @@
      * statement, in order to deliver this exception.
      *
      * If no exceptions were suppressed or {@linkplain
-     * Throwable(String, Throwable, boolean) suppression is disabled},
-     * an empty array is returned.
+     * #Throwable(String, Throwable, boolean, boolean) suppression is
+     * disabled}, an empty array is returned.
      *
      * @return an array containing all of the exceptions that were
      *         suppressed to deliver this exception.
--- a/jdk/src/share/native/java/lang/Throwable.c	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/src/share/native/java/lang/Throwable.c	Thu Apr 21 15:55:59 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2000, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2011, 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
@@ -44,7 +44,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_fillInStackTrace(JNIEnv *env, jobject throwable, int dummy)
 {
     JVM_FillInStackTrace(env, throwable);
     return throwable;
--- a/jdk/test/java/lang/Throwable/ChainedExceptions.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/test/java/lang/Throwable/ChainedExceptions.java	Thu Apr 21 15:55:59 2011 -0700
@@ -13,28 +13,28 @@
             StackTraceElement[] highTrace = e.getStackTrace();
             int depthTrim = highTrace.length - 2;
 
-            check(highTrace[0], "a",    48);
-            check(highTrace[1], "main", 11);
+            check(e, highTrace[0], "a",    48);
+            check(e, highTrace[1], "main", 11);
 
             Throwable mid = e.getCause();
             StackTraceElement[] midTrace = mid.getStackTrace();
             if (midTrace.length - depthTrim != 4)
                 throw new RuntimeException("Mid depth");
-            check(midTrace[0], "c",    58);
-            check(midTrace[1], "b",    52);
-            check(midTrace[2], "a",    46);
-            check(midTrace[3], "main", 11);
+            check(mid, midTrace[0], "c",    58);
+            check(mid, midTrace[1], "b",    52);
+            check(mid, midTrace[2], "a",    46);
+            check(mid, midTrace[3], "main", 11);
 
             Throwable low = mid.getCause();
             StackTraceElement[] lowTrace = low.getStackTrace();
             if (lowTrace.length - depthTrim != 6)
                 throw new RuntimeException("Low depth");
-            check(lowTrace[0], "e",    65);
-            check(lowTrace[1], "d",    62);
-            check(lowTrace[2], "c",    56);
-            check(lowTrace[3], "b",    52);
-            check(lowTrace[4], "a",    46);
-            check(lowTrace[5], "main", 11);
+            check(low, lowTrace[0], "e",    65);
+            check(low, lowTrace[1], "d",    62);
+            check(low, lowTrace[2], "c",    56);
+            check(low, lowTrace[3], "b",    52);
+            check(low, lowTrace[4], "a",    46);
+            check(low, lowTrace[5], "main", 11);
 
             if (low.getCause() != null)
                 throw new RuntimeException("Low cause != null");
@@ -68,15 +68,15 @@
     private static final String OUR_CLASS  = ChainedExceptions.class.getName();
     private static final String OUR_FILE_NAME = "ChainedExceptions.java";
 
-    private static void check(StackTraceElement e, String methodName, int n) {
+    private static void check(Throwable t, StackTraceElement e, String methodName, int n) {
         if (!e.getClassName().equals(OUR_CLASS))
-            throw new RuntimeException("Class: " + e);
+            throw new RuntimeException("Class: " + e, t);
         if (!e.getMethodName().equals(methodName))
-            throw new RuntimeException("Method name: " + e);
+            throw new RuntimeException("Method name: " + e, t);
         if (!e.getFileName().equals(OUR_FILE_NAME))
-            throw new RuntimeException("File name: " + e);
+            throw new RuntimeException("File name: " + e, t);
         if (e.getLineNumber() != n)
-            throw new RuntimeException("Line number: " + e);
+            throw new RuntimeException("Line number: " + e, t);
     }
 }
 
--- a/jdk/test/java/lang/Throwable/StackTraceSerialization.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/test/java/lang/Throwable/StackTraceSerialization.java	Thu Apr 21 15:55:59 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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,7 +26,7 @@
 
 /*
  * @test
- * @bug     4202914 4363318 6991528
+ * @bug     4202914 4363318 6991528 6998871
  * @summary Basic test of serialization of stack trace information
  * @author  Josh Bloch
  */
@@ -37,14 +37,52 @@
         testWithFillInStackTrace();
     }
 
-    private static void testWithSetStackTrace() throws Exception {
-        Throwable t = new Throwable();
+    private static void testWithSetStackTrace() {
+        StackTraceElement[] stackTrace = {new StackTraceElement("foo", "bar", "baz", -1)};
+
+        Throwable t = new TestThrowable(true, false); // Immutable and empty stack
+        assertEmptyStackTrace(t);
+
+        // Verify fillInStackTrace is now a no-op.
+        t.fillInStackTrace();
+        assertEmptyStackTrace(t);
+
+        // Verify setStackTrace is now a no-op.
+        t.setStackTrace(stackTrace);
+        assertEmptyStackTrace(t);
 
-        t.setStackTrace(new StackTraceElement[]
-            {new StackTraceElement("foo", "bar", "baz", -1)});
+        // Verify null-handling
+        try {
+            t.setStackTrace(null);
+            throw new RuntimeException("No NPE on a null stack trace.");
+        } catch(NullPointerException npe) {
+            assertEmptyStackTrace(t);
+        }
+
+        try {
+            t.setStackTrace(new StackTraceElement[]{null});
+            throw new RuntimeException("No NPE on a null stack trace element.");
+        } catch(NullPointerException npe) {
+            assertEmptyStackTrace(t);
+        }
 
         if (!equal(t, reconstitute(t)))
-            throw new Exception("Unequal Throwables with set stacktrace");
+            throw new RuntimeException("Unequal Throwables with set stacktrace");
+
+        Throwable t2 = new Throwable();
+        t2.setStackTrace(stackTrace);
+        if (!equal(t2, reconstitute(t2)))
+            throw new RuntimeException("Unequal Throwables with set stacktrace");
+
+    }
+
+    private static class TestThrowable extends Throwable {
+        public TestThrowable(boolean enableSuppression,
+                             boolean writableStackTrace) {
+            super("the medium", null,
+                  enableSuppression,
+                  writableStackTrace);
+        }
     }
 
     private static void assertEmptyStackTrace(Throwable t) {
@@ -52,7 +90,7 @@
             throw new AssertionError("Nonempty stacktrace.");
     }
 
-    private static void testWithFillInStackTrace() throws Exception {
+    private static void testWithFillInStackTrace() {
         Throwable original = null;
         try {
             a();
@@ -61,16 +99,14 @@
         }
 
         if (!equal(original, reconstitute(original)))
-            throw new Exception("Unequal Throwables with filled-in stacktrace");
+            throw new RuntimeException("Unequal Throwables with filled-in stacktrace");
     }
 
-
     /**
      * Serialize the argument and return the deserialized result.
      */
-    private static Throwable reconstitute(Throwable t) throws Exception {
+    private static Throwable reconstitute(Throwable t) {
         Throwable result = null;
-
         try(ByteArrayOutputStream bout = new ByteArrayOutputStream();
             ObjectOutputStream out = new ObjectOutputStream(bout)) {
             out.writeObject(t);
@@ -80,8 +116,9 @@
                 ObjectInputStream in = new ObjectInputStream(bin)) {
                 result = (Throwable) in.readObject();
             }
+        } catch(IOException | ClassNotFoundException e) {
+            throw new RuntimeException(e);
         }
-
         return result;
     }
 
--- a/jdk/test/java/lang/Throwable/SuppressedExceptions.java	Thu Apr 21 17:44:55 2011 -0400
+++ b/jdk/test/java/lang/Throwable/SuppressedExceptions.java	Thu Apr 21 15:55:59 2011 -0700
@@ -193,6 +193,7 @@
         // Make sure addSuppressed(null) throws an NPE
         try {
             t.addSuppressed(null);
+            throw new RuntimeException("NPE not thrown!");
         } catch(NullPointerException e) {
             ; // Expected
         }
@@ -204,7 +205,7 @@
 
     private static class NoSuppression extends Throwable {
         public NoSuppression(boolean enableSuppression) {
-            super("The medium.", null, enableSuppression);
+            super("The medium.", null, enableSuppression, true);
         }
     }
 }