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,171 ---- * * @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 shared value for an empty stack. */ ! 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, ! * 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)); --- 197,215 ---- 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(StackTraceElement[])} and {@link + * #fillInStackTrace()} will be be no-ops. + * * @serial * @since 1.4 */ ! private StackTraceElement[] stackTrace = UNASSIGNED_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; } --- 312,365 ---- 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()}, 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 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 * @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 --- 752,777 ---- /** * 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 = UNASSIGNED_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; } /** --- 797,815 ---- 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 == 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; } /**
*** 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; } } --- 821,857 ---- * 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) { + // Validate argument 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; 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. */ --- 878,892 ---- /** * 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 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 * valid values for the field. */
*** 835,875 **** } } 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 * typically called (automatically and implicitly) by the {@code * 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 * nothing other than to validate its argument. * * <p>Note that when one exception {@linkplain * #initCause(Throwable) causes} another exception, the first * exception is usually caught and then the second exception is --- 911,988 ---- } } 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) { ! 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; 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 { ! // 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 * typically called (automatically and implicitly) by the {@code * try}-with-resources statement. * * <p>The suppression behavior is enabled <em>unless</em> disabled ! * {@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 * #initCause(Throwable) causes} another exception, the first * exception is usually caught and then the second exception is
*** 931,942 **** * 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 */ --- 1044,1055 ---- * 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, 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 */