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 
  30 import jdk.experimental.value.MethodHandleBuilder;
  31 import jdk.incubator.mvt.ValueType;
  32 
  33 import static jdk.test.lib.Asserts.*;
  34 import jdk.test.lib.Utils;
  35 import sun.hotspot.WhiteBox;
  36 
  37 /**
  38  * @test ValueOops
  39  * @summary Test embedding oops into Value types
  40  * @modules java.base/jdk.experimental.value
  41  *          jdk.incubator.mvt
  42  * @library /test/lib
  43  * @compile PersonVcc.java
  44  * @compile -XDenableValueTypes Person.java
  45  * @compile -XDenableValueTypes ValueOops.java
  46  * @run main ClassFileInstaller sun.hotspot.WhiteBox
  47  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  48  *                                sun.hotspot.WhiteBox$WhiteBoxPermission
  49  * @run main/othervm -Xint -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  50  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  51  *                   runtime.valhalla.valuetypes.ValueOops
  52  * @run main/othervm -Xint -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  53  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  54  *                   runtime.valhalla.valuetypes.ValueOops
  55  * @run main/othervm -Xint -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  56  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  57  *                   runtime.valhalla.valuetypes.ValueOops
  58  * @run main/othervm -Xint -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  59  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  60  *                   runtime.valhalla.valuetypes.ValueOops
  61  * @run main/othervm -Xcomp -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  62  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  63  *                   runtime.valhalla.valuetypes.ValueOops
  64  * @run main/othervm -Xcomp -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  65  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  66  *                   runtime.valhalla.valuetypes.ValueOops
  67  * @run main/othervm -Xcomp -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  68  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  69  *                   runtime.valhalla.valuetypes.ValueOops
  70  * @run main/othervm -Xcomp -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla
  71  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  72  *                   runtime.valhalla.valuetypes.ValueOops
  73  */
  74 public class ValueOops {
  75 
  76     // Note: -noverify can not be eliminated. Possible issue with ValueType::mhName(),
  77     //       does not translate '.' to '/' for JVM.
  78     //       java.lang.ClassFormatError: Illegal method name "runtime.valhalla.valuetypes.PersonVcc_default"
  79     //
  80     // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap
  81     // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info
  82 
  83     static final int NOF_PEOPLE = 1000; // Exercise arrays of this size
  84 
  85     static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes
  86 
  87     static int MED_ACTIVE_GC_COUNT = 4;  // Medium life span in terms of GC passes
  88 
  89     static final String TEST_STRING1 = "Test String 1";
  90     static final String TEST_STRING2 = "Test String 2";
  91 
  92     static boolean USE_COMPILER = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler");
  93 
  94     public static void main(String[] args) {
  95         if (args.length > 0) {
  96             MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]);
  97         }
  98         testClassLoad();
  99         testBytecodes();
 100         testVvt();
 101         testMvt();
 102 
 103         if (!USE_COMPILER) {
 104             testOopMaps();
 105         }
 106 
 107         // Check we survive GC...
 108         testOverGc();   // Exercise root scan / oopMap
 109         testActiveGc(); // Brute force
 110     }
 111 
 112     /**
 113      * Test ClassFileParser can load values with reference fields
 114      */
 115     public static void testClassLoad() {
 116         // VVT
 117         String s = Person.class.toString();
 118         new Bar();
 119         new BarWithValue();
 120         s = BarValue.class.toString();
 121         s = ObjectWithObjectValue.class.toString();
 122         s = ObjectWithObjectValues.class.toString();
 123 
 124         // MVT
 125         Class<?> vccClass = PersonVcc.class;
 126         ValueType<?> vt = ValueType.forClass(vccClass);
 127         Class<?> boxClass = vt.boxClass();
 128         Class<?> dvtClass = vt.valueClass();
 129         Class<?> arrayClass = vt.arrayValueClass();
 130         s = dvtClass.toString();
 131     }
 132 
 133     /**
 134      * Test value type opcodes are okay with reference fields in values
 135      * I.e. ValueKlass::value_store()
 136      */
 137     public static void testBytecodes() {
 138         try {
 139             // Craft Value Type class how you will, using MVT class for simplicity just here
 140             ValueType<?> vt = ValueType.forClass(PersonVcc.class);
 141             Class<?> vtClass = vt.valueClass();
 142             Class<?> arrayClass = vt.arrayValueClass();
 143             Class<?> boxClass = vt.boxClass();
 144             MethodHandles.Lookup lookup = MethodHandles.lookup();
 145 
 146             // Exercise with null refs...
 147 
 148             // anewarray
 149             Object array = MethodHandleBuilder.loadCode(
 150                 lookup,
 151                 "anewarrayPerson",
 152                 MethodType.methodType(Object.class, Integer.TYPE),
 153                 CODE->{
 154                 CODE
 155                 .iload(0)
 156                 .anewarray(vtClass)
 157                 .astore(0)
 158                 .aload(0)
 159                 .areturn();
 160             }).invoke(NOF_PEOPLE);
 161 
 162             // vaload
 163             // vstore
 164             // vload
 165             // vastore
 166             // vbox
 167             int arraySlot = 0;
 168             int indexSlot = 1;
 169             int valueSlot = 2;
 170             Object obj = MethodHandleBuilder.loadCode(
 171                 lookup,
 172                 "valoadBoxPerson",
 173                 MethodType.methodType(Object.class, arrayClass, Integer.TYPE),
 174                 CODE->{
 175                 CODE
 176                 .aload(arraySlot)
 177                 .iload(indexSlot)
 178                 .vaload()
 179                 .vstore(valueSlot)
 180                 .aload(arraySlot)
 181                 .iload(indexSlot)
 182                 .vload(valueSlot)
 183                 .vastore()
 184                 .vload(valueSlot)
 185                 .vbox(boxClass)
 186                 .areturn();
 187             }).invoke(array, 7);
 188             validateDefaultPersonVcc(obj);
 189 
 190             // vreturn
 191             MethodHandle loadValueFromArray = MethodHandleBuilder.loadCode(
 192                 lookup,
 193                 "valoadVreturnPerson",
 194                 MethodType.methodType(vtClass, arrayClass, Integer.TYPE),
 195                 CODE->{
 196                 CODE
 197                 .aload(arraySlot)
 198                 .iload(indexSlot)
 199                 .vaload()
 200                 .vreturn();
 201             });
 202             MethodHandle box = MethodHandleBuilder.loadCode(
 203                 lookup,
 204                 "boxPersonVcc",
 205                 MethodType.methodType(boxClass, vtClass),
 206                 CODE->{
 207                 CODE
 208                 .vload(0)
 209                 .vbox(boxClass)
 210                 .areturn();
 211             });
 212             MethodHandle loadValueFromArrayBoxed =
 213                 MethodHandles.filterReturnValue(loadValueFromArray, box);
 214             obj = loadValueFromArrayBoxed.invoke(array, 0);
 215             validateDefaultPersonVcc(obj);
 216 
 217             // vunbox
 218             MethodHandle unbox = MethodHandleBuilder.loadCode(
 219                 lookup,
 220                 "unboxPersonVcc",
 221                 MethodType.methodType(vtClass, boxClass),
 222                 CODE->{
 223                 CODE
 224                 .aload(0)
 225                 .vunbox(vtClass)
 226                 .vreturn();
 227             });
 228             MethodHandle unboxBox = MethodHandles.filterReturnValue(unbox, box);
 229             obj = unboxBox.invoke(createDefaultPersonVcc());
 230             validateDefaultPersonVcc(obj);
 231 
 232             /*
 233                vgetfield
 234                qputfield
 235                qgetfield
 236 
 237                going to need VVT for VT fields and vgetfield
 238                check test coverage in testVvt()
 239             */
 240 
 241             // Exercise with live refs...
 242             Thread.holdsLock("Debug here");
 243             // vunbox
 244             // vastore
 245             // vaload
 246             // vstore
 247             // vload
 248             // vbox
 249             int index = 3;
 250             obj = MethodHandleBuilder.loadCode(
 251                 lookup,
 252                 "unboxStoreLoadPersonVcc",
 253                 MethodType.methodType(boxClass, arrayClass, Integer.TYPE, boxClass),
 254                 CODE->{
 255                 CODE
 256                 .aload(arraySlot)
 257                 .iload(indexSlot)
 258                 .aload(valueSlot)
 259                 .vunbox(vtClass)
 260                 .vastore()
 261                 .aload(arraySlot)
 262                 .iload(indexSlot)
 263                 .vaload()
 264                 .vstore(valueSlot)
 265                 .vload(valueSlot)
 266                 .vbox(boxClass)
 267                 .areturn();
 268             }).invoke(array, index, createIndexedPersonVcc(index));
 269             validateIndexedPersonVcc(obj, index);
 270 
 271             // Check the neighbours
 272             validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index - 1));
 273             validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index + 1));
 274 
 275             // vreturn
 276             validateIndexedPersonVcc(unboxBox.invoke((PersonVcc)obj), index);
 277         }
 278         catch (Throwable t) { fail("testBytecodes", t); }
 279     }
 280 
 281     static class Couple {
 282         public Person onePerson;
 283         public Person otherPerson;
 284     }
 285 
 286     static final __ByValue class Composition {
 287         public final Person onePerson;
 288         public final Person otherPerson;
 289 
 290         private Composition() {
 291             this.onePerson   = Person.create(0, null, null);
 292             this.otherPerson = Person.create(0, null, null);
 293         }
 294 
 295         __ValueFactory public static Composition create(Person onePerson, Person otherPerson) {
 296             Composition comp = __MakeDefault Composition();
 297             comp.onePerson   = onePerson;
 298             comp.otherPerson = otherPerson;
 299             return comp;
 300         }
 301     }
 302 
 303     /**
 304      * Check value type operations with "Valhalla Value Types" (VVT)
 305      */
 306     public static void testVvt() {
 307         // Exercise creation, vgetfield, vreturn with null refs
 308         validateDefaultPerson(createDefaultPerson());
 309 
 310         // anewarray, vaload, vastore
 311         int index = 7;
 312         Person[] array =  new Person[NOF_PEOPLE];
 313         validateDefaultPerson(array[index]);
 314 
 315         // Now with refs...
 316         validateIndexedPerson(createIndexedPerson(index), index);
 317         array[index] = createIndexedPerson(index);
 318         validateIndexedPerson(array[index], index);
 319 
 320         // Check the neighbours
 321         validateDefaultPerson(array[index - 1]);
 322         validateDefaultPerson(array[index + 1]);
 323 
 324         // getfield/putfield
 325         Couple couple = new Couple();
 326         validateDefaultPerson(couple.onePerson);
 327         validateDefaultPerson(couple.otherPerson);
 328 
 329         couple.onePerson = createIndexedPerson(index);
 330         validateIndexedPerson(couple.onePerson, index);
 331 
 332         Composition composition = Composition.create(couple.onePerson, couple.onePerson);
 333         validateIndexedPerson(composition.onePerson, index);
 334         validateIndexedPerson(composition.otherPerson, index);
 335     }
 336 
 337     /**
 338      * Check value type operations with "Minimal Value Types" (MVT)
 339      */
 340     public static void testMvt() {
 341         try {
 342             // MVT...
 343             ValueType<?> vt = ValueType.forClass(PersonVcc.class);
 344             Class<?> vtClass = vt.valueClass();
 345             Class<?> arrayClass = vt.arrayValueClass();
 346 
 347             Object obj = vt.defaultValueConstant().invoke();
 348             validateDefaultPersonVcc(obj);
 349 
 350             obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box())
 351                 .invoke(createDefaultPersonVcc());
 352             validateDefaultPersonVcc(obj);
 353 
 354             int index = 11;
 355             obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box())
 356                 .invoke(createIndexedPersonVcc(index));
 357             validateIndexedPersonVcc(obj, index);
 358 
 359             testMvtArray("testMvt.array.1", 1);
 360         }
 361         catch (Throwable t) {
 362             fail("testMvtfailed", t);
 363         }
 364     }
 365 
 366     /**
 367      * MVT array operations...
 368      */
 369     public static void testMvtPeopleArray() {
 370         testMvtArray("testMvtPeopleArray", NOF_PEOPLE);
 371     }
 372 
 373     public static void testMvtArray(String testName, int arrayLength) {
 374         try {
 375             Class<?> vcc = PersonVcc.class;
 376             ValueType<?> vt = ValueType.forClass(vcc);
 377             Class<?> dvtClass = vt.valueClass();
 378             Class<?> arrayClass = vt.arrayValueClass();
 379 
 380             MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(arrayClass);
 381             MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(arrayClass);
 382 
 383             MethodHandles.Lookup lookup = MethodHandles.lookup();
 384             MethodHandle getId = lookup.findGetter(dvtClass, "id", Integer.TYPE);
 385             MethodHandle getFirstName = lookup.findGetter(dvtClass, "firstName", String.class);
 386             MethodHandle getLastName = lookup.findGetter(dvtClass, "lastName", String.class);
 387 
 388             MethodHandle getIdFromArray = MethodHandles.filterReturnValue(arrayElemGet, getId);
 389             MethodHandle getFnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getFirstName);
 390             MethodHandle getLnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getLastName);
 391 
 392             Object people = vt.newArray().invoke(arrayLength);
 393             for (int i = 0; i < arrayLength; i++) {
 394                 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 395             }
 396 
 397             for (int i = 0; i < arrayLength; i++) {
 398                 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 399 
 400                 int id = (int) getIdFromArray.invoke(people, i);
 401                 assertTrue(id == i, "Invalid field: Id");
 402                 String fn = (String) getFnFromArray.invoke(people, i);
 403                 assertTrue(fn.equals(firstName(i)), "Invalid field: firstName");
 404                 String ln = (String) getLnFromArray.invoke(people, i);
 405                 assertTrue(ln.equals(lastName(i)), "Invalid field: lastName");
 406             }
 407         }
 408         catch (Throwable t) {
 409             fail(testName + " failed", t);
 410         }
 411     }
 412 
 413     /**
 414      * Check oop map generation for klass layout and frame...
 415      */
 416     public static void testOopMaps() {
 417         Object[] objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(new Couple());
 418         assertTrue(objects.length == 4, "Expected 4 oops");
 419         for (int i = 0; i < objects.length; i++) {
 420             assertTrue(objects[i] == null, "not-null");
 421         }
 422 
 423         String fn1 = "Sam";
 424         String ln1 = "Smith";
 425         String fn2 = "Jane";
 426         String ln2 = "Jones";
 427         Couple couple = new Couple();
 428         couple.onePerson = Person.create(0, fn1, ln1);
 429         couple.otherPerson = Person.create(1, fn2, ln2);
 430         objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(couple);
 431         assertTrue(objects.length == 4, "Expected 4 oops");
 432         assertTrue(objects[0] == fn1, "Bad oop fn1");
 433         assertTrue(objects[1] == ln1, "Bad oop ln1");
 434         assertTrue(objects[2] == fn2, "Bad oop fn2");
 435         assertTrue(objects[3] == ln2, "Bad oop ln2");
 436 
 437         objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(couple);
 438         assertTrue(objects.length == 4, "Expected 4 oops");
 439         assertTrue(objects[0] == fn1, "Bad oop fn1");
 440         assertTrue(objects[1] == ln1, "Bad oop ln1");
 441         assertTrue(objects[2] == fn2, "Bad oop fn2");
 442         assertTrue(objects[3] == ln2, "Bad oop ln2");
 443 
 444         // Array..
 445         objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(createPeople());
 446         assertTrue(objects.length == NOF_PEOPLE * 2, "Unexpected length: " + objects.length);
 447         int o = 0;
 448         for (int i = 0; i < NOF_PEOPLE; i++) {
 449             assertTrue(objects[o++].equals(firstName(i)), "Bad firstName");
 450             assertTrue(objects[o++].equals(lastName(i)), "Bad lastName");
 451         }
 452 
 453         // Sanity check, FixMe need more test cases
 454         objects = testFrameOops(couple);
 455         assertTrue(objects.length == 5, "Number of frame oops incorrect = " + objects.length);
 456         assertTrue(objects[0] == couple, "Bad oop 0");
 457         assertTrue(objects[1] == fn1, "Bad oop 1");
 458         assertTrue(objects[2] == ln1, "Bad oop 2");
 459         assertTrue(objects[3] == TEST_STRING1, "Bad oop 3");
 460         assertTrue(objects[4] == TEST_STRING2, "Bad oop 4");
 461 
 462         testFrameOopsVBytecodes();
 463     }
 464 
 465     static final String GET_OOP_MAP_NAME = "getOopMap";
 466     static final String GET_OOP_MAP_DESC = "()[Ljava/lang/Object;";
 467 
 468     static Object[] getOopMap() {
 469         Object[] oopMap = WhiteBox.getWhiteBox().getObjectsViaFrameOopIterator(2);
 470         /* Remove this frame (class mirror for this method), and above class mirror */
 471         Object[] trimmedOopMap = new Object[oopMap.length - 2];
 472         System.arraycopy(oopMap, 2, trimmedOopMap, 0, trimmedOopMap.length);
 473         return trimmedOopMap;
 474     }
 475 
 476     // Expecting Couple couple, Person couple.onePerson, and Person (created here)
 477     public static Object[] testFrameOops(Couple couple) {
 478         int someId = 89898;
 479         Person person = couple.onePerson;
 480         assertTrue(person.getId() == 0, "Bad Person");
 481         Person anotherPerson = Person.create(someId, TEST_STRING1, TEST_STRING2);
 482         assertTrue(anotherPerson.getId() == someId, "Bad Person");
 483         return getOopMap();
 484     }
 485 
 486     // Debug...
 487     static void dumpOopMap(Object[] oopMap) {
 488         System.out.println("Oop Map len: " + oopMap.length);
 489         for (int i = 0; i < oopMap.length; i++) {
 490             System.out.println("[" + i + "] = " + oopMap[i]);
 491         }
 492     }
 493 
 494     /**
 495      * Just some check sanity checks with vdefault, vwithfield, vstore and vload
 496      *
 497      * Changes to javac slot usage may well break this test
 498      */
 499     public static void testFrameOopsVBytecodes() {
 500         int nofOopMaps = 4;
 501         Object[][] oopMaps = new Object[nofOopMaps][];
 502         String[] inputArgs = new String[] { "aName", "aDescription", "someNotes" };
 503 
 504         FooValue.testFrameOopsDefault(oopMaps);
 505 
 506         dumpOopMap(oopMaps[0]);
 507         dumpOopMap(oopMaps[1]);
 508         dumpOopMap(oopMaps[2]);
 509         dumpOopMap(oopMaps[3]);
 510 
 511         // Test-D0 Slots=R Stack=Q(RRR)RV
 512         assertTrue(oopMaps[0].length == 5 &&
 513                    oopMaps[0][1] == null &&
 514                    oopMaps[0][2] == null &&
 515                    oopMaps[0][3] == null, "Test-D0 incorrect");
 516 
 517         // Test-D1 Slots=R Stack=RV
 518         assertTrue(oopMaps[1].length == 2, "Test-D1 incorrect");
 519 
 520         // Test-D2 Slots=RQ(RRR) Stack=RV
 521         assertTrue(oopMaps[2].length == 5 &&
 522                    oopMaps[2][1] == null &&
 523                    oopMaps[2][2] == null &&
 524                    oopMaps[2][3] == null, "Test-D2 incorrect");
 525 
 526         // Test-D3 Slots=R Stack=Q(RRR)RV
 527         assertTrue(oopMaps[3].length == 6 &&
 528                    oopMaps[3][1] == null &&
 529                    oopMaps[3][2] == null &&
 530                    oopMaps[3][3] == null &&
 531                    oopMaps[3][4] == null, "Test-D3 incorrect");
 532 
 533         // With ref fields...
 534         String name = "TestName";
 535         String desc = "TestDesc";
 536         String note = "TestNotes";
 537         FooValue.testFrameOopsRefs(name, desc, note, oopMaps);
 538         dumpOopMap(oopMaps[0]);
 539 
 540         // Test-R0 Slots=RR Stack=Q(RRR)RV
 541         assertTrue(oopMaps[0].length == 6 &&
 542                    oopMaps[0][2] == name &&
 543                    oopMaps[0][3] == desc &&
 544                    oopMaps[0][4] == note, "Test-R0 incorrect");
 545 
 546         /**
 547          * TODO: vwithfield from method handle cooked from anonymous class within the value class
 548          *       even with "MethodHandles.privateLookupIn()" will fail final putfield rules
 549          */
 550     }
 551 
 552     /**
 553      * Check forcing GC for combination of VT on stack/LVT etc works
 554      */
 555     public static void testOverGc() {
 556         try {
 557             Class<?> vccClass = PersonVcc.class;
 558             ValueType<?> vt = ValueType.forClass(vccClass);
 559             Class<?> vtClass = vt.valueClass();
 560             Class<?> arrayClass = vt.arrayValueClass();
 561 
 562             MethodHandles.Lookup lookup = MethodHandles.lookup();
 563             doGc();
 564 
 565             // VT on stack and lvt, null refs, see if GC flies
 566             MethodHandle moveValueThroughStackAndLvt = MethodHandleBuilder.loadCode(
 567                 lookup,
 568                 "gcOverPerson",
 569                 MethodType.methodType(vccClass, vccClass),
 570                 CODE->{
 571                 CODE
 572                 .aload(0)
 573                 .vunbox(vtClass)
 574                 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack
 575                 .vstore(0)
 576                 .invokestatic(ValueOops.class, "doGc", "()V", false) // LVT
 577                 .vload(0)
 578                 .iconst_1()  // push a litte further down
 579                 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack,LVT
 580                 .pop()
 581                 .vbox(vccClass)
 582                 .areturn();
 583             });
 584             Object obj = moveValueThroughStackAndLvt.invoke(createDefaultPersonVcc());
 585             validateDefaultPersonVcc(obj);
 586             doGc();
 587             obj = null;
 588             doGc();
 589 
 590             int index = 4711;
 591             obj = moveValueThroughStackAndLvt.invoke(createIndexedPersonVcc(index));
 592             validateIndexedPersonVcc(obj, index);
 593             doGc();
 594             obj = null;
 595             doGc();
 596         }
 597         catch (Throwable t) { fail("testOverGc", t); }
 598     }
 599 
 600     static void submitNewWork(ForkJoinPool fjPool, int size) {
 601         for (int i = 0; i < size; i++) {
 602             fjPool.execute(ValueOops::testMvtPeopleArray);
 603             for (int j = 0; j < 100; j++) {
 604                 // JDK-8186718 random crashes in interpreter vbox and vunbox (with G1)
 605                 // test needs refactoring to more specific use cases for debugging.
 606                 //fjPool.execute(ValueOops::testBytecodes);
 607                 fjPool.execute(ValueOops::testVvt);
 608                 fjPool.execute(ValueOops::testMvt);
 609             }
 610         }
 611     }
 612 
 613     static void sleepNoThrow(long ms) {
 614         try {
 615             Thread.sleep(ms);
 616         }
 617         catch (Throwable t) {}
 618     }
 619 
 620     /**
 621      * Run some workloads with different object/value life times...
 622      */
 623     public static void testActiveGc() {
 624         try {
 625             int nofThreads = 7;
 626             int workSize = nofThreads * 10;
 627 
 628             Object longLivedObjects = createLongLived();
 629             Object longLivedPeople = createPeople();
 630             Object longLivedPeopleVcc = createPeopleVcc();
 631 
 632             Object medLivedObjects = createLongLived();
 633             Object medLivedPeople = createPeople();
 634             Object medLivedPeopleVcc = createPeopleVcc();
 635 
 636             doGc();
 637 
 638             ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
 639 
 640             // submit work until we see some GC
 641             Reference ref = createRef();
 642             submitNewWork(fjPool, workSize);
 643             while (ref.get() != null) {
 644                 if (fjPool.hasQueuedSubmissions()) {
 645                     sleepNoThrow(1L);
 646                 }
 647                 else {
 648                     workSize *= 2; // Grow the submission size
 649                     submitNewWork(fjPool, workSize);
 650                 }
 651             }
 652 
 653             // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT
 654             int nofActiveGc = 1;
 655             ref = createRef();
 656             while (nofActiveGc < MIN_ACTIVE_GC_COUNT) {
 657                 if (ref.get() == null) {
 658                     nofActiveGc++;
 659                     ref = createRef();
 660                     if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) {
 661                         validateLongLived(medLivedObjects);
 662                         validatePeople(medLivedPeople);
 663                         validatePeopleVcc(medLivedPeopleVcc);
 664 
 665                         medLivedObjects = createLongLived();
 666                         medLivedPeople = createPeople();
 667                         medLivedPeopleVcc = createPeopleVcc();
 668                     }
 669                 }
 670                 else if (fjPool.hasQueuedSubmissions()) {
 671                     sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000));
 672                     doGc();
 673                 }
 674                 else {
 675                     submitNewWork(fjPool, workSize);
 676                 }
 677             }
 678             fjPool.shutdown();
 679 
 680             validateLongLived(medLivedObjects);
 681             validatePeople(medLivedPeople);
 682             validatePeopleVcc(medLivedPeopleVcc);
 683             medLivedObjects = null;
 684             medLivedPeople = null;
 685             medLivedPeopleVcc = null;
 686 
 687             validateLongLived(longLivedObjects);
 688             validatePeople(longLivedPeople);
 689             validatePeopleVcc(longLivedPeopleVcc);
 690 
 691             longLivedObjects = null;
 692             longLivedPeople = null;
 693             longLivedPeopleVcc = null;
 694 
 695             doGc();
 696         }
 697         catch (Throwable t) { fail("testMvtActiveGc", t); }
 698     }
 699 
 700     static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>();
 701 
 702     public static void doGc() {
 703         // Create Reference, wait until it clears...
 704         Reference ref = createRef();
 705         while (ref.get() != null) {
 706             System.gc();
 707         }
 708     }
 709 
 710     static Reference createRef() {
 711         return new WeakReference<Object>(new Object(), REFQ);
 712     }
 713 
 714     static void validatePersonVcc(Object obj, int id, String fn, String ln, boolean equals) {
 715         assertTrue(obj.getClass() == PersonVcc.class, "Expected VCC class");
 716         PersonVcc person = (PersonVcc) obj;
 717         assertTrue(person.id == id);
 718         if (equals) {
 719             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 720             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 721         }
 722         else {
 723             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 724             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 725         }
 726     }
 727 
 728     static PersonVcc createIndexedPersonVcc(int i) {
 729         return PersonVcc.create(i, firstName(i), lastName(i));
 730     }
 731 
 732     static void validateIndexedPersonVcc(Object obj, int i) {
 733         validatePersonVcc(obj, i, firstName(i), lastName(i), true);
 734     }
 735 
 736     static PersonVcc createDefaultPersonVcc() {
 737         return PersonVcc.create(0, null, null);
 738     }
 739 
 740     static void validateDefaultPersonVcc(Object obj) {
 741         validatePersonVcc(obj, 0, null, null, false);
 742     }
 743 
 744 
 745     static void validatePerson(Person person, int id, String fn, String ln, boolean equals) {
 746         assertTrue(person.id == id);
 747         if (equals) {
 748             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 749             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 750         }
 751         else {
 752             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 753             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 754         }
 755     }
 756 
 757     static Person createIndexedPerson(int i) {
 758         return Person.create(i, firstName(i), lastName(i));
 759     }
 760 
 761     static void validateIndexedPerson(Person person, int i) {
 762         validatePerson(person, i, firstName(i), lastName(i), true);
 763     }
 764 
 765     static Person createDefaultPerson() {
 766         return Person.create(0, null, null);
 767     }
 768 
 769     static void validateDefaultPerson(Person person) {
 770         validatePerson(person, 0, null, null, false);
 771     }
 772 
 773     static String firstName(int i) {
 774         return "FirstName-" + i;
 775     }
 776 
 777     static String lastName(int i) {
 778         return "LastName-" + i;
 779     }
 780 
 781     static Object createLongLived()  throws Throwable {
 782         Object[] population = new Object[2];
 783         population[0] = createPeople();
 784         population[1] = createPeopleVcc();
 785         return population;
 786     }
 787 
 788     static void validateLongLived(Object pop) throws Throwable {
 789         Object[] population = (Object[]) pop;
 790         validatePeople(population[0]);
 791         validatePeopleVcc(population[1]);
 792     }
 793 
 794     static Object createPeopleVcc() throws Throwable {
 795         int arrayLength = NOF_PEOPLE;
 796         Class<?> vccClass = PersonVcc.class;
 797         ValueType<?> vt = ValueType.forClass(vccClass);
 798         MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(vt.arrayValueClass());
 799 
 800         Object people = vt.newArray().invoke(arrayLength);
 801         for (int i = 0; i < arrayLength; i++) {
 802             arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 803         }
 804         return people;
 805     }
 806 
 807     static void validatePeopleVcc(Object people) throws Throwable {
 808         MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(
 809             ValueType.forClass((Class<?>)PersonVcc.class).arrayValueClass());
 810 
 811         int arrayLength = java.lang.reflect.Array.getLength(people);
 812         assertTrue(arrayLength == NOF_PEOPLE);
 813         for (int i = 0; i < arrayLength; i++) {
 814             validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 815         }
 816     }
 817 
 818     static Object createPeople() {
 819         int arrayLength = NOF_PEOPLE;
 820         Person[] people = new Person[arrayLength];
 821         for (int i = 0; i < arrayLength; i++) {
 822             people[i] = createIndexedPerson(i);
 823         }
 824         return people;
 825     }
 826 
 827     static void validatePeople(Object array) {
 828         Person[] people = (Person[]) array;
 829         int arrayLength = people.length;
 830         assertTrue(arrayLength == NOF_PEOPLE);
 831         for (int i = 0; i < arrayLength; i++) {
 832             validateIndexedPerson(people[i], i);
 833         }
 834     }
 835 
 836     // Various field layouts...sanity testing, see MVTCombo testing for full-set
 837 
 838     static final __ByValue class ObjectValue {
 839         final Object object;
 840 
 841         private ObjectValue(Object object) {
 842             this.object = object;
 843         }
 844     }
 845 
 846     static class ObjectWithObjectValue {
 847         ObjectValue value1;
 848         Object      ref1;
 849     }
 850 
 851     static class ObjectWithObjectValues {
 852         ObjectValue value1;
 853         ObjectValue value2;
 854         Object      ref1;
 855     }
 856 
 857     static class Foo {
 858         int id;
 859         String name;
 860         String description;
 861         long timestamp;
 862         String notes;
 863     }
 864 
 865     static class Bar extends Foo {
 866         long extendedId;
 867         String moreNotes;
 868         int count;
 869         String otherStuff;
 870     }
 871 
 872     static final __ByValue class FooValue {
 873         final int id;
 874         final String name;
 875         final String description;
 876         final long timestamp;
 877         final String notes;
 878 
 879         private FooValue() {
 880             this.id          = 0;
 881             this.name        = null;
 882             this.description = null;
 883             this.timestamp   = 0L;
 884             this.notes       = null;
 885         }
 886 
 887         __ValueFactory public static FooValue create(int id, String name, String description, long timestamp, String notes) {
 888             FooValue f = __MakeDefault FooValue();
 889             f.id          = id;
 890             f.name        = name;
 891             f.description = description;
 892             f.timestamp   = timestamp;
 893             f.notes       = notes;
 894             return f;
 895         }
 896 
 897         public static void testFrameOopsDefault(Object[][] oopMaps) {
 898             Class<?> fooValueCls = FooValue.class;
 899             MethodType mt = MethodType.methodType(Void.TYPE, oopMaps.getClass());
 900             int oopMapsSlot   = 0;
 901             int vtSlot        = 1;
 902 
 903             // Slots 1=oopMaps
 904             // OopMap Q=RRR (.name .description .someNotes)
 905             try {
 906                 MethodHandleBuilder
 907                     .loadCode(MethodHandles.lookup(),
 908                               "exerciseVBytecodeExprStackWithDefault", mt,
 909                               CODE->{
 910                                   CODE
 911                                       .vdefault(fooValueCls)
 912                                       .aload(oopMapsSlot)
 913                                       .iconst_0()  // Test-D0 Slots=R Stack=Q(RRR)RV
 914                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 915                                       .aastore()
 916                                       .pop()
 917                                       .aload(oopMapsSlot)
 918                                       .iconst_1()  // Test-D1 Slots=R Stack=RV
 919                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 920                                       .aastore()
 921                                       .vdefault(fooValueCls)
 922                                       .vstore(vtSlot)
 923                                       .aload(oopMapsSlot)
 924                                       .iconst_2()  // Test-D2 Slots=RQ(RRR) Stack=RV
 925                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 926                                       .aastore()
 927                                       .vload(vtSlot)
 928                                       .aconst_null()
 929                                       .astore(vtSlot) // Storing null over the Q slot won't remove the ref, but should be single null ref
 930                                       .aload(oopMapsSlot)
 931                                       .iconst_3()  // Test-D3 Slots=R Stack=Q(RRR)RV
 932                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 933                                       .aastore()
 934                                       .pop()
 935                                       .return_();
 936                               }).invoke(oopMaps);
 937             } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); }
 938         }
 939 
 940         public static void testFrameOopsRefs(String name, String description, String notes, Object[][] oopMaps) {
 941             Class<?> fooValueCls = FooValue.class;
 942             FooValue f = create(4711, name, description, 9876543231L, notes);
 943             FooValue[] fa = new FooValue[] { f };
 944             MethodType mt = MethodType.methodType(Void.TYPE, fa.getClass(), oopMaps.getClass());
 945             int fooArraySlot  = 0;
 946             int oopMapsSlot   = 1;
 947             try {
 948                 MethodHandleBuilder
 949                     .loadCode(MethodHandles.lookup(),
 950                               "exerciseVBytecodeExprStackWithRefs", mt,
 951                               CODE->{
 952                                   CODE
 953                                       .aload(fooArraySlot)
 954                                       .iconst_0()
 955                                       .vaload()
 956                                       .aload(oopMapsSlot)
 957                                       .iconst_0()  // Test-R0 Slots=RR Stack=Q(RRR)RV
 958                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 959                                       .aastore()
 960                                       .pop()
 961                                       .return_();
 962                               }).invoke(fa, oopMaps);
 963             } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); }
 964         }
 965     }
 966 
 967     static class BarWithValue {
 968         FooValue foo;
 969         long extendedId;
 970         String moreNotes;
 971         int count;
 972         String otherStuff;
 973     }
 974 
 975     static final __ByValue class BarValue {
 976         final FooValue foo;
 977         final long extendedId;
 978         final String moreNotes;
 979         final int count;
 980         final String otherStuff;
 981 
 982         private BarValue(FooValue foo, long extendedId, String moreNotes, int count, String otherStuff) {
 983             this.foo = foo;
 984             this.extendedId = extendedId;
 985             this.moreNotes = moreNotes;
 986             this.count = count;
 987             this.otherStuff = otherStuff;
 988         }
 989     }
 990 
 991 }
 992