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 
  36 import java.lang.invoke.VarHandle;
  37 import java.lang.reflect.Method;
  38 import java.lang.reflect.Modifier;
  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.stream.Stream;
  47 
  48 import static org.testng.Assert.*;
  49 
  50 public class TestSegments {
  51 
  52     @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class)
  53     public void testBadAllocateAlign(long size, long align) {
  54         MemorySegment.allocateNative(size, align);
  55     }
  56 
  57     @Test(dataProvider = "badLayouts", expectedExceptions = UnsupportedOperationException.class)
  58     public void testBadAllocateLayout(MemoryLayout layout) {
  59         MemorySegment.allocateNative(layout);
  60     }
  61 
  62     @Test(expectedExceptions = { OutOfMemoryError.class,
  63                                  IllegalArgumentException.class })
  64     public void testAllocateTooBig() {
  65         MemorySegment.allocateNative(Long.MAX_VALUE);
  66     }
  67 
  68     @Test(dataProvider = "segmentOperations")
  69     public void testOpOutsideConfinement(SegmentMember member) throws Throwable {
  70         try (MemorySegment segment = MemorySegment.allocateNative(4)) {
  71             AtomicBoolean failed = new AtomicBoolean(false);
  72             Thread t = new Thread(() -> {
  73                 try {
  74                     Object o = member.method.invoke(segment, member.params);
  75                     if (member.method.getName().equals("acquire")) {
  76                         ((MemorySegment)o).close();
  77                     }
  78                 } catch (ReflectiveOperationException ex) {
  79                     throw new IllegalStateException(ex);
  80                 }
  81             });
  82             t.setUncaughtExceptionHandler((thread, ex) -> failed.set(true));
  83             t.start();
  84             t.join();
  85             assertEquals(failed.get(), member.isConfined());
  86         }
  87     }
  88 
  89     @Test
  90     public void testNativeSegmentIsZeroed() {
  91         VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
  92                 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
  93         try (MemorySegment segment = MemorySegment.allocateNative(1000)) {
  94             for (long i = 0 ; i < segment.byteSize() ; i++) {
  95                 assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i));
  96             }
  97         }
  98     }
  99 
 100     @Test
 101     public void testNothingSegmentAccess() {
 102         VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
 103         long[] values = { 0L, Integer.MAX_VALUE - 1, (long) Integer.MAX_VALUE + 1 };
 104         for (long value : values) {
 105             MemoryAddress addr = MemoryAddress.ofLong(value);
 106             try {
 107                 longHandle.get(addr);
 108             } catch (UnsupportedOperationException ex) {
 109                 assertTrue(ex.getMessage().contains("Required access mode"));
 110             }
 111         }
 112     }
 113 
 114     @Test(expectedExceptions = UnsupportedOperationException.class)
 115     public void testNothingSegmentOffset() {
 116         MemoryAddress addr = MemoryAddress.ofLong(42);
 117         assertNull(addr.segment());
 118         addr.segmentOffset();
 119     }
 120 
 121     @Test
 122     public void testSlices() {
 123         VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
 124                 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
 125         try (MemorySegment segment = MemorySegment.allocateNative(10)) {
 126             //init
 127             for (byte i = 0 ; i < segment.byteSize() ; i++) {
 128                 byteHandle.set(segment.baseAddress(), (long)i, i);
 129             }
 130             long start = 0;
 131             MemoryAddress base = segment.baseAddress();
 132             MemoryAddress last = base.addOffset(10);
 133             while (!base.equals(last)) {
 134                 MemorySegment slice = segment.asSlice(base.segmentOffset(), 10 - start);
 135                 for (long i = start ; i < 10 ; i++) {
 136                     assertEquals(
 137                             byteHandle.get(segment.baseAddress(), i),
 138                             byteHandle.get(slice.baseAddress(), i - start)
 139                     );
 140                 }
 141                 base = base.addOffset(1);
 142                 start++;
 143             }
 144         }
 145     }
 146 
 147     @Test(dataProvider = "accessModes")
 148     public void testAccessModes(int accessModes) {
 149         int[] arr = new int[1];
 150         for (AccessActions action : AccessActions.values()) {
 151             MemorySegment segment = MemorySegment.ofArray(arr);
 152             MemorySegment restrictedSegment = segment.withAccessModes(accessModes);
 153             assertEquals(restrictedSegment.accessModes(), accessModes);
 154             boolean shouldFail = !restrictedSegment.hasAccessModes(action.accessMode);
 155             try {
 156                 action.run(restrictedSegment);
 157                 assertFalse(shouldFail);
 158             } catch (UnsupportedOperationException ex) {
 159                 assertTrue(shouldFail);
 160             }
 161         }
 162     }
 163 
 164     @Test(expectedExceptions = IllegalArgumentException.class)
 165     public void testBadWithAccessModes() {
 166         int[] arr = new int[1];
 167         MemorySegment segment = MemorySegment.ofArray(arr);
 168         segment.withAccessModes((1 << AccessActions.values().length) + 1);
 169     }
 170 
 171     @Test(expectedExceptions = IllegalArgumentException.class)
 172     public void testBadHasAccessModes() {
 173         int[] arr = new int[1];
 174         MemorySegment segment = MemorySegment.ofArray(arr);
 175         segment.hasAccessModes((1 << AccessActions.values().length) + 1);
 176     }
 177 
 178     @DataProvider(name = "badSizeAndAlignments")
 179     public Object[][] sizesAndAlignments() {
 180         return new Object[][] {
 181                 { -1, 8 },
 182                 { 1, 15 },
 183                 { 1, -15 }
 184         };
 185     }
 186 
 187     @DataProvider(name = "badLayouts")
 188     public Object[][] layouts() {
 189         SizedLayoutFactory[] layoutFactories = SizedLayoutFactory.values();
 190         Object[][] values = new Object[layoutFactories.length * 2][2];
 191         for (int i = 0; i < layoutFactories.length ; i++) {
 192             values[i * 2] = new Object[] { MemoryLayout.ofStruct(layoutFactories[i].make(7), MemoryLayout.ofPaddingBits(9)) }; // good size, bad align
 193             values[(i * 2) + 1] = new Object[] { layoutFactories[i].make(15).withBitAlignment(16) }; // bad size, good align
 194         }
 195         return values;
 196     }
 197 
 198     enum SizedLayoutFactory {
 199         VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
 200         VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
 201         PADDING(MemoryLayout::ofPaddingBits);
 202 
 203         private final LongFunction<MemoryLayout> factory;
 204 
 205         SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
 206             this.factory = factory;
 207         }
 208 
 209         MemoryLayout make(long size) {
 210             return factory.apply(size);
 211         }
 212     }
 213 
 214     @DataProvider(name = "segmentOperations")
 215     static Object[][] segmentMembers() {
 216         List<SegmentMember> members = new ArrayList<>();
 217         for (Method m : MemorySegment.class.getDeclaredMethods()) {
 218             //skip statics and method declared in j.l.Object
 219             if (m.getDeclaringClass().equals(Object.class) ||
 220                     (m.getModifiers() & Modifier.STATIC) != 0) continue;
 221             Object[] args = Stream.of(m.getParameterTypes())
 222                     .map(TestSegments::defaultValue)
 223                     .toArray();
 224             members.add(new SegmentMember(m, args));
 225         }
 226         return members.stream().map(ms -> new Object[] { ms }).toArray(Object[][]::new);
 227     }
 228 
 229     static class SegmentMember {
 230         final Method method;
 231         final Object[] params;
 232 
 233         final static List<String> CONFINED_NAMES = List.of(
 234                 "close",
 235                 "toByteArray",
 236                 "withOwnerThread"
 237         );
 238 
 239         public SegmentMember(Method method, Object[] params) {
 240             this.method = method;
 241             this.params = params;
 242         }
 243 
 244         boolean isConfined() {
 245             return CONFINED_NAMES.contains(method.getName());
 246         }
 247 
 248         @Override
 249         public String toString() {
 250             return method.getName();
 251         }
 252     }
 253 
 254     static Object defaultValue(Class<?> c) {
 255         if (c.isPrimitive()) {
 256             if (c == char.class) {
 257                 return (char)0;
 258             } else if (c == boolean.class) {
 259                 return false;
 260             } else if (c == byte.class) {
 261                 return (byte)0;
 262             } else if (c == short.class) {
 263                 return (short)0;
 264             } else if (c == int.class) {
 265                 return 0;
 266             } else if (c == long.class) {
 267                 return 0L;
 268             } else if (c == float.class) {
 269                 return 0f;
 270             } else if (c == double.class) {
 271                 return 0d;
 272             } else {
 273                 throw new IllegalStateException();
 274             }
 275         } else {
 276             return null;
 277         }
 278     }
 279 
 280     @DataProvider(name = "accessModes")
 281     public Object[][] accessModes() {
 282         int nActions = AccessActions.values().length;
 283         Object[][] results = new Object[1 << nActions][];
 284         for (int accessModes = 0 ; accessModes < results.length ; accessModes++) {
 285             results[accessModes] = new Object[] { accessModes };
 286         }
 287         return results;
 288     }
 289 
 290     enum AccessActions {
 291         ACQUIRE(MemorySegment.ACQUIRE) {
 292             @Override
 293             void run(MemorySegment segment) {
 294                 Spliterator<MemorySegment> spliterator =
 295                         MemorySegment.spliterator(segment, MemoryLayout.ofSequence(segment.byteSize(), MemoryLayouts.JAVA_BYTE));
 296                 AtomicReference<RuntimeException> exception = new AtomicReference<>();
 297                 Runnable action = () -> {
 298                     try {
 299                         spliterator.tryAdvance(s -> { });
 300                     } catch (RuntimeException e) {
 301                         exception.set(e);
 302                     }
 303                 };
 304                 Thread thread = new Thread(action);
 305                 thread.start();
 306                 try {
 307                     thread.join();
 308                 } catch (InterruptedException ex) {
 309                     throw new AssertionError(ex);
 310                 }
 311                 RuntimeException e = exception.get();
 312                 if (e != null) {
 313                     throw e;
 314                 }
 315             }
 316         },
 317         CLOSE(MemorySegment.CLOSE) {
 318             @Override
 319             void run(MemorySegment segment) {
 320                 segment.close();
 321             }
 322         },
 323         READ(MemorySegment.READ) {
 324             @Override
 325             void run(MemorySegment segment) {
 326                 INT_HANDLE.get(segment.baseAddress());
 327             }
 328         },
 329         WRITE(MemorySegment.WRITE) {
 330             @Override
 331             void run(MemorySegment segment) {
 332                 INT_HANDLE.set(segment.baseAddress(), 42);
 333             }
 334         },
 335         HANDOFF(MemorySegment.HANDOFF) {
 336             @Override
 337             void run(MemorySegment segment) {
 338                 segment.withOwnerThread(new Thread());
 339             }
 340         };
 341 
 342         final int accessMode;
 343 
 344         static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.varHandle(int.class);
 345 
 346         AccessActions(int accessMode) {
 347             this.accessMode = accessMode;
 348         }
 349 
 350         abstract void run(MemorySegment segment);
 351     }
 352 }