提交 4364dc20 编写于 作者: D darcy

7005628: Clarify NPE behavior of Throwable.addSuppressed(null)

Reviewed-by: dholmes, mchung, jjb
上级 c1da6382
/* /*
* Copyright (c) 1994, 2008, 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
...@@ -30,15 +30,18 @@ package java.lang; ...@@ -30,15 +30,18 @@ package java.lang;
* example, an integer "divide by zero" throws an * example, an integer "divide by zero" throws an
* instance of this class. * instance of this class.
* *
* {@code ArithmeticException} objects may be constructed by the
* virtual machine as if {@linkplain Throwable#Throwable(String,
* Throwable, boolean) suppression were disabled}.
*
* @author unascribed * @author unascribed
* @since JDK1.0 * @since JDK1.0
*/ */
public public class ArithmeticException extends RuntimeException {
class ArithmeticException extends RuntimeException {
private static final long serialVersionUID = 2256477558314496007L; private static final long serialVersionUID = 2256477558314496007L;
/** /**
* Constructs an <code>ArithmeticException</code> with no detail * Constructs an {@code ArithmeticException} with no detail
* message. * message.
*/ */
public ArithmeticException() { public ArithmeticException() {
...@@ -46,7 +49,7 @@ class ArithmeticException extends RuntimeException { ...@@ -46,7 +49,7 @@ class ArithmeticException extends RuntimeException {
} }
/** /**
* Constructs an <code>ArithmeticException</code> with the specified * Constructs an {@code ArithmeticException} with the specified
* detail message. * detail message.
* *
* @param s the detail message. * @param s the detail message.
......
/* /*
* Copyright (c) 1994, 2008, 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
...@@ -26,20 +26,24 @@ ...@@ -26,20 +26,24 @@
package java.lang; package java.lang;
/** /**
* Thrown when an application attempts to use <code>null</code> in a * Thrown when an application attempts to use {@code null} in a
* case where an object is required. These include: * case where an object is required. These include:
* <ul> * <ul>
* <li>Calling the instance method of a <code>null</code> object. * <li>Calling the instance method of a {@code null} object.
* <li>Accessing or modifying the field of a <code>null</code> object. * <li>Accessing or modifying the field of a {@code null} object.
* <li>Taking the length of <code>null</code> as if it were an array. * <li>Taking the length of {@code null} as if it were an array.
* <li>Accessing or modifying the slots of <code>null</code> as if it * <li>Accessing or modifying the slots of {@code null} as if it
* were an array. * were an array.
* <li>Throwing <code>null</code> as if it were a <code>Throwable</code> * <li>Throwing {@code null} as if it were a {@code Throwable}
* value. * value.
* </ul> * </ul>
* <p> * <p>
* Applications should throw instances of this class to indicate * Applications should throw instances of this class to indicate
* other illegal uses of the <code>null</code> object. * other illegal uses of the {@code null} object.
*
* {@code NullPointerException} objects may be constructed by the
* virtual machine as if {@linkplain Throwable#Throwable(String,
* Throwable, boolean) suppression were disabled}.
* *
* @author unascribed * @author unascribed
* @since JDK1.0 * @since JDK1.0
...@@ -49,14 +53,14 @@ class NullPointerException extends RuntimeException { ...@@ -49,14 +53,14 @@ class NullPointerException extends RuntimeException {
private static final long serialVersionUID = 5162710183389028792L; private static final long serialVersionUID = 5162710183389028792L;
/** /**
* Constructs a <code>NullPointerException</code> with no detail message. * Constructs a {@code NullPointerException} with no detail message.
*/ */
public NullPointerException() { public NullPointerException() {
super(); super();
} }
/** /**
* Constructs a <code>NullPointerException</code> with the specified * Constructs a {@code NullPointerException} with the specified
* detail message. * detail message.
* *
* @param s the detail message. * @param s the detail message.
......
/* /*
* Copyright (c) 1994, 2008, 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
...@@ -30,22 +30,25 @@ package java.lang; ...@@ -30,22 +30,25 @@ package java.lang;
* because it is out of memory, and no more memory could be made * because it is out of memory, and no more memory could be made
* available by the garbage collector. * available by the garbage collector.
* *
* {@code OutOfMemoryError} objects may be constructed by the virtual
* machine as if {@linkplain Throwable#Throwable(String, Throwable,
* boolean) suppression were disabled}.
*
* @author unascribed * @author unascribed
* @since JDK1.0 * @since JDK1.0
*/ */
public public class OutOfMemoryError extends VirtualMachineError {
class OutOfMemoryError extends VirtualMachineError {
private static final long serialVersionUID = 8228564086184010517L; private static final long serialVersionUID = 8228564086184010517L;
/** /**
* Constructs an <code>OutOfMemoryError</code> with no detail message. * Constructs an {@code OutOfMemoryError} with no detail message.
*/ */
public OutOfMemoryError() { public OutOfMemoryError() {
super(); super();
} }
/** /**
* Constructs an <code>OutOfMemoryError</code> with the specified * Constructs an {@code OutOfMemoryError} with the specified
* detail message. * detail message.
* *
* @param s the detail message. * @param s the detail message.
......
...@@ -52,7 +52,7 @@ import java.util.*; ...@@ -52,7 +52,7 @@ import java.util.*;
* throwable can {@linkplain Throwable#addSuppressed suppress} other * throwable can {@linkplain Throwable#addSuppressed suppress} other
* throwables from being propagated. Finally, the throwable can also * throwables from being propagated. Finally, the throwable can also
* contain a <i>cause</i>: another throwable that caused this * contain a <i>cause</i>: another throwable that caused this
* throwable to get thrown. The recording of this causal information * throwable to be constructed. The recording of this causal information
* is referred to as the <i>chained exception</i> facility, as the * is referred to as the <i>chained exception</i> facility, as the
* cause can, itself, have a cause, and so on, leading to a "chain" of * cause can, itself, have a cause, and so on, leading to a "chain" of
* exceptions, each caused by another. * exceptions, each caused by another.
...@@ -282,6 +282,41 @@ public class Throwable implements Serializable { ...@@ -282,6 +282,41 @@ public class Throwable implements Serializable {
this.cause = cause; this.cause = cause;
} }
/**
* Constructs a new throwable with the specified detail message,
* cause, and {@linkplain #addSuppressed suppression} enabled or
* disabled. If suppression is disabled, {@link #getSuppressed}
* for this object will return a zero-length array and calls to
* {@link #addSuppressed} that would otherwise append an exception
* to the suppressed list will have no effect.
*
* <p>Note that the other constructors of {@code Throwable} treat
* suppression as being enabled. Subclasses of {@code Throwable}
* should document any conditions under which suppression is
* disabled. Disabling of suppression should only occur in
* exceptional circumstances where special requirements exist,
* such as a virtual machine reusing exception objects under
* low-memory situations.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled or disabled
*
* @see OutOfMemoryError
* @see NullPointerException
* @see ArithmeticException
* @since 1.7
*/
protected Throwable(String message, Throwable cause,
boolean enableSuppression) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
if (!enableSuppression)
suppressedExceptions = null;
}
/** /**
* Returns the detail message string of this throwable. * Returns the detail message string of this throwable.
* *
...@@ -830,13 +865,10 @@ public class Throwable implements Serializable { ...@@ -830,13 +865,10 @@ public class Throwable implements Serializable {
* typically called (automatically and implicitly) by the {@code * typically called (automatically and implicitly) by the {@code
* try}-with-resources statement. * try}-with-resources statement.
* *
* If the first exception to be suppressed is {@code null}, that * <p>The suppression behavior is enabled <em>unless</em> disabled
* indicates suppressed exception information will <em>not</em> be * {@linkplain #Throwable(String, Throwable, boolean) via a
* recorded for this exception. Subsequent calls to this method * constructor}. When suppression is disabled, this method does
* will not record any suppressed exceptions. Otherwise, * nothing other than to validate its argument.
* 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
...@@ -874,33 +906,23 @@ public class Throwable implements Serializable { ...@@ -874,33 +906,23 @@ public class Throwable implements Serializable {
* suppressed exceptions * suppressed exceptions
* @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 * @throws NullPointerException if {@code exception} is {@code null}
* an exception has already been suppressed by this exception
* @since 1.7 * @since 1.7
*/ */
public final synchronized void addSuppressed(Throwable exception) { public final synchronized void addSuppressed(Throwable exception) {
if (exception == this) if (exception == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
if (exception == null) { if (exception == null)
if (suppressedExceptions == SUPPRESSED_SENTINEL) { throw new NullPointerException(NULL_CAUSE_MESSAGE);
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 if (suppressedExceptions == null) // Suppressed exceptions not recorded
return; return;
if (suppressedExceptions == SUPPRESSED_SENTINEL) if (suppressedExceptions == SUPPRESSED_SENTINEL)
suppressedExceptions = new ArrayList<>(1); suppressedExceptions = new ArrayList<>(1);
assert suppressedExceptions != SUPPRESSED_SENTINEL; 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];
...@@ -910,7 +932,9 @@ public class Throwable implements Serializable { ...@@ -910,7 +932,9 @@ 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. * If no exceptions were suppressed or {@linkplain
* Throwable(String, Throwable, boolean) suppression is disabled},
* an empty array is returned.
* *
* @return an array containing all of the exceptions that were * @return an array containing all of the exceptions that were
* suppressed to deliver this exception. * suppressed to deliver this exception.
......
/* /*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2010, 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 6911258 6962571 6963622 6991528 * @bug 6911258 6962571 6963622 6991528 7005628
* @summary Basic tests of suppressed exceptions * @summary Basic tests of suppressed exceptions
* @author Joseph D. Darcy * @author Joseph D. Darcy
*/ */
...@@ -50,14 +50,6 @@ public class SuppressedExceptions { ...@@ -50,14 +50,6 @@ public class SuppressedExceptions {
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
; // Expected ; // Expected
} }
throwable.addSuppressed(null); // Immutable suppression list
try {
throwable.addSuppressed(throwable);
throw new RuntimeException("IllegalArgumentException for self-suppresion not thrown.");
} catch (IllegalArgumentException iae) {
; // Expected
}
} }
private static void basicSupressionTest() { private static void basicSupressionTest() {
...@@ -153,19 +145,19 @@ public class SuppressedExceptions { ...@@ -153,19 +145,19 @@ public class SuppressedExceptions {
(byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
}; };
ByteArrayInputStream bais = new ByteArrayInputStream(bytes); try(ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais); ObjectInputStream ois = new ObjectInputStream(bais)) {
Object o = ois.readObject();
Object o = ois.readObject(); Throwable throwable = (Throwable) o;
Throwable throwable = (Throwable) o;
System.err.println("TESTING SERIALIZED EXCEPTION"); System.err.println("TESTING SERIALIZED EXCEPTION");
Throwable[] t0 = throwable.getSuppressed(); 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);
}
throwable.printStackTrace();
} }
throwable.printStackTrace();
} }
private static void selfReference() { private static void selfReference() {
...@@ -183,8 +175,7 @@ public class SuppressedExceptions { ...@@ -183,8 +175,7 @@ public class SuppressedExceptions {
} }
private static void noModification() { private static void noModification() {
Throwable t = new Throwable(); Throwable t = new NoSuppression(false);
t.addSuppressed(null);
Throwable[] t0 = t.getSuppressed(); Throwable[] t0 = t.getSuppressed();
if (t0.length != 0) if (t0.length != 0)
...@@ -196,5 +187,24 @@ public class SuppressedExceptions { ...@@ -196,5 +187,24 @@ public class SuppressedExceptions {
t0 = t.getSuppressed(); t0 = t.getSuppressed();
if (t0.length != 0) if (t0.length != 0)
throw new RuntimeException("Bad nonzero length of suppressed exceptions."); throw new RuntimeException("Bad nonzero length of suppressed exceptions.");
Throwable suppressed = new ArithmeticException();
t = new NoSuppression(true); // Suppression enabled
// Make sure addSuppressed(null) throws an NPE
try {
t.addSuppressed(null);
} catch(NullPointerException e) {
; // Expected
}
t.addSuppressed(suppressed);
t0 = t.getSuppressed();
if (t0.length != 1 || t0[0] != suppressed)
throw new RuntimeException("Expected suppression did not occur.");
}
private static class NoSuppression extends Throwable {
public NoSuppression(boolean enableSuppression) {
super("The medium.", null, enableSuppression);
}
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册