1 /*
  2  * Copyright (c) 2018, 2021, 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 primitive class array
 28  * @run testng/othervm -XX:FlatArrayElementMaxSize=-1 ArrayElementVarHandleTest
 29  * @run testng/othervm -XX:FlatArrayElementMaxSize=0  ArrayElementVarHandleTest
 30  */
 31 
 32 import java.lang.invoke.*;
 33 
 34 import org.testng.annotations.DataProvider;
 35 import org.testng.annotations.Test;
 36 import static org.testng.Assert.*;
 37 
 38 public class ArrayElementVarHandleTest {
 39     private static final Point P = Point.makePoint(10, 20);
 40     private static final Line L = Line.makeLine(10, 20, 30, 40);
 41     private static final MutablePath PATH = MutablePath.makePath(10, 20, 30, 40);
 42 
 43     private static final Point[] POINTS = new Point[]{
 44             Point.makePoint(1, 2),
 45             Point.makePoint(10, 20),
 46             Point.makePoint(100, 200)
 47     };
 48 
 49     private static final Point.ref[] NULL_POINTS = new Point.ref[]{
 50             Point.makePoint(11, 22),
 51             Point.makePoint(110, 220),
 52             null
 53     };
 54 
 55     private static final Line[] LINES = new Line[]{
 56             Line.makeLine(1, 2, 3, 4),
 57             Line.makeLine(10, 20, 30, 40),
 58             Line.makeLine(15, 25, 35, 45),
 59             Line.makeLine(20, 30, 40, 50)
 60     };
 61 
 62     private static final Line.ref[] NULL_LINES = new Line.ref[] { null, null };
 63 
 64     private static final NonFlattenValue[] NFV_ARRAY = new NonFlattenValue[]{
 65             NonFlattenValue.make(1, 2),
 66             NonFlattenValue.make(10, 20),
 67             NonFlattenValue.make(100, 200)
 68     };
 69 
 70     /*
 71      * VarHandle of Object[].class
 72      */
 73     @Test
 74     public void testObjectArrayVarHandle() throws Throwable {
 75         // Point[] <: Point.ref[] <: Object
 76         Object[] array1 = newArray(Object[].class, POINTS.length);
 77         setElements(array1, POINTS);
 78         setElements(array1, NULL_POINTS);
 79         setElements(array1, new Object[] { "abc", Point.makePoint(1, 2) });
 80 
 81         Point.ref[] array2 = new Point.ref[NULL_POINTS.length];
 82         setElements(array2, POINTS);
 83         setElements(array2, NULL_POINTS);
 84 
 85         Point[] array3 = new Point[POINTS.length];
 86         setElements(array3, POINTS);
 87     }
 88 
 89     /*
 90      * VarHandle of Point.ref[].class
 91      */
 92     @Test
 93     public void testPointRefVarHandle() throws Throwable {
 94         // Point[] <: Point.ref[] <: Object
 95         Point.ref[] array1 = (Point.ref[])newArray(Point.ref[].class, POINTS.length);
 96         assertTrue(array1.getClass().componentType() == Point.ref.class);
 97 
 98         setElements(array1, POINTS);
 99         setElements(array1, NULL_POINTS);
100 
101         Point.ref[] array2 = new Point.ref[NULL_POINTS.length];
102         setElements(array2, POINTS);
103         setElements(array2, NULL_POINTS);
104 
105         Point[] array3 = new Point[POINTS.length];
106         setElements(array3, POINTS);
107     }
108 
109     /*
110      * VarHandle of Point[].class
111      */
112     @Test
113     public void testPointArrayVarHandle()  throws Throwable {
114         // Point[] <: Point.ref[] <: Object
115         Point[] array1 = (Point[]) newArray(Point[].class, POINTS.length);
116         assertTrue(array1.getClass().componentType() == Point.class.asValueType());
117         setElements(array1, POINTS);
118 
119         Point[] array3 = new Point[POINTS.length];
120         setElements(array3, POINTS);
121     }
122 
123     /*
124      * VarHandle of Line.ref[].class
125      */
126     @Test
127     public void testLineRefVarHandle() throws Throwable {
128         // Line[] <: Line.ref[]
129         Line.ref[] array1 = (Line.ref[])newArray(Line.ref[].class, LINES.length);
130         assertTrue(array1.getClass().componentType() == Line.ref.class);
131 
132         setElements(array1, LINES);
133         setElements(array1, NULL_LINES);
134 
135         Line.ref[] array2 = new Line.ref[LINES.length];
136         setElements(array2, LINES);
137         setElements(array2, NULL_LINES);
138 
139         Line[] array3 = new Line[LINES.length];
140         setElements(array3, LINES);
141     }
142 
143     /*
144      * VarHandle of Line[].class
145      */
146     @Test
147     public void testLineVarHandle() throws Throwable {
148         Line[] array1 = (Line[])newArray(Line[].class, LINES.length);
149         assertTrue(array1.getClass().componentType() == Line.class.asValueType());
150         setElements(array1, LINES);
151 
152         Line[] array3 = new Line[LINES.length];
153         setElements(array3, LINES);
154     }
155 
156     /*
157      * VarHandle of NonFlattenValue[].class
158      */
159     @Test
160     public void testNonFlattenedValueVarHandle() throws Throwable {
161         NonFlattenValue[] array1 = (NonFlattenValue[])newArray(NonFlattenValue[].class, NFV_ARRAY.length);
162         assertTrue(array1.getClass().componentType() == NonFlattenValue.class.asValueType());
163         setElements(array1, NFV_ARRAY);
164 
165         NonFlattenValue[] array3 = new NonFlattenValue[POINTS.length];
166         setElements(array3, NFV_ARRAY);
167     }
168 
169     Object[] newArray(Class<?> arrayType, int size) throws Throwable {
170         MethodHandle ctor = MethodHandles.arrayConstructor(arrayType);
171         return (Object[]) ctor.invoke(size);
172     }
173 
174     void setElements(Object[] array, Object[] elements) {
175         Class<?> arrayType = array.getClass();
176         assertTrue(array.length >= elements.length);
177 
178         VarHandle vh = MethodHandles.arrayElementVarHandle(arrayType);
179         set(vh, array.clone(), elements);
180         setVolatile(vh, array.clone(), elements);
181         setOpaque(vh, array.clone(), elements);
182         setRelease(vh, array.clone(), elements);
183         getAndSet(vh, array.clone(), elements);
184         compareAndSet(vh, array.clone(), elements);
185         compareAndExchange(vh, array.clone(), elements);
186     }
187 
188     // VarHandle::set
189     void set(VarHandle vh, Object[] array, Object[] elements) {
190         for (int i = 0; i < elements.length; i++) {
191             vh.set(array, i, elements[i]);
192         }
193         for (int i = 0; i < elements.length; i++) {
194             Object v = (Object) vh.get(array, i);
195             assertEquals(v, elements[i]);
196         }
197     }
198 
199     // VarHandle::setVolatile
200     void setVolatile(VarHandle vh, Object[] array, Object[] elements) {
201         for (int i = 0; i < elements.length; i++) {
202             vh.setVolatile(array, i, elements[i]);
203         }
204         for (int i = 0; i < elements.length; i++) {
205             Object v = (Object) vh.getVolatile(array, i);
206             assertEquals(v, elements[i]);
207         }
208     }
209 
210     // VarHandle::setOpaque
211     void setOpaque(VarHandle vh, Object[] array, Object[] elements) {
212         for (int i = 0; i < elements.length; i++) {
213             vh.setOpaque(array, i, elements[i]);
214         }
215         for (int i = 0; i < elements.length; i++) {
216             Object v = (Object) vh.getOpaque(array, i);
217             assertEquals(v, elements[i]);
218         }
219     }
220 
221     // VarHandle::setRelease
222     void setRelease(VarHandle vh, Object[] array, Object[] elements) {
223         for (int i = 0; i < elements.length; i++) {
224             vh.setRelease(array, i, elements[i]);
225         }
226         for (int i = 0; i < elements.length; i++) {
227             Object v = (Object) vh.getAcquire(array, i);
228             assertEquals(v, elements[i]);
229         }
230     }
231 
232     void getAndSet(VarHandle vh, Object[] array, Object[] elements) {
233         for (int i = 0; i < elements.length; i++) {
234             Object o = vh.getAndSet(array, i, elements[i]);
235         }
236         for (int i = 0; i < elements.length; i++) {
237             Object v = (Object) vh.get(array, i);
238             assertEquals(v, elements[i]);
239         }
240     }
241 
242     // sanity CAS test
243     // see test/jdk/java/lang/invoke/VarHandles tests
244     void compareAndSet(VarHandle vh, Object[] array, Object[] elements) {
245         // initialize to some values
246         for (int i = 0; i < elements.length; i++) {
247             vh.set(array, i, elements[i]);
248         }
249         // shift to the right element
250         for (int i = 0; i < elements.length; i++) {
251             Object v = elements[i + 1 < elements.length ? i + 1 : 0];
252             boolean cas = vh.compareAndSet(array, i, elements[i], v);
253             if (!cas)
254                 System.out.format("cas = %s array[%d] = %s vs old = %s new = %s%n", cas, i, array[i], elements[i], v);
255             assertTrue(cas);
256         }
257     }
258 
259     void compareAndExchange(VarHandle vh, Object[] array, Object[] elements) {
260         // initialize to some values
261         for (int i = 0; i < elements.length; i++) {
262             vh.set(array, i, elements[i]);
263         }
264         // shift to the right element
265         for (int i = 0; i < elements.length; i++) {
266             Object v = elements[i + 1 < elements.length ? i + 1 : 0];
267             assertEquals(vh.compareAndExchange(array, i, elements[i], v), elements[i]);
268         }
269     }
270 
271 
272 }