提交 24acdd3f 编写于 作者: D darcy

6991528: Support making Throwable.suppressedExceptions immutable

Reviewed-by: mchung, dholmes
上级 644b712e
/* /*
* Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2010, 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
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package java.lang; package java.lang;
import java.util.Objects;
/** /**
* An element in a stack trace, as returned by {@link * An element in a stack trace, as returned by {@link
* Throwable#getStackTrace()}. Each element represents a single stack frame. * Throwable#getStackTrace()}. Each element represents a single stack frame.
...@@ -53,26 +55,21 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -53,26 +55,21 @@ public final class StackTraceElement implements java.io.Serializable {
* @param methodName the name of the method containing the execution point * @param methodName the name of the method containing the execution point
* represented by the stack trace element * represented by the stack trace element
* @param fileName the name of the file containing the execution point * @param fileName the name of the file containing the execution point
* represented by the stack trace element, or <tt>null</tt> if * represented by the stack trace element, or {@code null} if
* this information is unavailable * this information is unavailable
* @param lineNumber the line number of the source line containing the * @param lineNumber the line number of the source line containing the
* execution point represented by this stack trace element, or * execution point represented by this stack trace element, or
* a negative number if this information is unavailable. A value * a negative number if this information is unavailable. A value
* of -2 indicates that the method containing the execution point * of -2 indicates that the method containing the execution point
* is a native method * is a native method
* @throws NullPointerException if <tt>declaringClass</tt> or * @throws NullPointerException if {@code declaringClass} or
* <tt>methodName</tt> is null * {@code methodName} is null
* @since 1.5 * @since 1.5
*/ */
public StackTraceElement(String declaringClass, String methodName, public StackTraceElement(String declaringClass, String methodName,
String fileName, int lineNumber) { String fileName, int lineNumber) {
if (declaringClass == null) this.declaringClass = Objects.nonNull(declaringClass, "Declaring class is null");
throw new NullPointerException("Declaring class is null"); this.methodName = Objects.nonNull(methodName, "Method name is null");
if (methodName == null)
throw new NullPointerException("Method name is null");
this.declaringClass = declaringClass;
this.methodName = methodName;
this.fileName = fileName; this.fileName = fileName;
this.lineNumber = lineNumber; this.lineNumber = lineNumber;
} }
...@@ -80,13 +77,13 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -80,13 +77,13 @@ public final class StackTraceElement implements java.io.Serializable {
/** /**
* Returns the name of the source file containing the execution point * Returns the name of the source file containing the execution point
* represented by this stack trace element. Generally, this corresponds * represented by this stack trace element. Generally, this corresponds
* to the <tt>SourceFile</tt> attribute of the relevant <tt>class</tt> * to the {@code SourceFile} attribute of the relevant {@code class}
* file (as per <i>The Java Virtual Machine Specification</i>, Section * file (as per <i>The Java Virtual Machine Specification</i>, Section
* 4.7.7). In some systems, the name may refer to some source code unit * 4.7.7). In some systems, the name may refer to some source code unit
* other than a file, such as an entry in source repository. * other than a file, such as an entry in source repository.
* *
* @return the name of the file containing the execution point * @return the name of the file containing the execution point
* represented by this stack trace element, or <tt>null</tt> if * represented by this stack trace element, or {@code null} if
* this information is unavailable. * this information is unavailable.
*/ */
public String getFileName() { public String getFileName() {
...@@ -96,8 +93,8 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -96,8 +93,8 @@ public final class StackTraceElement implements java.io.Serializable {
/** /**
* Returns the line number of the source line containing the execution * Returns the line number of the source line containing the execution
* point represented by this stack trace element. Generally, this is * point represented by this stack trace element. Generally, this is
* derived from the <tt>LineNumberTable</tt> attribute of the relevant * derived from the {@code LineNumberTable} attribute of the relevant
* <tt>class</tt> file (as per <i>The Java Virtual Machine * {@code class} file (as per <i>The Java Virtual Machine
* Specification</i>, Section 4.7.8). * Specification</i>, Section 4.7.8).
* *
* @return the line number of the source line containing the execution * @return the line number of the source line containing the execution
...@@ -112,7 +109,7 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -112,7 +109,7 @@ public final class StackTraceElement implements java.io.Serializable {
* Returns the fully qualified name of the class containing the * Returns the fully qualified name of the class containing the
* execution point represented by this stack trace element. * execution point represented by this stack trace element.
* *
* @return the fully qualified name of the <tt>Class</tt> containing * @return the fully qualified name of the {@code Class} containing
* the execution point represented by this stack trace element. * the execution point represented by this stack trace element.
*/ */
public String getClassName() { public String getClassName() {
...@@ -123,8 +120,8 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -123,8 +120,8 @@ public final class StackTraceElement implements java.io.Serializable {
* Returns the name of the method containing the execution point * Returns the name of the method containing the execution point
* represented by this stack trace element. If the execution point is * represented by this stack trace element. If the execution point is
* contained in an instance or class initializer, this method will return * contained in an instance or class initializer, this method will return
* the appropriate <i>special method name</i>, <tt>&lt;init&gt;</tt> or * the appropriate <i>special method name</i>, {@code <init>} or
* <tt>&lt;clinit&gt;</tt>, as per Section 3.9 of <i>The Java Virtual * {@code <clinit>}, as per Section 3.9 of <i>The Java Virtual
* Machine Specification</i>. * Machine Specification</i>.
* *
* @return the name of the method containing the execution point * @return the name of the method containing the execution point
...@@ -138,7 +135,7 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -138,7 +135,7 @@ public final class StackTraceElement implements java.io.Serializable {
* Returns true if the method containing the execution point * Returns true if the method containing the execution point
* represented by this stack trace element is a native method. * represented by this stack trace element is a native method.
* *
* @return <tt>true</tt> if the method containing the execution point * @return {@code true} if the method containing the execution point
* represented by this stack trace element is a native method. * represented by this stack trace element is a native method.
*/ */
public boolean isNativeMethod() { public boolean isNativeMethod() {
...@@ -151,21 +148,21 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -151,21 +148,21 @@ public final class StackTraceElement implements java.io.Serializable {
* examples may be regarded as typical: * examples may be regarded as typical:
* <ul> * <ul>
* <li> * <li>
* <tt>"MyClass.mash(MyClass.java:9)"</tt> - Here, <tt>"MyClass"</tt> * {@code "MyClass.mash(MyClass.java:9)"} - Here, {@code "MyClass"}
* is the <i>fully-qualified name</i> of the class containing the * is the <i>fully-qualified name</i> of the class containing the
* execution point represented by this stack trace element, * execution point represented by this stack trace element,
* <tt>"mash"</tt> is the name of the method containing the execution * {@code "mash"} is the name of the method containing the execution
* point, <tt>"MyClass.java"</tt> is the source file containing the * point, {@code "MyClass.java"} is the source file containing the
* execution point, and <tt>"9"</tt> is the line number of the source * execution point, and {@code "9"} is the line number of the source
* line containing the execution point. * line containing the execution point.
* <li> * <li>
* <tt>"MyClass.mash(MyClass.java)"</tt> - As above, but the line * {@code "MyClass.mash(MyClass.java)"} - As above, but the line
* number is unavailable. * number is unavailable.
* <li> * <li>
* <tt>"MyClass.mash(Unknown Source)"</tt> - As above, but neither * {@code "MyClass.mash(Unknown Source)"} - As above, but neither
* the file name nor the line number are available. * the file name nor the line number are available.
* <li> * <li>
* <tt>"MyClass.mash(Native Method)"</tt> - As above, but neither * {@code "MyClass.mash(Native Method)"} - As above, but neither
* the file name nor the line number are available, and the method * the file name nor the line number are available, and the method
* containing the execution point is known to be a native method. * containing the execution point is known to be a native method.
* </ul> * </ul>
...@@ -181,25 +178,21 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -181,25 +178,21 @@ public final class StackTraceElement implements java.io.Serializable {
/** /**
* Returns true if the specified object is another * Returns true if the specified object is another
* <tt>StackTraceElement</tt> instance representing the same execution * {@code StackTraceElement} instance representing the same execution
* point as this instance. Two stack trace elements <tt>a</tt> and * point as this instance. Two stack trace elements {@code a} and
* <tt>b</tt> are equal if and only if: * {@code b} are equal if and only if:
* <pre> * <pre>
* equals(a.getFileName(), b.getFileName()) && * equals(a.getFileName(), b.getFileName()) &&
* a.getLineNumber() == b.getLineNumber()) && * a.getLineNumber() == b.getLineNumber()) &&
* equals(a.getClassName(), b.getClassName()) && * equals(a.getClassName(), b.getClassName()) &&
* equals(a.getMethodName(), b.getMethodName()) * equals(a.getMethodName(), b.getMethodName())
* </pre> * </pre>
* where <tt>equals</tt> is defined as: * where {@code equals} has the semantics of {@link
* <pre> * java.util.Objects#equals(Object, Object) Objects.equals}.
* static boolean equals(Object a, Object b) {
* return a==b || (a != null && a.equals(b));
* }
* </pre>
* *
* @param obj the object to be compared with this stack trace element. * @param obj the object to be compared with this stack trace element.
* @return true if the specified object is another * @return true if the specified object is another
* <tt>StackTraceElement</tt> instance representing the same * {@code StackTraceElement} instance representing the same
* execution point as this instance. * execution point as this instance.
*/ */
public boolean equals(Object obj) { public boolean equals(Object obj) {
...@@ -208,12 +201,10 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -208,12 +201,10 @@ public final class StackTraceElement implements java.io.Serializable {
if (!(obj instanceof StackTraceElement)) if (!(obj instanceof StackTraceElement))
return false; return false;
StackTraceElement e = (StackTraceElement)obj; StackTraceElement e = (StackTraceElement)obj;
return e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber return e.declaringClass.equals(declaringClass) &&
&& eq(methodName, e.methodName) && eq(fileName, e.fileName); e.lineNumber == lineNumber &&
} Objects.equals(methodName, e.methodName) &&
Objects.equals(fileName, e.fileName);
private static boolean eq(Object a, Object b) {
return a==b || (a != null && a.equals(b));
} }
/** /**
...@@ -221,7 +212,7 @@ public final class StackTraceElement implements java.io.Serializable { ...@@ -221,7 +212,7 @@ public final class StackTraceElement implements java.io.Serializable {
*/ */
public int hashCode() { public int hashCode() {
int result = 31*declaringClass.hashCode() + methodName.hashCode(); int result = 31*declaringClass.hashCode() + methodName.hashCode();
result = 31*result + (fileName == null ? 0 : fileName.hashCode()); result = 31*result + Objects.hashCode(fileName);
result = 31*result + lineNumber; result = 31*result + lineNumber;
return result; return result;
} }
......
/* /*
* Copyright (c) 1994, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1994, 2010, 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
...@@ -169,6 +169,36 @@ public class Throwable implements Serializable { ...@@ -169,6 +169,36 @@ public class Throwable implements Serializable {
*/ */
private String detailMessage; 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
* are forbidden
*
* 3) The sentinel value may be replaced with another non-null
* value.
*
* For example, implementations of the HotSpot JVM have
* preallocated OutOfMemoryError objects to provide for better
* diagnosability of that situation. These objects are created
* without calling the constructor for that class and the fields
* in question are initialized to null. To support this
* capability, any new fields added to Throwable that require
* being initialized to a non-null value require a coordinated JVM
* change.
*/
/** /**
* The throwable that caused this throwable to get thrown, or null if this * The throwable that caused this throwable to get thrown, or null if this
* throwable was not caused by another throwable, or if the causative * throwable was not caused by another throwable, or if the causative
...@@ -188,32 +218,30 @@ public class Throwable implements Serializable { ...@@ -188,32 +218,30 @@ public class Throwable implements Serializable {
* @since 1.4 * @since 1.4
*/ */
private StackTraceElement[] stackTrace; private StackTraceElement[] stackTrace;
/*
* This field is lazily initialized on first use or serialization and // Setting this static field introduces an acceptable
* nulled out when fillInStackTrace is called. // initialization dependency on a few java.util classes.
*/ private static final List<Throwable> SUPPRESSED_SENTINEL =
Collections.unmodifiableList(new ArrayList<Throwable>(0));
/** /**
* The list of suppressed exceptions, as returned by * The list of suppressed exceptions, as returned by {@link
* {@link #getSuppressedExceptions()}. * #getSuppressed()}. The list is initialized to a zero-element
* unmodifiable sentinel list. When a serialized Throwable is
* read in, if the {@code suppressedExceptions} field points to a
* zero-element list, the field is reset to the sentinel value.
* *
* @serial * @serial
* @since 1.7 * @since 1.7
*/ */
private List<Throwable> suppressedExceptions = null; private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
/*
* This field is lazily initialized when the first suppressed
* exception is added.
*
* OutOfMemoryError is preallocated in the VM for better OOM
* diagnosability during VM initialization. Constructor can't
* be not invoked. If a new field to be added in the future must
* be initialized to non-null, it requires a synchronized VM change.
*/
/** Message for trying to suppress a null exception. */ /** Message for trying to suppress a null exception. */
private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception.";
/** Message for trying to suppress oneself. */
private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted";
/** Caption for labeling causative exception stack traces */ /** Caption for labeling causative exception stack traces */
private static final String CAUSE_CAPTION = "Caused by: "; private static final String CAUSE_CAPTION = "Caused by: ";
...@@ -572,7 +600,7 @@ public class Throwable implements Serializable { ...@@ -572,7 +600,7 @@ public class Throwable implements Serializable {
s.println("\tat " + traceElement); s.println("\tat " + traceElement);
// Print suppressed exceptions, if any // Print suppressed exceptions, if any
for (Throwable se : getSuppressedExceptions()) for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
// Print cause, if any // Print cause, if any
...@@ -613,7 +641,7 @@ public class Throwable implements Serializable { ...@@ -613,7 +641,7 @@ public class Throwable implements Serializable {
s.println(prefix + "\t... " + framesInCommon + " more"); s.println(prefix + "\t... " + framesInCommon + " more");
// Print suppressed exceptions, if any // Print suppressed exceptions, if any
for (Throwable se : getSuppressedExceptions()) for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
prefix +"\t", dejaVu); prefix +"\t", dejaVu);
...@@ -780,25 +808,58 @@ public class Throwable implements Serializable { ...@@ -780,25 +808,58 @@ public class Throwable implements Serializable {
*/ */
native StackTraceElement getStackTraceElement(int index); native StackTraceElement getStackTraceElement(int index);
/**
* Read 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.
*/
private void readObject(ObjectInputStream s) private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException { throws IOException, ClassNotFoundException {
s.defaultReadObject(); // read in all fields s.defaultReadObject(); // read in all fields
List<Throwable> suppressed = null; if (suppressedExceptions != null) {
if (suppressedExceptions != null && List<Throwable> suppressed = null;
!suppressedExceptions.isEmpty()) { // Copy Throwables to new list if (suppressedExceptions.isEmpty()) {
suppressed = new ArrayList<Throwable>(); // Use the sentinel for a zero-length list
for (Throwable t : suppressedExceptions) { suppressed = SUPPRESSED_SENTINEL;
if (t == null) } else { // Copy Throwables to new list
throw new NullPointerException(NULL_CAUSE_MESSAGE); suppressed = new ArrayList<Throwable>(1);
suppressed.add(t); for (Throwable t : suppressedExceptions) {
// Enforce constraints on suppressed exceptions in
// case of corrupt or malicious stream.
if (t == null)
throw new NullPointerException(NULL_CAUSE_MESSAGE);
if (t == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
suppressed.add(t);
}
}
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;
} }
suppressedExceptions = suppressed;
} }
/**
* Write a {@code Throwable} object to a stream.
*/
private synchronized void writeObject(ObjectOutputStream s) private synchronized void writeObject(ObjectOutputStream s)
throws IOException throws IOException {
{
getOurStackTrace(); // Ensure that stackTrace field is initialized. getOurStackTrace(); // Ensure that stackTrace field is initialized.
s.defaultWriteObject(); s.defaultWriteObject();
} }
...@@ -808,6 +869,14 @@ public class Throwable implements Serializable { ...@@ -808,6 +869,14 @@ public class Throwable implements Serializable {
* were suppressed, typically by the {@code try}-with-resources * were suppressed, typically by the {@code try}-with-resources
* statement, in order to deliver this exception. * statement, in order to deliver this exception.
* *
* If the first exception to be suppressed is {@code null}, that
* indicates suppressed exception information will <em>not</em> be
* recorded for this exception. Subsequent calls to this method
* will not record any suppressed exceptions. Otherwise,
* attempting to suppress {@code null} after an exception has
* already been successfully suppressed results in a {@code
* NullPointerException}.
*
* <p>Note that when one exception {@linkplain * <p>Note that when one exception {@linkplain
* #initCause(Throwable) causes} another exception, the first * #initCause(Throwable) causes} another exception, the first
* exception is usually caught and then the second exception is * exception is usually caught and then the second exception is
...@@ -819,20 +888,35 @@ public class Throwable implements Serializable { ...@@ -819,20 +888,35 @@ public class Throwable implements Serializable {
* *
* @param exception the exception to be added to the list of * @param exception the exception to be added to the list of
* suppressed exceptions * suppressed exceptions
* @throws NullPointerException if {@code exception} is null
* @throws IllegalArgumentException if {@code exception} is this * @throws IllegalArgumentException if {@code exception} is this
* throwable; a throwable cannot suppress itself. * throwable; a throwable cannot suppress itself.
* @throws NullPointerException if {@code exception} is null and
* an exception has already been suppressed by this exception
* @since 1.7 * @since 1.7
*/ */
public synchronized void addSuppressedException(Throwable exception) { public final synchronized void addSuppressed(Throwable exception) {
if (exception == null)
throw new NullPointerException(NULL_CAUSE_MESSAGE);
if (exception == this) if (exception == this)
throw new IllegalArgumentException("Self-suppression not permitted"); throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
if (exception == null) {
if (suppressedExceptions == SUPPRESSED_SENTINEL) {
suppressedExceptions = null; // No suppression information recorded
return;
} else
throw new NullPointerException(NULL_CAUSE_MESSAGE);
} else {
assert exception != null && exception != this;
if (suppressedExceptions == null) // Suppressed exceptions not recorded
return;
if (suppressedExceptions == SUPPRESSED_SENTINEL)
suppressedExceptions = new ArrayList<Throwable>(1);
if (suppressedExceptions == null) assert suppressedExceptions != SUPPRESSED_SENTINEL;
suppressedExceptions = new ArrayList<Throwable>();
suppressedExceptions.add(exception); suppressedExceptions.add(exception);
}
} }
private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0]; private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
...@@ -842,12 +926,15 @@ public class Throwable implements Serializable { ...@@ -842,12 +926,15 @@ public class Throwable implements Serializable {
* suppressed, typically by the {@code try}-with-resources * suppressed, typically by the {@code try}-with-resources
* statement, in order to deliver this exception. * statement, in order to deliver this exception.
* *
* If no exceptions were suppressed, 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.
* @since 1.7 * @since 1.7
*/ */
public synchronized Throwable[] getSuppressedExceptions() { public final synchronized Throwable[] getSuppressed() {
if (suppressedExceptions == null) if (suppressedExceptions == SUPPRESSED_SENTINEL ||
suppressedExceptions == null)
return EMPTY_THROWABLE_ARRAY; return EMPTY_THROWABLE_ARRAY;
else else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
......
/* /*
* Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2010, 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,13 +26,33 @@ import java.util.*; ...@@ -26,13 +26,33 @@ import java.util.*;
/* /*
* @test * @test
* @bug 4202914 4363318 * @bug 4202914 4363318 6991528
* @summary Basic test of serialization of stack trace information * @summary Basic test of serialization of stack trace information
* @author Josh Bloch * @author Josh Bloch
*/ */
public class StackTraceSerialization { public class StackTraceSerialization {
public static void main(String args[]) throws Exception { public static void main(String args[]) throws Exception {
testWithSetStackTrace();
testWithFillInStackTrace();
}
private static void testWithSetStackTrace() throws Exception {
Throwable t = new Throwable();
t.setStackTrace(new StackTraceElement[]
{new StackTraceElement("foo", "bar", "baz", -1)});
if (!equal(t, reconstitute(t)))
throw new Exception("Unequal Throwables with set stacktrace");
}
private static void assertEmptyStackTrace(Throwable t) {
if (t.getStackTrace().length != 0)
throw new AssertionError("Nonempty stacktrace.");
}
private static void testWithFillInStackTrace() throws Exception {
Throwable original = null; Throwable original = null;
try { try {
a(); a();
...@@ -40,27 +60,42 @@ public class StackTraceSerialization { ...@@ -40,27 +60,42 @@ public class StackTraceSerialization {
original = e; original = e;
} }
ByteArrayOutputStream bout = new ByteArrayOutputStream(); if (!equal(original, reconstitute(original)))
ObjectOutputStream out = new ObjectOutputStream(bout); throw new Exception("Unequal Throwables with filled-in stacktrace");
out.writeObject(original); }
out.flush();
ByteArrayInputStream bin =
new ByteArrayInputStream(bout.toByteArray()); /**
ObjectInputStream in = new ObjectInputStream(bin); * Serialize the argument and return the deserialized result.
Throwable clone = (Throwable) in.readObject(); */
private static Throwable reconstitute(Throwable t) throws Exception {
if (!equal(original, clone)) Throwable result = null;
throw new Exception();
try(ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout)) {
out.writeObject(t);
out.flush();
try(ByteArrayInputStream bin =
new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin)) {
result = (Throwable) in.readObject();
}
}
return result;
} }
/** /**
* Returns true if e1 and e2 have equal stack traces and their causes * Returns true if e1 and e2 have equal stack traces and their
* are recursively equal (by the same definition). Returns false * causes are recursively equal (by the same definition) and their
* or throws NullPointerExeption otherwise. * suppressed exception information is equals. Returns false or
* throws NullPointerExeption otherwise.
*/ */
private static boolean equal(Throwable t1, Throwable t2) { private static boolean equal(Throwable t1, Throwable t2) {
return t1==t2 || (Arrays.equals(t1.getStackTrace(), t2.getStackTrace()) return t1==t2 ||
&& equal(t1.getCause(), t2.getCause())); (Arrays.equals(t1.getStackTrace(), t2.getStackTrace()) &&
equal(t1.getCause(), t2.getCause()) &&
Objects.equals(t1.getSuppressed(), t2.getSuppressed()));
} }
static void a() throws HighLevelException { static void a() throws HighLevelException {
......
...@@ -26,7 +26,7 @@ import java.util.*; ...@@ -26,7 +26,7 @@ import java.util.*;
/* /*
* @test * @test
* @bug 6911258 6962571 6963622 * @bug 6911258 6962571 6963622 6991528
* @summary Basic tests of suppressed exceptions * @summary Basic tests of suppressed exceptions
* @author Joseph D. Darcy * @author Joseph D. Darcy
*/ */
...@@ -39,12 +39,21 @@ public class SuppressedExceptions { ...@@ -39,12 +39,21 @@ public class SuppressedExceptions {
basicSupressionTest(); basicSupressionTest();
serializationTest(); serializationTest();
selfReference(); selfReference();
noModification();
} }
private static void noSelfSuppression() { private static void noSelfSuppression() {
Throwable throwable = new Throwable(); Throwable throwable = new Throwable();
try { try {
throwable.addSuppressedException(throwable); throwable.addSuppressed(throwable);
throw new RuntimeException("IllegalArgumentException for self-suppresion not thrown.");
} catch (IllegalArgumentException iae) {
; // Expected
}
throwable.addSuppressed(null); // Immutable suppression list
try {
throwable.addSuppressed(throwable);
throw new RuntimeException("IllegalArgumentException for self-suppresion not thrown."); throw new RuntimeException("IllegalArgumentException for self-suppresion not thrown.");
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
; // Expected ; // Expected
...@@ -56,21 +65,21 @@ public class SuppressedExceptions { ...@@ -56,21 +65,21 @@ public class SuppressedExceptions {
RuntimeException suppressed = new RuntimeException("A suppressed exception."); RuntimeException suppressed = new RuntimeException("A suppressed exception.");
AssertionError repressed = new AssertionError("A repressed error."); AssertionError repressed = new AssertionError("A repressed error.");
Throwable[] t0 = throwable.getSuppressedExceptions(); Throwable[] t0 = throwable.getSuppressed();
if (t0.length != 0) { if (t0.length != 0) {
throw new RuntimeException(message); throw new RuntimeException(message);
} }
throwable.printStackTrace(); throwable.printStackTrace();
throwable.addSuppressedException(suppressed); throwable.addSuppressed(suppressed);
Throwable[] t1 = throwable.getSuppressedExceptions(); Throwable[] t1 = throwable.getSuppressed();
if (t1.length != 1 || if (t1.length != 1 ||
t1[0] != suppressed) {throw new RuntimeException(message); t1[0] != suppressed) {throw new RuntimeException(message);
} }
throwable.printStackTrace(); throwable.printStackTrace();
throwable.addSuppressedException(repressed); throwable.addSuppressed(repressed);
Throwable[] t2 = throwable.getSuppressedExceptions(); Throwable[] t2 = throwable.getSuppressed();
if (t2.length != 2 || if (t2.length != 2 ||
t2[0] != suppressed || t2[0] != suppressed ||
t2[1] != repressed) { t2[1] != repressed) {
...@@ -152,7 +161,7 @@ public class SuppressedExceptions { ...@@ -152,7 +161,7 @@ public class SuppressedExceptions {
System.err.println("TESTING SERIALIZED EXCEPTION"); System.err.println("TESTING SERIALIZED EXCEPTION");
Throwable[] t0 = throwable.getSuppressedExceptions(); Throwable[] t0 = throwable.getSuppressed();
if (t0.length != 0) { // Will fail if t0 is null. if (t0.length != 0) { // Will fail if t0 is null.
throw new RuntimeException(message); throw new RuntimeException(message);
} }
...@@ -167,9 +176,25 @@ public class SuppressedExceptions { ...@@ -167,9 +176,25 @@ public class SuppressedExceptions {
throwable1.printStackTrace(); throwable1.printStackTrace();
throwable1.addSuppressedException(throwable2); throwable1.addSuppressed(throwable2);
throwable2.addSuppressedException(throwable1); throwable2.addSuppressed(throwable1);
throwable1.printStackTrace(); throwable1.printStackTrace();
} }
private static void noModification() {
Throwable t = new Throwable();
t.addSuppressed(null);
Throwable[] t0 = t.getSuppressed();
if (t0.length != 0)
throw new RuntimeException("Bad nonzero length of suppressed exceptions.");
t.addSuppressed(new ArithmeticException());
// Make sure a suppressed exception did *not* get added.
t0 = t.getSuppressed();
if (t0.length != 0)
throw new RuntimeException("Bad nonzero length of suppressed exceptions.");
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册