1 /*
   2  * Copyright (c) 2018, 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 package runtime.valhalla.valuetypes;
  25 
  26 import java.lang.reflect.Array;
  27 import java.util.Arrays;
  28 import java.util.ArrayList;
  29 import java.util.List;
  30 
  31 import static jdk.test.lib.Asserts.*;
  32 
  33 /*
  34  * @test ValueTypeArray
  35  * @summary Plain array test for Value Types
  36  * @library /test/lib
  37  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator -XDallowFlattenabilityModifiers -XDallowGenericsOverValues ValueTypeArray.java Point.java Long8Value.java Person.java
  38  * @run main/othervm -Xint -XX:+ValueArrayFlatten -XX:+EnableValhalla runtime.valhalla.valuetypes.ValueTypeArray
  39  * @run main/othervm -Xint -XX:-ValueArrayFlatten -XX:+EnableValhalla runtime.valhalla.valuetypes.ValueTypeArray
  40  * @run main/othervm -Xcomp -XX:+ValueArrayFlatten -XX:+EnableValhalla runtime.valhalla.valuetypes.ValueTypeArray
  41  * @run main/othervm -Xcomp -XX:-ValueArrayFlatten -XX:+EnableValhalla runtime.valhalla.valuetypes.ValueTypeArray
  42  */
  43 public class ValueTypeArray {
  44     public static void main(String[] args) {
  45         ValueTypeArray valueTypeArray = new ValueTypeArray();
  46         valueTypeArray.run();
  47     }
  48 
  49     public void run() {
  50         testClassForName();
  51         testSimplePointArray();
  52         testLong8Array();
  53         testMixedPersonArray();
  54         testMultiDimPointArray();
  55         testComposition();
  56 
  57         testSanityCheckcasts();
  58         testObjectArrayOfValues();
  59 
  60         testReflectArray();
  61         testUtilArrays();
  62     }
  63 
  64     void testClassForName() {
  65         String arrayClsName = "[Lruntime.valhalla.valuetypes.Point;";
  66         try {
  67             Class<?> arrayCls = Class.forName(arrayClsName);
  68             assertTrue(arrayCls.isArray(), "Expected an array class");
  69             // array-of-L-type not supported yet
  70             // the component type of a flattened value array is of the value type
  71             // the component type of a non-flattened array is of the box type
  72             assertTrue(arrayCls.getComponentType().asBoxType() == Point.class,
  73                        "Expected component type of Point.class got: " + arrayCls.getComponentType());
  74 
  75             arrayClsName = "[" + arrayClsName;
  76             Class<?> mulArrayCls = Class.forName(arrayClsName);
  77             assertTrue(mulArrayCls.isArray());
  78             assertTrue(mulArrayCls.getComponentType() == arrayCls);
  79         }
  80         catch (ClassNotFoundException cnfe) {
  81             fail("Class.forName(" + arrayClsName + ") failed", cnfe);
  82         }
  83     }
  84 
  85     void testSimplePointArray() {
  86         Point[] defaultPoint = new Point[1];
  87         Point p = defaultPoint[0];
  88         assertEquals(p.x, 0, "invalid default loaded from array");
  89         assertEquals(p.y, 0, "invalid default loaded from array");
  90         boolean gotNpe = false;
  91         try {
  92             defaultPoint[0] = (Point) getNull();
  93         } catch (NullPointerException npe) {
  94             gotNpe = true;
  95         }
  96         assertTrue(gotNpe, "Expected NullPointerException");
  97 
  98         Point[] points = createSimplePointArray();
  99         checkSimplePointArray(points);
 100         System.gc(); // check that VTs survive GC
 101 
 102         assertTrue(points instanceof Point[], "Instance of");
 103 
 104         Point[] pointsCopy = new Point[points.length];
 105         System.arraycopy(points, 0, pointsCopy, 0, points.length);
 106         checkSimplePointArray(pointsCopy);
 107     }
 108 
 109     static Point[] createSimplePointArray() {
 110         Point[] ps = new Point[2];
 111         assertEquals(ps.length, 2, "Length");
 112         ps.toString();
 113         ps[0] = Point.createPoint(1, 2);
 114         ps[1] = Point.createPoint(3, 4);
 115         boolean sawOob = false;
 116         try {
 117             ps[2] = Point.createPoint(0, 0);
 118         } catch (ArrayIndexOutOfBoundsException aioobe) { sawOob = true; }
 119         assertTrue(sawOob, "Didn't see AIOOBE");
 120         System.gc(); // check that VTs survive GC
 121         return ps;
 122     }
 123 
 124     static void checkSimplePointArray(Point[] points) {
 125         assertEquals(points[0].x, 1, "invalid 0 point x value");
 126         assertEquals(points[0].y, 2, "invalid 0 point y value");
 127         assertEquals(points[1].x, 3, "invalid 1 point x value");
 128         assertEquals(points[1].y, 4, "invalid 1 point y value");
 129     }
 130 
 131     void testLong8Array() {
 132         Long8Value[] values = new Long8Value[3];
 133         assertEquals(values.length, 3, "length");
 134         values.toString();
 135         Long8Value value = values[1];
 136         long zl = 0;
 137         Long8Value.check(value, zl, zl, zl, zl, zl, zl, zl, zl);
 138         values[1] = Long8Value.create(1, 2, 3, 4, 5, 6, 7, 8);
 139         value = values[1];
 140         Long8Value.check(value, 1, 2, 3, 4, 5, 6, 7, 8);
 141 
 142         Long8Value[] copy = new Long8Value[values.length];
 143         System.arraycopy(values, 0, copy, 0, values.length);
 144         value = copy[1];
 145         Long8Value.check(value, 1, 2, 3, 4, 5, 6, 7, 8);
 146     }
 147 
 148     void testMixedPersonArray() {
 149         Person[] people = new Person[3];
 150 
 151         people[0] = Person.create(1, "First", "Last");
 152         assertEquals(people[0].getId(), 1, "Invalid Id person");
 153         assertEquals(people[0].getFirstName(), "First", "Invalid First Name");
 154         assertEquals(people[0].getLastName(), "Last", "Invalid Last Name");
 155 
 156         people[1] = Person.create(2, "Jane", "Wayne");
 157         people[2] = Person.create(3, "Bob", "Dobalina");
 158 
 159         Person[] peopleCopy = new Person[people.length];
 160         System.arraycopy(people, 0, peopleCopy, 0, people.length);
 161         assertEquals(peopleCopy[2].getId(), 3, "Invalid Id");
 162         assertEquals(peopleCopy[2].getFirstName(), "Bob", "Invalid First Name");
 163         assertEquals(peopleCopy[2].getLastName(), "Dobalina", "Invalid Last Name");
 164     }
 165 
 166     void testMultiDimPointArray() {
 167         Point[][][] multiPoints = new Point[2][3][4];
 168         assertEquals(multiPoints.length, 2, "1st dim length");
 169         assertEquals(multiPoints[0].length, 3, "2st dim length");
 170         assertEquals(multiPoints[0][0].length, 4, "3rd dim length");
 171 
 172         Point defaultPoint = multiPoints[1][2][3];
 173         assertEquals(defaultPoint.x, 0, "invalid point x value");
 174         assertEquals(defaultPoint.y, 0, "invalid point x value");
 175     }
 176 
 177     void testReflectArray() {
 178         // Check the java.lang.reflect.Array.newInstance methods...
 179         Class<?> cls = (Class<?>) Point[].class;
 180         Point[][] array = (Point[][]) Array.newInstance(cls, 1);
 181         assertEquals(array.length, 1, "Incorrect length");
 182         assertTrue(array[0] == null, "Expected NULL");
 183 
 184         Point[][][] array3 = (Point[][][]) Array.newInstance(cls, 1, 2);
 185         assertEquals(array3.length, 1, "Incorrect length");
 186         assertEquals(array3[0].length, 2, "Incorrect length");
 187         assertTrue(array3[0][0] == null, "Expected NULL");
 188 
 189         // Now create ObjArrays of ValueArray...
 190         cls = (Class<?>) Point.class;
 191         array = (Point[][]) Array.newInstance(cls, 1, 2);
 192         assertEquals(array.length, 1, "Incorrect length");
 193         assertEquals(array[0].length, 2, "Incorrect length");
 194         Point p = array[0][1];
 195         int x = p.x;
 196         assertEquals(x, 0, "Bad Point Value");
 197     }
 198 
 199     static final value class MyInt implements Comparable<MyInt> {
 200         final int value;
 201 
 202         private MyInt() { value = 0; }
 203         public int getValue() { return value; }
 204         public String toString() { return "MyInt: " + getValue(); }
 205         public int compareTo(MyInt that) { return Integer.compare(this.getValue(), that.getValue()); }
 206         public boolean equals(Object o) {
 207             if (o instanceof MyInt) {
 208                 return this.getValue() == ((MyInt) o).getValue();
 209             }
 210             return false;
 211         }
 212 
 213         public static MyInt create(int v) {
 214             MyInt mi = MyInt.default;
 215             mi = __WithField(mi.value, v);
 216             return mi;
 217         }
 218 
 219         public static final MyInt.box MIN = MyInt.create(Integer.MIN_VALUE);
 220         public static final MyInt.box ZERO = MyInt.create(0);
 221         public static final MyInt.box MAX = MyInt.create(Integer.MAX_VALUE);
 222     }
 223 
 224     static interface SomeSecondaryType {
 225         default String hi() { return "Hi"; }
 226     }
 227 
 228     static final value class MyOtherInt implements SomeSecondaryType {
 229         final int value;
 230         private MyOtherInt() { value = 0; }
 231     }
 232 
 233     void testSanityCheckcasts() {
 234         MyInt[] myInts = new MyInt[1];
 235         assertTrue(myInts instanceof Object[]);
 236         assertTrue(myInts instanceof Comparable[]);
 237 
 238         Object arrObj = Array.newInstance(MyInt.class, 1);
 239         assertTrue(arrObj instanceof Object[], "Not Object array");
 240         assertTrue(arrObj instanceof Comparable[], "Not Comparable array");
 241         assertTrue(arrObj instanceof MyInt[], "Not MyInt array");
 242 
 243         Object[] arr = (Object[]) arrObj;
 244         assertTrue(arr instanceof Comparable[], "Not Comparable array");
 245         assertTrue(arr instanceof MyInt[], "Not MyInt array");
 246         Comparable[] comparables = (Comparable[])arr;
 247         MyInt[] myIntArr = (MyInt[]) arr;
 248 
 249         // multi-dim, check secondary array types are setup...
 250         MyOtherInt[][] matrix = new MyOtherInt[1][1];
 251         assertTrue(matrix[0] instanceof MyOtherInt[]);
 252         assertTrue(matrix[0] instanceof SomeSecondaryType[]);
 253     }
 254 
 255     void testUtilArrays() {
 256         // Sanity check j.u.Arrays
 257         MyInt[] myInts = new MyInt[] { MyInt.MAX, MyInt.MIN };
 258         // Sanity sort another copy
 259         MyInt[] copyMyInts = Arrays.copyOf(myInts, myInts.length + 1);
 260         checkArrayElementsEqual(copyMyInts, new MyInt[] { myInts[0], myInts[1], MyInt.ZERO});
 261 
 262         Arrays.sort(copyMyInts);
 263         checkArrayElementsEqual(copyMyInts, new MyInt[] { MyInt.MIN, MyInt.ZERO, MyInt.MAX });
 264 
 265         List myIntList = Arrays.asList(copyMyInts);
 266         checkArrayElementsEqual(copyMyInts, myIntList.toArray(new MyInt[copyMyInts.length]));
 267         // This next line needs testMixedLayoutArrays to work
 268         checkArrayElementsEqual(copyMyInts, myIntList.toArray());
 269 
 270         // Sanity check j.u.ArrayList
 271         ArrayList<MyInt> aList = new ArrayList<MyInt>(Arrays.asList(copyMyInts));
 272         assertTrue(aList.indexOf(MyInt.MIN) == 0, "Bad Index");
 273         assertTrue(aList.indexOf(MyInt.ZERO) == 1, "Bad Index");
 274         assertTrue(aList.indexOf(MyInt.MAX) == 2, "Bad Index");
 275 
 276         aList.remove(2);
 277         aList.add(MyInt.create(5));
 278 
 279         // Interesting:
 280         //aList.add((MyInt)getNull());
 281 
 282         // javac currently generating "java/util/Objects.requireNonNull
 283         // should checkcast treat null against Value class as CCE ?
 284         // Then in the end, ArrayList.elementData is Object[], (that's why remove works)
 285         // why can't I write add(null) then ?
 286     }
 287 
 288     void testObjectArrayOfValues() {
 289         testSanityObjectArrays();
 290         testMixedLayoutArrays();
 291     }
 292 
 293     void testSanityObjectArrays() {
 294         Object[] objects = new Object[2];
 295         assertTrue(objects[0] == null && objects[1] == null, "Not null ?");
 296 
 297         objects[0] = MyInt.create(1);
 298         objects[1] = Integer.valueOf(2);
 299         assertTrue(objects[0].equals(MyInt.create(1)), "Bad Value");
 300         assertTrue(objects[1].equals(Integer.valueOf(2)), "Bad Object");
 301 
 302         Comparable[] copyComparables = new Comparable[objects.length];
 303         System.arraycopy(objects, 0, copyComparables, 0, objects.length);
 304         checkArrayElementsEqual(objects, copyComparables);
 305 
 306         objects[0] = null;
 307         objects[1] = null;
 308         assertTrue(objects[0] == null && objects[1] == null, "Not null ?");
 309 
 310         Comparable[] comparables = new Comparable[2];
 311         assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?");
 312         comparables[0] = MyInt.create(3);
 313         comparables[1] = Integer.valueOf(4);
 314         assertTrue(comparables[0].equals(MyInt.create(3)), "Bad Value");
 315         assertTrue(comparables[1].equals(Integer.valueOf(4)), "Bad Object");
 316 
 317         Object[] copyObjects = new Object[2];
 318         System.arraycopy(comparables, 0, copyObjects, 0, comparables.length);
 319         checkArrayElementsEqual(comparables, copyObjects);
 320 
 321         comparables[0] = null;
 322         comparables[1] = null;
 323         assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?");
 324     }
 325 
 326     void testMixedLayoutArrays() {
 327         Object[] objArray = new Object[3];
 328         Comparable[] compArray = new Comparable[3];
 329         MyInt[] valArray = new MyInt[] { MyInt.MIN, MyInt.ZERO, MyInt.MAX };
 330 
 331         arrayCopy(valArray, 0, objArray, 0, 3);
 332         checkArrayElementsEqual(valArray, objArray);
 333         arrayCopy(valArray, 0, objArray, 0, 3);
 334 
 335         objArray = new Object[3];
 336         System.arraycopy(valArray, 0, objArray, 0, 3);
 337         checkArrayElementsEqual(valArray, objArray);
 338 
 339         System.arraycopy(valArray, 0, compArray, 0, 3);
 340         checkArrayElementsEqual(valArray, compArray);
 341 
 342         valArray = new MyInt[] { MyInt.ZERO, MyInt.ZERO, MyInt.ZERO };
 343         System.arraycopy(compArray, 0, valArray, 0, 3);
 344         checkArrayElementsEqual(valArray, compArray);
 345 
 346         valArray = new MyInt[] { MyInt.ZERO, MyInt.ZERO, MyInt.ZERO };
 347         System.arraycopy(objArray, 0, valArray, 0, 3);
 348         checkArrayElementsEqual(valArray, objArray);
 349 
 350         // Sanity check dst == src
 351         System.arraycopy(valArray, 0, valArray, 0, 3);
 352         checkArrayElementsEqual(valArray, objArray);
 353 
 354         objArray[0] = "Not a value object";
 355         try {
 356             System.arraycopy(objArray, 0, valArray, 0, 3);
 357             throw new RuntimeException("Expected ArrayStoreException");
 358         } catch (ArrayStoreException ase) {}
 359     }
 360 
 361     static final value class MyPoint {
 362         final               MyInt.val x;
 363         final               MyInt y;
 364 
 365         private MyPoint() {
 366             x = MyInt.ZERO;
 367             y = x;
 368         }
 369         public boolean equals(Object that) {
 370             if (that instanceof MyPoint) {
 371                 MyPoint thatPoint = (MyPoint) that;
 372                 return x.equals(thatPoint.x) && java.util.Objects.equals(y, thatPoint.y);
 373             }
 374             return false;
 375         }
 376         static MyPoint create(int x) {
 377             MyPoint mp = MyPoint.default;
 378             mp = __WithField(mp.x, MyInt.create(x));
 379             return mp;
 380         }
 381         static MyPoint create(int x, int y) {
 382             MyPoint mp = MyPoint.default;
 383             mp = __WithField(mp.x, MyInt.create(x));
 384             mp = __WithField(mp.y, MyInt.create(y));
 385             return mp;
 386         }
 387         static final MyPoint.box ORIGIN = create(0);
 388     }
 389 
 390     void testComposition() {
 391         // Test array operations with compostion of values, check element payload is correct...
 392         MyPoint a = MyPoint.create(1, 2);
 393         MyPoint b = MyPoint.create(7, 21);
 394         MyPoint c = MyPoint.create(Integer.MAX_VALUE, Integer.MIN_VALUE);
 395 
 396         MyPoint[] pts = new MyPoint[3];
 397         if (!pts[0].equals(MyPoint.ORIGIN)) {
 398             throw new RuntimeException("Equals failed: " + pts[0] + " vs " + MyPoint.ORIGIN);
 399         }
 400         pts = new MyPoint[] { a, b, c };
 401         checkArrayElementsEqual(pts, new Object[] { a, b, c});
 402         Object[] oarr = new Object[3];
 403 
 404         arrayCopy(pts, 0, oarr, 0, 3);
 405         checkArrayElementsEqual(pts, oarr);
 406 
 407         oarr = new Object[3];
 408         System.arraycopy(pts, 0, oarr, 0, 3);
 409         checkArrayElementsEqual(pts, oarr);
 410 
 411         System.arraycopy(oarr, 0, pts, 0, 3);
 412         checkArrayElementsEqual(pts, oarr);
 413 
 414         oarr = new Object[3];
 415         try {
 416             System.arraycopy(oarr, 0, pts, 0, 3);
 417             throw new RuntimeException("Expected NPE");
 418         }
 419         catch (NullPointerException npe) {}
 420 
 421         oarr = new Object[3];
 422         oarr[0] = new Object();
 423         try {
 424             System.arraycopy(oarr, 0, pts, 0, 3);
 425             throw new RuntimeException("Expected ASE");
 426         }
 427         catch (ArrayStoreException ase) {}
 428     }
 429 
 430     void checkArrayElementsEqual(Object[] arr1, Object[] arr2) {
 431         assertTrue(arr1.length == arr2.length, "Bad length");
 432         for (int i = 0; i < arr1.length; i++) {
 433             assertTrue(java.util.Objects.equals(arr1[i], arr2[i]), "Element " + i + " not equal");
 434         }
 435     }
 436 
 437     void arrayCopy(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
 438         for (int i = 0; i < length ; i++) {
 439             dst[dstPos++] = src[srcPos++];
 440         }
 441     }
 442 
 443     Object getNull() { return null; }
 444 
 445 }