提交 8a5e8a3b 编写于 作者: D darcy

6998871: Support making the Throwable.stackTrace field immutable

Reviewed-by: dholmes, mchung, forax
上级 23a305fc
...@@ -32,7 +32,8 @@ package java.lang; ...@@ -32,7 +32,8 @@ package java.lang;
* *
* {@code ArithmeticException} objects may be constructed by the * {@code ArithmeticException} objects may be constructed by the
* virtual machine as if {@linkplain Throwable#Throwable(String, * 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 * @author unascribed
* @since JDK1.0 * @since JDK1.0
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -79,7 +79,7 @@ public class Error extends Throwable { ...@@ -79,7 +79,7 @@ public class Error extends Throwable {
* @param message the detail message (which is saved for later retrieval * @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method). * by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the * @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 * permitted, and indicates that the cause is nonexistent or
* unknown.) * unknown.)
* @since 1.4 * @since 1.4
...@@ -90,13 +90,13 @@ public class Error extends Throwable { ...@@ -90,13 +90,13 @@ public class Error extends Throwable {
/** /**
* Constructs a new error with the specified cause and a detail * Constructs a new error with the specified cause and a detail
* message of <tt>(cause==null ? null : cause.toString())</tt> (which * message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of <tt>cause</tt>). * typically contains the class and detail message of {@code cause}).
* This constructor is useful for errors that are little more than * This constructor is useful for errors that are little more than
* wrappers for other throwables. * wrappers for other throwables.
* *
* @param cause the cause (which is saved for later retrieval by the * @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 * permitted, and indicates that the cause is nonexistent or
* unknown.) * unknown.)
* @since 1.4 * @since 1.4
...@@ -104,4 +104,25 @@ public class Error extends Throwable { ...@@ -104,4 +104,25 @@ public class Error extends Throwable {
public Error(Throwable cause) { public Error(Throwable cause) {
super(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);
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -101,4 +101,24 @@ public class Exception extends Throwable { ...@@ -101,4 +101,24 @@ public class Exception extends Throwable {
public Exception(Throwable cause) { public Exception(Throwable cause) {
super(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);
}
} }
...@@ -43,7 +43,8 @@ package java.lang; ...@@ -43,7 +43,8 @@ package java.lang;
* *
* {@code NullPointerException} objects may be constructed by the * {@code NullPointerException} objects may be constructed by the
* virtual machine as if {@linkplain Throwable#Throwable(String, * 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 * @author unascribed
* @since JDK1.0 * @since JDK1.0
......
...@@ -32,7 +32,8 @@ package java.lang; ...@@ -32,7 +32,8 @@ package java.lang;
* *
* {@code OutOfMemoryError} objects may be constructed by the virtual * {@code OutOfMemoryError} objects may be constructed by the virtual
* machine as if {@linkplain Throwable#Throwable(String, Throwable, * 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 * @author unascribed
* @since JDK1.0 * @since JDK1.0
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -95,4 +95,25 @@ public class RuntimeException extends Exception { ...@@ -95,4 +95,25 @@ public class RuntimeException extends Exception {
public RuntimeException(Throwable cause) { public RuntimeException(Throwable cause) {
super(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);
}
} }
...@@ -129,16 +129,41 @@ public class Throwable implements Serializable { ...@@ -129,16 +129,41 @@ public class Throwable implements Serializable {
*/ */
private String detailMessage; 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. * 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 * To allow Throwable objects to be made immutable and safely
* reused by the JVM, such as OutOfMemoryErrors, fields of * reused by the JVM, such as OutOfMemoryErrors, fields of
* Throwable that are writable in response to user actions, cause * Throwable that are writable in response to user actions, cause,
* and suppressedExceptions obey the following protocol: * stackTrace, and suppressedExceptions obey the following
* protocol:
* *
* 1) The fields are initialized to a non-null sentinel value * 1) The fields are initialized to a non-null sentinel value
* which indicates the value has logically not been set. * which indicates the value has logically not been set.
...@@ -174,10 +199,15 @@ public class Throwable implements Serializable { ...@@ -174,10 +199,15 @@ public class Throwable implements Serializable {
/** /**
* The stack trace, as returned by {@link #getStackTrace()}. * 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 * @serial
* @since 1.4 * @since 1.4
*/ */
private StackTraceElement[] stackTrace; private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
// Setting this static field introduces an acceptable // Setting this static field introduces an acceptable
// initialization dependency on a few java.util classes. // initialization dependency on a few java.util classes.
...@@ -284,24 +314,36 @@ public class Throwable implements Serializable { ...@@ -284,24 +314,36 @@ public class Throwable implements Serializable {
/** /**
* Constructs a new throwable with the specified detail message, * Constructs a new throwable with the specified detail message,
* cause, and {@linkplain #addSuppressed suppression} enabled or * cause, {@linkplain #addSuppressed suppression} enabled or
* disabled. If suppression is disabled, {@link #getSuppressed} * disabled, and writable stack trace enabled or disabled. If
* for this object will return a zero-length array and calls to * suppression is disabled, {@link #getSuppressed} for this object
* {@link #addSuppressed} that would otherwise append an exception * will return a zero-length array and calls to {@link
* to the suppressed list will have no effect. * #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 * <p>Note that the other constructors of {@code Throwable} treat
* suppression as being enabled. Subclasses of {@code Throwable} * suppression as being enabled and the stack trace as being
* should document any conditions under which suppression is * writable. Subclasses of {@code Throwable} should document any
* disabled. Disabling of suppression should only occur in * conditions under which suppression is disabled and document
* exceptional circumstances where special requirements exist, * conditions under which the stack trace is not writable.
* such as a virtual machine reusing exception objects under * Disabling of suppression should only occur in exceptional
* low-memory situations. * circumstances where special requirements exist, such as a
* virtual machine reusing exception objects under low-memory
* situations.
* *
* @param message the detail message. * @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted, * @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.) * and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled or disabled * @param enableSuppression whether or not suppression is enabled or disabled
* @param writableStackTrace whether or not the stack trace should be
* writable
* *
* @see OutOfMemoryError * @see OutOfMemoryError
* @see NullPointerException * @see NullPointerException
...@@ -309,8 +351,13 @@ public class Throwable implements Serializable { ...@@ -309,8 +351,13 @@ public class Throwable implements Serializable {
* @since 1.7 * @since 1.7
*/ */
protected Throwable(String message, Throwable cause, protected Throwable(String message, Throwable cause,
boolean enableSuppression) { boolean enableSuppression,
fillInStackTrace(); boolean writableStackTrace) {
if (writableStackTrace) {
fillInStackTrace();
} else {
stackTrace = null;
}
detailMessage = message; detailMessage = message;
this.cause = cause; this.cause = cause;
if (!enableSuppression) if (!enableSuppression)
...@@ -707,10 +754,22 @@ public class Throwable implements Serializable { ...@@ -707,10 +754,22 @@ public class Throwable implements Serializable {
* {@code Throwable} object information about the current state of * {@code Throwable} object information about the current state of
* the stack frames for the current thread. * 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. * @return a reference to this {@code Throwable} instance.
* @see java.lang.Throwable#printStackTrace() * @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 * Provides programmatic access to the stack trace information printed by
...@@ -740,12 +799,15 @@ public class Throwable implements Serializable { ...@@ -740,12 +799,15 @@ public class Throwable implements Serializable {
} }
private synchronized StackTraceElement[] getOurStackTrace() { private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace if this is the first call to this method // Initialize stack trace field with information from
if (stackTrace == null) { // backtrace if this is the first call to this method
if (stackTrace == UNASSIGNED_STACK) {
int depth = getStackTraceDepth(); int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth]; stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++) for (int i=0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i); stackTrace[i] = getStackTraceElement(i);
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
} }
return stackTrace; return stackTrace;
} }
...@@ -761,6 +823,11 @@ public class Throwable implements Serializable { ...@@ -761,6 +823,11 @@ public class Throwable implements Serializable {
* when a throwable is constructed or deserialized when a throwable is * when a throwable is constructed or deserialized when a throwable is
* read from a serialization stream. * 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 * @param stackTrace the stack trace elements to be associated with
* this {@code Throwable}. The specified array is copied by this * this {@code Throwable}. The specified array is copied by this
* call; changes in the specified array after the method invocation * call; changes in the specified array after the method invocation
...@@ -768,16 +835,21 @@ public class Throwable implements Serializable { ...@@ -768,16 +835,21 @@ public class Throwable implements Serializable {
* trace. * trace.
* *
* @throws NullPointerException if {@code stackTrace} is * @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} * {@code stackTrace} are {@code null}
* *
* @since 1.4 * @since 1.4
*/ */
public void setStackTrace(StackTraceElement[] stackTrace) { public void setStackTrace(StackTraceElement[] stackTrace) {
// Validate argument
StackTraceElement[] defensiveCopy = stackTrace.clone(); StackTraceElement[] defensiveCopy = stackTrace.clone();
for (int i = 0; i < defensiveCopy.length; i++) for (int i = 0; i < defensiveCopy.length; i++) {
if (defensiveCopy[i] == null) if (defensiveCopy[i] == null)
throw new NullPointerException("stackTrace[" + i + "]"); throw new NullPointerException("stackTrace[" + i + "]");
}
if (this.stackTrace == null) // Immutable stack
return;
synchronized (this) { synchronized (this) {
this.stackTrace = defensiveCopy; this.stackTrace = defensiveCopy;
...@@ -808,7 +880,11 @@ public class Throwable implements Serializable { ...@@ -808,7 +880,11 @@ public class Throwable implements Serializable {
* well-formedness constraints on fields. Null entries and * well-formedness constraints on fields. Null entries and
* self-pointers are not allowed in the list of {@code * self-pointers are not allowed in the list of {@code
* suppressedExceptions}. Null entries are not allowed for stack * 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 * Note that there are no constraints on the value the {@code
* cause} field can hold; both {@code null} and {@code this} are * cause} field can hold; both {@code null} and {@code this} are
...@@ -837,26 +913,63 @@ public class Throwable implements Serializable { ...@@ -837,26 +913,63 @@ public class Throwable implements Serializable {
suppressedExceptions = suppressed; suppressedExceptions = suppressed;
} // else a null suppressedExceptions field remains null } // 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 != null) {
for (StackTraceElement ste : stackTrace) { if (stackTrace.length == 0) {
if (ste == null) stackTrace = UNASSIGNED_STACK.clone();
throw new NullPointerException("null StackTraceElement in serial stream. "); } 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 { } else {
// A null stackTrace field in the serial form can result from // A null stackTrace field in the serial form can result
// an exception serialized without that field in older JDK releases. // from an exception serialized without that field in
stackTrace = EMPTY_STACK; // older JDK releases; treat such exceptions as having
// empty stack traces.
stackTrace = UNASSIGNED_STACK.clone();
} }
} }
/** /**
* Write a {@code Throwable} object to a stream. * 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) private synchronized void writeObject(ObjectOutputStream s)
throws IOException { throws IOException {
getOurStackTrace(); // Ensure that stackTrace field is initialized. // Ensure that the stackTrace field is initialized to a
s.defaultWriteObject(); // 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 @@ public class Throwable implements Serializable { ...@@ -866,8 +979,8 @@ public class Throwable implements Serializable {
* try}-with-resources statement. * try}-with-resources statement.
* *
* <p>The suppression behavior is enabled <em>unless</em> disabled * <p>The suppression behavior is enabled <em>unless</em> disabled
* {@linkplain #Throwable(String, Throwable, boolean) via a * {@linkplain #Throwable(String, Throwable, boolean, boolean) via
* constructor}. When suppression is disabled, this method does * a constructor}. When suppression is disabled, this method does
* nothing other than to validate its argument. * nothing other than to validate its argument.
* *
* <p>Note that when one exception {@linkplain * <p>Note that when one exception {@linkplain
...@@ -933,8 +1046,8 @@ public class Throwable implements Serializable { ...@@ -933,8 +1046,8 @@ public class Throwable implements Serializable {
* statement, in order to deliver this exception. * statement, in order to deliver this exception.
* *
* If no exceptions were suppressed or {@linkplain * If no exceptions were suppressed or {@linkplain
* Throwable(String, Throwable, boolean) suppression is disabled}, * #Throwable(String, Throwable, boolean, boolean) suppression is
* an empty array is returned. * disabled}, an empty array is returned.
* *
* @return an array containing all of the exceptions that were * @return an array containing all of the exceptions that were
* suppressed to deliver this exception. * suppressed to deliver this exception.
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
* `this' so you can write 'throw e.fillInStackTrace();' * `this' so you can write 'throw e.fillInStackTrace();'
*/ */
JNIEXPORT jobject JNICALL 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); JVM_FillInStackTrace(env, throwable);
return throwable; return throwable;
......
...@@ -13,28 +13,28 @@ public class ChainedExceptions { ...@@ -13,28 +13,28 @@ public class ChainedExceptions {
StackTraceElement[] highTrace = e.getStackTrace(); StackTraceElement[] highTrace = e.getStackTrace();
int depthTrim = highTrace.length - 2; int depthTrim = highTrace.length - 2;
check(highTrace[0], "a", 48); check(e, highTrace[0], "a", 48);
check(highTrace[1], "main", 11); check(e, highTrace[1], "main", 11);
Throwable mid = e.getCause(); Throwable mid = e.getCause();
StackTraceElement[] midTrace = mid.getStackTrace(); StackTraceElement[] midTrace = mid.getStackTrace();
if (midTrace.length - depthTrim != 4) if (midTrace.length - depthTrim != 4)
throw new RuntimeException("Mid depth"); throw new RuntimeException("Mid depth");
check(midTrace[0], "c", 58); check(mid, midTrace[0], "c", 58);
check(midTrace[1], "b", 52); check(mid, midTrace[1], "b", 52);
check(midTrace[2], "a", 46); check(mid, midTrace[2], "a", 46);
check(midTrace[3], "main", 11); check(mid, midTrace[3], "main", 11);
Throwable low = mid.getCause(); Throwable low = mid.getCause();
StackTraceElement[] lowTrace = low.getStackTrace(); StackTraceElement[] lowTrace = low.getStackTrace();
if (lowTrace.length - depthTrim != 6) if (lowTrace.length - depthTrim != 6)
throw new RuntimeException("Low depth"); throw new RuntimeException("Low depth");
check(lowTrace[0], "e", 65); check(low, lowTrace[0], "e", 65);
check(lowTrace[1], "d", 62); check(low, lowTrace[1], "d", 62);
check(lowTrace[2], "c", 56); check(low, lowTrace[2], "c", 56);
check(lowTrace[3], "b", 52); check(low, lowTrace[3], "b", 52);
check(lowTrace[4], "a", 46); check(low, lowTrace[4], "a", 46);
check(lowTrace[5], "main", 11); check(low, lowTrace[5], "main", 11);
if (low.getCause() != null) if (low.getCause() != null)
throw new RuntimeException("Low cause != null"); throw new RuntimeException("Low cause != null");
...@@ -68,15 +68,15 @@ public class ChainedExceptions { ...@@ -68,15 +68,15 @@ public class ChainedExceptions {
private static final String OUR_CLASS = ChainedExceptions.class.getName(); private static final String OUR_CLASS = ChainedExceptions.class.getName();
private static final String OUR_FILE_NAME = "ChainedExceptions.java"; 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)) if (!e.getClassName().equals(OUR_CLASS))
throw new RuntimeException("Class: " + e); throw new RuntimeException("Class: " + e, t);
if (!e.getMethodName().equals(methodName)) 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)) if (!e.getFileName().equals(OUR_FILE_NAME))
throw new RuntimeException("File name: " + e); throw new RuntimeException("File name: " + e, t);
if (e.getLineNumber() != n) if (e.getLineNumber() != n)
throw new RuntimeException("Line number: " + e); throw new RuntimeException("Line number: " + e, t);
} }
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,7 +26,7 @@ import java.util.*; ...@@ -26,7 +26,7 @@ import java.util.*;
/* /*
* @test * @test
* @bug 4202914 4363318 6991528 * @bug 4202914 4363318 6991528 6998871
* @summary Basic test of serialization of stack trace information * @summary Basic test of serialization of stack trace information
* @author Josh Bloch * @author Josh Bloch
*/ */
...@@ -37,14 +37,52 @@ public class StackTraceSerialization { ...@@ -37,14 +37,52 @@ public class StackTraceSerialization {
testWithFillInStackTrace(); testWithFillInStackTrace();
} }
private static void testWithSetStackTrace() throws Exception { private static void testWithSetStackTrace() {
Throwable t = new Throwable(); StackTraceElement[] stackTrace = {new StackTraceElement("foo", "bar", "baz", -1)};
t.setStackTrace(new StackTraceElement[] Throwable t = new TestThrowable(true, false); // Immutable and empty stack
{new StackTraceElement("foo", "bar", "baz", -1)}); 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);
// 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))) 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) { private static void assertEmptyStackTrace(Throwable t) {
...@@ -52,7 +90,7 @@ public class StackTraceSerialization { ...@@ -52,7 +90,7 @@ public class StackTraceSerialization {
throw new AssertionError("Nonempty stacktrace."); throw new AssertionError("Nonempty stacktrace.");
} }
private static void testWithFillInStackTrace() throws Exception { private static void testWithFillInStackTrace() {
Throwable original = null; Throwable original = null;
try { try {
a(); a();
...@@ -61,16 +99,14 @@ public class StackTraceSerialization { ...@@ -61,16 +99,14 @@ public class StackTraceSerialization {
} }
if (!equal(original, reconstitute(original))) 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. * 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; Throwable result = null;
try(ByteArrayOutputStream bout = new ByteArrayOutputStream(); try(ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout)) { ObjectOutputStream out = new ObjectOutputStream(bout)) {
out.writeObject(t); out.writeObject(t);
...@@ -80,8 +116,9 @@ public class StackTraceSerialization { ...@@ -80,8 +116,9 @@ public class StackTraceSerialization {
ObjectInputStream in = new ObjectInputStream(bin)) { ObjectInputStream in = new ObjectInputStream(bin)) {
result = (Throwable) in.readObject(); result = (Throwable) in.readObject();
} }
} catch(IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
} }
return result; return result;
} }
......
...@@ -193,6 +193,7 @@ public class SuppressedExceptions { ...@@ -193,6 +193,7 @@ public class SuppressedExceptions {
// Make sure addSuppressed(null) throws an NPE // Make sure addSuppressed(null) throws an NPE
try { try {
t.addSuppressed(null); t.addSuppressed(null);
throw new RuntimeException("NPE not thrown!");
} catch(NullPointerException e) { } catch(NullPointerException e) {
; // Expected ; // Expected
} }
...@@ -204,7 +205,7 @@ public class SuppressedExceptions { ...@@ -204,7 +205,7 @@ public class SuppressedExceptions {
private static class NoSuppression extends Throwable { private static class NoSuppression extends Throwable {
public NoSuppression(boolean enableSuppression) { public NoSuppression(boolean enableSuppression) {
super("The medium.", null, enableSuppression); super("The medium.", null, enableSuppression, true);
} }
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册