Skip to content

Commit 54b8d4b

Browse files
authored
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
1 parent fa90fe8 commit 54b8d4b

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

src/main/java/org/apache/commons/xml/secure/MethodHandleFactory.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ static <T, E extends Throwable> T invokeExact(final ThrowableCallable<T> methodH
5656
if (rethrow.isInstance(e)) {
5757
throw rethrow.cast(e);
5858
}
59-
// Unreachable: the looked-up method declares no other exceptions.
59+
if (e instanceof Error) {
60+
// A JVM error (OutOfMemoryError, ...) must keep its type; only exceptions are wrapped.
61+
throw (Error) e;
62+
}
63+
// The looked-up method declares no checked exceptions besides rethrow's type, so this wraps runtime exceptions only.
6064
throw new IllegalStateException(e);
6165
}
6266
}

src/test/java/org/apache/commons/xml/secure/MethodHandleFactoryTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
2222
import static org.junit.jupiter.api.Assertions.assertNotNull;
2323
import static org.junit.jupiter.api.Assertions.assertNull;
24+
import static org.junit.jupiter.api.Assertions.assertSame;
2425
import static org.junit.jupiter.api.Assertions.assertThrows;
2526

2627
import java.lang.invoke.MethodHandle;
@@ -54,9 +55,20 @@ void findStaticReturnsNullForMissingMethod() {
5455

5556
@Test
5657
void invokeExactRethrowsDeclaredException() {
57-
assertThrows(FactoryConfigurationError.class, () -> MethodHandleFactory.invokeExact(() -> {
58-
throw new FactoryConfigurationError("boom");
58+
final FactoryConfigurationError declared = new FactoryConfigurationError("boom");
59+
final FactoryConfigurationError thrown = assertThrows(FactoryConfigurationError.class, () -> MethodHandleFactory.invokeExact(() -> {
60+
throw declared;
5961
}, FactoryConfigurationError.class), "an exception of the declared type must be rethrown");
62+
assertSame(declared, thrown, "the declared exception must propagate unchanged");
63+
}
64+
65+
@Test
66+
void invokeExactRethrowsUndeclaredError() {
67+
final OutOfMemoryError error = new OutOfMemoryError("boom");
68+
final OutOfMemoryError thrown = assertThrows(OutOfMemoryError.class, () -> MethodHandleFactory.invokeExact(() -> {
69+
throw error;
70+
}, FactoryConfigurationError.class), "a JVM error must keep its type");
71+
assertSame(error, thrown, "the error must propagate unchanged");
6072
}
6173

6274
@Test

0 commit comments

Comments
 (0)