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 * @library ../ /test/lib 27 * @run testng/othervm --enable-native-access=ALL-UNNAMED TestAddressDereference 28 */ 29 30 import java.lang.foreign.Arena; 31 import java.lang.foreign.FunctionDescriptor; 32 import java.lang.foreign.Linker; 33 import java.lang.foreign.MemorySegment; 34 import java.lang.foreign.SymbolLookup; 35 import java.lang.foreign.ValueLayout; 36 import java.lang.invoke.MethodHandle; 37 import java.lang.invoke.MethodHandles; 38 import java.lang.invoke.MethodType; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.regex.Matcher; 42 import java.util.regex.Pattern; 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 private static final Pattern LAYOUT_PATTERN = Pattern.compile("^(?<align>\\d+%)?(?<char>[azcsifjdAZCSIFJD])\\d+$"); 188 189 static LayoutKind parse(String layoutString) { 190 Matcher matcher = LAYOUT_PATTERN.matcher(layoutString); 191 if (matcher.matches()) { 192 switch (matcher.group("char")) { 193 case "A","a": return ADDRESS; 194 case "z","Z": return BOOL; 195 case "c","C": return CHAR; 196 case "s","S": return SHORT; 197 case "i","I": return INT; 198 case "f","F": return FLOAT; 199 case "j","J": return LONG; 200 case "d","D": return DOUBLE; 201 }; 202 } 203 throw new AssertionError("Invalid layout string: " + layoutString); 204 } 205 } 206 }