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