6998871: Support making the Throwable.stackTrace field immutable
Reviewed-by: dholmes, mchung, forax
--- 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);
}
}
}