1 /* 2 * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @library ../ /test/lib 27 * @run testng/othervm --enable-native-access=ALL-UNNAMED TestCaptureCallState 28 */ 29 30 import org.testng.annotations.DataProvider; 31 import org.testng.annotations.Test; 32 33 import java.lang.foreign.Arena; 34 import java.lang.foreign.FunctionDescriptor; 35 import java.lang.foreign.Linker; 36 import java.lang.foreign.MemoryLayout; 37 import java.lang.foreign.MemorySegment; 38 import java.lang.foreign.StructLayout; 39 import java.lang.invoke.MethodHandle; 40 import java.lang.invoke.VarHandle; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.function.Consumer; 45 46 import static java.lang.foreign.MemoryLayout.PathElement.groupElement; 47 import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; 48 import static java.lang.foreign.ValueLayout.JAVA_INT; 49 import static java.lang.foreign.ValueLayout.JAVA_LONG; 50 import static org.testng.Assert.assertEquals; 51 import static org.testng.Assert.assertTrue; 52 53 public class TestCaptureCallState extends NativeTestHelper { 54 55 static { 56 System.loadLibrary("CaptureCallState"); 57 if (IS_WINDOWS) { 58 String system32 = System.getenv("SystemRoot") + "\\system32"; 59 System.load(system32 + "\\Kernel32.dll"); 60 System.load(system32 + "\\Ws2_32.dll"); 61 } 62 } 63 64 private record SaveValuesCase(String nativeTarget, FunctionDescriptor nativeDesc, String threadLocalName, Consumer<Object> resultCheck) {} 65 66 @Test(dataProvider = "cases") 67 public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { 68 Linker.Option stl = Linker.Option.captureCallState(testCase.threadLocalName()); 69 MethodHandle handle = downcallHandle(testCase.nativeTarget(), testCase.nativeDesc(), stl); 70 71 StructLayout capturedStateLayout = Linker.Option.captureStateLayout(); 72 VarHandle errnoHandle = capturedStateLayout.varHandle(groupElement(testCase.threadLocalName())); 73 74 try (Arena arena = Arena.ofConfined()) { 75 MemorySegment saveSeg = arena.allocate(capturedStateLayout); 76 int testValue = 42; 77 boolean needsAllocator = testCase.nativeDesc().returnLayout().map(StructLayout.class::isInstance).orElse(false); 78 Object result = needsAllocator 79 ? handle.invoke(arena, saveSeg, testValue) 80 : handle.invoke(saveSeg, testValue); 81 testCase.resultCheck().accept(result); 82 int savedErrno = (int) errnoHandle.get(saveSeg, 0L); 83 assertEquals(savedErrno, testValue); 84 } 85 } 86 87 @Test(dataProvider = "invalidCaptureSegmentCases") 88 public void testInvalidCaptureSegment(MemorySegment captureSegment, 89 Class<?> expectedExceptionType, String expectedExceptionMessage) { 90 Linker.Option stl = Linker.Option.captureCallState("errno"); 91 MethodHandle handle = downcallHandle("set_errno_V", FunctionDescriptor.ofVoid(C_INT), stl); 92 93 try { 94 int testValue = 42; 95 handle.invoke(captureSegment, testValue); // should throw 96 } catch (Throwable t) { 97 assertTrue(expectedExceptionType.isInstance(t)); 98 assertTrue(t.getMessage().matches(expectedExceptionMessage)); 99 } 100 } 101 102 @DataProvider 103 public static Object[][] cases() { 104 List<SaveValuesCase> cases = new ArrayList<>(); 105 106 cases.add(new SaveValuesCase("set_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), "errno", o -> {})); 107 cases.add(new SaveValuesCase("set_errno_I", FunctionDescriptor.of(JAVA_INT, JAVA_INT), "errno", o -> assertEquals((int) o, 42))); 108 cases.add(new SaveValuesCase("set_errno_D", FunctionDescriptor.of(JAVA_DOUBLE, JAVA_INT), "errno", o -> assertEquals((double) o, 42.0))); 109 110 cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L))); 111 cases.add(structCase("SLL", Map.of(JAVA_LONG.withName("x"), 42L, 112 JAVA_LONG.withName("y"), 42L))); 113 cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, 114 JAVA_LONG.withName("y"), 42L, 115 JAVA_LONG.withName("z"), 42L))); 116 cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D))); 117 cases.add(structCase("SDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, 118 JAVA_DOUBLE.withName("y"), 42D))); 119 cases.add(structCase("SDDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, 120 JAVA_DOUBLE.withName("y"), 42D, 121 JAVA_DOUBLE.withName("z"), 42D))); 122 123 if (IS_WINDOWS) { 124 cases.add(new SaveValuesCase("SetLastError", FunctionDescriptor.ofVoid(JAVA_INT), "GetLastError", o -> {})); 125 cases.add(new SaveValuesCase("WSASetLastError", FunctionDescriptor.ofVoid(JAVA_INT), "WSAGetLastError", o -> {})); 126 } 127 128 return cases.stream().map(tc -> new Object[] {tc}).toArray(Object[][]::new); 129 } 130 131 static SaveValuesCase structCase(String name, Map<MemoryLayout, Object> fields) { 132 StructLayout layout = MemoryLayout.structLayout(fields.keySet().toArray(MemoryLayout[]::new)); 133 134 Consumer<Object> check = o -> {}; 135 for (var field : fields.entrySet()) { 136 MemoryLayout fieldLayout = field.getKey(); 137 VarHandle fieldHandle = layout.varHandle(MemoryLayout.PathElement.groupElement(fieldLayout.name().get())); 138 Object value = field.getValue(); 139 check = check.andThen(o -> assertEquals(fieldHandle.get(o, 0L), value)); 140 } 141 142 return new SaveValuesCase("set_errno_" + name, FunctionDescriptor.of(layout, JAVA_INT), "errno", check); 143 } 144 145 @DataProvider 146 public static Object[][] invalidCaptureSegmentCases() { 147 return new Object[][]{ 148 {Arena.ofAuto().allocate(1), IndexOutOfBoundsException.class, ".*Out of bound access on segment.*"}, 149 {MemorySegment.NULL, IllegalArgumentException.class, ".*Capture segment is NULL.*"}, 150 {Arena.ofAuto().allocate(Linker.Option.captureStateLayout().byteSize() + 3).asSlice(3), // misaligned 151 IllegalArgumentException.class, ".*Target offset incompatible with alignment constraints.*"}, 152 }; 153 } 154 }