/* * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @library /test/lib * @build TestUpcallException * * @run testng/othervm/native * --enable-native-access=ALL-UNNAMED * TestUpcallException */ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class TestUpcallException extends UpcallTestHelper { @Test(dataProvider = "exceptionCases") public void testException(Class target, boolean useSpec) throws InterruptedException, IOException { runInNewProcess(target, useSpec) .shouldNotHaveExitValue(0) .stderrShouldContain("Testing upcall exceptions"); } @DataProvider public static Object[][] exceptionCases() { return new Object[][]{ { VoidUpcallRunner.class, false }, { NonVoidUpcallRunner.class, false }, { VoidUpcallRunner.class, true }, { NonVoidUpcallRunner.class, true } }; } public static class VoidUpcallRunner extends ExceptionRunnerBase { public static void main(String[] args) throws Throwable { try (Arena arena = Arena.ofConfined()) { MemorySegment stub = Linker.nativeLinker().upcallStub(VOID_TARGET, FunctionDescriptor.ofVoid(), arena); downcallVoid.invoke(stub); // should call Shutdown.exit(1); } } } public static class NonVoidUpcallRunner extends ExceptionRunnerBase { public static void main(String[] args) throws Throwable { try (Arena arena = Arena.ofConfined()) { MemorySegment stub = Linker.nativeLinker().upcallStub(INT_TARGET, FunctionDescriptor.of(C_INT, C_INT), arena); downcallNonVoid.invoke(42, stub); // should call Shutdown.exit(1); } } } // where private static class ExceptionRunnerBase { static final MethodHandle downcallVoid; static final MethodHandle downcallNonVoid; static final MethodHandle VOID_TARGET; static final MethodHandle INT_TARGET; static final Thread.UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER = (thread, throwable) -> System.out.println("From uncaught exception handler"); static { System.loadLibrary("TestUpcall"); downcallVoid = Linker.nativeLinker().downcallHandle( findNativeOrThrow("f0_V__"), FunctionDescriptor.ofVoid(C_POINTER) ); downcallNonVoid = Linker.nativeLinker().downcallHandle( findNativeOrThrow("f10_I_I_"), FunctionDescriptor.of(C_INT, C_INT, C_POINTER) ); try { MethodHandles.Lookup lookup = MethodHandles.lookup(); VOID_TARGET = lookup.findStatic(ExceptionRunnerBase.class, "throwException", MethodType.methodType(void.class)); INT_TARGET = lookup.findStatic(ExceptionRunnerBase.class, "throwException", MethodType.methodType(int.class, int.class)); } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); } } public static void throwException() { throw new RuntimeException("Testing upcall exceptions"); } public static int throwException(int x) { throw new RuntimeException("Testing upcall exceptions"); } } }