X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=libjava%2Fjava%2Flang%2FThrowable.java;h=ad5157823f5b53f488e133298a5b037426c6787e;hb=f37a936b5693300c762ce969380003e74b5911fd;hp=4aa21399c5ab445b259e7bad21aacb1824ba0803;hpb=86a42a5f2443414d63765a4bc84df0530bc32557;p=gcc.git diff --git a/libjava/java/lang/Throwable.java b/libjava/java/lang/Throwable.java index 4aa21399c5a..ad5157823f5 100644 --- a/libjava/java/lang/Throwable.java +++ b/libjava/java/lang/Throwable.java @@ -1,171 +1,567 @@ -// Throwable.java - Superclass for all exceptions. +/* java.lang.Throwable -- Root class for all Exceptions and Errors + Copyright (C) 1998, 1999, 2002, 2004, 2005 Free Software Foundation, Inc. -/* Copyright (C) 1998, 1999 Free Software Foundation +This file is part of GNU Classpath. - This file is part of libgcj. +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ package java.lang; + import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; -import java.io.OutputStreamWriter; -import java.io.OutputStream; -import java.io.FilterOutputStream; -import java.io.IOException; /** - * @author Tom Tromey - * @date October 30, 1998 + * Throwable is the superclass of all exceptions that can be raised. + * + *

There are two special cases: {@link Error} and {@link RuntimeException}: + * these two classes (and their subclasses) are considered unchecked + * exceptions, and are either frequent enough or catastrophic enough that you + * do not need to declare them in throws clauses. Everything + * else is a checked exception, and is ususally a subclass of + * {@link Exception}; these exceptions have to be handled or declared. + * + *

Instances of this class are usually created with knowledge of the + * execution context, so that you can get a stack trace of the problem spot + * in the code. Also, since JDK 1.4, Throwables participate in "exception + * chaining." This means that one exception can be caused by another, and + * preserve the information of the original. + * + *

One reason this is useful is to wrap exceptions to conform to an + * interface. For example, it would be bad design to require all levels + * of a program interface to be aware of the low-level exceptions thrown + * at one level of abstraction. Another example is wrapping a checked + * exception in an unchecked one, to communicate that failure occured + * while still obeying the method throws clause of a superclass. + * + *

A cause is assigned in one of two ways; but can only be assigned once + * in the lifetime of the Throwable. There are new constructors added to + * several classes in the exception hierarchy that directly initialize the + * cause, or you can use the initCause method. This second + * method is especially useful if the superclass has not been retrofitted + * with new constructors:
+ *

+ * try
+ *   {
+ *     lowLevelOp();
+ *   }
+ * catch (LowLevelException lle)
+ *   {
+ *     throw (HighLevelException) new HighLevelException().initCause(lle);
+ *   }
+ * 
+ * Notice the cast in the above example; without it, your method would need + * a throws clase that declared Throwable, defeating the purpose of chainig + * your exceptions. + * + *

By convention, exception classes have two constructors: one with no + * arguments, and one that takes a String for a detail message. Further, + * classes which are likely to be used in an exception chain also provide + * a constructor that takes a Throwable, with or without a detail message + * string. + * + *

Another 1.4 feature is the StackTrace, a means of reflection that + * allows the program to inspect the context of the exception, and which is + * serialized, so that remote procedure calls can correctly pass exceptions. + * + * @author Brian Jones + * @author John Keiser + * @author Mark Wielaard + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 */ +public class Throwable implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3042686055658047285L; -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 - * "The Java Language Specification", ISBN 0-201-63451-1 - * Status: Sufficient for compiled code, but methods applicable to - * bytecode not implemented. JDK 1.1. - */ + /** + * The detail message. + * + * @serial specific details about the exception, may be null + */ + private final String detailMessage; -/* A CPlusPlusDemangler sits on top of a PrintWriter. All input is - * passed through the "c++filt" program (part of GNU binutils) which - * demangles internal symbols to their C++ source form. - * - * Closing a CPlusPlusDemangler doesn't close the underlying - * PrintWriter; it does, however close underlying process and flush - * all its buffers, so it's possible to guarantee that after a - * CPlusPlusDemangler has been closed no more will ever be written to - * the underlying PrintWriter. - * - * FIXME: This implictly converts data from the input stream, which is - * a stream of characters, to a stream of bytes. We need a way of - * handling Unicode characters in demangled identifiers. */ + /** + * The cause of the throwable, including null for an unknown or non-chained + * cause. This may only be set once; so the field is set to + * this until initialized. + * + * @serial the cause, or null if unknown, or this if not yet set + * @since 1.4 + */ + private Throwable cause = this; -class CPlusPlusDemangler extends OutputStream -{ - java.io.OutputStream procOut; - java.io.InputStream procIn; - java.lang.Process proc; - PrintWriter p; + /** + * The stack trace, in a serialized form. + * + * @serial the elements of the stack trace; this is non-null, and has + * no null entries + * @since 1.4 + */ + private StackTraceElement[] stackTrace; - /* The number of bytes written to the underlying PrintWriter. This - provides a crude but fairly portable way to determine whether or - not the attempt to exec c++filt worked. */ - public int written = 0; + /** + * Instantiate this Throwable with an empty message. The cause remains + * uninitialized. {@link #fillInStackTrace()} will be called to set + * up the stack trace. + */ + public Throwable() + { + this((String) null); + } - CPlusPlusDemangler (PrintWriter writer) throws IOException + /** + * Instantiate this Throwable with the given message. The cause remains + * uninitialized. {@link #fillInStackTrace()} will be called to set + * up the stack trace. + * + * @param message the message to associate with the Throwable + */ + public Throwable(String message) { - p = writer; - proc = Runtime.getRuntime ().exec ("c++filt"); - procOut = proc.getOutputStream (); - procIn = proc.getInputStream (); + fillInStackTrace(); + detailMessage = message; } - public void write (int b) throws IOException + /** + * Instantiate this Throwable with the given message and cause. Note that + * the message is unrelated to the message of the cause. + * {@link #fillInStackTrace()} will be called to set up the stack trace. + * + * @param message the message to associate with the Throwable + * @param cause the cause, may be null + * @since 1.4 + */ + public Throwable(String message, Throwable cause) { - procOut.write (b); - while (procIn.available () != 0) - { - int c = procIn.read (); - if (c == -1) - break; - else - { - p.write (c); - written++; - } - } + this(message); + initCause(cause); } - - public void close () throws IOException + + /** + * Instantiate this Throwable with the given cause. The message is then + * built as cause == null ? null : cause.toString(). + * {@link #fillInStackTrace()} will be called to set up the stack trace. + * + * @param cause the cause, may be null + * @since 1.4 + */ + public Throwable(Throwable cause) { - procOut.close (); - int c; - while ((c = procIn.read ()) != -1) - { - p.write (c); - written++; - } - p.flush (); - try - { - proc.waitFor (); - } - catch (InterruptedException _) - { - } - } -} + this(cause == null ? null : cause.toString(), cause); + } -public class Throwable implements Serializable -{ - public native Throwable fillInStackTrace (); + /** + * Get the message associated with this Throwable. + * + * @return the error message associated with this Throwable, may be null + */ + public String getMessage() + { + return detailMessage; + } - public String getLocalizedMessage () + /** + * Get a localized version of this Throwable's error message. + * This method must be overridden in a subclass of Throwable + * to actually produce locale-specific methods. The Throwable + * implementation just returns getMessage(). + * + * @return a localized version of this error message + * @see #getMessage() + * @since 1.1 + */ + public String getLocalizedMessage() { - return getMessage (); + return getMessage(); } - public String getMessage () + /** + * Returns the cause of this exception, or null if the cause is not known + * or non-existant. This cause is initialized by the new constructors, + * or by calling initCause. + * + * @return the cause of this Throwable + * @since 1.4 + */ + public Throwable getCause() { - return detailMessage; + return cause == this ? null : cause; + } + + /** + * Initialize the cause of this Throwable. This may only be called once + * during the object lifetime, including implicitly by chaining + * constructors. + * + * @param cause the cause of this Throwable, may be null + * @return this + * @throws IllegalArgumentException if cause is this (a Throwable can't be + * its own cause!) + * @throws IllegalStateException if the cause has already been set + * @since 1.4 + */ + public Throwable initCause(Throwable cause) + { + if (cause == this) + throw new IllegalArgumentException(); + if (this.cause != this) + throw new IllegalStateException(); + this.cause = cause; + return this; + } + + /** + * Get a human-readable representation of this Throwable. The detail message + * is retrieved by getLocalizedMessage(). Then, with a null detail + * message, this string is simply the object's class name; otherwise + * the string is getClass().getName() + ": " + message. + * + * @return a human-readable String represting this Throwable + */ + public String toString() + { + String msg = getLocalizedMessage(); + return getClass().getName() + (msg == null ? "" : ": " + msg); + } + + /** + * Print a stack trace to the standard error stream. This stream is the + * current contents of System.err. The first line of output + * is the result of {@link #toString()}, and the remaining lines represent + * the data created by {@link #fillInStackTrace()}. While the format is + * unspecified, this implementation uses the suggested format, demonstrated + * by this example:
+ *

+   * public class Junk
+   * {
+   *   public static void main(String args[])
+   *   {
+   *     try
+   *       {
+   *         a();
+   *       }
+   *     catch(HighLevelException e)
+   *       {
+   *         e.printStackTrace();
+   *       }
+   *   }
+   *   static void a() throws HighLevelException
+   *   {
+   *     try
+   *       {
+   *         b();
+   *       }
+   *     catch(MidLevelException e)
+   *       {
+   *         throw new HighLevelException(e);
+   *       }
+   *   }
+   *   static void b() throws MidLevelException
+   *   {
+   *     c();
+   *   }
+   *   static void c() throws MidLevelException
+   *   {
+   *     try
+   *       {
+   *         d();
+   *       }
+   *     catch(LowLevelException e)
+   *       {
+   *         throw new MidLevelException(e);
+   *       }
+   *   }
+   *   static void d() throws LowLevelException
+   *   {
+   *     e();
+   *   }
+   *   static void e() throws LowLevelException
+   *   {
+   *     throw new LowLevelException();
+   *   }
+   * }
+   * class HighLevelException extends Exception
+   * {
+   *   HighLevelException(Throwable cause) { super(cause); }
+   * }
+   * class MidLevelException extends Exception
+   * {
+   *   MidLevelException(Throwable cause)  { super(cause); }
+   * }
+   * class LowLevelException extends Exception
+   * {
+   * }
+   * 
+ *

+ *

+   *  HighLevelException: MidLevelException: LowLevelException
+   *          at Junk.a(Junk.java:13)
+   *          at Junk.main(Junk.java:4)
+   *  Caused by: MidLevelException: LowLevelException
+   *          at Junk.c(Junk.java:23)
+   *          at Junk.b(Junk.java:17)
+   *          at Junk.a(Junk.java:11)
+   *          ... 1 more
+   *  Caused by: LowLevelException
+   *          at Junk.e(Junk.java:30)
+   *          at Junk.d(Junk.java:27)
+   *          at Junk.c(Junk.java:21)
+   *          ... 3 more
+   * 
+ */ + public void printStackTrace() + { + printStackTrace(System.err); } - public void printStackTrace () + /** + * Print a stack trace to the specified PrintStream. See + * {@link #printStackTrace()} for the sample format. + * + * @param s the PrintStream to write the trace to + */ + public void printStackTrace(PrintStream s) { - printStackTrace (System.err); + s.print(stackTraceString()); } - public void printStackTrace (PrintStream ps) + /** + * Prints the exception, the detailed message and the stack trace + * associated with this Throwable to the given PrintWriter. + * The actual output written is implemention specific. Use the result of + * getStackTrace() when more precise information is needed. + * + *

This implementation first prints a line with the result of this + * object's toString() method. + *
+ * Then for all elements given by getStackTrace it prints + * a line containing three spaces, the string "at " and the result of calling + * the toString() method on the StackTraceElement + * object. If getStackTrace() returns an empty array it prints + * a line containing three spaces and the string + * "<<No stacktrace available>>". + *
+ * Then if getCause() doesn't return null it adds a line + * starting with "Caused by: " and the result of calling + * toString() on the cause. + *
+ * Then for every cause (of a cause, etc) the stacktrace is printed the + * same as for the top level Throwable except that as soon + * as all the remaining stack frames of the cause are the same as the + * the last stack frames of the throwable that the cause is wrapped in + * then a line starting with three spaces and the string "... X more" is + * printed, where X is the number of remaining stackframes. + * + * @param pw the PrintWriter to write the trace to + * @since 1.1 + */ + public void printStackTrace (PrintWriter pw) { - PrintWriter writer = new PrintWriter (ps); - printStackTrace (writer); + pw.print(stackTraceString()); + } + + /* + * We use inner class to avoid a static initializer in this basic class. + */ + private static class StaticData + { + static final String nl; + + static + { + // Access package private properties field to prevent Security check. + nl = System.properties.getProperty("line.separator"); + } + } + + // Create whole stack trace in a stringbuffer so we don't have to print + // it line by line. This prevents printing multiple stack traces from + // different threads to get mixed up when written to the same PrintWriter. + private String stackTraceString() + { + StringBuffer sb = new StringBuffer(); + + // Main stacktrace + StackTraceElement[] stack = getStackTrace(); + stackTraceStringBuffer(sb, this.toString(), stack, 0); + + // The cause(s) + Throwable cause = getCause(); + while (cause != null) + { + // Cause start first line + sb.append("Caused by: "); + + // Cause stacktrace + StackTraceElement[] parentStack = stack; + stack = cause.getStackTrace(); + if (parentStack == null || parentStack.length == 0) + stackTraceStringBuffer(sb, cause.toString(), stack, 0); + else + { + int equal = 0; // Count how many of the last stack frames are equal + int frame = stack.length-1; + int parentFrame = parentStack.length-1; + while (frame > 0 && parentFrame > 0) + { + if (stack[frame].equals(parentStack[parentFrame])) + { + equal++; + frame--; + parentFrame--; + } + else + break; + } + stackTraceStringBuffer(sb, cause.toString(), stack, equal); + } + cause = cause.getCause(); + } + + return sb.toString(); } - public void printStackTrace (PrintWriter wr) + // Adds to the given StringBuffer a line containing the name and + // all stacktrace elements minus the last equal ones. + private static void stackTraceStringBuffer(StringBuffer sb, String name, + StackTraceElement[] stack, int equal) { - try + String nl = StaticData.nl; + // (finish) first line + sb.append(name); + sb.append(nl); + + // The stacktrace + if (stack == null || stack.length == 0) { - CPlusPlusDemangler cPlusPlusFilter = new CPlusPlusDemangler (wr); - PrintWriter writer = new PrintWriter (cPlusPlusFilter); - printRawStackTrace (writer); - writer.close (); - if (cPlusPlusFilter.written == 0) // The demangler has failed... - printRawStackTrace (wr); + sb.append(" <>"); + sb.append(nl); } - catch (Exception e1) + else { - printRawStackTrace (wr); + for (int i = 0; i < stack.length-equal; i++) + { + sb.append(" at "); + sb.append(stack[i] == null ? "<>" : stack[i].toString()); + sb.append(nl); + } + if (equal > 0) + { + sb.append(" ..."); + sb.append(equal); + sb.append(" more"); + sb.append(nl); + } } } - public Throwable () + /** + * Fill in the stack trace with the current execution stack. + * + * @return this same throwable + * @see #printStackTrace() + */ + public Throwable fillInStackTrace() { - detailMessage = null; - fillInStackTrace (); + vmState = VMThrowable.fillInStackTrace(this); + stackTrace = null; // Should be regenerated when used. + + return this; } - public Throwable (String message) + /** + * Provides access to the information printed in {@link #printStackTrace()}. + * The array is non-null, with no null entries, although the virtual + * machine is allowed to skip stack frames. If the array is not 0-length, + * then slot 0 holds the information on the stack frame where the Throwable + * was created (or at least where fillInStackTrace() was + * called). + * + * @return an array of stack trace information, as available from the VM + * @since 1.4 + */ + public StackTraceElement[] getStackTrace() { - detailMessage = message; - fillInStackTrace (); + if (stackTrace == null) + if (vmState == null) + stackTrace = new StackTraceElement[0]; + else + { + stackTrace = vmState.getStackTrace(this); + vmState = null; // No longer needed + } + + return stackTrace; } - public String toString () + /** + * Change the stack trace manually. This method is designed for remote + * procedure calls, which intend to alter the stack trace before or after + * serialization according to the context of the remote call. + *

+ * The contents of the given stacktrace is copied so changes to the + * original array do not change the stack trace elements of this + * throwable. + * + * @param stackTrace the new trace to use + * @throws NullPointerException if stackTrace is null or has null elements + * @since 1.4 + */ + public void setStackTrace(StackTraceElement[] stackTrace) { - return ((detailMessage == null) - ? getClass().getName() - : getClass().getName() + ": " + getMessage ()); - } + int i = stackTrace.length; + StackTraceElement[] st = new StackTraceElement[i]; - private native final void printRawStackTrace (PrintWriter wr); - - // Name of this field comes from serialization spec. - private String detailMessage; + while (--i >= 0) + { + st[i] = stackTrace[i]; + if (st[i] == null) + throw new NullPointerException("Element " + i + " null"); + } - // Setting this flag to false prevents fillInStackTrace() from running. - static boolean trace_enabled = true; - private transient byte stackTrace[]; - private static final long serialVersionUID = -3042686055658047285L; + this.stackTrace = st; + } + + /** + * VM state when fillInStackTrace was called. + * Used by getStackTrace() to get an array of StackTraceElements. + * Cleared when no longer needed. + */ + private transient VMThrowable vmState; }