src/share/classes/java/lang/Throwable.java

Print this page

        

@@ -1,6 +1,6 @@
-/*
+*
  * 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
  * under the terms of the GNU General Public License version 2 only, as

@@ -127,20 +127,52 @@
      *
      * @serial
      */
     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 value indicating that the logical stack trace has been
+     * populated into the backtrace field.
+     */
+    private static final StackTraceElement[] FILLED_IN_STACK =
+        new StackTraceElement[] {new StackTraceElement("FILLED_IN", "STACK", null, -1)};
+
     /**
      * A shared value for an empty stack.
      */
     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, 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.
      *
      * 2) Writing a null to the field indicates further writes

@@ -172,14 +204,18 @@
     private Throwable cause = this;
 
     /**
      * 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()} and {@link #fillInStackTrace} will be be no-ops.
+     *
      * @serial
      * @since 1.4
      */
-    private StackTraceElement[] stackTrace;
+    private StackTraceElement[] stackTrace = EMPTY_STACK;
 
     // Setting this static field introduces an acceptable
     // initialization dependency on a few java.util classes.
     private static final List<Throwable> SUPPRESSED_SENTINEL =
         Collections.unmodifiableList(new ArrayList<Throwable>(0));

@@ -282,37 +318,51 @@
         this.cause = cause;
     }
 
     /**
      * 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()} 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.  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
      * @see ArithmeticException
      * @since 1.7
      */
     protected Throwable(String message, Throwable cause,
-                        boolean enableSuppression) {
+                        boolean enableSuppression,
+                        boolean writableStackTrace) {
+        if (writableStackTrace) {
         fillInStackTrace();
+        } else {
+            stackTrace = null;
+        }
         detailMessage = message;
         this.cause = cause;
         if (!enableSuppression)
             suppressedExceptions = null;
     }

@@ -705,14 +755,26 @@
     /**
      * Fills in the execution stack trace. This method records within this
      * {@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 = FILLED_IN_STACK;
+        }
+        return this;
+    }
+
+    private native Throwable fillInStackTrace(int dummy);
 
     /**
      * Provides programmatic access to the stack trace information printed by
      * {@link #printStackTrace()}.  Returns an array of stack trace elements,
      * each representing one stack frame.  The zeroth element of the array

@@ -738,16 +800,19 @@
     public StackTraceElement[] getStackTrace() {
         return getOurStackTrace().clone();
     }
 
     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 == FILLED_IN_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;
     }
 
     /**

@@ -759,27 +824,37 @@
      * advanced systems, allows the client to override the default
      * stack trace that is either generated by {@link #fillInStackTrace()}
      * 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
      * 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
+     *         {@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;
         }
     }

@@ -806,11 +881,14 @@
     /**
      * Reads 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.
+     * 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 {@code this} are
      * valid values for the field.
      */

@@ -835,30 +913,59 @@
                 }
             }
             suppressedExceptions = suppressed;
         } // else a null suppressedExceptions field remains null
 
+        // Check for the marker of an immutable stack trace
         if (stackTrace != null) {
-            for (StackTraceElement ste : stackTrace) {
+            // 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. ");
             }
+            }
         } else {
-            // A null stackTrace field in the serial form can result from
-            // an exception serialized without that field in older JDK releases.
+            // 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 = EMPTY_STACK;
         }
-
     }
 
     /**
      * 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();
     }
 
     /**
      * Appends the specified exception to the exceptions that were
      * suppressed in order to deliver this exception. This method is

@@ -931,11 +1038,11 @@
      * Returns an array containing all of the exceptions that were
      * suppressed, typically by the {@code try}-with-resources
      * statement, in order to deliver this exception.
      *
      * If no exceptions were suppressed or {@linkplain
-     * Throwable(String, Throwable, boolean) suppression is disabled},
+     * #Throwable(String, Throwable, boolean) suppression is disabled},
      * an empty array is returned.
      *
      * @return an array containing all of the exceptions that were
      *         suppressed to deliver this exception.
      * @since 1.7