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

Reply via email to