1 /* 2 * Copyright (c) 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 * @enablePreview 27 * @library ../ /test/lib 28 * @requires jdk.foreign.linker != "UNSUPPORTED" 29 * @run testng/othervm --enable-native-access=ALL-UNNAMED TestAddressDereference 30 */ 31 32 import java.lang.foreign.Arena; 33 import java.lang.foreign.FunctionDescriptor; 34 import java.lang.foreign.Linker; 35 import java.lang.foreign.MemorySegment; 36 import java.lang.foreign.SymbolLookup; 37 import java.lang.foreign.ValueLayout; 38 import java.lang.invoke.MethodHandle; 39 import java.lang.invoke.MethodHandles; 40 import java.lang.invoke.MethodType; 41 import java.util.ArrayList; 42 import java.util.List; 43 44 import org.testng.annotations.*; 45 46 import static org.testng.Assert.*; 47 48 public class TestAddressDereference extends UpcallTestHelper { 49 50 static final Linker LINKER = Linker.nativeLinker(); 51 static final MemorySegment GET_ADDR_SYM; 52 static final MethodHandle GET_ADDR_CB_HANDLE, TEST_ARG_HANDLE; 53 54 static { 55 System.loadLibrary("AddressDereference"); 56 GET_ADDR_SYM = SymbolLookup.loaderLookup().find("get_addr").get(); 57 GET_ADDR_CB_HANDLE = LINKER.downcallHandle( 58 SymbolLookup.loaderLookup().find("get_addr_cb").get(), 59 FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); 60 try { 61 TEST_ARG_HANDLE = MethodHandles.lookup().findStatic(TestAddressDereference.class, "testArg", 62 MethodType.methodType(void.class, MemorySegment.class, long.class)); 63 } catch (Throwable ex) { 64 throw new AssertionError(ex); 65 } 66 } 67 68 @Test(dataProvider = "layoutsAndAlignments") 69 public void testGetAddress(long alignment, ValueLayout layout) { 70 boolean badAlign = layout.byteAlignment() > alignment; 71 try (Arena arena = Arena.ofConfined()) { 72 MemorySegment segment = arena.allocate(ValueLayout.ADDRESS); 73 segment.set(ValueLayout.ADDRESS, 0, MemorySegment.ofAddress(alignment)); 74 MemorySegment deref = segment.get(ValueLayout.ADDRESS.withTargetLayout(layout), 0); 75 assertFalse(badAlign); 76 assertEquals(deref.byteSize(), layout.byteSize()); 77 } catch (IllegalArgumentException ex) { 78 assertTrue(badAlign); 79 assertTrue(ex.getMessage().contains("alignment constraint for address")); 80 } 81 } 82 83 @Test(dataProvider = "layoutsAndAlignments") 84 public void testGetAddressIndex(long alignment, ValueLayout layout) { 85 boolean badAlign = layout.byteAlignment() > alignment; 86 try (Arena arena = Arena.ofConfined()) { 87 MemorySegment segment = arena.allocate(ValueLayout.ADDRESS); 88 segment.set(ValueLayout.ADDRESS, 0, MemorySegment.ofAddress(alignment)); 89 MemorySegment deref = segment.getAtIndex(ValueLayout.ADDRESS.withTargetLayout(layout), 0); 90 assertFalse(badAlign); 91 assertEquals(deref.byteSize(), layout.byteSize()); 92 } catch (IllegalArgumentException ex) { 93 assertTrue(badAlign); 94 assertTrue(ex.getMessage().contains("alignment constraint for address")); 95 } 96 } 97 98 @Test(dataProvider = "layoutsAndAlignments") 99 public void testNativeReturn(long alignment, ValueLayout layout) throws Throwable { 100 boolean badAlign = layout.byteAlignment() > alignment; 101 try { 102 MethodHandle get_addr_handle = LINKER.downcallHandle(GET_ADDR_SYM, 103 FunctionDescriptor.of(ValueLayout.ADDRESS.withTargetLayout(layout), ValueLayout.ADDRESS)); 104 MemorySegment deref = (MemorySegment)get_addr_handle.invokeExact(MemorySegment.ofAddress(alignment)); 105 assertFalse(badAlign); 106 assertEquals(deref.byteSize(), layout.byteSize()); 107 } catch (IllegalArgumentException ex) { 108 assertTrue(badAlign); 109 assertTrue(ex.getMessage().contains("alignment constraint for address")); 110 } 111 } 112 113 @Test(dataProvider = "layoutsAndAlignments") 114 public void testNativeUpcallArgPos(long alignment, ValueLayout layout) throws Throwable { 115 boolean badAlign = layout.byteAlignment() > alignment; 116 if (badAlign) return; // this will crash the JVM (exception occurs when going into the upcall stub) 117 try (Arena arena = Arena.ofConfined()) { 118 FunctionDescriptor testDesc = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS.withTargetLayout(layout)); 119 MethodHandle upcallHandle = MethodHandles.insertArguments(TEST_ARG_HANDLE, 1, layout.byteSize()); 120 MemorySegment testStub = LINKER.upcallStub(upcallHandle, testDesc, arena); 121 GET_ADDR_CB_HANDLE.invokeExact(MemorySegment.ofAddress(alignment), testStub); 122 } 123 } 124 125 @Test(dataProvider = "layoutsAndAlignments") 126 public void testNativeUpcallArgNeg(long alignment, ValueLayout layout) throws Throwable { 127 boolean badAlign = layout.byteAlignment() > alignment; 128 if (!badAlign) return; 129 runInNewProcess(UpcallTestRunner.class, true, 130 new String[] {Long.toString(alignment), layout.toString() }) 131 .assertStdErrContains("alignment constraint for address"); 132 } 133 134 public static class UpcallTestRunner { 135 public static void main(String[] args) throws Throwable { 136 long alignment = parseAlignment(args[0]); 137 ValueLayout layout = parseLayout(args[1]); 138 try (Arena arena = Arena.ofConfined()) { 139 FunctionDescriptor testDesc = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS.withTargetLayout(layout)); 140 MethodHandle upcallHandle = MethodHandles.insertArguments(TEST_ARG_HANDLE, 1, layout.byteSize()); 141 MemorySegment testStub = LINKER.upcallStub(upcallHandle, testDesc, arena); 142 GET_ADDR_CB_HANDLE.invokeExact(MemorySegment.ofAddress(alignment), testStub); 143 } 144 } 145 146 static long parseAlignment(String s) { 147 return Long.parseLong(s); 148 } 149 150 static ValueLayout parseLayout(String s) { 151 return LayoutKind.parse(s).layout; 152 } 153 } 154 155 static void testArg(MemorySegment deref, long expectedSize) { 156 assertEquals(deref.byteSize(), expectedSize); 157 } 158 159 @DataProvider(name = "layoutsAndAlignments") 160 static Object[][] layoutsAndAlignments() { 161 List<Object[]> layoutsAndAlignments = new ArrayList<>(); 162 for (LayoutKind lk : LayoutKind.values()) { 163 for (int align : new int[]{ 1, 2, 4, 8 }) { 164 layoutsAndAlignments.add(new Object[] { align, lk.layout }); 165 } 166 } 167 return layoutsAndAlignments.toArray(Object[][]::new); 168 } 169 170 enum LayoutKind { 171 BOOL(ValueLayout.JAVA_BOOLEAN), 172 CHAR(ValueLayout.JAVA_CHAR), 173 SHORT(ValueLayout.JAVA_SHORT), 174 INT(ValueLayout.JAVA_INT), 175 FLOAT(ValueLayout.JAVA_FLOAT), 176 LONG(ValueLayout.JAVA_LONG), 177 DOUBLE(ValueLayout.JAVA_DOUBLE), 178 ADDRESS(ValueLayout.ADDRESS); 179 180 181 final ValueLayout layout; 182 183 LayoutKind(ValueLayout segment) { 184 this.layout = segment; 185 } 186 187 static LayoutKind parse(String layoutString) { 188 return switch (layoutString.charAt(0)) { 189 case 'A','a' -> ADDRESS; 190 case 'z','Z' -> BOOL; 191 case 'c','C' -> CHAR; 192 case 's','S' -> SHORT; 193 case 'i','I' -> INT; 194 case 'f','F' -> FLOAT; 195 case 'j','J' -> LONG; 196 case 'd','D' -> DOUBLE; 197 default -> throw new AssertionError("Invalid layout string: " + layoutString); 198 }; 199 } 200 } 201 }