diff options
author | Elliott Hughes <enh@google.com> | 2013-05-14 17:40:17 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2013-05-14 17:40:18 +0000 |
commit | c86b6a1addc08cfc3a3a6587ca46ad1be363d408 (patch) | |
tree | d82da049833bec9396bb6d66234144bcf03e552c | |
parent | 0230da3614fd012851bd42898dcd1f4db1c57223 (diff) | |
parent | c2f2aaaae219c69d50eee6549d507c91e9a08519 (diff) | |
download | libcore-c86b6a1addc08cfc3a3a6587ca46ad1be363d408.zip libcore-c86b6a1addc08cfc3a3a6587ca46ad1be363d408.tar.gz libcore-c86b6a1addc08cfc3a3a6587ca46ad1be363d408.tar.bz2 |
Merge "Fix java.lang.Throwable's protected constructor."
-rw-r--r-- | luni/src/main/java/java/lang/Throwable.java | 85 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/lang/ThrowableTest.java | 35 |
2 files changed, 76 insertions, 44 deletions
diff --git a/luni/src/main/java/java/lang/Throwable.java b/luni/src/main/java/java/lang/Throwable.java index 6bb8fa1..f5005c0 100644 --- a/luni/src/main/java/java/lang/Throwable.java +++ b/luni/src/main/java/java/lang/Throwable.java @@ -81,63 +81,66 @@ public class Throwable implements java.io.Serializable { * Constructs a new {@code Throwable} that includes the current stack trace. */ public Throwable() { + this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; fillInStackTrace(); } /** * Constructs a new {@code Throwable} with the current stack trace and the - * specified detail message. - * - * @param detailMessage - * the detail message for this {@code Throwable}. + * given detail message. */ public Throwable(String detailMessage) { - this(); this.detailMessage = detailMessage; + this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + fillInStackTrace(); } /** * Constructs a new {@code Throwable} with the current stack trace, the - * specified detail message and the specified cause. - * - * @param detailMessage - * the detail message for this {@code Throwable}. - * @param throwable - * the cause of this {@code Throwable}. + * given detail message and cause. */ - public Throwable(String detailMessage, Throwable throwable) { - this(); + public Throwable(String detailMessage, Throwable cause) { this.detailMessage = detailMessage; - cause = throwable; + this.cause = cause; + this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + fillInStackTrace(); } /** * Constructs a new {@code Throwable} with the current stack trace and the - * specified cause. - * - * @param throwable - * the cause of this {@code Throwable}. + * given cause. */ - public Throwable(Throwable throwable) { - this(); - this.detailMessage = throwable == null ? null : throwable.toString(); - cause = throwable; + public Throwable(Throwable cause) { + this.detailMessage = cause == null ? null : cause.toString(); + this.cause = cause; + this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + fillInStackTrace(); } /** * Constructs a new {@code Throwable} with the current stack trace, the * specified detail message and the specified cause. * - * @param enableSuppression if false, throwables passed to {@link - * #addSuppressed(Throwable)} will be silently discarded. + * @param enableSuppression if false, {@link #addSuppressed(Throwable)} will be a no-op. + * @param writableStackTrace if false, {@link #fillInStackTrace} will not be called, + * this object's {@code stackTrace} will be null, + * calls to {@link #fillInStackTrace} and {@link #setStackTrace} will be no-ops, + * and {@link #getStackTrace} will return a zero-length array. * @since 1.7 * @hide 1.7 */ - protected Throwable(String detailMessage, Throwable cause, boolean enableSuppression) { - this(detailMessage, cause); + protected Throwable(String detailMessage, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + this.detailMessage = detailMessage; + this.cause = cause; if (!enableSuppression) { this.suppressedExceptions = null; } + if (writableStackTrace) { + this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + fillInStackTrace(); + } else { + this.stackTrace = null; + } } /** @@ -157,31 +160,30 @@ public class Throwable implements java.io.Serializable { * @return this {@code Throwable} instance. */ public Throwable fillInStackTrace() { - // Fill in the intermediate representation + if (stackTrace == null) { + return this; // writableStackTrace was false. + } + // Fill in the intermediate representation. stackState = nativeFillInStackTrace(); - // Mark the full representation as empty - stackTrace = null; + // Mark the full representation as in need of update. + stackTrace = EmptyArray.STACK_TRACE_ELEMENT; return this; } /** - * Returns the extra information message which was provided when this + * Returns the detail message which was provided when this * {@code Throwable} was created. Returns {@code null} if no message was * provided at creation time. - * - * @return this {@code Throwable}'s detail message. */ public String getMessage() { return detailMessage; } /** - * Returns the extra information message which was provided when this + * Returns the detail message which was provided when this * {@code Throwable} was created. Returns {@code null} if no message was * provided at creation time. Subclasses may override this method to return * localized text for the message. Android returns the regular detail message. - * - * @return this {@code Throwable}'s localized detail message. */ public String getLocalizedMessage() { return getMessage(); @@ -218,6 +220,9 @@ public class Throwable implements java.io.Serializable { * @see #printStackTrace() */ public void setStackTrace(StackTraceElement[] trace) { + if (stackTrace == null) { + return; // writableStackTrace was false. + } StackTraceElement[] newTrace = trace.clone(); for (int i = 0; i < newTrace.length; i++) { if (newTrace[i] == null) { @@ -263,15 +268,17 @@ public class Throwable implements java.io.Serializable { /** * Returns an array of StackTraceElement. Each StackTraceElement * represents a entry on the stack. - * - * @return an array of StackTraceElement representing the stack */ private StackTraceElement[] getInternalStackTrace() { - if (stackTrace == null) { + if (stackTrace == EmptyArray.STACK_TRACE_ELEMENT) { stackTrace = nativeGetStackTrace(stackState); stackState = null; // Clean up intermediate representation + return stackTrace; + } else if (stackTrace == null) { + return EmptyArray.STACK_TRACE_ELEMENT; + } else { + return stackTrace; } - return stackTrace; } /** diff --git a/luni/src/test/java/libcore/java/lang/ThrowableTest.java b/luni/src/test/java/libcore/java/lang/ThrowableTest.java index 1906784..7c8ae66 100644 --- a/luni/src/test/java/libcore/java/lang/ThrowableTest.java +++ b/luni/src/test/java/libcore/java/lang/ThrowableTest.java @@ -38,10 +38,35 @@ public class ThrowableTest extends TestCase { } } + public void testNonWritableStackTrace() { + try { + // The 4th argument, writableStackTrace, is false... + throw new SuppressionsThrowable("hi", null, true, false); + } catch (Throwable th) { + assertEquals("hi", th.getMessage()); + + // We see an empty stack trace. + assertEquals(0, th.getStackTrace().length); + + // setStackTrace is a no-op. + th.setStackTrace(new StackTraceElement[] { new StackTraceElement("c", "m", "f", -2) }); + assertEquals(0, th.getStackTrace().length); + + // fillInStackTrace is a no-op. + th.fillInStackTrace(); + assertEquals(0, th.getStackTrace().length); + + // It's still possible to print an exception with writableStackTrace == false. + th.printStackTrace(new PrintWriter(new StringWriter())); + } + } + private static class SuppressionsThrowable extends Throwable { + private static final long serialVersionUID = 202649043897209143L; + public SuppressionsThrowable(String detailMessage, Throwable throwable, - boolean enableSuppression) { - super(detailMessage, throwable, enableSuppression); + boolean enableSuppression, boolean writableStackTrace) { + super(detailMessage, throwable, enableSuppression, writableStackTrace); } } @@ -78,7 +103,7 @@ public class ThrowableTest extends TestCase { } public void testAddSuppressedWithSuppressionDisabled() { - Throwable throwable = new SuppressionsThrowable("foo", null, false); + Throwable throwable = new SuppressionsThrowable("foo", null, false, true); assertSuppressed(throwable); throwable.addSuppressed(new Throwable()); assertSuppressed(throwable); @@ -246,7 +271,7 @@ public class ThrowableTest extends TestCase { + "4696f6e737400104c6a6176612f7574696c2f4c6973743b787070740003666f6f7572001e5b4c6a6" + "176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3cfd223902000078700" + "00000007078"; - Throwable throwable = new SuppressionsThrowable("foo", null, false); + Throwable throwable = new SuppressionsThrowable("foo", null, false, true); throwable.setStackTrace(new StackTraceElement[0]); new SerializationTester<Throwable>(throwable, s) { @Override protected boolean equals(Throwable a, Throwable b) { @@ -275,7 +300,7 @@ public class ThrowableTest extends TestCase { + "0cb5ef71e0200014c0001637400164c6a6176612f7574696c2f436f6c6c656374696f6e3b7870737" + "200136a6176612e7574696c2e41727261794c6973747881d21d99c7619d03000149000473697a657" + "870000000007704000000007871007e000f78"; - Throwable throwable = new SuppressionsThrowable("foo", null, true); + Throwable throwable = new SuppressionsThrowable("foo", null, true, true); throwable.setStackTrace(new StackTraceElement[0]); new SerializationTester<Throwable>(throwable, s) { @Override protected boolean equals(Throwable a, Throwable b) { |