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