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