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