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 d49837a9..75b8c996 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 invokeExact(final ThrowableCallable 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 c6c9d2ec..01963b56 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