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

Print this page

        

*** 127,146 **** * * @serial */ private String detailMessage; /** * 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: * * 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 --- 127,184 ---- * * @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 the stack trace field has not yet been initialized. + */ + private static final StackTraceElement[] UNINITIALIZED_STACK = + new StackTraceElement[] {new StackTraceElement("UNINITIALIZED", "STACK", null, -1)}; + + /** + * 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, ! * 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,185 **** private Throwable cause = this; /** * The stack trace, as returned by {@link #getStackTrace()}. * * @serial * @since 1.4 */ ! private StackTraceElement[] stackTrace; // 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)); --- 210,227 ---- 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 = UNINITIALIZED_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,318 **** 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. * * <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. * * @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 * * @see OutOfMemoryError * @see NullPointerException * @see ArithmeticException * @since 1.7 */ protected Throwable(String message, Throwable cause, ! boolean enableSuppression) { fillInStackTrace(); detailMessage = message; this.cause = cause; if (!enableSuppression) suppressedExceptions = null; } --- 324,374 ---- this.cause = cause; } /** * Constructs a new throwable with the specified detail message, ! * 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 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 writableStackTrace) { ! if (writableStackTrace) { fillInStackTrace(); + } else { + stackTrace = null; + } detailMessage = message; this.cause = cause; if (!enableSuppression) suppressedExceptions = null; }
*** 705,718 **** /** * 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. * * @return a reference to this {@code Throwable} instance. * @see java.lang.Throwable#printStackTrace() */ ! public synchronized native Throwable fillInStackTrace(); /** * 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 --- 761,786 ---- /** * 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 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,753 **** 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) { int depth = getStackTraceDepth(); stackTrace = new StackTraceElement[depth]; for (int i=0; i < depth; i++) stackTrace[i] = getStackTraceElement(i); } return stackTrace; } /** --- 806,824 ---- public StackTraceElement[] getStackTrace() { return getOurStackTrace().clone(); } private synchronized StackTraceElement[] getOurStackTrace() { ! // 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,785 **** * 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. * * @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 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 + "]"); synchronized (this) { this.stackTrace = defensiveCopy; } } --- 830,867 ---- * 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 stackTrace} are {@code null} * * @since 1.4 */ public void setStackTrace(StackTraceElement[] stackTrace) { + Objects.requireNonNull(stackTrace); + + if (this.stackTrace == null) // Immutable stack + return; + StackTraceElement[] 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; } }
*** 806,816 **** /** * 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. * * 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. */ --- 888,901 ---- /** * 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. 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,864 **** } } suppressedExceptions = suppressed; } // else a null suppressedExceptions field remains null if (stackTrace != 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; } ! } /** * Write a {@code Throwable} object to a stream. */ private synchronized void writeObject(ObjectOutputStream s) throws IOException { ! getOurStackTrace(); // Ensure that stackTrace field is initialized. ! s.defaultWriteObject(); } /** * Appends the specified exception to the exceptions that were * suppressed in order to deliver this exception. This method is --- 920,975 ---- } } suppressedExceptions = suppressed; } // else a null suppressedExceptions field remains null + // 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 { ! // 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,941 **** * 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}, * an empty array is returned. * * @return an array containing all of the exceptions that were * suppressed to deliver this exception. * @since 1.7 --- 1042,1052 ---- * 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}, * an empty array is returned. * * @return an array containing all of the exceptions that were * suppressed to deliver this exception. * @since 1.7