Class Throwables
getMessage() or getCause()
may throw. It delegates to the engine's own readers rather than keeping a copy of them, so a language that catches
what it calls decides as the engine decides: a warm-up that must skip an overflow but rethrow a class left unusable
by one (see ExpressionLanguage.prepare()), an action registered with EvaluationContext.onCancel(java.lang.Runnable)
that must let a fatal error through, or a class lookup that must read the JVM's "wrong name" error as "not found".
Every method here takes what it reads and returns a plain answer, with nothing retained: the class is
stateless, and its methods are safe to call from any thread at once. isOverflow(java.lang.Throwable) and isFatal(java.lang.Throwable)
allocate nothing and link no call site, so they can be called where handling a failure has itself failed, deep in
a stack or with no memory left. Each is fixed for 2.x: what it reads, in what order, how far, and what it answers
for null.
-
Method Summary
Modifier and TypeMethodDescriptioncauseChain(@Nullable Throwable thrown) Lists an exception and its causes, from the top: the exception, its cause, its cause's cause and so on, each read asmessageOf(java.lang.Throwable)reads a message, so agetCause()that throws ends the chain there.static @Nullable ErrorfatalError(@Nullable Throwable thrown) Finds the fatal error (seeisFatal(java.lang.Throwable)) in what was caught, as the engine finds the one it rethrows: the throwable itself, or one of its causes, or failing that one suppressed on any of them, or among the causes and suppressed exceptions of those, at any depth, as atry-with-resources whoseclose()hits anOutOfMemoryErrorwhile its body throws leaves it.static booleanTells whether a throwable is anErrorthe engine must not absorb, because the JVM itself is failing and no message about a rule would help: aVirtualMachineErrorother thanStackOverflowError, such asOutOfMemoryError,InternalErrororUnknownError, but not anInternalErrorthat wraps an overflow (seeisOverflow(java.lang.Throwable)).static booleanisInterrupt(@Nullable Throwable thrown) Tells whether what was caught was caused by an interrupt, or carries one: anInterruptedExceptionis the throwable itself, one of its causes, or suppressed on any of them, or among the causes and suppressed exceptions of those, read asfatalError(java.lang.Throwable)reads them, in the same order, with the same limits, and skipping what the engine itself added.static booleanisMissingRegistration(@Nullable Throwable error) Tells whether an error is the one a native image built with strict reachability metadata throws for a class or method lookup it has no metadata for, existing or not, where any other build, and the JVM, throwClassNotFoundExceptionorNoSuchMethodException, or list what is there: GraalVM'sorg.graalvm.nativeimage.MissingReflectionRegistrationError, anErrorin GraalVM for JDK 21 and aLinkageErrorin GraalVM for JDK 25.static booleanisOverflow(@Nullable Throwable thrown) Tells whether a throwable is aStackOverflowError, or what was thrown in place of one: walking its cause chain from the top, ascauseChain(java.lang.Throwable)walks it, aStackOverflowErrorcomes before anyLinkageError.static booleanisWrongName(@Nullable Throwable error, String name) Tells whether an error a class lookup threw only means that a class file was found for the name looked up in a different case, as a class directory on a case-insensitive file system finds one, so there is no class by that name: the JVM's "wrong name"NoClassDefFoundError.static @Nullable StringReads an exception's message without letting a failure to read it escape.static ThrowableFinds the last exception in an exception's cause chain, ascauseChain(java.lang.Throwable)reads it: the exception itself if it has no cause, or the last cause that could be read, which the engine names in a message when the description would otherwise hide it.
-
Method Details
-
isOverflow
Tells whether a throwable is aStackOverflowError, or what was thrown in place of one: walking its cause chain from the top, ascauseChain(java.lang.Throwable)walks it, aStackOverflowErrorcomes before anyLinkageError. ANoClassDefFoundErroror anExceptionInInitializerErrorabove the overflow says a class was left unusable, by an overflow in its static initializer, maybe in an earlier load, which no change to the rule or the stack's depth helps, as JDK 25 and later make one with the overflow as its cause; so that isn't an overflow. AnInternalErroraround one, as the JDK wraps an overflow it hits defining a hidden class, is. The engine reports an overflow as the rule's failure, with more stack as the fix, and never as a fatal error (seeisFatal(java.lang.Throwable)).The chain is read link by link, at most 100 links, a
getCause()that throws ending it, with nothing allocated and no call site linked, so it can be asked where handling a failure has itself failed. A chain that loops back on itself is read as its first 100 links.- Parameters:
thrown- What was caught, ornull, which isn't an overflow- Returns:
trueif aStackOverflowErroris in its cause chain with noLinkageErrorbefore it
-
isFatal
Tells whether a throwable is anErrorthe engine must not absorb, because the JVM itself is failing and no message about a rule would help: aVirtualMachineErrorother thanStackOverflowError, such asOutOfMemoryError,InternalErrororUnknownError, but not anInternalErrorthat wraps an overflow (seeisOverflow(java.lang.Throwable)). The engine rethrows such an error unchanged fromrun(),load(),validate()andclose(), even when a language wraps it or suppresses it in an exception (seefatalError(java.lang.Throwable)), and reports every other error, anAssertionErroror aLinkageErrorincluded, as the failure of the rule, listener or language it came from.Only the throwable itself is read, not its causes: it allocates nothing and links no call site, so it can be asked where handling a failure has itself failed, deep in a stack or with no memory left. Only an
InternalErrorhas its cause chain read, asisOverflow(java.lang.Throwable)reads it.- Parameters:
thrown- What was caught, ornull, which isn't fatal- Returns:
trueif the engine would rethrowthrownunchanged
-
fatalError
Finds the fatal error (seeisFatal(java.lang.Throwable)) in what was caught, as the engine finds the one it rethrows: the throwable itself, or one of its causes, or failing that one suppressed on any of them, or among the causes and suppressed exceptions of those, at any depth, as atry-with-resources whoseclose()hits anOutOfMemoryErrorwhile its body throws leaves it. The throwable's own cause chain is read first, ascauseChain(java.lang.Throwable)reads it, at most 100 links, so a fatal error further down it, or suppressed on a link further down, is missed; then the direct suppressed exceptions of each of those links, from the top link down, before what any of them leads to, depth first: a suppressed exception's cause chain is read in full, link by link, with no limit of its own, then the suppressed exceptions of its links the same way. Each exception is read once, by identity, and at most 10,000 are, so a fatal error past that many is missed, as the engine misses it. A suppressed exception the engine itself added to a failure it reported, beside the one it rethrows, isn't read: the engine keeps there a fatal error a listener threw, and finding it again would take it for the failure's own.It allocates the list and the identity set of
causeChain(java.lang.Throwable), and a walk when any link has suppressed exceptions.- Parameters:
thrown- What was caught, ornull, which holds no fatal error- Returns:
- The first fatal error found in that order, or
nullif there is none among the exceptions read
-
isInterrupt
Tells whether what was caught was caused by an interrupt, or carries one: anInterruptedExceptionis the throwable itself, one of its causes, or suppressed on any of them, or among the causes and suppressed exceptions of those, read asfatalError(java.lang.Throwable)reads them, in the same order, with the same limits, and skipping what the engine itself added. A rule, listener or language interrupted while it blocks throws anInterruptedException, which clears the thread's interrupt status, and the engine receives it wrapped by the language, or suppressed on the body's exception by atry-with-resources whoseclose()was interrupted; the engine sets the status again when it finds one, so the caller ofrun()can tell. AInterruptedIOExceptiondoesn't count: it isn't anInterruptedException.It allocates what
fatalError(java.lang.Throwable)allocates.- Parameters:
thrown- What was caught, ornull, which holds no interrupt- Returns:
trueif anInterruptedExceptionis among the exceptions read
-
messageOf
Reads an exception's message without letting a failure to read it escape. AgetMessage()of the application's own can throw, such as one built from a field that isnull, and a failure being handled would then end as that one. WhatevergetMessage()throws, a fatalErrortoo, only makes the message unavailable: it says nothing about the failure being handled. The accessor is called in atryof its own rather than through a lambda or method reference, so the JVM's first failure links no call site, which could overflow deep in a stack. It allocates only whengetMessage()throws.- Parameters:
thrown- The exception- Returns:
- Its message, or
nullif it has none, or(message unavailable: ...), naming the class of whatgetMessage()threw, if it can't be read - Throws:
NullPointerException- ifthrownisnull
-
causeChain
Lists an exception and its causes, from the top: the exception, its cause, its cause's cause and so on, each read asmessageOf(java.lang.Throwable)reads a message, so agetCause()that throws ends the chain there. The chain also stops before a link it holds already, by identity, as a chain that loops back on itself has one, and after 100 links, as agetCause()of its own that returns a new exception every time would otherwise make a chain that never ends. The engine reads a throwable's own cause chain this way wherever it describes a failure or looks for a fatal error in one; a chain under a suppressed exception has no limit of its own (seefatalError(java.lang.Throwable)).It allocates a new list, which the caller owns and may change, and an identity set while it reads.
- Parameters:
thrown- The exception, ornull, whose chain is empty- Returns:
- A new list of
thrownand the causes that could be read, in order, which is empty only fornull
-
rootCause
Finds the last exception in an exception's cause chain, ascauseChain(java.lang.Throwable)reads it: the exception itself if it has no cause, or the last cause that could be read, which the engine names in a message when the description would otherwise hide it. It allocates whatcauseChain(java.lang.Throwable)allocates.- Parameters:
thrown- The exception- Returns:
- The last link of its cause chain
- Throws:
NullPointerException- ifthrownisnull
-
isWrongName
Tells whether an error a class lookup threw only means that a class file was found for the name looked up in a different case, as a class directory on a case-insensitive file system finds one, so there is no class by that name: the JVM's "wrong name"NoClassDefFoundError. Its message names the class asked for, in internal form (com/acme/Rule), on one side, and only the side this JVM writes it on is read: first on HotSpot, as incom/acme/rule (wrong name: com/acme/Rule), and last on OpenJ9, as in... (wrong name: com/acme/rule), told by itsjava.vm.name. An error about a class the one looked up depends on, such as its superclass, doesn't name it on that side, so it isn't taken for it. That holds even for a superclass whose name differs from the class's only in case, whose lookup finds the class's own file: HotSpot's error forcom.acme.Rule's superclasscom.acme.ruleiscom/acme/rule (wrong name: com/acme/Rule), which namescom.acme.Rulelast. Nor is any other error taken for it, or one whose message can't be read (seemessageOf(java.lang.Throwable)). The engine treats a lookup that threw such an error as a class that isn't there, and tries the next form of the name.HotSpot cuts the message at 1023 bytes of UTF-8, so for a name over about 1,000 ASCII characters, or 500 two-byte ones,
" (wrong name: "is cut off and the error isn't recognised: the class then can't be loaded rather than being missing, an error, never a wrong answer.ImportLimits.MAX_IMPORT_LENGTHkeeps an ASCII import under the cut. For aNoClassDefFoundError, it allocates the internal form of a name with dots, the strings it compares the message with, and whatmessageOf(java.lang.Throwable)allocates when the message can't be read; for any other error, nothing.- Parameters:
error- What the lookup threw, ornull, which isn't the errorname- The binary name the lookup asked for, with dots, ascom.acme.Ruleorcom.acme.Rule$Nested- Returns:
truefor the JVM's "wrong name"NoClassDefFoundErrorforname- Throws:
NullPointerException- ifnameisnull
-
isMissingRegistration
Tells whether an error is the one a native image built with strict reachability metadata throws for a class or method lookup it has no metadata for, existing or not, where any other build, and the JVM, throwClassNotFoundExceptionorNoSuchMethodException, or list what is there: GraalVM'sorg.graalvm.nativeimage.MissingReflectionRegistrationError, anErrorin GraalVM for JDK 21 and aLinkageErrorin GraalVM for JDK 25. It is told by its class's name, so neither the engine nor a language needs a dependency on GraalVM's SDK, and no other error, aStackOverflowErrorleast of all, is taken for it. The engine treats such an error as "not found", so a package import still works in an image. It allocates nothing but the name of the error's class, the first time the JVM is asked for it.- Parameters:
error- What the lookup threw, ornull, which isn't the error- Returns:
truefor GraalVM'sMissingReflectionRegistrationError
-