1 /*
   2  * Copyright (c) 2017, 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.invoke.*;
  27 import java.lang.ref.*;
  28 import java.util.concurrent.*;
  29 import jdk.experimental.value.*;
  30 
  31 import static jdk.test.lib.Asserts.*;
  32 import jdk.test.lib.Utils;
  33 import sun.hotspot.WhiteBox;
  34 
  35 /*
  36  * @test ValueOops
  37  * @summary Test embedding oops into Value types
  38  * @library /testlibrary /test/lib /
  39  * @build sun.hotspot.WhiteBox
  40  *        runtime.valhalla.valuetypes.ValueOops
  41  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  42  *                                sun.hotspot.WhiteBox$WhiteBoxPermission
  43  * @run main/othervm -Xint -noverify -XX:+UseSerialGC -Xmx128m
  44  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  45  *                   runtime.valhalla.valuetypes.ValueOops
  46  * @run main/othervm -Xint -noverify -XX:+UseG1GC -Xmx128m
  47  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  48  *                   runtime.valhalla.valuetypes.ValueOops
  49  * @run main/othervm -Xint -noverify -XX:+UseParallelGC -Xmx128m
  50  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  51  *                   runtime.valhalla.valuetypes.ValueOops
  52  * @run main/othervm -Xint -noverify -XX:+UseConcMarkSweepGC -Xmx128m
  53  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  54  *                   runtime.valhalla.valuetypes.ValueOops
  55  */
  56 public class ValueOops {
  57 
  58     /*
  59      * Remaining work:
  60      *     -Xint -noverify -XX:+UseParallelGC -Xmx128m runtime.valhalla.valuetypes.ValueOops
  61      *     Generated layout permutations
  62      */
  63 
  64     // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap
  65     // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info
  66 
  67     static final int NOF_PEOPLE = 1000; // Exercise arrays of this size
  68 
  69     static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes
  70 
  71     static int MED_ACTIVE_GC_COUNT = 4;  // Medium life span in terms of GC passes
  72 
  73     static final String TEST_STRING1 = "Test String 1";
  74     static final String TEST_STRING2 = "Test String 2";
  75 
  76     public static void main(String[] args) {
  77         if (args.length > 0) {
  78             MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]);
  79         }
  80         testClassLoad();
  81         testBytecodes();
  82         testVvt();
  83         testMvt();
  84 
  85         testOopMaps();
  86 
  87         // Check we survive GC...
  88         testOverGc();   // Exercise root scan / oopMap
  89         testActiveGc(); // Brute force
  90     }
  91 
  92     /**
  93      * Test ClassFileParser can load values with reference fields
  94      */
  95     public static void testClassLoad() {
  96         // VVT
  97         String s = Person.class.toString();
  98         new Bar();
  99         new BarWithValue();
 100         s = BarValue.class.toString();
 101         s = ObjectWithObjectValue.class.toString();
 102         s = ObjectWithObjectValues.class.toString();
 103 
 104         // MVT
 105         Class<?> vccClass = PersonVcc.class;
 106         ValueType<?> vt = ValueType.forClass(vccClass);
 107         Class<?> boxClass = vt.boxClass();
 108         Class<?> dvtClass = vt.valueClass();
 109         Class<?> arrayClass = vt.arrayValueClass();
 110         s = dvtClass.toString();
 111     }
 112 
 113     /**
 114      * Test value type opcodes are okay with reference fields in values
 115      * I.e. ValueKlass::value_store()
 116      */
 117     public static void testBytecodes() {
 118         try {
 119             // Craft Value Type class how you will, using MVT class for simplicity just here
 120             ValueType<?> vt = ValueType.forClass(PersonVcc.class);
 121             Class<?> vtClass = vt.valueClass();
 122             Class<?> arrayClass = vt.arrayValueClass();
 123             Class<?> boxClass = vt.boxClass();
 124             MethodHandles.Lookup lookup = MethodHandles.lookup();
 125 
 126             // Exercise with null refs...
 127 
 128             // anewarray
 129             Object array = MethodHandleBuilder.loadCode(
 130                 lookup,
 131                 "anewarrayPerson",
 132                 MethodType.methodType(Object.class, Integer.TYPE),
 133                 CODE->{
 134                 CODE
 135                 .iload(0)
 136                 .anewarray(vtClass)
 137                 .astore(0)
 138                 .aload(0)
 139                 .areturn();
 140             }).invoke(NOF_PEOPLE);
 141 
 142             // vaload
 143             // vstore
 144             // vload
 145             // vastore
 146             // vbox
 147             int arraySlot = 0;
 148             int indexSlot = 1;
 149             int valueSlot = 2;
 150             Object obj = MethodHandleBuilder.loadCode(
 151                 lookup,
 152                 "valoadBoxPerson",
 153                 MethodType.methodType(Object.class, arrayClass, Integer.TYPE),
 154                 CODE->{
 155                 CODE
 156                 .aload(arraySlot)
 157                 .iload(indexSlot)
 158                 .vaload()
 159                 .vstore(valueSlot)
 160                 .aload(arraySlot)
 161                 .iload(indexSlot)
 162                 .vload(valueSlot)
 163                 .vastore()
 164                 .vload(valueSlot)
 165                 .vbox(boxClass)
 166                 .areturn();
 167             }).invoke(array, 7);
 168             validateDefaultPersonVcc(obj);
 169 
 170             // vreturn
 171             MethodHandle loadValueFromArray = MethodHandleBuilder.loadCode(
 172                 lookup,
 173                 "valoadVreturnPerson",
 174                 MethodType.methodType(vtClass, arrayClass, Integer.TYPE),
 175                 CODE->{
 176                 CODE
 177                 .aload(arraySlot)
 178                 .iload(indexSlot)
 179                 .vaload()
 180                 .vreturn();
 181             });
 182             MethodHandle box = MethodHandleBuilder.loadCode(
 183                 lookup,
 184                 "boxPersonVcc",
 185                 MethodType.methodType(boxClass, vtClass),
 186                 CODE->{
 187                 CODE
 188                 .vload(0)
 189                 .vbox(boxClass)
 190                 .areturn();
 191             });
 192             MethodHandle loadValueFromArrayBoxed =
 193                 MethodHandles.filterReturnValue(loadValueFromArray, box);
 194             obj = loadValueFromArrayBoxed.invoke(array, 0);
 195             validateDefaultPersonVcc(obj);
 196 
 197             // vunbox
 198             MethodHandle unbox = MethodHandleBuilder.loadCode(
 199                 lookup,
 200                 "unboxPersonVcc",
 201                 MethodType.methodType(vtClass, boxClass),
 202                 CODE->{
 203                 CODE
 204                 .aload(0)
 205                 .vunbox(vtClass)
 206                 .vreturn();
 207             });
 208             MethodHandle unboxBox = MethodHandles.filterReturnValue(unbox, box);
 209             obj = unboxBox.invoke(createDefaultPersonVcc());
 210             validateDefaultPersonVcc(obj);
 211 
 212             /*
 213                vgetfield
 214                qputfield
 215                qgetfield
 216 
 217                going to need VVT for VT fields and vgetfield
 218                check test coverage in testVvt()
 219             */
 220 
 221             // Exercise with live refs...
 222             Thread.holdsLock("Debug here");
 223             // vunbox
 224             // vastore
 225             // vaload
 226             // vstore
 227             // vload
 228             // vbox
 229             int index = 3;
 230             obj = MethodHandleBuilder.loadCode(
 231                 lookup,
 232                 "unboxStoreLoadPersonVcc",
 233                 MethodType.methodType(boxClass, arrayClass, Integer.TYPE, boxClass),
 234                 CODE->{
 235                 CODE
 236                 .aload(arraySlot)
 237                 .iload(indexSlot)
 238                 .aload(valueSlot)
 239                 .vunbox(vtClass)
 240                 .vastore()
 241                 .aload(arraySlot)
 242                 .iload(indexSlot)
 243                 .vaload()
 244                 .vstore(valueSlot)
 245                 .vload(valueSlot)
 246                 .vbox(boxClass)
 247                 .areturn();
 248             }).invoke(array, index, createIndexedPersonVcc(index));
 249             validateIndexedPersonVcc(obj, index);
 250 
 251             // Check the neighbours
 252             validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index - 1));
 253             validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index + 1));
 254 
 255             // vreturn
 256             validateIndexedPersonVcc(unboxBox.invoke((PersonVcc)obj), index);
 257         }
 258         catch (Throwable t) { fail("testBytecodes", t); }
 259     }
 260 
 261     static class Couple {
 262         public Person onePerson;
 263         public Person otherPerson;
 264     }
 265 
 266     static final __ByValue class Composition {
 267         public final Person onePerson;
 268         public final Person otherPerson;
 269 
 270         private Composition(Person onePerson, Person otherPerson) {
 271             this.onePerson = onePerson;
 272             this.otherPerson = otherPerson;
 273         }
 274 
 275         public static Composition create(Person onePerson, Person otherPerson) {
 276             return __Make Composition(onePerson, otherPerson);
 277         }
 278     }
 279 
 280     /**
 281      * Check value type operations with "Valhalla Value Types" (VVT)
 282      */
 283     public static void testVvt() {
 284         // Exercise creation, vgetfield, vreturn with null refs
 285         validateDefaultPerson(createDefaultPerson());
 286 
 287         // anewarray, vaload, vastore
 288         int index = 7;
 289         Person[] array =  new Person[NOF_PEOPLE];
 290         validateDefaultPerson(array[index]);
 291 
 292         // Now with refs...
 293         validateIndexedPerson(createIndexedPerson(index), index);
 294         array[index] = createIndexedPerson(index);
 295         validateIndexedPerson(array[index], index);
 296 
 297         // Check the neighbours
 298         validateDefaultPerson(array[index - 1]);
 299         validateDefaultPerson(array[index + 1]);
 300 
 301         // getfield/putfield
 302         Couple couple = new Couple();
 303         validateDefaultPerson(couple.onePerson);
 304         validateDefaultPerson(couple.otherPerson);
 305 
 306         couple.onePerson = createIndexedPerson(index);
 307         validateIndexedPerson(couple.onePerson, index);
 308 
 309         Composition composition = Composition.create(couple.onePerson, couple.onePerson);
 310         validateIndexedPerson(composition.onePerson, index);
 311         validateIndexedPerson(composition.otherPerson, index);
 312     }
 313 
 314     /**
 315      * Check value type operations with "Minimal Value Types" (MVT)
 316      */
 317     public static void testMvt() {
 318         try {
 319             // MVT...
 320             ValueType<?> vt = ValueType.forClass(PersonVcc.class);
 321             Class<?> vtClass = vt.valueClass();
 322             Class<?> arrayClass = vt.arrayValueClass();
 323 
 324             Object obj = vt.defaultValueConstant().invoke();
 325             validateDefaultPersonVcc(obj);
 326 
 327             obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box())
 328                 .invoke(createDefaultPersonVcc());
 329             validateDefaultPersonVcc(obj);
 330 
 331             int index = 11;
 332             obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box())
 333                 .invoke(createIndexedPersonVcc(index));
 334             validateIndexedPersonVcc(obj, index);
 335 
 336             testMvtArray("testMvt.array.1", 1);
 337         }
 338         catch (Throwable t) {
 339             fail("testMvtfailed", t);
 340         }
 341     }
 342 
 343     /**
 344      * MVT array operations...
 345      */
 346     public static void testMvtPeopleArray() {
 347         testMvtArray("testMvtPeopleArray", NOF_PEOPLE);
 348     }
 349 
 350     public static void testMvtArray(String testName, int arrayLength) {
 351         try {
 352             Class<?> vcc = PersonVcc.class;
 353             ValueType<?> vt = ValueType.forClass(vcc);
 354             Class<?> dvtClass = vt.valueClass();
 355             Class<?> arrayClass = vt.arrayValueClass();
 356 
 357             MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(arrayClass);
 358             MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(arrayClass);
 359 
 360             MethodHandles.Lookup lookup = MethodHandles.lookup();
 361             MethodHandle getId = lookup.findGetter(dvtClass, "id", Integer.TYPE);
 362             MethodHandle getFirstName = lookup.findGetter(dvtClass, "firstName", String.class);
 363             MethodHandle getLastName = lookup.findGetter(dvtClass, "lastName", String.class);
 364 
 365             MethodHandle getIdFromArray = MethodHandles.filterReturnValue(arrayElemGet, getId);
 366             MethodHandle getFnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getFirstName);
 367             MethodHandle getLnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getLastName);
 368 
 369             Object people = vt.newArray().invoke(arrayLength);
 370             for (int i = 0; i < arrayLength; i++) {
 371                 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 372             }
 373 
 374             for (int i = 0; i < arrayLength; i++) {
 375                 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 376 
 377                 int id = (int) getIdFromArray.invoke(people, i);
 378                 assertTrue(id == i, "Invalid field: Id");
 379                 String fn = (String) getFnFromArray.invoke(people, i);
 380                 assertTrue(fn.equals(firstName(i)), "Invalid field: firstName");
 381                 String ln = (String) getLnFromArray.invoke(people, i);
 382                 assertTrue(ln.equals(lastName(i)), "Invalid field: lastName");
 383             }
 384         }
 385         catch (Throwable t) {
 386             fail(testName + " failed", t);
 387         }
 388     }
 389 
 390     /**
 391      * Check oop map generation for klass layout and frame...
 392      */
 393     public static void testOopMaps() {
 394         Object[] objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(new Couple());
 395         assertTrue(objects.length == 4, "Expected 4 oops");
 396         for (int i = 0; i < objects.length; i++) {
 397             assertTrue(objects[i] == null, "not-null");
 398         }
 399 
 400         String fn1 = "Sam";
 401         String ln1 = "Smith";
 402         String fn2 = "Jane";
 403         String ln2 = "Jones";
 404         Couple couple = new Couple();
 405         couple.onePerson = Person.create(0, fn1, ln1);
 406         couple.otherPerson = Person.create(1, fn2, ln2);
 407         objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(couple);
 408         assertTrue(objects.length == 4, "Expected 4 oops");
 409         assertTrue(objects[0] == fn1, "Bad oop fn1");
 410         assertTrue(objects[1] == ln1, "Bad oop ln1");
 411         assertTrue(objects[2] == fn2, "Bad oop fn2");
 412         assertTrue(objects[3] == ln2, "Bad oop ln2");
 413 
 414         objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(couple);
 415         assertTrue(objects.length == 4, "Expected 4 oops");
 416         assertTrue(objects[0] == fn1, "Bad oop fn1");
 417         assertTrue(objects[1] == ln1, "Bad oop ln1");
 418         assertTrue(objects[2] == fn2, "Bad oop fn2");
 419         assertTrue(objects[3] == ln2, "Bad oop ln2");
 420 
 421         // Array..
 422         objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(createPeople());
 423         assertTrue(objects.length == NOF_PEOPLE * 2, "Unexpected length: " + objects.length);
 424         int o = 0;
 425         for (int i = 0; i < NOF_PEOPLE; i++) {
 426             assertTrue(objects[o++].equals(firstName(i)), "Bad firstName");
 427             assertTrue(objects[o++].equals(lastName(i)), "Bad lastName");
 428         }
 429 
 430         // Sanity check, FixMe need more test cases
 431         objects = testFrameOops(couple);
 432         assertTrue(objects.length == 5, "Number of frame oops incorrect");
 433         assertTrue(objects[0] == couple, "Bad oop 0");
 434         assertTrue(objects[1] == fn1, "Bad oop 1");
 435         assertTrue(objects[2] == ln1, "Bad oop 2");
 436         assertTrue(objects[3] == TEST_STRING1, "Bad oop 3");
 437         assertTrue(objects[4] == TEST_STRING2, "Bad oop 4");
 438     }
 439 
 440     // Expecting Couple couple, Person couple.onePerson, and Person (created here)
 441     public static Object[] testFrameOops(Couple couple) {
 442         int someId = 89898;
 443         Person person = couple.onePerson;
 444         assertTrue(person.getId() == 0, "Bad Person");
 445         Person anotherPerson = Person.create(someId, TEST_STRING1, TEST_STRING2);
 446         assertTrue(anotherPerson.getId() == someId, "Bad Person");
 447         return WhiteBox.getWhiteBox().getObjectsViaFrameOopIterator(1);
 448     }
 449 
 450     /**
 451      * Check forcing GC for combination of VT on stack/LVT etc works
 452      */
 453     public static void testOverGc() {
 454         try {
 455             Class<?> vccClass = PersonVcc.class;
 456             ValueType<?> vt = ValueType.forClass(vccClass);
 457             Class<?> vtClass = vt.valueClass();
 458             Class<?> arrayClass = vt.arrayValueClass();
 459 
 460             MethodHandles.Lookup lookup = MethodHandles.lookup();
 461             doGc();
 462 
 463             // VT on stack and lvt, null refs, see if GC flies
 464             MethodHandle moveValueThroughStackAndLvt = MethodHandleBuilder.loadCode(
 465                 lookup,
 466                 "gcOverPerson",
 467                 MethodType.methodType(vccClass, vccClass),
 468                 CODE->{
 469                 CODE
 470                 .aload(0)
 471                 .vunbox(vtClass)
 472                 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack
 473                 .vstore(0)
 474                 .invokestatic(ValueOops.class, "doGc", "()V", false) // LVT
 475                 .vload(0)
 476                 .iconst_1()  // push a litte further down
 477                 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack,LVT
 478                 .pop()
 479                 .vbox(vccClass)
 480                 .areturn();
 481             });
 482             Object obj = moveValueThroughStackAndLvt.invoke(createDefaultPersonVcc());
 483             validateDefaultPersonVcc(obj);
 484             doGc();
 485             obj = null;
 486             doGc();
 487 
 488             int index = 4711;
 489             obj = moveValueThroughStackAndLvt.invoke(createIndexedPersonVcc(index));
 490             validateIndexedPersonVcc(obj, index);
 491             doGc();
 492             obj = null;
 493             doGc();
 494         }
 495         catch (Throwable t) { fail("testOverGc", t); }
 496     }
 497 
 498     static void submitNewWork(ForkJoinPool fjPool, int size) {
 499         for (int i = 0; i < size; i++) {
 500             fjPool.execute(ValueOops::testMvtPeopleArray);
 501             for (int j = 0; j < 100; j++) {
 502                 fjPool.execute(ValueOops::testBytecodes);
 503                 fjPool.execute(ValueOops::testVvt);
 504                 fjPool.execute(ValueOops::testMvt);
 505             }
 506         }
 507     }
 508 
 509     static void sleepNoThrow(long ms) {
 510         try {
 511             Thread.sleep(ms);
 512         }
 513         catch (Throwable t) {}
 514     }
 515 
 516     /**
 517      * Run some workloads with differ object/value life times...
 518      */
 519     public static void testActiveGc() {
 520         try {
 521             int nofThreads = 7;
 522             int workSize = nofThreads * 10;
 523 
 524             Object longLivedObjects = createLongLived();
 525             Object longLivedPeople = createPeople();
 526             Object longLivedPeopleVcc = createPeopleVcc();
 527 
 528             Object medLivedObjects = createLongLived();
 529             Object medLivedPeople = createPeople();
 530             Object medLivedPeopleVcc = createPeopleVcc();
 531 
 532             doGc();
 533 
 534             ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
 535 
 536             // submit work until we see some GC
 537             Reference ref = createRef();
 538             submitNewWork(fjPool, workSize);
 539             while (ref.get() != null) {
 540                 if (fjPool.hasQueuedSubmissions()) {
 541                     sleepNoThrow(1L);
 542                 }
 543                 else {
 544                     workSize *= 2; // Grow the submission size
 545                     submitNewWork(fjPool, workSize);
 546                 }
 547             }
 548 
 549             // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT
 550             int nofActiveGc = 1;
 551             ref = createRef();
 552             while (nofActiveGc < MIN_ACTIVE_GC_COUNT) {
 553                 if (ref.get() == null) {
 554                     nofActiveGc++;
 555                     ref = createRef();
 556                     if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) {
 557                         validateLongLived(medLivedObjects);
 558                         validatePeople(medLivedPeople);
 559                         validatePeopleVcc(medLivedPeopleVcc);
 560 
 561                         medLivedObjects = createLongLived();
 562                         medLivedPeople = createPeople();
 563                         medLivedPeopleVcc = createPeopleVcc();
 564                     }
 565                 }
 566                 else if (fjPool.hasQueuedSubmissions()) {
 567                     sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000));
 568                     doGc();
 569                 }
 570                 else {
 571                     submitNewWork(fjPool, workSize);
 572                 }
 573             }
 574             fjPool.shutdown();
 575 
 576             validateLongLived(medLivedObjects);
 577             validatePeople(medLivedPeople);
 578             validatePeopleVcc(medLivedPeopleVcc);
 579             medLivedObjects = null;
 580             medLivedPeople = null;
 581             medLivedPeopleVcc = null;
 582 
 583             validateLongLived(longLivedObjects);
 584             validatePeople(longLivedPeople);
 585             validatePeopleVcc(longLivedPeopleVcc);
 586 
 587             longLivedObjects = null;
 588             longLivedPeople = null;
 589             longLivedPeopleVcc = null;
 590 
 591             doGc();
 592         }
 593         catch (Throwable t) { fail("testMvtActiveGc", t); }
 594     }
 595 
 596     static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>();
 597 
 598     public static void doGc() {
 599         // Create Reference, wait until it clears...
 600         Reference ref = createRef();
 601         while (ref.get() != null) {
 602             System.gc();
 603         }
 604     }
 605 
 606     static Reference createRef() {
 607         return new WeakReference<Object>(new Object(), REFQ);
 608     }
 609 
 610     static void validatePersonVcc(Object obj, int id, String fn, String ln, boolean equals) {
 611         assertTrue(obj.getClass() == PersonVcc.class, "Expected VCC class");
 612         PersonVcc person = (PersonVcc) obj;
 613         assertTrue(person.id == id);
 614         if (equals) {
 615             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 616             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 617         }
 618         else {
 619             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 620             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 621         }
 622     }
 623 
 624     static PersonVcc createIndexedPersonVcc(int i) {
 625         return PersonVcc.create(i, firstName(i), lastName(i));
 626     }
 627 
 628     static void validateIndexedPersonVcc(Object obj, int i) {
 629         validatePersonVcc(obj, i, firstName(i), lastName(i), true);
 630     }
 631 
 632     static PersonVcc createDefaultPersonVcc() {
 633         return PersonVcc.create(0, null, null);
 634     }
 635 
 636     static void validateDefaultPersonVcc(Object obj) {
 637         validatePersonVcc(obj, 0, null, null, false);
 638     }
 639 
 640 
 641     static void validatePerson(Person person, int id, String fn, String ln, boolean equals) {
 642         assertTrue(person.id == id);
 643         if (equals) {
 644             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 645             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 646         }
 647         else {
 648             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 649             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 650         }
 651     }
 652 
 653     static Person createIndexedPerson(int i) {
 654         return Person.create(i, firstName(i), lastName(i));
 655     }
 656 
 657     static void validateIndexedPerson(Person person, int i) {
 658         validatePerson(person, i, firstName(i), lastName(i), true);
 659     }
 660 
 661     static Person createDefaultPerson() {
 662         return Person.create(0, null, null);
 663     }
 664 
 665     static void validateDefaultPerson(Person person) {
 666         validatePerson(person, 0, null, null, false);
 667     }
 668 
 669     static String firstName(int i) {
 670         return "FirstName-" + i;
 671     }
 672 
 673     static String lastName(int i) {
 674         return "LastName-" + i;
 675     }
 676 
 677     static Object createLongLived()  throws Throwable {
 678         Object[] population = new Object[2];
 679         population[0] = createPeople();
 680         population[1] = createPeopleVcc();
 681         return population;
 682     }
 683 
 684     static void validateLongLived(Object pop) throws Throwable {
 685         Object[] population = (Object[]) pop;
 686         validatePeople(population[0]);
 687         validatePeopleVcc(population[1]);
 688     }
 689 
 690     static Object createPeopleVcc() throws Throwable {
 691         int arrayLength = NOF_PEOPLE;
 692         Class<?> vccClass = PersonVcc.class;
 693         ValueType<?> vt = ValueType.forClass(vccClass);
 694         MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(vt.arrayValueClass());
 695 
 696         Object people = vt.newArray().invoke(arrayLength);
 697         for (int i = 0; i < arrayLength; i++) {
 698             arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 699         }
 700         return people;
 701     }
 702 
 703     static void validatePeopleVcc(Object people) throws Throwable {
 704         MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(
 705             ValueType.forClass((Class<?>)PersonVcc.class).arrayValueClass());
 706 
 707         int arrayLength = java.lang.reflect.Array.getLength(people);
 708         assertTrue(arrayLength == NOF_PEOPLE);
 709         for (int i = 0; i < arrayLength; i++) {
 710             validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 711         }
 712     }
 713 
 714     static Object createPeople() {
 715         int arrayLength = NOF_PEOPLE;
 716         Person[] people = new Person[arrayLength];
 717         for (int i = 0; i < arrayLength; i++) {
 718             people[i] = createIndexedPerson(i);
 719         }
 720         return people;
 721     }
 722 
 723     static void validatePeople(Object array) {
 724         Person[] people = (Person[]) array;
 725         int arrayLength = people.length;
 726         assertTrue(arrayLength == NOF_PEOPLE);
 727         for (int i = 0; i < arrayLength; i++) {
 728             validateIndexedPerson(people[i], i);
 729         }
 730     }
 731 
 732     // Various field layouts...
 733 
 734     static final __ByValue class ObjectValue {
 735         final Object object;
 736 
 737         private ObjectValue(Object object) {
 738             this.object = object;
 739         }
 740     }
 741 
 742     static class ObjectWithObjectValue {
 743         ObjectValue value1;
 744         Object      ref1;
 745     }
 746 
 747     static class ObjectWithObjectValues {
 748         ObjectValue value1;
 749         ObjectValue value2;
 750         Object      ref1;
 751     }
 752 
 753     static class Foo {
 754         int id;
 755         String name;
 756         String description;
 757         long timestamp;
 758         String notes;
 759     }
 760 
 761     static class Bar extends Foo {
 762         long extendedId;
 763         String moreNotes;
 764         int count;
 765         String otherStuff;
 766     }
 767 
 768     static final __ByValue class FooValue {
 769         final int id;
 770         final String name;
 771         final String description;
 772         final long timestamp;
 773         final String notes;
 774 
 775         private FooValue(int id, String name, String description, long timestamp, String notes) {
 776             this.id = id;
 777             this.name = name;
 778             this.description = description;
 779             this.timestamp = timestamp;
 780             this.notes = notes;
 781         }
 782     }
 783 
 784     static class BarWithValue {
 785         FooValue foo;
 786         long extendedId;
 787         String moreNotes;
 788         int count;
 789         String otherStuff;
 790     }
 791 
 792     static final __ByValue class BarValue {
 793         final FooValue foo;
 794         final long extendedId;
 795         final String moreNotes;
 796         final int count;
 797         final String otherStuff;
 798 
 799         private BarValue(FooValue foo, long extendedId, String moreNotes, int count, String otherStuff) {
 800             this.foo = foo;
 801             this.extendedId = extendedId;
 802             this.moreNotes = moreNotes;
 803             this.count = count;
 804             this.otherStuff = otherStuff;
 805         }
 806     }
 807 
 808     /*
 809        TODO: Current repo state doesn't pick up the common test/lib Asserts
 810        remove this implementation when fixed
 811      */
 812     public static void fail(String msg, Throwable thr) { // Missing Asserts.fail(String, Throwable)
 813         throw new RuntimeException(msg, thr);
 814     }
 815 }
 816