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