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  * @test
 26  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAccessModes
 27  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAccessModes
 28  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAccessModes
 29  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAccessModes
 30  */
 31 
 32 import java.lang.foreign.AddressLayout;
 33 import java.lang.foreign.Arena;
 34 import java.lang.foreign.MemoryLayout;
 35 import java.lang.foreign.MemorySegment;
 36 import java.lang.foreign.ValueLayout;
 37 import java.lang.invoke.MethodHandle;
 38 import java.lang.invoke.MethodHandles;
 39 import java.lang.invoke.MethodType;
 40 import java.lang.invoke.VarHandle;
 41 import java.lang.invoke.VarHandle.AccessMode;
 42 import java.util.ArrayList;
 43 import java.util.EnumSet;
 44 import java.util.List;
 45 import java.util.Set;
 46 
 47 import org.testng.annotations.*;
 48 
 49 import static org.testng.Assert.*;
 50 public class TestAccessModes {
 51 
 52     @Test(dataProvider = "segmentsAndLayoutsAndModes")
 53     public void testAccessModes(MemorySegment segment, ValueLayout layout, AccessMode mode) throws Throwable {
 54         VarHandle varHandle = layout.varHandle();
 55         MethodHandle methodHandle = varHandle.toMethodHandle(mode);
 56         boolean compatible = AccessModeKind.supportedModes(layout).contains(AccessModeKind.of(mode));
 57         try {
 58             Object o = methodHandle.invokeWithArguments(makeArgs(segment, varHandle.accessModeType(mode)));
 59             assertTrue(compatible);
 60         } catch (UnsupportedOperationException ex) {
 61             assertFalse(compatible);
 62         } catch (IllegalArgumentException ex) {
 63             // access is unaligned, but access mode is supported
 64             assertTrue(compatible);
 65         }
 66     }
 67 
 68     Object[] makeArgs(MemorySegment segment, MethodType type) throws Throwable {
 69         List<Object> args = new ArrayList<>();
 70         args.add(segment);
 71         for (Class argType : type.dropParameterTypes(0, 1).parameterList()) {
 72             args.add(defaultValue(argType));
 73         }
 74         return args.toArray();
 75     }
 76 
 77     Object defaultValue(Class<?> clazz) throws Throwable {
 78         if (clazz == MemorySegment.class) {
 79             return MemorySegment.NULL;
 80         } else if (clazz.isPrimitive()) {
 81             return MethodHandles.zero(clazz).invoke();
 82         } else {
 83             throw new UnsupportedOperationException();
 84         }
 85     }
 86 
 87     /*
 88      * See the javadoc of MemoryLayout::varHandle.
 89      */
 90     enum AccessModeKind {
 91         PLAIN,
 92         READ_WRITE,
 93         ATOMIC_UPDATE,
 94         ATOMIC_NUMERIC_UPDATE,
 95         ATOMIC_BITWISE_UPDATE;
 96 
 97         static AccessModeKind of(AccessMode mode) {
 98             return switch (mode) {
 99                 case GET, SET -> PLAIN;
100                 case GET_ACQUIRE, GET_OPAQUE, GET_VOLATILE, SET_VOLATILE,
101                         SET_OPAQUE, SET_RELEASE -> READ_WRITE;
102                 case GET_AND_SET, GET_AND_SET_ACQUIRE, GET_AND_SET_RELEASE,
103                         WEAK_COMPARE_AND_SET, WEAK_COMPARE_AND_SET_RELEASE,
104                         WEAK_COMPARE_AND_SET_ACQUIRE, WEAK_COMPARE_AND_SET_PLAIN,
105                         COMPARE_AND_EXCHANGE, COMPARE_AND_EXCHANGE_ACQUIRE,
106                         COMPARE_AND_EXCHANGE_RELEASE, COMPARE_AND_SET -> ATOMIC_UPDATE;
107                 case GET_AND_ADD, GET_AND_ADD_ACQUIRE, GET_AND_ADD_RELEASE -> ATOMIC_NUMERIC_UPDATE;
108                 default -> ATOMIC_BITWISE_UPDATE;
109             };
110         }
111 
112         static Set<AccessModeKind> supportedModes(ValueLayout layout) {
113             Set<AccessModeKind> supportedModes = EnumSet.noneOf(AccessModeKind.class);
114             supportedModes.add(PLAIN);
115             if (layout.byteAlignment() >= layout.byteSize()) {
116                 supportedModes.add(READ_WRITE);
117                 if (layout instanceof ValueLayout.OfInt || layout instanceof ValueLayout.OfLong ||
118                         layout instanceof ValueLayout.OfFloat || layout instanceof ValueLayout.OfDouble ||
119                         layout instanceof AddressLayout) {
120                     supportedModes.add(ATOMIC_UPDATE);
121                 }
122                 if (layout instanceof ValueLayout.OfInt || layout instanceof ValueLayout.OfLong ||
123                         layout instanceof AddressLayout) {
124                     supportedModes.add(ATOMIC_NUMERIC_UPDATE);
125                     supportedModes.add(ATOMIC_BITWISE_UPDATE);
126                 }
127             }
128             return supportedModes;
129         }
130     }
131 
132     static MemoryLayout[] layouts() {
133         MemoryLayout[] valueLayouts = {
134                 ValueLayout.JAVA_BOOLEAN,
135                 ValueLayout.JAVA_CHAR,
136                 ValueLayout.JAVA_BYTE,
137                 ValueLayout.JAVA_SHORT,
138                 ValueLayout.JAVA_INT,
139                 ValueLayout.JAVA_FLOAT,
140                 ValueLayout.JAVA_LONG,
141                 ValueLayout.JAVA_DOUBLE,
142                 ValueLayout.ADDRESS
143         };
144         List<MemoryLayout> layouts = new ArrayList<>();
145         for (MemoryLayout layout : valueLayouts) {
146             for (int align : new int[] { 1, 2, 4, 8 }) {
147                 layouts.add(layout.withByteAlignment(align));
148             }
149         }
150         return layouts.toArray(new MemoryLayout[0]);
151     }
152 
153     static MemorySegment[] segments() {
154         return new MemorySegment[]{
155                 Arena.ofAuto().allocate(8),
156                 MemorySegment.ofArray(new byte[8]),
157                 MemorySegment.ofArray(new char[4]),
158                 MemorySegment.ofArray(new short[4]),
159                 MemorySegment.ofArray(new int[2]),
160                 MemorySegment.ofArray(new float[2]),
161                 MemorySegment.ofArray(new long[1]),
162                 MemorySegment.ofArray(new double[1])
163         };
164     }
165 
166     @DataProvider(name = "segmentsAndLayoutsAndModes")
167     static Object[][] segmentsAndLayoutsAndModes() {
168         List<Object[]> segmentsAndLayouts = new ArrayList<>();
169         for (MemorySegment segment : segments()) {
170             for (MemoryLayout layout : layouts()) {
171                 for (AccessMode mode : AccessMode.values()) {
172                     segmentsAndLayouts.add(new Object[]{segment, layout, mode});
173                 }
174             }
175         }
176         return segmentsAndLayouts.toArray(new Object[0][]);
177     }
178 
179 }