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