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