1 /*
  2  * Copyright (c) 2018, 2025, 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  * @summary test VarHandle on value class array
 28  * @enablePreview
 29  * @run junit/othervm -XX:+UseArrayFlattening ArrayElementVarHandleTest
 30  * @run junit/othervm -XX:-UseArrayFlattening  ArrayElementVarHandleTest
 31  */
 32 
 33 import java.lang.invoke.*;
 34 import java.util.stream.Stream;
 35 
 36 import jdk.internal.vm.annotation.NullRestricted;
 37 import jdk.internal.vm.annotation.Strict;
 38 import org.junit.jupiter.params.ParameterizedTest;
 39 import org.junit.jupiter.params.provider.Arguments;
 40 import org.junit.jupiter.params.provider.MethodSource;
 41 import static org.junit.jupiter.api.Assertions.*;
 42 
 43 public class ArrayElementVarHandleTest {
 44     static value class Point {
 45         public int x;
 46         public int y;
 47         Point(int x, int y) {
 48             this.x = x;
 49             this.y = y;
 50         }
 51     }
 52 
 53     static value class Line {
 54         @NullRestricted  @Strict
 55         Point p1;
 56         @NullRestricted  @Strict
 57         Point p2;
 58 
 59         Line(Point p1, Point p2) {
 60             this.p1 = p1;
 61             this.p2 = p2;
 62         }
 63         Line(int x1, int y1, int x2, int y2) {
 64             this(new Point(x1, y1), new Point(x2, y2));
 65         }
 66     }
 67 
 68     private static final Point[] POINTS = new Point[]{
 69             new Point(1, 2),
 70             new Point(10, 20),
 71             new Point(100, 200),
 72             null
 73     };
 74 
 75     private static final Line[] LINES = new Line[]{
 76             new Line(1, 2, 3, 4),
 77             new Line(10, 20, 30, 40),
 78             null
 79     };
 80 
 81     static Stream<Arguments> testCases() throws Throwable {
 82         int plen = POINTS.length;
 83         int llen = LINES.length;
 84         return Stream.of(
 85                 Arguments.of(newArray(Object[].class, plen),    POINTS),
 86                 Arguments.of(newArray(Object[].class, plen),    new Object[] { "abc", new Point(1, 2) }),
 87                 Arguments.of(newArray(Point[].class, plen),     POINTS),
 88                 Arguments.of(new Point[plen],                   POINTS),
 89 
 90                 Arguments.of(newArray(Object[].class, llen),    LINES),
 91                 Arguments.of(newArray(Line[].class, llen),      LINES),
 92                 Arguments.of(new Line[llen],                    LINES)
 93         );
 94     }
 95 
 96     /*
 97      * Constructs a new array of the specified type and size using
 98      * MethodHandle.
 99      */
100     private static Object[] newArray(Class<?> arrayType, int size) throws Throwable {
101         MethodHandle ctor = MethodHandles.arrayConstructor(arrayType);
102         return (Object[]) ctor.invoke(size);
103     }
104 
105     /*
106      * Test VarHandle to set elements of the given array with
107      * various access mode.
108      */
109     @ParameterizedTest
110     @MethodSource("testCases")
111     public void testSetArrayElements(Object[] array, Object[] elements) {
112         Class<?> arrayType = array.getClass();
113         assertTrue(array.length >= elements.length);
114 
115         VarHandle vh = MethodHandles.arrayElementVarHandle(arrayType);
116         set(vh, array.clone(), elements);
117         setVolatile(vh, array.clone(), elements);
118         setOpaque(vh, array.clone(), elements);
119         setRelease(vh, array.clone(), elements);
120         getAndSet(vh, array.clone(), elements);
121         compareAndSet(vh, array.clone(), elements);
122         compareAndExchange(vh, array.clone(), elements);
123     }
124 
125     // VarHandle::set
126     void set(VarHandle vh, Object[] array, Object[] elements) {
127         for (int i = 0; i < elements.length; i++) {
128             vh.set(array, i, elements[i]);
129         }
130         for (int i = 0; i < elements.length; i++) {
131             Object v = (Object) vh.get(array, i);
132             assertEquals(elements[i], v);
133         }
134     }
135 
136     // VarHandle::setVolatile
137     void setVolatile(VarHandle vh, Object[] array, Object[] elements) {
138         for (int i = 0; i < elements.length; i++) {
139             vh.setVolatile(array, i, elements[i]);
140         }
141         for (int i = 0; i < elements.length; i++) {
142             Object v = (Object) vh.getVolatile(array, i);
143             assertEquals(elements[i], v);
144         }
145     }
146 
147     // VarHandle::setOpaque
148     void setOpaque(VarHandle vh, Object[] array, Object[] elements) {
149         for (int i = 0; i < elements.length; i++) {
150             vh.setOpaque(array, i, elements[i]);
151         }
152         for (int i = 0; i < elements.length; i++) {
153             Object v = (Object) vh.getOpaque(array, i);
154             assertEquals(elements[i], v);
155         }
156     }
157 
158     // VarHandle::setRelease
159     void setRelease(VarHandle vh, Object[] array, Object[] elements) {
160         for (int i = 0; i < elements.length; i++) {
161             vh.setRelease(array, i, elements[i]);
162         }
163         for (int i = 0; i < elements.length; i++) {
164             Object v = (Object) vh.getAcquire(array, i);
165             assertEquals(elements[i], v);
166         }
167     }
168 
169     void getAndSet(VarHandle vh, Object[] array, Object[] elements) {
170         for (int i = 0; i < elements.length; i++) {
171             Object o = vh.getAndSet(array, i, elements[i]);
172         }
173         for (int i = 0; i < elements.length; i++) {
174             Object v = (Object) vh.get(array, i);
175             assertEquals(elements[i], v);
176         }
177     }
178 
179     // sanity CAS test
180     // see test/jdk/java/lang/invoke/VarHandles tests
181     void compareAndSet(VarHandle vh, Object[] array, Object[] elements) {
182         // initialize to some values
183         for (int i = 0; i < elements.length; i++) {
184             vh.set(array, i, elements[i]);
185         }
186         // shift to the right element
187         for (int i = 0; i < elements.length; i++) {
188             Object v = elements[i + 1 < elements.length ? i + 1 : 0];
189             boolean cas = vh.compareAndSet(array, i, elements[i], v);
190             if (!cas)
191                 System.out.format("cas = %s array[%d] = %s vs old = %s new = %s%n", cas, i, array[i], elements[i], v);
192             assertTrue(cas);
193         }
194     }
195 
196     void compareAndExchange(VarHandle vh, Object[] array, Object[] elements) {
197         // initialize to some values
198         for (int i = 0; i < elements.length; i++) {
199             vh.set(array, i, elements[i]);
200         }
201         // shift to the right element
202         for (int i = 0; i < elements.length; i++) {
203             Object v = elements[i + 1 < elements.length ? i + 1 : 0];
204             assertEquals(elements[i], vh.compareAndExchange(array, i, elements[i], v));
205         }
206     }
207 }