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