1 /*
2 * Copyright (c) 2019, 2020, 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 * @run testng TestSegments
27 */
28
29 import jdk.incubator.foreign.MemoryAddress;
30 import jdk.incubator.foreign.MemoryLayout;
31 import jdk.incubator.foreign.MemoryLayouts;
32 import jdk.incubator.foreign.MemorySegment;
33 import org.testng.annotations.DataProvider;
34 import org.testng.annotations.Test;
35 import java.lang.invoke.VarHandle;
36 import java.lang.reflect.Method;
37 import java.lang.reflect.Modifier;
38 import java.nio.ByteBuffer;
39 import java.nio.ByteOrder;
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Spliterator;
43 import java.util.concurrent.atomic.AtomicBoolean;
44 import java.util.concurrent.atomic.AtomicReference;
45 import java.util.function.LongFunction;
46 import java.util.function.Supplier;
47 import java.util.stream.Stream;
48 import static jdk.incubator.foreign.MemorySegment.WRITE;
49 import static org.testng.Assert.*;
50
51 public class TestSegments {
52
53 @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class)
54 public void testBadAllocateAlign(long size, long align) {
55 MemorySegment.allocateNative(size, align);
56 }
57
58 @Test(dataProvider = "badLayouts", expectedExceptions = UnsupportedOperationException.class)
59 public void testBadAllocateLayout(MemoryLayout layout) {
60 MemorySegment.allocateNative(layout);
61 }
62
63 @Test(expectedExceptions = { OutOfMemoryError.class,
64 IllegalArgumentException.class })
65 public void testAllocateTooBig() {
66 MemorySegment.allocateNative(Long.MAX_VALUE);
67 }
68
69 @Test(dataProvider = "segmentOperations")
70 public void testOpOutsideConfinement(SegmentMember member) throws Throwable {
71 try (MemorySegment segment = MemorySegment.allocateNative(4)) {
72 AtomicBoolean failed = new AtomicBoolean(false);
73 Thread t = new Thread(() -> {
74 try {
75 Object o = member.method.invoke(segment, member.params);
76 if (member.method.getName().equals("acquire")) {
77 ((MemorySegment)o).close();
78 }
79 } catch (ReflectiveOperationException ex) {
80 throw new IllegalStateException(ex);
81 }
82 });
83 t.setUncaughtExceptionHandler((thread, ex) -> failed.set(true));
84 t.start();
85 t.join();
86 assertEquals(failed.get(), member.isConfined());
87 }
88 }
89
90 @Test
91 public void testNativeSegmentIsZeroed() {
92 VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
93 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
94 try (MemorySegment segment = MemorySegment.allocateNative(1000)) {
95 for (long i = 0 ; i < segment.byteSize() ; i++) {
96 assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i));
97 }
98 }
99 }
100
101 @Test
102 public void testNothingSegmentAccess() {
103 VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
104 long[] values = { 0L, Integer.MAX_VALUE - 1, (long) Integer.MAX_VALUE + 1 };
105 for (long value : values) {
106 MemoryAddress addr = MemoryAddress.ofLong(value);
107 try {
108 longHandle.get(addr);
109 } catch (UnsupportedOperationException ex) {
110 assertTrue(ex.getMessage().contains("Required access mode"));
111 }
112 }
113 }
114
115 @Test(expectedExceptions = UnsupportedOperationException.class)
116 public void testNothingSegmentOffset() {
117 MemoryAddress addr = MemoryAddress.ofLong(42);
118 assertNull(addr.segment());
119 addr.segmentOffset();
120 }
121
122 @Test
123 public void testSlices() {
124 VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
125 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
126 try (MemorySegment segment = MemorySegment.allocateNative(10)) {
127 //init
128 for (byte i = 0 ; i < segment.byteSize() ; i++) {
129 byteHandle.set(segment.baseAddress(), (long)i, i);
130 }
131 long start = 0;
132 MemoryAddress base = segment.baseAddress();
133 MemoryAddress last = base.addOffset(10);
134 while (!base.equals(last)) {
135 MemorySegment slice = segment.asSlice(base.segmentOffset(), 10 - start);
136 for (long i = start ; i < 10 ; i++) {
137 assertEquals(
138 byteHandle.get(segment.baseAddress(), i),
139 byteHandle.get(slice.baseAddress(), i - start)
140 );
141 }
142 base = base.addOffset(1);
143 start++;
144 }
145 }
146 }
147
148 @Test(dataProvider = "accessModes")
149 public void testAccessModes(int accessModes) {
150 int[] arr = new int[1];
151 for (AccessActions action : AccessActions.values()) {
152 MemorySegment segment = MemorySegment.ofArray(arr);
153 MemorySegment restrictedSegment = segment.withAccessModes(accessModes);
154 assertEquals(restrictedSegment.accessModes(), accessModes);
155 boolean shouldFail = !restrictedSegment.hasAccessModes(action.accessMode);
156 try {
157 action.run(restrictedSegment);
158 assertFalse(shouldFail);
159 } catch (UnsupportedOperationException ex) {
160 assertTrue(shouldFail);
161 }
162 }
163 }
164
165 @DataProvider(name = "segmentFactories")
166 public Object[][] segmentFactories() {
167 List<Supplier<MemorySegment>> l = List.of(
168 () -> MemorySegment.ofArray(new byte[] { 0x00, 0x01, 0x02, 0x03 }),
169 () -> MemorySegment.ofArray(new char[] {'a', 'b', 'c', 'd' }),
170 () -> MemorySegment.ofArray(new double[] { 1d, 2d, 3d, 4d} ),
171 () -> MemorySegment.ofArray(new float[] { 1.0f, 2.0f, 3.0f, 4.0f }),
172 () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }),
173 () -> MemorySegment.ofArray(new long[] { 1l, 2l, 3l, 4l } ),
174 () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ),
175 () -> MemorySegment.allocateNative(4),
176 () -> MemorySegment.allocateNative(4, 8),
177 () -> MemorySegment.allocateNative(MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder()))
178 );
179 return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
180 }
181
182 @Test(dataProvider = "segmentFactories")
183 public void testFill(Supplier<MemorySegment> memorySegmentSupplier) {
184 VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
185 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
186
187 for (byte value : new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0x45}) {
188 try (MemorySegment segment = memorySegmentSupplier.get()) {
189 segment.fill(value);
190 for (long l = 0; l < segment.byteSize(); l++) {
191 assertEquals((byte) byteHandle.get(segment.baseAddress(), l), value);
192 }
193
194 // fill a slice
195 var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value);
196 for (long l = 0; l < sliceSegment.byteSize(); l++) {
197 assertEquals((byte) byteHandle.get(sliceSegment.baseAddress(), l), ~value);
198 }
199 // assert enclosing slice
200 assertEquals((byte) byteHandle.get(segment.baseAddress(), 0L), value);
201 for (long l = 1; l < segment.byteSize() - 2; l++) {
202 assertEquals((byte) byteHandle.get(segment.baseAddress(), l), (byte) ~value);
203 }
204 assertEquals((byte) byteHandle.get(segment.baseAddress(), segment.byteSize() - 1L), value);
205 }
206 }
207 }
208
209 @Test(dataProvider = "segmentFactories", expectedExceptions = IllegalStateException.class)
210 public void testFillClosed(Supplier<MemorySegment> memorySegmentSupplier) {
211 MemorySegment segment = memorySegmentSupplier.get();
212 segment.close();
213 segment.fill((byte) 0xFF);
214 }
215
216 @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class)
217 public void testFillIllegalAccessMode(Supplier<MemorySegment> memorySegmentSupplier) {
218 try (MemorySegment segment = memorySegmentSupplier.get()) {
219 segment.withAccessModes(segment.accessModes() & ~WRITE).fill((byte) 0xFF);
220 }
221 }
222
223 @Test(dataProvider = "segmentFactories")
224 public void testFillThread(Supplier<MemorySegment> memorySegmentSupplier) throws Exception {
225 try (MemorySegment segment = memorySegmentSupplier.get()) {
226 AtomicReference<RuntimeException> exception = new AtomicReference<>();
227 Runnable action = () -> {
228 try {
229 segment.fill((byte) 0xBA);
230 } catch (RuntimeException e) {
231 exception.set(e);
232 }
233 };
234 Thread thread = new Thread(action);
235 thread.start();
236 thread.join();
237
238 RuntimeException e = exception.get();
239 if (!(e instanceof IllegalStateException)) {
240 throw e;
241 }
242 }
243 }
244
245 @Test
246 public void testFillEmpty() {
247 MemorySegment.ofArray(new byte[] { }).fill((byte) 0xFF);
248 MemorySegment.ofArray(new byte[2]).asSlice(0, 0).fill((byte) 0xFF);
249 MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(0)).fill((byte) 0xFF);
250 }
251
252 @Test(expectedExceptions = IllegalArgumentException.class)
253 public void testBadWithAccessModes() {
254 int[] arr = new int[1];
255 MemorySegment segment = MemorySegment.ofArray(arr);
256 segment.withAccessModes((1 << AccessActions.values().length) + 1);
257 }
258
259 @Test(expectedExceptions = IllegalArgumentException.class)
260 public void testBadHasAccessModes() {
261 int[] arr = new int[1];
262 MemorySegment segment = MemorySegment.ofArray(arr);
263 segment.hasAccessModes((1 << AccessActions.values().length) + 1);
264 }
265
266 @DataProvider(name = "badSizeAndAlignments")
267 public Object[][] sizesAndAlignments() {
268 return new Object[][] {
269 { -1, 8 },
270 { 1, 15 },
271 { 1, -15 }
272 };
273 }
274
275 @DataProvider(name = "badLayouts")
276 public Object[][] layouts() {
277 SizedLayoutFactory[] layoutFactories = SizedLayoutFactory.values();
278 Object[][] values = new Object[layoutFactories.length * 2][2];
279 for (int i = 0; i < layoutFactories.length ; i++) {
280 values[i * 2] = new Object[] { MemoryLayout.ofStruct(layoutFactories[i].make(7), MemoryLayout.ofPaddingBits(9)) }; // good size, bad align
281 values[(i * 2) + 1] = new Object[] { layoutFactories[i].make(15).withBitAlignment(16) }; // bad size, good align
282 }
283 return values;
284 }
285
286 enum SizedLayoutFactory {
287 VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
288 VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
289 PADDING(MemoryLayout::ofPaddingBits);
290
291 private final LongFunction<MemoryLayout> factory;
292
293 SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
294 this.factory = factory;
295 }
296
297 MemoryLayout make(long size) {
298 return factory.apply(size);
299 }
300 }
301
302 @DataProvider(name = "segmentOperations")
303 static Object[][] segmentMembers() {
304 List<SegmentMember> members = new ArrayList<>();
305 for (Method m : MemorySegment.class.getDeclaredMethods()) {
306 //skip statics and method declared in j.l.Object
307 if (m.getDeclaringClass().equals(Object.class) ||
308 (m.getModifiers() & Modifier.STATIC) != 0) continue;
309 Object[] args = Stream.of(m.getParameterTypes())
310 .map(TestSegments::defaultValue)
311 .toArray();
312 members.add(new SegmentMember(m, args));
313 }
314 return members.stream().map(ms -> new Object[] { ms }).toArray(Object[][]::new);
315 }
316
317 static class SegmentMember {
318 final Method method;
319 final Object[] params;
320
321 final static List<String> CONFINED_NAMES = List.of(
322 "close",
323 "fill",
324 "toByteArray",
325 "withOwnerThread"
326 );
327
328 public SegmentMember(Method method, Object[] params) {
329 this.method = method;
330 this.params = params;
331 }
332
333 boolean isConfined() {
334 return CONFINED_NAMES.contains(method.getName());
335 }
336
337 @Override
338 public String toString() {
339 return method.getName();
340 }
341 }
342
343 static Object defaultValue(Class<?> c) {
344 if (c.isPrimitive()) {
345 if (c == char.class) {
346 return (char)0;
347 } else if (c == boolean.class) {
348 return false;
349 } else if (c == byte.class) {
350 return (byte)0;
351 } else if (c == short.class) {
352 return (short)0;
353 } else if (c == int.class) {
354 return 0;
355 } else if (c == long.class) {
356 return 0L;
357 } else if (c == float.class) {
358 return 0f;
359 } else if (c == double.class) {
360 return 0d;
361 } else {
362 throw new IllegalStateException();
363 }
364 } else {
365 return null;
366 }
367 }
368
369 @DataProvider(name = "accessModes")
370 public Object[][] accessModes() {
371 int nActions = AccessActions.values().length;
372 Object[][] results = new Object[1 << nActions][];
373 for (int accessModes = 0 ; accessModes < results.length ; accessModes++) {
374 results[accessModes] = new Object[] { accessModes };
375 }
376 return results;
377 }
378
379 enum AccessActions {
380 ACQUIRE(MemorySegment.ACQUIRE) {
381 @Override
382 void run(MemorySegment segment) {
383 Spliterator<MemorySegment> spliterator =
384 MemorySegment.spliterator(segment, MemoryLayout.ofSequence(segment.byteSize(), MemoryLayouts.JAVA_BYTE));
385 AtomicReference<RuntimeException> exception = new AtomicReference<>();
386 Runnable action = () -> {
387 try {
388 spliterator.tryAdvance(s -> { });
389 } catch (RuntimeException e) {
390 exception.set(e);
391 }
392 };
393 Thread thread = new Thread(action);
394 thread.start();
395 try {
396 thread.join();
397 } catch (InterruptedException ex) {
398 throw new AssertionError(ex);
399 }
400 RuntimeException e = exception.get();
401 if (e != null) {
402 throw e;
403 }
404 }
405 },
406 CLOSE(MemorySegment.CLOSE) {
407 @Override
408 void run(MemorySegment segment) {
409 segment.close();
410 }
411 },
412 READ(MemorySegment.READ) {
413 @Override
414 void run(MemorySegment segment) {
415 INT_HANDLE.get(segment.baseAddress());
416 }
417 },
418 WRITE(MemorySegment.WRITE) {
419 @Override
420 void run(MemorySegment segment) {
421 INT_HANDLE.set(segment.baseAddress(), 42);
422 }
423 },
424 HANDOFF(MemorySegment.HANDOFF) {
425 @Override
426 void run(MemorySegment segment) {
427 segment.withOwnerThread(new Thread());
428 }
429 };
430
431 final int accessMode;
432
433 static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.varHandle(int.class);
434
435 AccessActions(int accessMode) {
436 this.accessMode = accessMode;
437 }
438
439 abstract void run(MemorySegment segment);
440 }
441 }