Path: blob/master/jcl/src/java.base/share/classes/java/lang/Throwable.java
12513 views
/*[INCLUDE-IF Sidecar18-SE]*/1/*******************************************************************************2* Copyright (c) 1998, 2021 IBM Corp. and others3*4* This program and the accompanying materials are made available under5* the terms of the Eclipse Public License 2.0 which accompanies this6* distribution and is available at https://www.eclipse.org/legal/epl-2.0/7* or the Apache License, Version 2.0 which accompanies this distribution and8* is available at https://www.apache.org/licenses/LICENSE-2.0.9*10* This Source Code may also be made available under the following11* Secondary Licenses when the conditions for such availability set12* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU13* General Public License, version 2 with the GNU Classpath14* Exception [1] and GNU General Public License, version 2 with the15* OpenJDK Assembly Exception [2].16*17* [1] https://www.gnu.org/software/classpath/license.html18* [2] http://openjdk.java.net/legal/assembly-exception.html19*20* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception21*******************************************************************************/22package java.lang;2324import java.io.*;25import java.util.ArrayList;26import java.util.Collections;27import java.util.List;28import java.util.IdentityHashMap;29import java.util.Set;3031import com.ibm.oti.util.Msg;32import com.ibm.oti.util.Util;33import static com.ibm.oti.util.Util.appendTo;34import static com.ibm.oti.util.Util.appendLnTo;3536/**37* This class is the superclass of all classes which38* can be thrown by the virtual machine. The two direct39* subclasses represent recoverable exceptions (Exception)40* and unrecoverable errors (Error). This class provides41* common methods for accessing a string message which42* provides extra information about the circumstances in43* which the throwable was created, and for filling in a44* walkback (i.e. a record of the call stack at a45* particular point in time) which can be printed later.46*47* @author OTI48* @version initial49*50* @see Error51* @see Exception52* @see RuntimeException53*/54public class Throwable implements java.io.Serializable {55private static final long serialVersionUID = -3042686055658047285L;5657/**58* The message provided when the exception was created.59*/60private String detailMessage;6162/**63* An object which describes the walkback. This field is stored64* by the fillInStackTrace() native, and used by the65* J9VMInternals.getStackTrace() native.66*/67private transient Object walkback;6869/**70* The cause of this Throwable. Null when there is71* no cause.72*/73private Throwable cause = this;74private StackTraceElement[] stackTrace;7576private static final Throwable[] ZeroElementArray = new Throwable[0];77private static final StackTraceElement[] ZeroStackTraceElementArray = new StackTraceElement[0];78/**79* The list containing the exceptions suppressed80*/81private List<Throwable> suppressedExceptions = Collections.EMPTY_LIST;82private transient boolean disableWritableStackTrace;8384/**85* Constructs a new instance of this class with its86* walkback filled in.87*/88public Throwable () {89super ();90fillInStackTrace();91}9293/**94* Constructs a new instance of this class with its95* walkback and message filled in.96*97* @param detailMessage String98* The detail message for the exception.99*/100public Throwable (String detailMessage) {101this ();102this.detailMessage = detailMessage;103}104105/**106* Constructs a new instance of this class with its107* walkback, message and cause filled in.108*109* @param detailMessage String110* The detail message for the exception.111* @param throwable The cause of this Throwable112*/113public Throwable (String detailMessage, Throwable throwable) {114this ();115this.detailMessage = detailMessage;116cause = throwable;117}118119/**120* Constructs a new instance of this class with its121* walkback and cause filled in.122*123* @param throwable The cause of this Throwable124*/125public Throwable (Throwable throwable) {126this ();127this.detailMessage = throwable==null ? null : throwable.toString();128cause = throwable;129}130131/**132* Constructs a new instance of this class with its walkback, message133* and cause filled in.134* enableSuppression and enableWritableStackTrace are true by default135* in other constructors136* If enableSuppression is false, suppression is disabled, getSuppressed()137* returns a zero-length array and calls to addSuppressed(Throwable) have138* no effect.139* If enableWritableStackTrace is false, fillInStackTrace() will not be140* called within this constructor, stackTrace field will be set to null,141* subsequent calls to fillInStackTrace() and setStackTrace(StackTraceElement[])142* will not set the stack trace, and getStackTrace() will return a zero143* length array.144*145* @param detailMessage String146* The detail message for the exception.147* @param throwable The cause of this Throwable148* @param enableSuppression boolean149* enable or disable suppression150* @param enableWritableStackTrace boolean151* whether the stack trace is writable152*153* @since 1.7154*/155protected Throwable(String detailMessage, Throwable throwable,156boolean enableSuppression, boolean enableWritableStackTrace) {157super ();158159this.detailMessage = detailMessage;160cause = throwable;161162if (enableSuppression == false) {163suppressedExceptions = null;164}165166if (enableWritableStackTrace == false) {167this.disableWritableStackTrace = true;168} else {169fillInStackTrace();170}171}172173/**174* Record in the receiver a walkback from the point175* where this message was sent. The message is176* public so that code which catches a throwable and177* then <em>re-throws</em> it can adjust the walkback178* to represent the location where the exception was179* re-thrown.180*181* @return the receiver182*/183public native Throwable fillInStackTrace();184185/**186* Answers the extra information message which was provided187* when the throwable was created. If no message was provided188* at creation time, then answer null.189*190* @return String191* The receiver's message.192*/193public String getMessage() {194return detailMessage;195}196197/*[PR 1FDRSWI] : J9JCL:ALL - Plum Hall failures. (Added getLocalizedMessage)*/198/**199* Answers the extra information message which was provided200* when the throwable was created. If no message was provided201* at creation time, then answer null. Subclasses may override202* this method to answer localized text for the message.203*204* @return String205* The receiver's message.206*/207public String getLocalizedMessage() {208return getMessage();209}210211/**212* Answers an array of StackTraceElement. Each StackTraceElement represents213* a entry on the stack.214*215* @return an array of StackTraceElement representing the stack216*/217public StackTraceElement[] getStackTrace() {218return (StackTraceElement[])getInternalStackTrace().clone();219}220221/**222* Sets the array of StackTraceElements. Each StackTraceElement represents223* a entry on the stack. A copy of this array will be returned by getStackTrace()224* and printed by printStackTrace().225*226* @param trace The array of StackTraceElement227*/228public void setStackTrace(StackTraceElement[] trace) {229/*[PR 95395]*/230if (trace == null) {231throw new NullPointerException();232}233StackTraceElement[] localCopy = trace.clone();234for (int i=0; i<localCopy.length; i++) {235if (localCopy[i] == null) {236throw new NullPointerException();237}238}239240if (disableWritableStackTrace) {241return;242}243244stackTrace = localCopy;245}246247/**248* Outputs a printable representation of the receiver's249* walkback on the System.err stream.250*/251public void printStackTrace () {252printStackTrace(System.err);253}254255/**256* Count the number of duplicate stack frames, starting from257* the end of the stack.258*259* @param currentStack a stack to compare260* @param parentStack a stack to compare261*262* @return the number of duplicate stack frames.263*/264private static int countDuplicates(StackTraceElement[] currentStack, StackTraceElement[] parentStack) {265int duplicates = 0;266int parentIndex = parentStack.length;267for (int i=currentStack.length; --i >= 0 && --parentIndex >= 0;) {268StackTraceElement parentFrame = parentStack[parentIndex];269if (parentFrame.equals(currentStack[i])) {270duplicates++;271} else {272break;273}274}275return duplicates;276}277278/**279* Answers an array of StackTraceElement. Each StackTraceElement represents280* a entry on the stack. Cache the stack trace in the stackTrace field, returning281* the cached field when it has already been initialized.282*283* @return an array of StackTraceElement representing the stack284*/285StackTraceElement[] getInternalStackTrace() {286if (disableWritableStackTrace) {287return ZeroStackTraceElementArray;288}289290StackTraceElement[] localStackTrace = stackTrace;291if (localStackTrace == null) {292// Assign the result to a local variable to avoid refetching293// the instance variable and any memory ordering issues294localStackTrace = J9VMInternals.getStackTrace(this, true);295stackTrace = localStackTrace;296}297298return localStackTrace;299}300301/**302* Outputs a printable representation of the receiver's303* walkback on the stream specified by the argument.304*305* @param err PrintStream306* The stream to write the walkback on.307*/308public void printStackTrace (PrintStream err) {309printStackTraceHelper(err);310}311312/**313* Outputs a printable representation of the receiver's314* walkback on the writer specified by the argument.315*316* @param err PrintWriter317* The writer to write the walkback on.318*/319public void printStackTrace(PrintWriter err) {320printStackTraceHelper(err);321}322323/**324* Outputs representation of the receiver's325* walkback on the Appendable specified by the argument.326*327* @param appendable Appendable328* The Appendable object to the walkback will be written.329*/330private void printStackTraceHelper(Appendable appendable) {331StackTraceElement[] stack;332Set<Throwable> exceptionChainSet = null;333try {334exceptionChainSet = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());335} catch(OutOfMemoryError e) {336/* If OOM is thrown when creating exception set, then we won't be able to check for circular exception chain,337* which can cause OOM to be thrown again. This should be ok as we are already running out of heap memory.338*/339}340stack = printStackTrace(appendable, null, 0, false, exceptionChainSet);341342Throwable throwable = getCause();343while (throwable != null && stack != null) {344stack = throwable.printStackTrace(appendable, stack, 0, false, exceptionChainSet);345throwable = throwable.getCause();346}347}348349/**350* Answers a string containing a concise, human-readable351* description of the receiver.352*353* @return String354* a printable representation for the receiver.355*/356@Override357public String toString () {358/*[PR 102230] Should call getLocalizedMessage() */359String msg = getLocalizedMessage();360String name = getClass().getName();361if (msg == null) {362return name;363} else {364int length = name.length() + 2 + msg.length();365StringBuilder buffer = new StringBuilder(length);366return buffer.append(name).append(": ").append(msg).toString(); //$NON-NLS-1$367}368}369370/**371* Initialize the cause of the receiver. The cause cannot be372* reassigned.373*374* @param throwable The cause of this Throwable375*376* @exception IllegalArgumentException when the cause is the receiver377* @exception IllegalStateException when the cause has already been initialized378*379* @return the receiver.380*/381public synchronized Throwable initCause(Throwable throwable) {382if (cause != this) {383/*[MSG "K05c9", "Cause already initialized"]*/384throw new IllegalStateException(Msg.getString("K05c9")); //$NON-NLS-1$385}386if (throwable == this) {387/*[MSG "K05c8", "Cause cannot be the receiver"]*/388throw new IllegalArgumentException(Msg.getString("K05c8")); //$NON-NLS-1$389}390return setCause(throwable);391}392393/**394* Helper method to set Throwable cause without going through public method initCause.395* There is no need for synchronization for this helper method cause the only caller Throwable396* object is instantiated within J9VMInternals.copyThrowable and not exposed to others.397* Synchronization need to be considered if this assumption is NOT true.398*399* @param throwable The cause of this Throwable400*401* @exception IllegalArgumentException when the cause is the receiver402* @exception IllegalStateException when the cause has already been initialized403*404* @return the receiver.405*/406Throwable setCause(Throwable throwable) {407cause = throwable;408return this;409}410411/**412* Answers the cause of this Throwable, or null if there413* is no cause.414*415* @return Throwable416* The receiver's cause.417*/418public Throwable getCause() {419if (cause == this) return null;420return cause;421}422423private void writeObject(ObjectOutputStream s) throws IOException {424// ensure the stackTrace field is initialized425getInternalStackTrace();426s.defaultWriteObject();427}428429private void readObject(ObjectInputStream s)430throws IOException, ClassNotFoundException {431s.defaultReadObject();432433disableWritableStackTrace = (stackTrace == null);434435if (stackTrace != null) {436if (stackTrace.length == 1) {437if (stackTrace[0] == null) {438/*[MSG "K0560", "Null stack trace element not permitted in serial stream"]*/439throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0560")); //$NON-NLS-1$440}441if (stackTrace[0].equals(new StackTraceElement("", "", null, Integer.MIN_VALUE))) { //$NON-NLS-1$ //$NON-NLS-2$442stackTrace = null;443}444} else {445for (int i=0; i<stackTrace.length; i++) {446if (stackTrace[i] == null) {447/*[MSG "K0560", "Null stack trace element not permitted in serial stream"]*/448throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0560")); //$NON-NLS-1$449}450}451}452}453454455if (suppressedExceptions != null) {456List<Throwable> newList = Collections.EMPTY_LIST;457try {458/*[IF Sidecar19-SE]*/459Module classModule = suppressedExceptions.getClass().getModule();460if (Object.class.getModule().equals(classModule)) {461/*[ELSE]*/462ClassLoader listClassLoader = suppressedExceptions.getClass().getClassLoader();463/* null ClassLoader from getClassLoader() call represents the bootstrap ClassLoader */464if (listClassLoader == null) {465/*[ENDIF]*/466int listSize = suppressedExceptions.size();467if (listSize != 0) {468newList = new ArrayList<Throwable>(listSize);469for (Throwable t : suppressedExceptions) {470if (t == null) {471/*[MSG "K0561", "Null entries not permitted in suppressedExceptions serial stream"]*/472throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0561")); //$NON-NLS-1$473} else if (t == this) {474/*[MSG "K0562", "Self-pointers not permitted in suppressedExceptions serial stream"]*/475throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0562")); //$NON-NLS-1$476} else {477newList.add(t);478}479}480}481} else {482/*[MSG "K0C00", "Non-standard List class not permitted in suppressedExceptions serial stream"]*/483throw new java.io.StreamCorruptedException(com.ibm.oti.util.Msg.getString("K0C00")); //$NON-NLS-1$484}485} finally {486suppressedExceptions = newList;487}488}489}490491/**492* Print stack trace.493*494* The stack trace is constructed with appendTo() and avoids allocating Objects (i.e. Strings)495* to try to continue printing as much as possible of the stack trace even in the presence of496* OutOfMemoryErrors (CMVC 97756).497*498* @param err499* the specified print stream/writer500* @param parentStack501* parent stack elements502* @param indents503* number of indents (\t) to be printed504* @param suppressed505* if this is an exception suppressed506* @param exceptionChainSet507* set of exceptions in the exception chain508*509* @return an array of stack trace elements printed510*511*/512private StackTraceElement[] printStackTrace(513Appendable err, StackTraceElement[] parentStack, int indents, boolean suppressed, Set<Throwable> exceptionChainSet) {514/*[PR 120593] CDC 1.0 and CDC 1.1 TCK fails in java.lang.. Exception tests */515if (err == null) throw new NullPointerException();516StackTraceElement[] stack;517boolean outOfMemory = this instanceof OutOfMemoryError;518if ((exceptionChainSet != null) && (exceptionChainSet.contains(this))) {519if (!outOfMemory) {520try {521appendTo(err, "\t[CIRCULAR REFERENCE:" + toString() + "]", 0); //$NON-NLS-1$522} catch(OutOfMemoryError e) {523outOfMemory = true;524}525}526if (outOfMemory) {527appendTo(err, "\t[CIRCULAR REFERENCE:"); //$NON-NLS-1$528try {529appendTo(err, getClass().getName());530} catch(OutOfMemoryError e) {531appendTo(err, "java.lang.OutOfMemoryError(?)");532}533appendTo(err, "]");534}535appendLnTo(err);536return null;537}538try {539exceptionChainSet.add(this);540} catch(OutOfMemoryError e) {541/* If OOM is thrown when adding Throwable to exception set, then we may not be able to identify circular exception chain,542* which can cause OOM to be thrown again. This should be ok as we are already running out of heap memory.543*/544}545if (parentStack != null) {546if (suppressed) {547appendTo(err, "Suppressed: ", indents); //$NON-NLS-1$548} else {549appendTo(err, "Caused by: ", indents); //$NON-NLS-1$550}551}552if (!outOfMemory) {553try {554appendTo(err, toString());555} catch(OutOfMemoryError e) {556outOfMemory = true;557}558}559if (outOfMemory) {560try {561appendTo(err, getClass().getName());562} catch(OutOfMemoryError e) {563outOfMemory = true;564appendTo(err, "java.lang.OutOfMemoryError(?)"); //$NON-NLS-1$565}566try {567String message = getLocalizedMessage();568if (message != null) {569appendTo(err, ": "); //$NON-NLS-1$570appendTo(err, message);571}572} catch(OutOfMemoryError e) {573outOfMemory = true;574}575}576appendLnTo(err);577int duplicates = 0;578try {579// Don't use getStackTrace() as it calls clone()580// Get stackTrace, in case stackTrace is reassigned581stack = getInternalStackTrace();582/*[PR CMVC 90361] look for duplicate entries in parent stack traces */583if (parentStack != null) {584duplicates = countDuplicates(stack, parentStack);585}586} catch(OutOfMemoryError e) {587appendTo(err, "\tat ?", indents); //$NON-NLS-1$588appendLnTo(err);589return null;590}591for (int i=0; i < stack.length - duplicates; i++) {592StackTraceElement element = stack[i];593if (!outOfMemory) {594try {595appendTo(err, "\tat " + element, indents); //$NON-NLS-1$596} catch(OutOfMemoryError e) {597outOfMemory = true;598}599}600if (outOfMemory) {601appendTo(err, "\tat ", indents); //$NON-NLS-1$602Util.printStackTraceElement(element, null, err, false);603}604appendLnTo(err);605}606if (duplicates > 0) {607if (!outOfMemory) {608try {609appendTo(err, "\t... " + duplicates + " more", indents); //$NON-NLS-1$ //$NON-NLS-2$610} catch(OutOfMemoryError e) {611outOfMemory = true;612}613}614if (outOfMemory) {615appendTo(err, "\t... ", indents); //$NON-NLS-1$616appendTo(err, duplicates);617appendTo(err, " more"); //$NON-NLS-1$618}619appendLnTo(err);620}621622synchronized (this) {623if (suppressedExceptions != null) {624for (Throwable t : suppressedExceptions) {625StackTraceElement[] stackSuppressed;626stackSuppressed = t.printStackTrace(err, stack, indents + 1, true, exceptionChainSet);627628Throwable throwableSuppressed = t.getCause();629while (throwableSuppressed != null && stackSuppressed != null) {630stackSuppressed = throwableSuppressed.printStackTrace(err, stackSuppressed, indents + 1, false, exceptionChainSet);631throwableSuppressed = throwableSuppressed.getCause();632}633}634}635}636return stack;637}638639/**640* The specified exception is going to be suppressed in order to give priority641* to this exception (primary exception) and to be appended to the list of642* suppressed exceptions.643*644* This method is typically called by the automatically generated code from the645* try-with-resources statement.646*647* @param exception Throwable648* an exception to be suppressed and added to649* the list of suppressed exceptions650*651* @throws IllegalArgumentException652* if exception is this throwable, can't suppress itself653* @throws NullPointerException654* if exception is null and there is an exception suppressed before655*656* @since 1.7657*658*/659public final void addSuppressed(Throwable exception) {660/*[PR CMVC 181567] Java7:JCK:java_lang/Throwable/SuppressedTests fails */661if (exception == null) {662/*[MSG "K0563", "Null not permitted when an exception has already been suppressed"]*/663throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0563")); //$NON-NLS-1$664}665666if (exception == this) {667/*[MSG "K0559", "A throwable cannot suppress itself"]*/668throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0559")); //$NON-NLS-1$669}670synchronized (this) {671if (suppressedExceptions != null) {672if (suppressedExceptions.size() == 0) {673suppressedExceptions = new ArrayList<Throwable>(2);674}675suppressedExceptions.add(exception);676}677}678}679680/**681* Returns an array of exceptions suppressed, typically by the automatically682* generated code from the try-with-resources statement, in order to give683* priority to this exception (primary exception).684*685* @return an array of exceptions representing all exceptions suppressed to686* give priority to this exception (primary exception)687*688* @since 1.7689*690*/691public final Throwable[] getSuppressed() {692synchronized (this) {693if (suppressedExceptions == null || suppressedExceptions.size() == 0) {694return ZeroElementArray;695} else {696return suppressedExceptions.toArray(new Throwable[suppressedExceptions.size()]);697}698}699}700}701702703