mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-16 13:25:34 +00:00
7005628: Clarify NPE behavior of Throwable.addSuppressed(null)
Reviewed-by: dholmes, mchung, jjb
This commit is contained in:
parent
5faf628164
commit
92c64a56ae
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,15 +30,18 @@ package java.lang;
|
||||
* example, an integer "divide by zero" throws an
|
||||
* 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
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public
|
||||
class ArithmeticException extends RuntimeException {
|
||||
public class ArithmeticException extends RuntimeException {
|
||||
private static final long serialVersionUID = 2256477558314496007L;
|
||||
|
||||
/**
|
||||
* Constructs an <code>ArithmeticException</code> with no detail
|
||||
* Constructs an {@code ArithmeticException} with no detail
|
||||
* message.
|
||||
*/
|
||||
public ArithmeticException() {
|
||||
@ -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.
|
||||
*
|
||||
* @param s the detail message.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,20 +26,24 @@
|
||||
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:
|
||||
* <ul>
|
||||
* <li>Calling the instance method of a <code>null</code> object.
|
||||
* <li>Accessing or modifying the field of a <code>null</code> object.
|
||||
* <li>Taking the length of <code>null</code> as if it were an array.
|
||||
* <li>Accessing or modifying the slots of <code>null</code> as if it
|
||||
* <li>Calling the instance method of a {@code null} object.
|
||||
* <li>Accessing or modifying the field of a {@code null} object.
|
||||
* <li>Taking the length of {@code null} as if it were an array.
|
||||
* <li>Accessing or modifying the slots of {@code null} as if it
|
||||
* 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.
|
||||
* </ul>
|
||||
* <p>
|
||||
* 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
|
||||
* @since JDK1.0
|
||||
@ -49,14 +53,14 @@ class NullPointerException extends RuntimeException {
|
||||
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() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>NullPointerException</code> with the specified
|
||||
* Constructs a {@code NullPointerException} with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,22 +30,25 @@ package java.lang;
|
||||
* because it is out of memory, and no more memory could be made
|
||||
* 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
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public
|
||||
class OutOfMemoryError extends VirtualMachineError {
|
||||
public class OutOfMemoryError extends VirtualMachineError {
|
||||
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() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>OutOfMemoryError</code> with the specified
|
||||
* Constructs an {@code OutOfMemoryError} with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
|
||||
@ -52,7 +52,7 @@ import java.util.*;
|
||||
* throwable can {@linkplain Throwable#addSuppressed suppress} other
|
||||
* throwables from being propagated. Finally, the throwable can also
|
||||
* 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
|
||||
* cause can, itself, have a cause, and so on, leading to a "chain" of
|
||||
* exceptions, each caused by another.
|
||||
@ -282,6 +282,41 @@ public class Throwable implements Serializable {
|
||||
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.
|
||||
*
|
||||
@ -830,13 +865,10 @@ public class Throwable implements Serializable {
|
||||
* typically called (automatically and implicitly) by the {@code
|
||||
* try}-with-resources statement.
|
||||
*
|
||||
* 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>The suppression behavior is enabled <em>unless</em> disabled
|
||||
* {@linkplain #Throwable(String, Throwable, boolean) via a
|
||||
* constructor}. When suppression is disabled, this method does
|
||||
* nothing other than to validate its argument.
|
||||
*
|
||||
* <p>Note that when one exception {@linkplain
|
||||
* #initCause(Throwable) causes} another exception, the first
|
||||
@ -874,33 +906,23 @@ public class Throwable implements Serializable {
|
||||
* suppressed exceptions
|
||||
* @throws IllegalArgumentException if {@code exception} is this
|
||||
* throwable; a throwable cannot suppress itself.
|
||||
* @throws NullPointerException if {@code exception} is null and
|
||||
* an exception has already been suppressed by this exception
|
||||
* @throws NullPointerException if {@code exception} is {@code null}
|
||||
* @since 1.7
|
||||
*/
|
||||
public final synchronized void addSuppressed(Throwable exception) {
|
||||
if (exception == this)
|
||||
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 (exception == null)
|
||||
throw new NullPointerException(NULL_CAUSE_MESSAGE);
|
||||
|
||||
if (suppressedExceptions == null) // Suppressed exceptions not recorded
|
||||
return;
|
||||
if (suppressedExceptions == null) // Suppressed exceptions not recorded
|
||||
return;
|
||||
|
||||
if (suppressedExceptions == SUPPRESSED_SENTINEL)
|
||||
suppressedExceptions = new ArrayList<>(1);
|
||||
if (suppressedExceptions == SUPPRESSED_SENTINEL)
|
||||
suppressedExceptions = new ArrayList<>(1);
|
||||
|
||||
assert suppressedExceptions != SUPPRESSED_SENTINEL;
|
||||
|
||||
suppressedExceptions.add(exception);
|
||||
}
|
||||
suppressedExceptions.add(exception);
|
||||
}
|
||||
|
||||
private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
|
||||
@ -910,7 +932,9 @@ public class Throwable implements Serializable {
|
||||
* suppressed, typically by the {@code try}-with-resources
|
||||
* 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
|
||||
* suppressed to deliver this exception.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,7 +26,7 @@ import java.util.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6911258 6962571 6963622 6991528
|
||||
* @bug 6911258 6962571 6963622 6991528 7005628
|
||||
* @summary Basic tests of suppressed exceptions
|
||||
* @author Joseph D. Darcy
|
||||
*/
|
||||
@ -50,14 +50,6 @@ public class SuppressedExceptions {
|
||||
} catch (IllegalArgumentException iae) {
|
||||
; // 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() {
|
||||
@ -153,19 +145,19 @@ public class SuppressedExceptions {
|
||||
(byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
|
||||
};
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
try(ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais)) {
|
||||
Object o = ois.readObject();
|
||||
Throwable throwable = (Throwable) o;
|
||||
|
||||
Object o = ois.readObject();
|
||||
Throwable throwable = (Throwable) o;
|
||||
System.err.println("TESTING SERIALIZED EXCEPTION");
|
||||
|
||||
System.err.println("TESTING SERIALIZED EXCEPTION");
|
||||
|
||||
Throwable[] t0 = throwable.getSuppressed();
|
||||
if (t0.length != 0) { // Will fail if t0 is null.
|
||||
throw new RuntimeException(message);
|
||||
Throwable[] t0 = throwable.getSuppressed();
|
||||
if (t0.length != 0) { // Will fail if t0 is null.
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
||||
private static void selfReference() {
|
||||
@ -183,8 +175,7 @@ public class SuppressedExceptions {
|
||||
}
|
||||
|
||||
private static void noModification() {
|
||||
Throwable t = new Throwable();
|
||||
t.addSuppressed(null);
|
||||
Throwable t = new NoSuppression(false);
|
||||
|
||||
Throwable[] t0 = t.getSuppressed();
|
||||
if (t0.length != 0)
|
||||
@ -196,5 +187,24 @@ public class SuppressedExceptions {
|
||||
t0 = t.getSuppressed();
|
||||
if (t0.length != 0)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user