This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-secure-xml.git
The following commit(s) were added to refs/heads/main by this push:
new 54b8d4b Rethrow JVM errors unchanged in
MethodHandleFactory.invokeExact (#70)
54b8d4b is described below
commit 54b8d4b29f6283f479395c7528001ea5a797a33e
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Tue Sep 1 13:28:33 2026 +0200
Rethrow JVM errors unchanged in MethodHandleFactory.invokeExact (#70)
An undeclared Error raised through the MethodHandle lookup path (an
OutOfMemoryError, for example) was wrapped in IllegalStateException,
demoting it from Error to RuntimeException and hiding it from
supervisors that handle Error separately. Rethrow it unchanged; the
IllegalStateException wrap now covers runtime exceptions only, since
the looked-up factory methods declare no other checked exceptions.
Assert instance identity, not just type, on both rethrow paths.
Assisted-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
---
.../apache/commons/xml/secure/MethodHandleFactory.java | 6 +++++-
.../commons/xml/secure/MethodHandleFactoryTest.java | 16 ++++++++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git
a/src/main/java/org/apache/commons/xml/secure/MethodHandleFactory.java
b/src/main/java/org/apache/commons/xml/secure/MethodHandleFactory.java
index d49837a..75b8c99 100644
--- a/src/main/java/org/apache/commons/xml/secure/MethodHandleFactory.java
+++ b/src/main/java/org/apache/commons/xml/secure/MethodHandleFactory.java
@@ -56,7 +56,11 @@ static <T, E extends Throwable> T invokeExact(final
ThrowableCallable<T> methodH
if (rethrow.isInstance(e)) {
throw rethrow.cast(e);
}
- // Unreachable: the looked-up method declares no other exceptions.
+ if (e instanceof Error) {
+ // A JVM error (OutOfMemoryError, ...) must keep its type;
only exceptions are wrapped.
+ throw (Error) e;
+ }
+ // The looked-up method declares no checked exceptions besides
rethrow's type, so this wraps runtime exceptions only.
throw new IllegalStateException(e);
}
}
diff --git
a/src/test/java/org/apache/commons/xml/secure/MethodHandleFactoryTest.java
b/src/test/java/org/apache/commons/xml/secure/MethodHandleFactoryTest.java
index c6c9d2e..01963b5 100644
--- a/src/test/java/org/apache/commons/xml/secure/MethodHandleFactoryTest.java
+++ b/src/test/java/org/apache/commons/xml/secure/MethodHandleFactoryTest.java
@@ -21,6 +21,7 @@
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.lang.invoke.MethodHandle;
@@ -54,9 +55,20 @@ void findStaticReturnsNullForMissingMethod() {
@Test
void invokeExactRethrowsDeclaredException() {
- assertThrows(FactoryConfigurationError.class, () ->
MethodHandleFactory.invokeExact(() -> {
- throw new FactoryConfigurationError("boom");
+ final FactoryConfigurationError declared = new
FactoryConfigurationError("boom");
+ final FactoryConfigurationError thrown =
assertThrows(FactoryConfigurationError.class, () ->
MethodHandleFactory.invokeExact(() -> {
+ throw declared;
}, FactoryConfigurationError.class), "an exception of the declared
type must be rethrown");
+ assertSame(declared, thrown, "the declared exception must propagate
unchanged");
+ }
+
+ @Test
+ void invokeExactRethrowsUndeclaredError() {
+ final OutOfMemoryError error = new OutOfMemoryError("boom");
+ final OutOfMemoryError thrown = assertThrows(OutOfMemoryError.class,
() -> MethodHandleFactory.invokeExact(() -> {
+ throw error;
+ }, FactoryConfigurationError.class), "a JVM error must keep its type");
+ assertSame(error, thrown, "the error must propagate unchanged");
}
@Test