1 /* 2 * Copyright (c) 2019, 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 /* 26 * @test 27 * @run testng TestLayoutPaths 28 */ 29 30 import java.lang.foreign.*; 31 import java.lang.foreign.MemoryLayout.PathElement; 32 33 import org.testng.annotations.*; 34 35 import java.lang.invoke.MethodHandle; 36 import java.lang.invoke.VarHandle; 37 import java.nio.ByteOrder; 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.function.IntFunction; 41 42 import static java.lang.foreign.MemoryLayout.PathElement.groupElement; 43 import static java.lang.foreign.MemoryLayout.PathElement.sequenceElement; 44 import static java.lang.foreign.ValueLayout.JAVA_INT; 45 import static java.lang.foreign.ValueLayout.JAVA_LONG; 46 import static java.lang.foreign.ValueLayout.JAVA_SHORT; 47 import static org.testng.Assert.*; 48 49 public class TestLayoutPaths { 50 51 @Test(expectedExceptions = IllegalArgumentException.class) 52 public void testBadByteSelectFromSeq() { 53 SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); 54 seq.byteOffset(groupElement("foo")); 55 } 56 57 @Test(expectedExceptions = IllegalArgumentException.class) 58 public void testBadByteSelectFromStruct() { 59 GroupLayout g = MemoryLayout.structLayout(JAVA_INT); 60 g.byteOffset(sequenceElement()); 61 } 62 63 @Test(expectedExceptions = IllegalArgumentException.class) 64 public void testBadByteSelectFromValue() { 65 SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); 66 seq.byteOffset(sequenceElement(), sequenceElement()); 67 } 68 69 @Test(expectedExceptions = IllegalArgumentException.class) 70 public void testUnknownByteStructField() { 71 GroupLayout g = MemoryLayout.structLayout(JAVA_INT); 72 g.byteOffset(groupElement("foo")); 73 } 74 75 @Test(expectedExceptions = IllegalArgumentException.class) 76 public void testTooBigGroupElementIndex() { 77 GroupLayout g = MemoryLayout.structLayout(JAVA_INT); 78 g.byteOffset(groupElement(1)); 79 } 80 81 @Test(expectedExceptions = IllegalArgumentException.class) 82 public void testNegativeGroupElementIndex() { 83 GroupLayout g = MemoryLayout.structLayout(JAVA_INT); 84 g.byteOffset(groupElement(-1)); 85 } 86 87 @Test(expectedExceptions = IllegalArgumentException.class) 88 public void testByteOutOfBoundsSeqIndex() { 89 SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); 90 seq.byteOffset(sequenceElement(6)); 91 } 92 93 @Test(expectedExceptions = IllegalArgumentException.class) 94 public void testNegativeSeqIndex() { 95 sequenceElement(-2); 96 } 97 98 @Test(expectedExceptions = IllegalArgumentException.class) 99 public void testByteNegativeSeqIndex() { 100 SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); 101 seq.byteOffset(sequenceElement(-2)); 102 } 103 104 @Test(expectedExceptions = IllegalArgumentException.class) 105 public void testOutOfBoundsSeqRange() { 106 SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); 107 seq.byteOffset(sequenceElement(6, 2)); 108 } 109 110 @Test(expectedExceptions = IllegalArgumentException.class) 111 public void testNegativeSeqRange() { 112 sequenceElement(-2, 2); 113 } 114 115 @Test(expectedExceptions = IllegalArgumentException.class) 116 public void testByteNegativeSeqRange() { 117 SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); 118 seq.byteOffset(sequenceElement(-2, 2)); 119 } 120 121 @Test(expectedExceptions = IllegalArgumentException.class) 122 public void testIncompleteAccess() { 123 SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); 124 seq.varHandle(sequenceElement()); 125 } 126 127 @Test 128 public void testByteOffsetHandleRange() { 129 SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); 130 seq.byteOffsetHandle(sequenceElement(0, 1)); 131 } 132 133 @Test(expectedExceptions = IllegalArgumentException.class) 134 public void testByteOffsetHandleBadRange() { 135 SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); 136 seq.byteOffsetHandle(sequenceElement(5, 1)); // invalid range (starting position is outside the sequence) 137 } 138 139 @Test 140 public void testBadAlignmentOfRoot() { 141 MemoryLayout struct = MemoryLayout.structLayout( 142 JAVA_INT, 143 JAVA_SHORT.withName("x")); 144 assertEquals(struct.byteAlignment(), 4); 145 146 try (Arena arena = Arena.ofConfined()) { 147 MemorySegment seg = arena.allocate(struct.byteSize() + 2, struct.byteAlignment()).asSlice(2); 148 assertEquals(seg.address() % JAVA_SHORT.byteAlignment(), 0); // should be aligned 149 assertNotEquals(seg.address() % struct.byteAlignment(), 0); // should not be aligned 150 151 String expectedMessage = "Target offset 0 is incompatible with alignment constraint " + struct.byteAlignment() + " (of [i4s2(x)]) for segment MemorySegment"; 152 153 VarHandle vhX = struct.varHandle(groupElement("x")); 154 IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> { 155 vhX.set(seg, 0L, (short) 42); 156 }); 157 assertTrue(iae.getMessage().startsWith(expectedMessage)); 158 159 MethodHandle sliceX = struct.sliceHandle(groupElement("x")); 160 iae = expectThrows(IllegalArgumentException.class, () -> { 161 MemorySegment slice = (MemorySegment) sliceX.invokeExact(seg, 0L); 162 }); 163 assertTrue(iae.getMessage().startsWith(expectedMessage)); 164 } 165 } 166 167 @Test 168 public void testWrongTypeRoot() { 169 MemoryLayout struct = MemoryLayout.structLayout( 170 JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), 171 JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN) 172 ); 173 174 var expectedMessage = "Bad layout path: attempting to select a sequence element from a non-sequence layout: [i4i4]"; 175 176 IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> 177 struct.select(PathElement.sequenceElement())); 178 assertEquals(iae.getMessage(), expectedMessage); 179 } 180 181 @Test 182 public void testWrongTypeEnclosing() { 183 MemoryLayout struct = MemoryLayout.structLayout( 184 MemoryLayout.sequenceLayout(2, MemoryLayout.structLayout( 185 JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withName("3a"), 186 JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withName("3b") 187 ).withName("2") 188 ).withName("1") 189 ).withName("0"); 190 191 var expectedMessage = "Bad layout path: attempting to select a sequence element from a non-sequence layout: " + 192 "[i4(3a)i4(3b)](2), selected from: " + 193 "[2:[i4(3a)i4(3b)](2)](1), selected from: " + 194 "[[2:[i4(3a)i4(3b)](2)](1)](0)"; 195 196 IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> 197 struct.select(PathElement.groupElement("1"), 198 PathElement.sequenceElement(), 199 PathElement.sequenceElement())); 200 assertEquals(iae.getMessage(), expectedMessage); 201 } 202 203 @Test 204 public void testBadSequencePathInOffset() { 205 SequenceLayout seq = MemoryLayout.sequenceLayout(10, JAVA_INT); 206 // bad path elements 207 for (PathElement e : List.of( sequenceElement(), sequenceElement(0, 2) )) { 208 try { 209 seq.byteOffset(e); 210 fail(); 211 } catch (IllegalArgumentException ex) { 212 assertTrue(true); 213 } 214 } 215 } 216 217 @Test 218 public void testBadSequencePathInSelect() { 219 SequenceLayout seq = MemoryLayout.sequenceLayout(10, JAVA_INT); 220 for (PathElement e : List.of( sequenceElement(0), sequenceElement(0, 2) )) { 221 try { 222 seq.select(e); 223 fail(); 224 } catch (IllegalArgumentException ex) { 225 assertTrue(true); 226 } 227 } 228 } 229 230 @Test(dataProvider = "groupSelectors") 231 public void testStructPaths(IntFunction<PathElement> groupSelector) { 232 long[] offsets = { 0, 1, 3, 7 }; 233 GroupLayout g = MemoryLayout.structLayout( 234 ValueLayout.JAVA_BYTE.withName("0"), 235 ValueLayout.JAVA_CHAR_UNALIGNED.withName("1"), 236 ValueLayout.JAVA_FLOAT_UNALIGNED.withName("2"), 237 ValueLayout.JAVA_LONG_UNALIGNED.withName("3") 238 ); 239 240 // test select 241 242 for (int i = 0 ; i < 4 ; i++) { 243 MemoryLayout selected = g.select(groupSelector.apply(i)); 244 assertTrue(selected == g.memberLayouts().get(i)); 245 } 246 247 // test offset 248 249 for (int i = 0 ; i < 4 ; i++) { 250 long byteOffset = g.byteOffset(groupSelector.apply(i)); 251 assertEquals(offsets[i], byteOffset); 252 } 253 } 254 255 @Test(dataProvider = "groupSelectors") 256 public void testUnionPaths(IntFunction<PathElement> groupSelector) { 257 long[] offsets = { 0, 0, 0, 0 }; 258 GroupLayout g = MemoryLayout.unionLayout( 259 ValueLayout.JAVA_BYTE.withName("0"), 260 ValueLayout.JAVA_CHAR.withName("1"), 261 ValueLayout.JAVA_FLOAT.withName("2"), 262 ValueLayout.JAVA_LONG.withName("3") 263 ); 264 265 // test select 266 267 for (int i = 0 ; i < 4 ; i++) { 268 MemoryLayout selected = g.select(groupSelector.apply(i)); 269 assertTrue(selected == g.memberLayouts().get(i)); 270 } 271 272 // test offset 273 274 for (int i = 0 ; i < 4 ; i++) { 275 long byteOffset = g.byteOffset(groupSelector.apply(i)); 276 assertEquals(offsets[i], byteOffset); 277 } 278 } 279 280 @DataProvider 281 public static Object[][] groupSelectors() { 282 return new Object[][] { 283 { (IntFunction<PathElement>) PathElement::groupElement }, // by index 284 { (IntFunction<PathElement>) i -> PathElement.groupElement(String.valueOf(i)) } // by name 285 }; 286 } 287 288 @Test 289 public void testSequencePaths() { 290 long[] offsets = { 0, 1, 2, 3 }; 291 SequenceLayout g = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_BYTE); 292 293 // test select 294 295 MemoryLayout selected = g.select(sequenceElement()); 296 assertTrue(selected == ValueLayout.JAVA_BYTE); 297 298 // test offset 299 300 for (int i = 0 ; i < 4 ; i++) { 301 long byteOffset = g.byteOffset(sequenceElement(i)); 302 assertEquals(offsets[i], byteOffset); 303 } 304 } 305 306 @Test(dataProvider = "testLayouts") 307 public void testOffsetHandle(MemoryLayout layout, PathElement[] pathElements, long[] indexes, 308 long expectedByteOffset) throws Throwable { 309 MethodHandle byteOffsetHandle = layout.byteOffsetHandle(pathElements); 310 byteOffsetHandle = byteOffsetHandle.asSpreader(long[].class, indexes.length); 311 long actualByteOffset = (long) byteOffsetHandle.invokeExact(0L, indexes); 312 assertEquals(actualByteOffset, expectedByteOffset); 313 } 314 315 @DataProvider 316 public static Object[][] testLayouts() { 317 List<Object[]> testCases = new ArrayList<>(); 318 319 testCases.add(new Object[] { 320 MemoryLayout.sequenceLayout(10, JAVA_INT), 321 new PathElement[] { sequenceElement() }, 322 new long[] { 4 }, 323 JAVA_INT.byteSize() * 4 324 }); 325 testCases.add(new Object[] { 326 MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(JAVA_INT, JAVA_INT.withName("y"))), 327 new PathElement[] { sequenceElement(), groupElement("y") }, 328 new long[] { 4 }, 329 (JAVA_INT.byteSize() * 2) * 4 + JAVA_INT.byteSize() 330 }); 331 testCases.add(new Object[] { 332 MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(4), JAVA_INT.withName("y"))), 333 new PathElement[] { sequenceElement(), groupElement("y") }, 334 new long[] { 4 }, 335 (JAVA_INT.byteSize() + 4) * 4 + 4 336 }); 337 testCases.add(new Object[] { 338 MemoryLayout.sequenceLayout(10, JAVA_INT), 339 new PathElement[] { sequenceElement() }, 340 new long[] { 4 }, 341 JAVA_INT.byteSize() * 4 342 }); 343 testCases.add(new Object[] { 344 MemoryLayout.structLayout( 345 MemoryLayout.sequenceLayout(10, JAVA_INT).withName("data") 346 ), 347 new PathElement[] { groupElement("data"), sequenceElement() }, 348 new long[] { 4 }, 349 JAVA_INT.byteSize() * 4 350 }); 351 352 MemoryLayout complexLayout = MemoryLayout.structLayout( 353 MemoryLayout.sequenceLayout(10, 354 MemoryLayout.sequenceLayout(10, 355 MemoryLayout.structLayout( 356 JAVA_INT.withName("x"), 357 JAVA_INT.withName("y") 358 ) 359 ) 360 ).withName("data") 361 ); 362 363 testCases.add(new Object[] { 364 complexLayout, 365 new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("x") }, 366 new long[] { 0, 1 }, 367 (JAVA_INT.byteSize() * 2) 368 }); 369 testCases.add(new Object[] { 370 complexLayout, 371 new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("x") }, 372 new long[] { 1, 0 }, 373 (JAVA_INT.byteSize() * 2) * 10 374 }); 375 testCases.add(new Object[] { 376 complexLayout, 377 new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("y") }, 378 new long[] { 0, 1 }, 379 (JAVA_INT.byteSize() * 2) + JAVA_INT.byteSize() 380 }); 381 testCases.add(new Object[] { 382 complexLayout, 383 new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("y") }, 384 new long[] { 1, 0 }, 385 (JAVA_INT.byteSize() * 2) * 10 + JAVA_INT.byteSize() 386 }); 387 388 return testCases.toArray(Object[][]::new); 389 } 390 391 @Test(dataProvider = "testLayouts") 392 public void testSliceHandle(MemoryLayout layout, PathElement[] pathElements, long[] indexes, 393 long expectedByteOffset) throws Throwable { 394 MemoryLayout selected = layout.select(pathElements); 395 MethodHandle sliceHandle = layout.sliceHandle(pathElements); 396 sliceHandle = sliceHandle.asSpreader(long[].class, indexes.length); 397 398 try (Arena arena = Arena.ofConfined()) { 399 MemorySegment segment = arena.allocate(layout); 400 MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, 0L, indexes); 401 assertEquals(slice.address() - segment.address(), expectedByteOffset); 402 assertEquals(slice.byteSize(), selected.byteSize()); 403 } 404 } 405 406 }