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  * @test
 26  * @summary Basic test for Array::get, Array::set, Arrays::setAll on primitive class array
 27  * @run testng/othervm -XX:FlatArrayElementMaxSize=-1 ValueArray
 28  * @run testng/othervm -XX:FlatArrayElementMaxSize=0  ValueArray
 29  */
 30 
 31 import java.lang.reflect.Array;
 32 import java.util.Arrays;
 33 
 34 import org.testng.annotations.DataProvider;
 35 import org.testng.annotations.Test;
 36 import static org.testng.Assert.*;
 37 
 38 public class ValueArray {
 39     @DataProvider(name="elementTypes")
 40     static Object[][] elementTypes() {
 41         return new Object[][]{
 42             new Object[] { Point.class.asValueType(), Point.default },
 43             new Object[] { Point.ref.class, null },
 44             new Object[] { ValueOptional.class, null },
 45         };
 46     }
 47 
 48     /*
 49      * Test an array created from the given element type via Array::newInstance
 50      */
 51     @Test(dataProvider="elementTypes")
 52     public void testElementType(Class<?> elementType, Object defaultValue) {
 53         assertTrue(elementType.isValue());
 54         assertTrue(elementType.isPrimaryType() || defaultValue != null);
 55 
 56         Object[] array = (Object[])Array.newInstance(elementType, 1);
 57         Class<?> arrayType = array.getClass();
 58         assertTrue(arrayType.componentType() == elementType);
 59         // Array is a reference type
 60         assertTrue(arrayType.isArray());
 61         assertTrue(arrayType.isPrimaryType());
 62         assertEquals(arrayType.asPrimaryType(), arrayType);
 63         assertTrue(array[0] == defaultValue);
 64 
 65         // check the element type of multi-dimensional array
 66         Object[][] multiArray = (Object[][])Array.newInstance(elementType, 1, 2, 3);
 67         Class<?> c = multiArray.getClass();
 68         while (c.getComponentType() != null) {
 69             c = c.getComponentType();
 70         }
 71         assertTrue(c == elementType);
 72     }
 73 
 74     @DataProvider(name="arrayTypes")
 75     static Object[][] arrayTypes() {
 76         return new Object[][] {
 77             new Object[] { Object[].class,
 78                            new Object[] { new Object(), new Object()}},
 79             new Object[] { Point[].class,
 80                            new Point[] { Point.makePoint(1, 2),
 81                                          Point.makePoint(10, 20),
 82                                          Point.makePoint(100, 200)}},
 83             new Object[] { Point[][].class,
 84                            new Point[][] { new Point[] { Point.makePoint(1, 2),
 85                                                          Point.makePoint(10, 20)}}},
 86             new Object[] { Point.ref[].class,
 87                            new Point.ref[] { Point.makePoint(11, 22),
 88                                              Point.makePoint(110, 220),
 89                                              null }},
 90             new Object[] { NonFlattenValue[].class,
 91                            new NonFlattenValue[] { NonFlattenValue.make(1, 2),
 92                                                    NonFlattenValue.make(10, 20),
 93                                                    NonFlattenValue.make(100, 200)}},
 94             new Object[] { Point[].class,  new Point[0] },
 95             new Object[] { Point.ref[].class,  new Point.ref[0] },
 96             new Object[] { ValueOptional[].class,  new ValueOptional[0] },
 97         };
 98     }
 99 
100     /*
101      * Test the following properties of an array of value class:
102      * - class name
103      * - array element can be null or not
104      * - array covariance if the element type is a primitive value type
105      */
106     @Test(dataProvider="arrayTypes")
107     public void testArrays(Class<?> arrayClass, Object[] array) {
108         testClassName(arrayClass);
109         testArrayElements(arrayClass, array);
110         Class<?> componentType = arrayClass.componentType();
111         if (componentType.isPrimitiveClass()) {
112             Object[] qArray = (Object[]) Array.newInstance(componentType.asValueType(), 0);
113             Object[] lArray = (Object[]) Array.newInstance(componentType.asPrimaryType(), 0);
114             testArrayCovariance(componentType, qArray, lArray);
115         }
116     }
117 
118     /**
119      * Verify the array class's name of the form "[QPoint;" or "[LPoint;"
120      */
121     static void testClassName(Class<?> arrayClass) {
122         // test class names
123         String arrayClassName = arrayClass.getName();
124         StringBuilder sb = new StringBuilder();
125         Class<?> c = arrayClass;
126         while (c.isArray()) {
127             sb.append("[");
128             c = c.getComponentType();
129         }
130         sb.append(c.isPrimitiveValueType() ? "Q" : "L").append(c.getName()).append(";");
131         assertEquals(sb.toString(), arrayClassName);
132     }
133 
134     /**
135      * Setting the elements of an array.
136      * NPE will be thrown if null is set on an element in an array of primitive value type
137      */
138     static void testArrayElements(Class<?> arrayClass, Object[] array) {
139         Class<?> componentType = arrayClass.getComponentType();
140         assertTrue(arrayClass.isArray());
141         assertTrue(array.getClass() == arrayClass);
142         Object[] newArray = (Object[]) Array.newInstance(componentType, array.length);
143         assertTrue(newArray.getClass() == arrayClass);
144         assertTrue(newArray.getClass().getComponentType() == componentType);
145 
146         // set elements
147         for (int i = 0; i < array.length; i++) {
148             Array.set(newArray, i, array[i]);
149         }
150         for (int i = 0; i < array.length; i++) {
151             Object o = Array.get(newArray, i);
152             assertEquals(o, array[i]);
153         }
154         Arrays.setAll(newArray, i -> array[i]);
155 
156         // test nullable
157         if (!componentType.isPrimitiveValueType()) {
158             for (int i = 0; i < newArray.length; i++) {
159                 Array.set(newArray, i, null);
160             }
161         } else {
162             for (int i = 0; i < newArray.length; i++) {
163                 try {
164                     Array.set(newArray, i, null);
165                     fail("expect NPE but not thrown");
166                 } catch (NullPointerException e) {
167                 }
168             }
169         }
170     }
171 
172     /**
173      * Point[] is a subtype of Point.ref[], which is a subtype of Object[].
174      */
175     static void testArrayCovariance(Class<?> componentType, Object[] qArray, Object[] lArray) {
176         assertTrue(componentType.isPrimitiveClass());
177 
178         // Class.instanceOf (self)
179         assertTrue(qArray.getClass().isInstance(qArray));
180         assertTrue(lArray.getClass().isInstance(lArray));
181 
182         // Class.isAssignableFrom (self)
183         assertTrue(qArray.getClass().isAssignableFrom(qArray.getClass()));
184         assertTrue(lArray.getClass().isAssignableFrom(lArray.getClass()));
185 
186         // V.val[] is a subtype of V.ref[]
187         assertFalse(qArray.getClass().isInstance(lArray));
188         assertTrue(lArray.getClass().isInstance(qArray));
189 
190         // V.val[] is a subtype of V.ref[]
191         assertTrue(lArray.getClass().isAssignableFrom(qArray.getClass()));
192         assertFalse(qArray.getClass().isAssignableFrom(lArray.getClass()));
193 
194         // Class.cast (self)
195         qArray.getClass().cast(qArray);
196         lArray.getClass().cast(lArray);
197 
198         // Class.cast
199         lArray.getClass().cast(qArray);
200         try {
201             qArray.getClass().cast(lArray);
202             fail("cast of Point.ref[] to Point[] should not succeed");
203         } catch (ClassCastException cce) {
204         }
205     }
206 
207     @Test
208     public static void testIntArray() {
209         int[] array = new int[] { 1, 2, 3};
210         for (int i=0; i < array.length; i++) {
211             Array.set(array, i, Integer.valueOf(i*10));
212         }
213 
214         for (int i=0; i < array.length; i++) {
215             Integer o = (Integer) Array.get(array, i);
216             assertTrue(o.intValue() == i*10);
217         }
218         Arrays.setAll(array, i -> array[i]);
219     }
220 
221     @Test
222     public static void testNonArrayObject() {
223         Object o = new Object();
224         try {
225             Array.get(o, 0);
226             fail("IAE not thrown");
227         } catch (IllegalArgumentException e) {}
228 
229         try {
230             Array.set(o, 0, o);
231             fail("IAE not thrown");
232         } catch (IllegalArgumentException e) {}
233 
234     }
235 
236     @Test
237     static void testInstanceOf() {
238         Point[] qArray = new Point[0];
239         Point.ref[] lArray = new Point.ref[0];
240         ValueOptional[] vArray = new ValueOptional[0];
241 
242         // language instanceof
243         assertTrue(qArray instanceof Point[]);
244         assertTrue(lArray instanceof Point.ref[]);
245         assertTrue(vArray instanceof ValueOptional[]);
246     }
247 }