Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/jcl/src/java.base/share/classes/java/lang/Throwable.java
12513 views
1
/*[INCLUDE-IF Sidecar18-SE]*/
2
/*******************************************************************************
3
* Copyright (c) 1998, 2021 IBM Corp. and others
4
*
5
* This program and the accompanying materials are made available under
6
* the terms of the Eclipse Public License 2.0 which accompanies this
7
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
8
* or the Apache License, Version 2.0 which accompanies this distribution and
9
* is available at https://www.apache.org/licenses/LICENSE-2.0.
10
*
11
* This Source Code may also be made available under the following
12
* Secondary Licenses when the conditions for such availability set
13
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
14
* General Public License, version 2 with the GNU Classpath
15
* Exception [1] and GNU General Public License, version 2 with the
16
* OpenJDK Assembly Exception [2].
17
*
18
* [1] https://www.gnu.org/software/classpath/license.html
19
* [2] http://openjdk.java.net/legal/assembly-exception.html
20
*
21
* 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-exception
22
*******************************************************************************/
23
package java.lang;
24
25
import java.io.*;
26
import java.util.ArrayList;
27
import java.util.Collections;
28
import java.util.List;
29
import java.util.IdentityHashMap;
30
import java.util.Set;
31
32
import com.ibm.oti.util.Msg;
33
import com.ibm.oti.util.Util;
34
import static com.ibm.oti.util.Util.appendTo;
35
import static com.ibm.oti.util.Util.appendLnTo;
36
37
/**
38
* This class is the superclass of all classes which
39
* can be thrown by the virtual machine. The two direct
40
* subclasses represent recoverable exceptions (Exception)
41
* and unrecoverable errors (Error). This class provides
42
* common methods for accessing a string message which
43
* provides extra information about the circumstances in
44
* which the throwable was created, and for filling in a
45
* walkback (i.e. a record of the call stack at a
46
* particular point in time) which can be printed later.
47
*
48
* @author OTI
49
* @version initial
50
*
51
* @see Error
52
* @see Exception
53
* @see RuntimeException
54
*/
55
public class Throwable implements java.io.Serializable {
56
private static final long serialVersionUID = -3042686055658047285L;
57
58
/**
59
* The message provided when the exception was created.
60
*/
61
private String detailMessage;
62
63
/**
64
* An object which describes the walkback. This field is stored
65
* by the fillInStackTrace() native, and used by the
66
* J9VMInternals.getStackTrace() native.
67
*/
68
private transient Object walkback;
69
70
/**
71
* The cause of this Throwable. Null when there is
72
* no cause.
73
*/
74
private Throwable cause = this;
75
private StackTraceElement[] stackTrace;
76
77
private static final Throwable[] ZeroElementArray = new Throwable[0];
78
private static final StackTraceElement[] ZeroStackTraceElementArray = new StackTraceElement[0];
79
/**
80
* The list containing the exceptions suppressed
81
*/
82
private List<Throwable> suppressedExceptions = Collections.EMPTY_LIST;
83
private transient boolean disableWritableStackTrace;
84
85
/**
86
* Constructs a new instance of this class with its
87
* walkback filled in.
88
*/
89
public Throwable () {
90
super ();
91
fillInStackTrace();
92
}
93
94
/**
95
* Constructs a new instance of this class with its
96
* walkback and message filled in.
97
*
98
* @param detailMessage String
99
* The detail message for the exception.
100
*/
101
public Throwable (String detailMessage) {
102
this ();
103
this.detailMessage = detailMessage;
104
}
105
106
/**
107
* Constructs a new instance of this class with its
108
* walkback, message and cause filled in.
109
*
110
* @param detailMessage String
111
* The detail message for the exception.
112
* @param throwable The cause of this Throwable
113
*/
114
public Throwable (String detailMessage, Throwable throwable) {
115
this ();
116
this.detailMessage = detailMessage;
117
cause = throwable;
118
}
119
120
/**
121
* Constructs a new instance of this class with its
122
* walkback and cause filled in.
123
*
124
* @param throwable The cause of this Throwable
125
*/
126
public Throwable (Throwable throwable) {
127
this ();
128
this.detailMessage = throwable==null ? null : throwable.toString();
129
cause = throwable;
130
}
131
132
/**
133
* Constructs a new instance of this class with its walkback, message
134
* and cause filled in.
135
* enableSuppression and enableWritableStackTrace are true by default
136
* in other constructors
137
* If enableSuppression is false, suppression is disabled, getSuppressed()
138
* returns a zero-length array and calls to addSuppressed(Throwable) have
139
* no effect.
140
* If enableWritableStackTrace is false, fillInStackTrace() will not be
141
* called within this constructor, stackTrace field will be set to null,
142
* subsequent calls to fillInStackTrace() and setStackTrace(StackTraceElement[])
143
* will not set the stack trace, and getStackTrace() will return a zero
144
* length array.
145
*
146
* @param detailMessage String
147
* The detail message for the exception.
148
* @param throwable The cause of this Throwable
149
* @param enableSuppression boolean
150
* enable or disable suppression
151
* @param enableWritableStackTrace boolean
152
* whether the stack trace is writable
153
*
154
* @since 1.7
155
*/
156
protected Throwable(String detailMessage, Throwable throwable,
157
boolean enableSuppression, boolean enableWritableStackTrace) {
158
super ();
159
160
this.detailMessage = detailMessage;
161
cause = throwable;
162
163
if (enableSuppression == false) {
164
suppressedExceptions = null;
165
}
166
167
if (enableWritableStackTrace == false) {
168
this.disableWritableStackTrace = true;
169
} else {
170
fillInStackTrace();
171
}
172
}
173
174
/**
175
* Record in the receiver a walkback from the point
176
* where this message was sent. The message is
177
* public so that code which catches a throwable and
178
* then <em>re-throws</em> it can adjust the walkback
179
* to represent the location where the exception was
180
* re-thrown.
181
*
182
* @return the receiver
183
*/
184
public native Throwable fillInStackTrace();
185
186
/**
187
* Answers the extra information message which was provided
188
* when the throwable was created. If no message was provided
189
* at creation time, then answer null.
190
*
191
* @return String
192
* The receiver's message.
193
*/
194
public String getMessage() {
195
return detailMessage;
196
}
197
198
/*[PR 1FDRSWI] : J9JCL:ALL - Plum Hall failures. (Added getLocalizedMessage)*/
199
/**
200
* Answers the extra information message which was provided
201
* when the throwable was created. If no message was provided
202
* at creation time, then answer null. Subclasses may override
203
* this method to answer localized text for the message.
204
*
205
* @return String
206
* The receiver's message.
207
*/
208
public String getLocalizedMessage() {
209
return getMessage();
210
}
211
212
/**
213
* Answers an array of StackTraceElement. Each StackTraceElement represents
214
* a entry on the stack.
215
*
216
* @return an array of StackTraceElement representing the stack
217
*/
218
public StackTraceElement[] getStackTrace() {
219
return (StackTraceElement[])getInternalStackTrace().clone();
220
}
221
222
/**
223
* Sets the array of StackTraceElements. Each StackTraceElement represents
224
* a entry on the stack. A copy of this array will be returned by getStackTrace()
225
* and printed by printStackTrace().
226
*
227
* @param trace The array of StackTraceElement
228
*/
229
public void setStackTrace(StackTraceElement[] trace) {
230
/*[PR 95395]*/
231
if (trace == null) {
232
throw new NullPointerException();
233
}
234
StackTraceElement[] localCopy = trace.clone();
235
for (int i=0; i<localCopy.length; i++) {
236
if (localCopy[i] == null) {
237
throw new NullPointerException();
238
}
239
}
240
241
if (disableWritableStackTrace) {
242
return;
243
}
244
245
stackTrace = localCopy;
246
}
247
248
/**
249
* Outputs a printable representation of the receiver's
250
* walkback on the System.err stream.
251
*/
252
public void printStackTrace () {
253
printStackTrace(System.err);
254
}
255
256
/**
257
* Count the number of duplicate stack frames, starting from
258
* the end of the stack.
259
*
260
* @param currentStack a stack to compare
261
* @param parentStack a stack to compare
262
*
263
* @return the number of duplicate stack frames.
264
*/
265
private static int countDuplicates(StackTraceElement[] currentStack, StackTraceElement[] parentStack) {
266
int duplicates = 0;
267
int parentIndex = parentStack.length;
268
for (int i=currentStack.length; --i >= 0 && --parentIndex >= 0;) {
269
StackTraceElement parentFrame = parentStack[parentIndex];
270
if (parentFrame.equals(currentStack[i])) {
271
duplicates++;
272
} else {
273
break;
274
}
275
}
276
return duplicates;
277
}
278
279
/**
280
* Answers an array of StackTraceElement. Each StackTraceElement represents
281
* a entry on the stack. Cache the stack trace in the stackTrace field, returning
282
* the cached field when it has already been initialized.
283
*
284
* @return an array of StackTraceElement representing the stack
285
*/
286
StackTraceElement[] getInternalStackTrace() {
287
if (disableWritableStackTrace) {
288
return ZeroStackTraceElementArray;
289
}
290
291
StackTraceElement[] localStackTrace = stackTrace;
292
if (localStackTrace == null) {
293
// Assign the result to a local variable to avoid refetching
294
// the instance variable and any memory ordering issues
295
localStackTrace = J9VMInternals.getStackTrace(this, true);
296
stackTrace = localStackTrace;
297
}
298
299
return localStackTrace;
300
}
301
302
/**
303
* Outputs a printable representation of the receiver's
304
* walkback on the stream specified by the argument.
305
*
306
* @param err PrintStream
307
* The stream to write the walkback on.
308
*/
309
public void printStackTrace (PrintStream err) {
310
printStackTraceHelper(err);
311
}
312
313
/**
314
* Outputs a printable representation of the receiver's
315
* walkback on the writer specified by the argument.
316
*
317
* @param err PrintWriter
318
* The writer to write the walkback on.
319
*/
320
public void printStackTrace(PrintWriter err) {
321
printStackTraceHelper(err);
322
}
323
324
/**
325
* Outputs representation of the receiver's
326
* walkback on the Appendable specified by the argument.
327
*
328
* @param appendable Appendable
329
* The Appendable object to the walkback will be written.
330
*/
331
private void printStackTraceHelper(Appendable appendable) {
332
StackTraceElement[] stack;
333
Set<Throwable> exceptionChainSet = null;
334
try {
335
exceptionChainSet = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
336
} catch(OutOfMemoryError e) {
337
/* If OOM is thrown when creating exception set, then we won't be able to check for circular exception chain,
338
* which can cause OOM to be thrown again. This should be ok as we are already running out of heap memory.
339
*/
340
}
341
stack = printStackTrace(appendable, null, 0, false, exceptionChainSet);
342
343
Throwable throwable = getCause();
344
while (throwable != null && stack != null) {
345
stack = throwable.printStackTrace(appendable, stack, 0, false, exceptionChainSet);
346
throwable = throwable.getCause();
347
}
348
}
349
350
/**
351
* Answers a string containing a concise, human-readable
352
* description of the receiver.
353
*
354
* @return String
355
* a printable representation for the receiver.
356
*/
357
@Override
358
public String toString () {
359
/*[PR 102230] Should call getLocalizedMessage() */
360
String msg = getLocalizedMessage();
361
String name = getClass().getName();
362
if (msg == null) {
363
return name;
364
} else {
365
int length = name.length() + 2 + msg.length();
366
StringBuilder buffer = new StringBuilder(length);
367
return buffer.append(name).append(": ").append(msg).toString(); //$NON-NLS-1$
368
}
369
}
370
371
/**
372
* Initialize the cause of the receiver. The cause cannot be
373
* reassigned.
374
*
375
* @param throwable The cause of this Throwable
376
*
377
* @exception IllegalArgumentException when the cause is the receiver
378
* @exception IllegalStateException when the cause has already been initialized
379
*
380
* @return the receiver.
381
*/
382
public synchronized Throwable initCause(Throwable throwable) {
383
if (cause != this) {
384
/*[MSG "K05c9", "Cause already initialized"]*/
385
throw new IllegalStateException(Msg.getString("K05c9")); //$NON-NLS-1$
386
}
387
if (throwable == this) {
388
/*[MSG "K05c8", "Cause cannot be the receiver"]*/
389
throw new IllegalArgumentException(Msg.getString("K05c8")); //$NON-NLS-1$
390
}
391
return setCause(throwable);
392
}
393
394
/**
395
* Helper method to set Throwable cause without going through public method initCause.
396
* There is no need for synchronization for this helper method cause the only caller Throwable
397
* object is instantiated within J9VMInternals.copyThrowable and not exposed to others.
398
* Synchronization need to be considered if this assumption is NOT true.
399
*
400
* @param throwable The cause of this Throwable
401
*
402
* @exception IllegalArgumentException when the cause is the receiver
403
* @exception IllegalStateException when the cause has already been initialized
404
*
405
* @return the receiver.
406
*/
407
Throwable setCause(Throwable throwable) {
408
cause = throwable;
409
return this;
410
}
411
412
/**
413
* Answers the cause of this Throwable, or null if there
414
* is no cause.
415
*
416
* @return Throwable
417
* The receiver's cause.
418
*/
419
public Throwable getCause() {
420
if (cause == this) return null;
421
return cause;
422
}
423
424
private void writeObject(ObjectOutputStream s) throws IOException {
425
// ensure the stackTrace field is initialized
426
getInternalStackTrace();
427
s.defaultWriteObject();
428
}
429
430
private void readObject(ObjectInputStream s)
431
throws IOException, ClassNotFoundException {
432
s.defaultReadObject();
433
434
disableWritableStackTrace = (stackTrace == null);
435
436
if (stackTrace != null) {
437
if (stackTrace.length == 1) {
438
if (stackTrace[0] == null) {
439
/*[MSG "K0560", "Null stack trace element not permitted in serial stream"]*/
440
throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0560")); //$NON-NLS-1$
441
}
442
if (stackTrace[0].equals(new StackTraceElement("", "", null, Integer.MIN_VALUE))) { //$NON-NLS-1$ //$NON-NLS-2$
443
stackTrace = null;
444
}
445
} else {
446
for (int i=0; i<stackTrace.length; i++) {
447
if (stackTrace[i] == null) {
448
/*[MSG "K0560", "Null stack trace element not permitted in serial stream"]*/
449
throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0560")); //$NON-NLS-1$
450
}
451
}
452
}
453
}
454
455
456
if (suppressedExceptions != null) {
457
List<Throwable> newList = Collections.EMPTY_LIST;
458
try {
459
/*[IF Sidecar19-SE]*/
460
Module classModule = suppressedExceptions.getClass().getModule();
461
if (Object.class.getModule().equals(classModule)) {
462
/*[ELSE]*/
463
ClassLoader listClassLoader = suppressedExceptions.getClass().getClassLoader();
464
/* null ClassLoader from getClassLoader() call represents the bootstrap ClassLoader */
465
if (listClassLoader == null) {
466
/*[ENDIF]*/
467
int listSize = suppressedExceptions.size();
468
if (listSize != 0) {
469
newList = new ArrayList<Throwable>(listSize);
470
for (Throwable t : suppressedExceptions) {
471
if (t == null) {
472
/*[MSG "K0561", "Null entries not permitted in suppressedExceptions serial stream"]*/
473
throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0561")); //$NON-NLS-1$
474
} else if (t == this) {
475
/*[MSG "K0562", "Self-pointers not permitted in suppressedExceptions serial stream"]*/
476
throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0562")); //$NON-NLS-1$
477
} else {
478
newList.add(t);
479
}
480
}
481
}
482
} else {
483
/*[MSG "K0C00", "Non-standard List class not permitted in suppressedExceptions serial stream"]*/
484
throw new java.io.StreamCorruptedException(com.ibm.oti.util.Msg.getString("K0C00")); //$NON-NLS-1$
485
}
486
} finally {
487
suppressedExceptions = newList;
488
}
489
}
490
}
491
492
/**
493
* Print stack trace.
494
*
495
* The stack trace is constructed with appendTo() and avoids allocating Objects (i.e. Strings)
496
* to try to continue printing as much as possible of the stack trace even in the presence of
497
* OutOfMemoryErrors (CMVC 97756).
498
*
499
* @param err
500
* the specified print stream/writer
501
* @param parentStack
502
* parent stack elements
503
* @param indents
504
* number of indents (\t) to be printed
505
* @param suppressed
506
* if this is an exception suppressed
507
* @param exceptionChainSet
508
* set of exceptions in the exception chain
509
*
510
* @return an array of stack trace elements printed
511
*
512
*/
513
private StackTraceElement[] printStackTrace(
514
Appendable err, StackTraceElement[] parentStack, int indents, boolean suppressed, Set<Throwable> exceptionChainSet) {
515
/*[PR 120593] CDC 1.0 and CDC 1.1 TCK fails in java.lang.. Exception tests */
516
if (err == null) throw new NullPointerException();
517
StackTraceElement[] stack;
518
boolean outOfMemory = this instanceof OutOfMemoryError;
519
if ((exceptionChainSet != null) && (exceptionChainSet.contains(this))) {
520
if (!outOfMemory) {
521
try {
522
appendTo(err, "\t[CIRCULAR REFERENCE:" + toString() + "]", 0); //$NON-NLS-1$
523
} catch(OutOfMemoryError e) {
524
outOfMemory = true;
525
}
526
}
527
if (outOfMemory) {
528
appendTo(err, "\t[CIRCULAR REFERENCE:"); //$NON-NLS-1$
529
try {
530
appendTo(err, getClass().getName());
531
} catch(OutOfMemoryError e) {
532
appendTo(err, "java.lang.OutOfMemoryError(?)");
533
}
534
appendTo(err, "]");
535
}
536
appendLnTo(err);
537
return null;
538
}
539
try {
540
exceptionChainSet.add(this);
541
} catch(OutOfMemoryError e) {
542
/* If OOM is thrown when adding Throwable to exception set, then we may not be able to identify circular exception chain,
543
* which can cause OOM to be thrown again. This should be ok as we are already running out of heap memory.
544
*/
545
}
546
if (parentStack != null) {
547
if (suppressed) {
548
appendTo(err, "Suppressed: ", indents); //$NON-NLS-1$
549
} else {
550
appendTo(err, "Caused by: ", indents); //$NON-NLS-1$
551
}
552
}
553
if (!outOfMemory) {
554
try {
555
appendTo(err, toString());
556
} catch(OutOfMemoryError e) {
557
outOfMemory = true;
558
}
559
}
560
if (outOfMemory) {
561
try {
562
appendTo(err, getClass().getName());
563
} catch(OutOfMemoryError e) {
564
outOfMemory = true;
565
appendTo(err, "java.lang.OutOfMemoryError(?)"); //$NON-NLS-1$
566
}
567
try {
568
String message = getLocalizedMessage();
569
if (message != null) {
570
appendTo(err, ": "); //$NON-NLS-1$
571
appendTo(err, message);
572
}
573
} catch(OutOfMemoryError e) {
574
outOfMemory = true;
575
}
576
}
577
appendLnTo(err);
578
int duplicates = 0;
579
try {
580
// Don't use getStackTrace() as it calls clone()
581
// Get stackTrace, in case stackTrace is reassigned
582
stack = getInternalStackTrace();
583
/*[PR CMVC 90361] look for duplicate entries in parent stack traces */
584
if (parentStack != null) {
585
duplicates = countDuplicates(stack, parentStack);
586
}
587
} catch(OutOfMemoryError e) {
588
appendTo(err, "\tat ?", indents); //$NON-NLS-1$
589
appendLnTo(err);
590
return null;
591
}
592
for (int i=0; i < stack.length - duplicates; i++) {
593
StackTraceElement element = stack[i];
594
if (!outOfMemory) {
595
try {
596
appendTo(err, "\tat " + element, indents); //$NON-NLS-1$
597
} catch(OutOfMemoryError e) {
598
outOfMemory = true;
599
}
600
}
601
if (outOfMemory) {
602
appendTo(err, "\tat ", indents); //$NON-NLS-1$
603
Util.printStackTraceElement(element, null, err, false);
604
}
605
appendLnTo(err);
606
}
607
if (duplicates > 0) {
608
if (!outOfMemory) {
609
try {
610
appendTo(err, "\t... " + duplicates + " more", indents); //$NON-NLS-1$ //$NON-NLS-2$
611
} catch(OutOfMemoryError e) {
612
outOfMemory = true;
613
}
614
}
615
if (outOfMemory) {
616
appendTo(err, "\t... ", indents); //$NON-NLS-1$
617
appendTo(err, duplicates);
618
appendTo(err, " more"); //$NON-NLS-1$
619
}
620
appendLnTo(err);
621
}
622
623
synchronized (this) {
624
if (suppressedExceptions != null) {
625
for (Throwable t : suppressedExceptions) {
626
StackTraceElement[] stackSuppressed;
627
stackSuppressed = t.printStackTrace(err, stack, indents + 1, true, exceptionChainSet);
628
629
Throwable throwableSuppressed = t.getCause();
630
while (throwableSuppressed != null && stackSuppressed != null) {
631
stackSuppressed = throwableSuppressed.printStackTrace(err, stackSuppressed, indents + 1, false, exceptionChainSet);
632
throwableSuppressed = throwableSuppressed.getCause();
633
}
634
}
635
}
636
}
637
return stack;
638
}
639
640
/**
641
* The specified exception is going to be suppressed in order to give priority
642
* to this exception (primary exception) and to be appended to the list of
643
* suppressed exceptions.
644
*
645
* This method is typically called by the automatically generated code from the
646
* try-with-resources statement.
647
*
648
* @param exception Throwable
649
* an exception to be suppressed and added to
650
* the list of suppressed exceptions
651
*
652
* @throws IllegalArgumentException
653
* if exception is this throwable, can't suppress itself
654
* @throws NullPointerException
655
* if exception is null and there is an exception suppressed before
656
*
657
* @since 1.7
658
*
659
*/
660
public final void addSuppressed(Throwable exception) {
661
/*[PR CMVC 181567] Java7:JCK:java_lang/Throwable/SuppressedTests fails */
662
if (exception == null) {
663
/*[MSG "K0563", "Null not permitted when an exception has already been suppressed"]*/
664
throw new NullPointerException(com.ibm.oti.util.Msg.getString("K0563")); //$NON-NLS-1$
665
}
666
667
if (exception == this) {
668
/*[MSG "K0559", "A throwable cannot suppress itself"]*/
669
throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0559")); //$NON-NLS-1$
670
}
671
synchronized (this) {
672
if (suppressedExceptions != null) {
673
if (suppressedExceptions.size() == 0) {
674
suppressedExceptions = new ArrayList<Throwable>(2);
675
}
676
suppressedExceptions.add(exception);
677
}
678
}
679
}
680
681
/**
682
* Returns an array of exceptions suppressed, typically by the automatically
683
* generated code from the try-with-resources statement, in order to give
684
* priority to this exception (primary exception).
685
*
686
* @return an array of exceptions representing all exceptions suppressed to
687
* give priority to this exception (primary exception)
688
*
689
* @since 1.7
690
*
691
*/
692
public final Throwable[] getSuppressed() {
693
synchronized (this) {
694
if (suppressedExceptions == null || suppressedExceptions.size() == 0) {
695
return ZeroElementArray;
696
} else {
697
return suppressedExceptions.toArray(new Throwable[suppressedExceptions.size()]);
698
}
699
}
700
}
701
}
702
703