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                 // JDK-8186718 random crashes in interpreter vbox and vunbox (with G1)
 597                 // test needs refactoring to more specific use cases for debugging.
 598                 //fjPool.execute(ValueOops::testBytecodes);
 599                 fjPool.execute(ValueOops::testVvt);
 600                 fjPool.execute(ValueOops::testMvt);
 601             }
 602         }
 603     }
 604 
 605     static void sleepNoThrow(long ms) {
 606         try {
 607             Thread.sleep(ms);
 608         }
 609         catch (Throwable t) {}
 610     }
 611 
 612     /**
 613      * Run some workloads with different object/value life times...
 614      */
 615     public static void testActiveGc() {
 616         try {
 617             int nofThreads = 7;
 618             int workSize = nofThreads * 10;
 619 
 620             Object longLivedObjects = createLongLived();
 621             Object longLivedPeople = createPeople();
 622             Object longLivedPeopleVcc = createPeopleVcc();
 623 
 624             Object medLivedObjects = createLongLived();
 625             Object medLivedPeople = createPeople();
 626             Object medLivedPeopleVcc = createPeopleVcc();
 627 
 628             doGc();
 629 
 630             ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
 631 
 632             // submit work until we see some GC
 633             Reference ref = createRef();
 634             submitNewWork(fjPool, workSize);
 635             while (ref.get() != null) {
 636                 if (fjPool.hasQueuedSubmissions()) {
 637                     sleepNoThrow(1L);
 638                 }
 639                 else {
 640                     workSize *= 2; // Grow the submission size
 641                     submitNewWork(fjPool, workSize);
 642                 }
 643             }
 644 
 645             // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT
 646             int nofActiveGc = 1;
 647             ref = createRef();
 648             while (nofActiveGc < MIN_ACTIVE_GC_COUNT) {
 649                 if (ref.get() == null) {
 650                     nofActiveGc++;
 651                     ref = createRef();
 652                     if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) {
 653                         validateLongLived(medLivedObjects);
 654                         validatePeople(medLivedPeople);
 655                         validatePeopleVcc(medLivedPeopleVcc);
 656 
 657                         medLivedObjects = createLongLived();
 658                         medLivedPeople = createPeople();
 659                         medLivedPeopleVcc = createPeopleVcc();
 660                     }
 661                 }
 662                 else if (fjPool.hasQueuedSubmissions()) {
 663                     sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000));
 664                     doGc();
 665                 }
 666                 else {
 667                     submitNewWork(fjPool, workSize);
 668                 }
 669             }
 670             fjPool.shutdown();
 671 
 672             validateLongLived(medLivedObjects);
 673             validatePeople(medLivedPeople);
 674             validatePeopleVcc(medLivedPeopleVcc);
 675             medLivedObjects = null;
 676             medLivedPeople = null;
 677             medLivedPeopleVcc = null;
 678 
 679             validateLongLived(longLivedObjects);
 680             validatePeople(longLivedPeople);
 681             validatePeopleVcc(longLivedPeopleVcc);
 682 
 683             longLivedObjects = null;
 684             longLivedPeople = null;
 685             longLivedPeopleVcc = null;
 686 
 687             doGc();
 688         }
 689         catch (Throwable t) { fail("testMvtActiveGc", t); }
 690     }
 691 
 692     static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>();
 693 
 694     public static void doGc() {
 695         // Create Reference, wait until it clears...
 696         Reference ref = createRef();
 697         while (ref.get() != null) {
 698             System.gc();
 699         }
 700     }
 701 
 702     static Reference createRef() {
 703         return new WeakReference<Object>(new Object(), REFQ);
 704     }
 705 
 706     static void validatePersonVcc(Object obj, int id, String fn, String ln, boolean equals) {
 707         assertTrue(obj.getClass() == PersonVcc.class, "Expected VCC class");
 708         PersonVcc person = (PersonVcc) obj;
 709         assertTrue(person.id == id);
 710         if (equals) {
 711             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 712             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 713         }
 714         else {
 715             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 716             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 717         }
 718     }
 719 
 720     static PersonVcc createIndexedPersonVcc(int i) {
 721         return PersonVcc.create(i, firstName(i), lastName(i));
 722     }
 723 
 724     static void validateIndexedPersonVcc(Object obj, int i) {
 725         validatePersonVcc(obj, i, firstName(i), lastName(i), true);
 726     }
 727 
 728     static PersonVcc createDefaultPersonVcc() {
 729         return PersonVcc.create(0, null, null);
 730     }
 731 
 732     static void validateDefaultPersonVcc(Object obj) {
 733         validatePersonVcc(obj, 0, null, null, false);
 734     }
 735 
 736 
 737     static void validatePerson(Person person, int id, String fn, String ln, boolean equals) {
 738         assertTrue(person.id == id);
 739         if (equals) {
 740             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 741             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 742         }
 743         else {
 744             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 745             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 746         }
 747     }
 748 
 749     static Person createIndexedPerson(int i) {
 750         return Person.create(i, firstName(i), lastName(i));
 751     }
 752 
 753     static void validateIndexedPerson(Person person, int i) {
 754         validatePerson(person, i, firstName(i), lastName(i), true);
 755     }
 756 
 757     static Person createDefaultPerson() {
 758         return Person.create(0, null, null);
 759     }
 760 
 761     static void validateDefaultPerson(Person person) {
 762         validatePerson(person, 0, null, null, false);
 763     }
 764 
 765     static String firstName(int i) {
 766         return "FirstName-" + i;
 767     }
 768 
 769     static String lastName(int i) {
 770         return "LastName-" + i;
 771     }
 772 
 773     static Object createLongLived()  throws Throwable {
 774         Object[] population = new Object[2];
 775         population[0] = createPeople();
 776         population[1] = createPeopleVcc();
 777         return population;
 778     }
 779 
 780     static void validateLongLived(Object pop) throws Throwable {
 781         Object[] population = (Object[]) pop;
 782         validatePeople(population[0]);
 783         validatePeopleVcc(population[1]);
 784     }
 785 
 786     static Object createPeopleVcc() throws Throwable {
 787         int arrayLength = NOF_PEOPLE;
 788         Class<?> vccClass = PersonVcc.class;
 789         ValueType<?> vt = ValueType.forClass(vccClass);
 790         MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(vt.arrayValueClass());
 791 
 792         Object people = vt.newArray().invoke(arrayLength);
 793         for (int i = 0; i < arrayLength; i++) {
 794             arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 795         }
 796         return people;
 797     }
 798 
 799     static void validatePeopleVcc(Object people) throws Throwable {
 800         MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(
 801             ValueType.forClass((Class<?>)PersonVcc.class).arrayValueClass());
 802 
 803         int arrayLength = java.lang.reflect.Array.getLength(people);
 804         assertTrue(arrayLength == NOF_PEOPLE);
 805         for (int i = 0; i < arrayLength; i++) {
 806             validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 807         }
 808     }
 809 
 810     static Object createPeople() {
 811         int arrayLength = NOF_PEOPLE;
 812         Person[] people = new Person[arrayLength];
 813         for (int i = 0; i < arrayLength; i++) {
 814             people[i] = createIndexedPerson(i);
 815         }
 816         return people;
 817     }
 818 
 819     static void validatePeople(Object array) {
 820         Person[] people = (Person[]) array;
 821         int arrayLength = people.length;
 822         assertTrue(arrayLength == NOF_PEOPLE);
 823         for (int i = 0; i < arrayLength; i++) {
 824             validateIndexedPerson(people[i], i);
 825         }
 826     }
 827 
 828     // Various field layouts...sanity testing, see MVTCombo testing for full-set
 829 
 830     static final __ByValue class ObjectValue {
 831         final Object object;
 832 
 833         private ObjectValue(Object object) {
 834             this.object = object;
 835         }
 836     }
 837 
 838     static class ObjectWithObjectValue {
 839         ObjectValue value1;
 840         Object      ref1;
 841     }
 842 
 843     static class ObjectWithObjectValues {
 844         ObjectValue value1;
 845         ObjectValue value2;
 846         Object      ref1;
 847     }
 848 
 849     static class Foo {
 850         int id;
 851         String name;
 852         String description;
 853         long timestamp;
 854         String notes;
 855     }
 856 
 857     static class Bar extends Foo {
 858         long extendedId;
 859         String moreNotes;
 860         int count;
 861         String otherStuff;
 862     }
 863 
 864     static final __ByValue class FooValue {
 865         final int id;
 866         final String name;
 867         final String description;
 868         final long timestamp;
 869         final String notes;
 870 
 871         private FooValue() {
 872             this.id          = 0;
 873             this.name        = null;
 874             this.description = null;
 875             this.timestamp   = 0L;
 876             this.notes       = null;
 877         }
 878 
 879         __ValueFactory public static FooValue create(int id, String name, String description, long timestamp, String notes) {
 880             FooValue f = __MakeDefault FooValue();
 881             f.id          = id;
 882             f.name        = name;
 883             f.description = description;
 884             f.timestamp   = timestamp;
 885             f.notes       = notes;
 886             return f;
 887         }
 888 
 889         public static void testFrameOopsDefault(Object[][] oopMaps) {
 890             Class<?> fooValueCls = FooValue.class;
 891             MethodType mt = MethodType.methodType(Void.TYPE, oopMaps.getClass());
 892             int oopMapsSlot   = 0;
 893             int vtSlot        = 1;
 894 
 895             // Slots 1=oopMaps
 896             // OopMap Q=RRR (.name .description .someNotes)
 897             try {
 898                 MethodHandleBuilder
 899                     .loadCode(MethodHandles.lookup(),
 900                               "exerciseVBytecodeExprStackWithDefault", mt,
 901                               CODE->{
 902                                   CODE
 903                                       .vdefault(fooValueCls)
 904                                       .aload(oopMapsSlot)
 905                                       .iconst_0()  // Test-D0 Slots=R Stack=Q(RRR)RV
 906                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 907                                       .aastore()
 908                                       .pop()
 909                                       .aload(oopMapsSlot)
 910                                       .iconst_1()  // Test-D1 Slots=R Stack=RV
 911                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 912                                       .aastore()
 913                                       .vdefault(fooValueCls)
 914                                       .vstore(vtSlot)
 915                                       .aload(oopMapsSlot)
 916                                       .iconst_2()  // Test-D2 Slots=RQ(RRR) Stack=RV
 917                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 918                                       .aastore()
 919                                       .vload(vtSlot)
 920                                       .aconst_null()
 921                                       .astore(vtSlot) // Storing null over the Q slot won't remove the ref, but should be single null ref
 922                                       .aload(oopMapsSlot)
 923                                       .iconst_3()  // Test-D3 Slots=R Stack=Q(RRR)RV
 924                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 925                                       .aastore()
 926                                       .pop()
 927                                       .return_();
 928                               }).invoke(oopMaps);
 929             } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); }
 930         }
 931 
 932         public static void testFrameOopsRefs(String name, String description, String notes, Object[][] oopMaps) {
 933             Class<?> fooValueCls = FooValue.class;
 934             FooValue f = create(4711, name, description, 9876543231L, notes);
 935             FooValue[] fa = new FooValue[] { f };
 936             MethodType mt = MethodType.methodType(Void.TYPE, fa.getClass(), oopMaps.getClass());
 937             int fooArraySlot  = 0;
 938             int oopMapsSlot   = 1;
 939             try {
 940                 MethodHandleBuilder
 941                     .loadCode(MethodHandles.lookup(),
 942                               "exerciseVBytecodeExprStackWithRefs", mt,
 943                               CODE->{
 944                                   CODE
 945                                       .aload(fooArraySlot)
 946                                       .iconst_0()
 947                                       .vaload()
 948                                       .aload(oopMapsSlot)
 949                                       .iconst_0()  // Test-R0 Slots=RR Stack=Q(RRR)RV
 950                                       .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 951                                       .aastore()
 952                                       .pop()
 953                                       .return_();
 954                               }).invoke(fa, oopMaps);
 955             } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); }
 956         }
 957     }
 958 
 959     static class BarWithValue {
 960         FooValue foo;
 961         long extendedId;
 962         String moreNotes;
 963         int count;
 964         String otherStuff;
 965     }
 966 
 967     static final __ByValue class BarValue {
 968         final FooValue foo;
 969         final long extendedId;
 970         final String moreNotes;
 971         final int count;
 972         final String otherStuff;
 973 
 974         private BarValue(FooValue foo, long extendedId, String moreNotes, int count, String otherStuff) {
 975             this.foo = foo;
 976             this.extendedId = extendedId;
 977             this.moreNotes = moreNotes;
 978             this.count = count;
 979             this.otherStuff = otherStuff;
 980         }
 981     }
 982 
 983 }
 984