Home | History | Annotate | Download | only in lang
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package java.lang;
     19 
     20 import java.io.IOException;
     21 import java.io.ObjectInputStream;
     22 import java.io.ObjectOutputStream;
     23 import java.io.PrintStream;
     24 import java.io.PrintWriter;
     25 import java.util.ArrayList;
     26 import java.util.Collections;
     27 import java.util.List;
     28 import libcore.util.EmptyArray;
     29 
     30 /**
     31  * The superclass of all classes which can be thrown by the VM. The
     32  * two direct subclasses are recoverable exceptions ({@code Exception}) and
     33  * unrecoverable errors ({@code Error}). This class provides common methods for
     34  * accessing a string message which provides extra information about the
     35  * circumstances in which the {@code Throwable} was created (basically an error
     36  * message in most cases), and for saving a stack trace (that is, a record of
     37  * the call stack at a particular point in time) which can be printed later.
     38  *
     39  * <p>A {@code Throwable} can also include a cause, which is a nested {@code
     40  * Throwable} that represents the original problem that led to this {@code
     41  * Throwable}. It is often used for wrapping various types of errors into a
     42  * common {@code Throwable} without losing the detailed original error
     43  * information. When printing the stack trace, the trace of the cause is
     44  * included.
     45  *
     46  * @see Error
     47  * @see Exception
     48  * @see RuntimeException
     49  */
     50 public class Throwable implements java.io.Serializable {
     51     private static final long serialVersionUID = -3042686055658047285L;
     52 
     53     /**
     54      * The message provided when the exception was created.
     55      */
     56     private String detailMessage;
     57 
     58     /**
     59      * The cause of this Throwable. Null when there is no cause.
     60      */
     61     private Throwable cause = this;
     62 
     63     /**
     64      * Throwables suppressed by this throwable. Null when suppressed exceptions
     65      * are disabled.
     66      */
     67     private List<Throwable> suppressedExceptions = Collections.emptyList();
     68 
     69     /**
     70      * An intermediate representation of the stack trace.  This field may
     71      * be accessed by the VM; do not rename.
     72      */
     73     private transient volatile Object stackState;
     74 
     75     /**
     76      * A fully-expanded representation of the stack trace.
     77      */
     78     private StackTraceElement[] stackTrace;
     79 
     80     /**
     81      * Constructs a new {@code Throwable} that includes the current stack trace.
     82      */
     83     public Throwable() {
     84         this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
     85         fillInStackTrace();
     86     }
     87 
     88     /**
     89      * Constructs a new {@code Throwable} with the current stack trace and the
     90      * given detail message.
     91      */
     92     public Throwable(String detailMessage) {
     93         this.detailMessage = detailMessage;
     94         this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
     95         fillInStackTrace();
     96     }
     97 
     98     /**
     99      * Constructs a new {@code Throwable} with the current stack trace, the
    100      * given detail message and cause.
    101      */
    102     public Throwable(String detailMessage, Throwable cause) {
    103         this.detailMessage = detailMessage;
    104         this.cause = cause;
    105         this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
    106         fillInStackTrace();
    107     }
    108 
    109     /**
    110      * Constructs a new {@code Throwable} with the current stack trace and the
    111      * given cause.
    112      */
    113     public Throwable(Throwable cause) {
    114         this.detailMessage = cause == null ? null : cause.toString();
    115         this.cause = cause;
    116         this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
    117         fillInStackTrace();
    118     }
    119 
    120     /**
    121      * Constructs a new {@code Throwable} with the current stack trace, the
    122      * specified detail message and the specified cause.
    123      *
    124      * @param enableSuppression if false, {@link #addSuppressed(Throwable)} will be a no-op.
    125      * @param writableStackTrace if false, {@link #fillInStackTrace} will not be called,
    126      * this object's {@code stackTrace} will be null,
    127      * calls to {@link #fillInStackTrace} and {@link #setStackTrace} will be no-ops,
    128      * and {@link #getStackTrace} will return a zero-length array.
    129      * @since 1.7
    130      */
    131     protected Throwable(String detailMessage, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
    132         this.detailMessage = detailMessage;
    133         this.cause = cause;
    134         if (!enableSuppression) {
    135             this.suppressedExceptions = null;
    136         }
    137         if (writableStackTrace) {
    138             this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
    139             fillInStackTrace();
    140         } else {
    141             this.stackTrace = null;
    142         }
    143     }
    144 
    145     /**
    146      * Records the stack trace from the point where this method has been called
    147      * to this {@code Throwable}. This method is invoked by the {@code Throwable} constructors.
    148      *
    149      * <p>This method is public so that code (such as an RPC system) which catches
    150      * a {@code Throwable} and then re-throws it can replace the construction-time stack trace
    151      * with a stack trace from the location where the exception was re-thrown, by <i>calling</i>
    152      * {@code fillInStackTrace}.
    153      *
    154      * <p>This method is non-final so that non-Java language implementations can disable VM stack
    155      * traces for their language. Filling in the stack trace is relatively expensive.
    156      * <i>Overriding</i> this method in the root of a language's exception hierarchy allows the
    157      * language to avoid paying for something it doesn't need.
    158      *
    159      * @return this {@code Throwable} instance.
    160      */
    161     public Throwable fillInStackTrace() {
    162         if (stackTrace == null) {
    163             return this; // writableStackTrace was false.
    164         }
    165         // Fill in the intermediate representation.
    166         stackState = nativeFillInStackTrace();
    167         // Mark the full representation as in need of update.
    168         stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
    169         return this;
    170     }
    171 
    172     /**
    173      * Returns the detail message which was provided when this
    174      * {@code Throwable} was created. Returns {@code null} if no message was
    175      * provided at creation time.
    176      */
    177     public String getMessage() {
    178         return detailMessage;
    179     }
    180 
    181     /**
    182      * Returns the detail message which was provided when this
    183      * {@code Throwable} was created. Returns {@code null} if no message was
    184      * provided at creation time. Subclasses may override this method to return
    185      * localized text for the message. Android returns the regular detail message.
    186      */
    187     public String getLocalizedMessage() {
    188         return getMessage();
    189     }
    190 
    191     /**
    192      * Returns a clone of the array of stack trace elements of this {@code Throwable}. Each
    193      * {@code StackTraceElement} represents an entry in the call stack. The
    194      * element at position 0 is the top of the stack, that is, the stack frame
    195      * where this {@code Throwable} is thrown.
    196      *
    197      * @see #printStackTrace()
    198      */
    199     public StackTraceElement[] getStackTrace() {
    200         return getInternalStackTrace().clone();
    201     }
    202 
    203     /**
    204      * Sets the array of stack trace elements. Each {@code StackTraceElement}
    205      * represents an entry in the call stack. A copy of the specified array is
    206      * stored in this {@code Throwable}. will be returned by {@code
    207      * getStackTrace()} and printed by {@code printStackTrace()}.
    208      *
    209      * @param trace
    210      *            the new array of {@code StackTraceElement}s. A copy of the
    211      *            array is stored in this {@code Throwable}, so subsequent
    212      *            changes to {@code trace} will not change the call stack stored
    213      *            in this {@code Throwable}.
    214      * @throws NullPointerException
    215      *             if any element in {@code trace} is {@code null}.
    216      * @see #printStackTrace()
    217      */
    218     public void setStackTrace(StackTraceElement[] trace) {
    219         if (stackTrace == null) {
    220             return; // writableStackTrace was false.
    221         }
    222         StackTraceElement[] newTrace = trace.clone();
    223         for (int i = 0; i < newTrace.length; i++) {
    224             if (newTrace[i] == null) {
    225                 throw new NullPointerException("trace[" + i + "] == null");
    226             }
    227         }
    228         stackTrace = newTrace;
    229     }
    230 
    231     /**
    232      * Writes a printable representation of this {@code Throwable}'s stack trace
    233      * to the {@code System.err} stream.
    234      */
    235     public void printStackTrace() {
    236         printStackTrace(System.err);
    237     }
    238 
    239     /**
    240      * Counts the number of duplicate stack frames, starting from the
    241      * end of the stack.
    242      */
    243     private static int countDuplicates(StackTraceElement[] currentStack,
    244             StackTraceElement[] parentStack) {
    245         int duplicates = 0;
    246         int parentIndex = parentStack.length;
    247         for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0;) {
    248             StackTraceElement parentFrame = parentStack[parentIndex];
    249             if (parentFrame.equals(currentStack[i])) {
    250                 duplicates++;
    251             } else {
    252                 break;
    253             }
    254         }
    255         return duplicates;
    256     }
    257 
    258     /**
    259      * Returns an array of StackTraceElement. Each StackTraceElement
    260      * represents a entry on the stack.
    261      */
    262     private StackTraceElement[] getInternalStackTrace() {
    263         if (stackTrace == EmptyArray.STACK_TRACE_ELEMENT) {
    264             stackTrace = nativeGetStackTrace(stackState);
    265             stackState = null; // Let go of intermediate representation.
    266             return stackTrace;
    267         } else if (stackTrace == null) {
    268             return EmptyArray.STACK_TRACE_ELEMENT;
    269         } else {
    270           return stackTrace;
    271         }
    272     }
    273 
    274     /**
    275      * Writes a printable representation of this {@code Throwable}'s stack trace
    276      * to the given print stream. If the {@code Throwable} contains a
    277      * {@link #getCause() cause}, the method will be invoked recursively for
    278      * the nested {@code Throwable}.
    279      */
    280     public void printStackTrace(PrintStream err) {
    281         try {
    282             printStackTrace(err, "", null);
    283         } catch (IOException e) {
    284             // Appendable.append throws IOException but PrintStream.append doesn't.
    285             throw new AssertionError();
    286         }
    287     }
    288 
    289     /**
    290      * Writes a printable representation of this {@code Throwable}'s stack trace
    291      * to the specified print writer. If the {@code Throwable} contains a
    292      * {@link #getCause() cause}, the method will be invoked recursively for the
    293      * nested {@code Throwable}.
    294      *
    295      * @param err
    296      *            the writer to write the stack trace on.
    297      */
    298     public void printStackTrace(PrintWriter err) {
    299         try {
    300             printStackTrace(err, "", null);
    301         } catch (IOException e) {
    302             // Appendable.append throws IOException, but PrintWriter.append doesn't.
    303             throw new AssertionError();
    304         }
    305     }
    306 
    307     /**
    308      * @param indent additional indentation on each line of the stack trace.
    309      *     This is the empty string for all but suppressed throwables.
    310      * @param parentStack the parent stack trace to suppress duplicates from, or
    311      *     null if this stack trace has no parent.
    312      */
    313     private void printStackTrace(Appendable err, String indent, StackTraceElement[] parentStack)
    314             throws IOException {
    315         err.append(toString());
    316         err.append("\n");
    317 
    318         StackTraceElement[] stack = getInternalStackTrace();
    319         if (stack != null) {
    320             int duplicates = parentStack != null ? countDuplicates(stack, parentStack) : 0;
    321             for (int i = 0; i < stack.length - duplicates; i++) {
    322                 err.append(indent);
    323                 err.append("\tat ");
    324                 err.append(stack[i].toString());
    325                 err.append("\n");
    326             }
    327 
    328             if (duplicates > 0) {
    329                 err.append(indent);
    330                 err.append("\t... ");
    331                 err.append(Integer.toString(duplicates));
    332                 err.append(" more\n");
    333             }
    334         }
    335 
    336         // Print suppressed exceptions indented one level deeper.
    337         if (suppressedExceptions != null) {
    338             for (Throwable throwable : suppressedExceptions) {
    339                 err.append(indent);
    340                 err.append("\tSuppressed: ");
    341                 throwable.printStackTrace(err, indent + "\t", stack);
    342             }
    343         }
    344 
    345         Throwable cause = getCause();
    346         if (cause != null) {
    347             err.append(indent);
    348             err.append("Caused by: ");
    349             cause.printStackTrace(err, indent, stack);
    350         }
    351     }
    352 
    353     @Override
    354     public String toString() {
    355         String msg = getLocalizedMessage();
    356         String name = getClass().getName();
    357         if (msg == null) {
    358             return name;
    359         }
    360         return name + ": " + msg;
    361     }
    362 
    363     /**
    364      * Initializes the cause of this {@code Throwable}. The cause can only be
    365      * initialized once.
    366      *
    367      * @param throwable
    368      *            the cause of this {@code Throwable}.
    369      * @return this {@code Throwable} instance.
    370      * @throws IllegalArgumentException
    371      *             if {@code Throwable} is this object.
    372      * @throws IllegalStateException
    373      *             if the cause has already been initialized.
    374      */
    375     public Throwable initCause(Throwable throwable) {
    376         if (cause != this) {
    377             throw new IllegalStateException("Cause already initialized");
    378         }
    379         if (throwable == this) {
    380             throw new IllegalArgumentException("throwable == this");
    381         }
    382         cause = throwable;
    383         return this;
    384     }
    385 
    386     /**
    387      * Returns the cause of this {@code Throwable}, or {@code null} if there is
    388      * no cause.
    389      */
    390     public Throwable getCause() {
    391         if (cause == this) {
    392             return null;
    393         }
    394         return cause;
    395     }
    396 
    397     /**
    398      * Adds {@code throwable} to the list of throwables suppressed by this. The
    399      * throwable will included when this exception's stack trace is printed.
    400      *
    401      * @throws IllegalArgumentException if {@code throwable == this}.
    402      * @throws NullPointerException if {@code throwable == null}.
    403      * @since 1.7
    404      */
    405     public final void addSuppressed(Throwable throwable) {
    406         if (throwable == this) {
    407             throw new IllegalArgumentException("throwable == this");
    408         }
    409         if (throwable == null) {
    410             throw new NullPointerException("throwable == null");
    411         }
    412         if (suppressedExceptions != null) {
    413             // Suppressed exceptions are enabled.
    414             if (suppressedExceptions.isEmpty()) {
    415                 // Ensure we have somewhere to place suppressed exceptions.
    416                 suppressedExceptions = new ArrayList<Throwable>(1);
    417             }
    418             suppressedExceptions.add(throwable);
    419         }
    420     }
    421 
    422     /**
    423      * Returns the throwables suppressed by this.
    424      *
    425      * @since 1.7
    426      */
    427     public final Throwable[] getSuppressed() {
    428         return (suppressedExceptions != null && !suppressedExceptions.isEmpty())
    429                 ? suppressedExceptions.toArray(new Throwable[suppressedExceptions.size()])
    430                 : EmptyArray.THROWABLE;
    431     }
    432 
    433     private void writeObject(ObjectOutputStream out) throws IOException {
    434         // Ensure the stackTrace field is initialized.
    435         getInternalStackTrace();
    436         out.defaultWriteObject();
    437     }
    438 
    439     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    440         in.defaultReadObject();
    441 
    442         if (suppressedExceptions != null) {
    443             // The deserialized list may be unmodifiable, so just create a mutable copy.
    444             suppressedExceptions = new ArrayList<Throwable>(suppressedExceptions);
    445         }
    446     }
    447 
    448     /*
    449      * Creates a compact, VM-specific collection of goodies, suitable for
    450      * storing in the "stackState" field, based on the current thread's
    451      * call stack.
    452      */
    453     private static native Object nativeFillInStackTrace();
    454 
    455     /*
    456      * Creates an array of StackTraceElement objects from the data held
    457      * in "stackState".
    458      */
    459     private static native StackTraceElement[] nativeGetStackTrace(Object stackState);
    460 }
    461