1 /*
   2  * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package runtime.valhalla.valuetypes;
  25 
  26 import java.lang.invoke.*;
  27 import java.lang.ref.*;
  28 import java.util.concurrent.*;
  29 
  30 import static jdk.test.lib.Asserts.*;
  31 import jdk.test.lib.Utils;
  32 import sun.hotspot.WhiteBox;
  33 
  34 import jdk.experimental.value.MethodHandleBuilder;
  35 
  36 /**
  37  * @test ValueOops_int_Serial
  38  * @requires vm.gc.Serial
  39  * @summary Test embedding oops into Value types
  40  * @modules java.base/jdk.experimental.bytecode
  41  *          java.base/jdk.experimental.value
  42  * @library /test/lib
  43  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
  44  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
  45  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  46  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
  47  * @run main/othervm -Xint -XX:+UseSerialGC -Xmx128m
  48  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  49  *                   runtime.valhalla.valuetypes.ValueOops
  50  */
  51 
  52 /**
  53  * @test ValueOops_int_G1
  54  * @requires vm.gc.G1
  55  * @summary Test embedding oops into Value types
  56  * @modules java.base/jdk.experimental.bytecode
  57  *          java.base/jdk.experimental.value
  58  * @library /test/lib
  59  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
  60  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
  61  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  62  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
  63  * @run main/othervm -Xint  -XX:+UseG1GC -Xmx128m
  64  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  65  *                   runtime.valhalla.valuetypes.ValueOops 100
  66  */
  67 
  68 /**
  69  * @test ValueOops_int_Parallel
  70  * @requires vm.gc.Parallel
  71  * @summary Test embedding oops into Value types
  72  * @modules java.base/jdk.experimental.bytecode
  73  *          java.base/jdk.experimental.value
  74  * @library /test/lib
  75  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
  76  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
  77  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  78  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
  79  * @run main/othervm -Xint -XX:+UseParallelGC -Xmx128m
  80  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  81  *                   runtime.valhalla.valuetypes.ValueOops
  82  */
  83 
  84 /**
  85  * @test ValueOops_int_Z
  86  * @requires vm.gc.Z
  87  * @summary Test embedding oops into Value types
  88  * @modules java.base/jdk.experimental.bytecode
  89  *          java.base/jdk.experimental.value
  90  * @library /test/lib
  91  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
  92  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
  93  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  94  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
  95  * @run main/othervm -Xint -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx128m
  96  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  97  *                   runtime.valhalla.valuetypes.ValueOops
  98  */
  99 
 100 /**
 101  * @test ValueOops_comp_serial
 102  * @requires vm.gc.Serial
 103  * @summary Test embedding oops into Value types
 104  * @modules java.base/jdk.experimental.bytecode
 105  *          java.base/jdk.experimental.value
 106  * @library /test/lib
 107  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
 108  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
 109  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
 110  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
 111  * @run main/othervm -Xcomp -XX:+UseSerialGC -Xmx128m
 112  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 113  *                   runtime.valhalla.valuetypes.ValueOops
 114  */
 115 
 116 /**
 117  * @test ValueOops_comp_G1
 118  * @requires vm.gc.G1
 119  * @summary Test embedding oops into Value types
 120  * @modules java.base/jdk.experimental.bytecode
 121  *          java.base/jdk.experimental.value
 122  * @library /test/lib
 123  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
 124  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
 125  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
 126  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
 127  * @run main/othervm -Xcomp -XX:+UseG1GC -Xmx128m
 128  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 129  *                   runtime.valhalla.valuetypes.ValueOops 100
 130  */
 131 
 132 /**
 133  * @test ValueOops_comp_Parallel
 134  * @requires vm.gc.Parallel
 135  * @summary Test embedding oops into Value types
 136  * @modules java.base/jdk.experimental.bytecode
 137  *          java.base/jdk.experimental.value
 138  * @library /test/lib
 139  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator Person.java
 140  * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator ValueOops.java
 141  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
 142  *                   sun.hotspot.WhiteBox$WhiteBoxPermission
 143  * @run main/othervm -Xcomp -XX:+UseParallelGC -Xmx128m
 144  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 145  *                   runtime.valhalla.valuetypes.ValueOops
 146  */
 147 public class ValueOops {
 148 
 149     // Xcomp with ZGC missing until C1 and C2 barrier code is in place (JDK-8231498)
 150 
 151     // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap
 152     // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info
 153 
 154     static final int NOF_PEOPLE = 1000; // Exercise arrays of this size
 155 
 156     static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes
 157 
 158     static int MED_ACTIVE_GC_COUNT = 4;  // Medium life span in terms of GC passes
 159 
 160     static final String TEST_STRING1 = "Test String 1";
 161     static final String TEST_STRING2 = "Test String 2";
 162 
 163     static boolean USE_COMPILER = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler");
 164 
 165     static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
 166 
 167     public static void main(String[] args) {
 168         if (args.length > 0) {
 169             MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]);
 170         }
 171         testClassLoad();
 172         testValues();
 173 
 174         if (!USE_COMPILER) {
 175             testOopMaps();
 176         }
 177 
 178         // Check we survive GC...
 179         testOverGc();   // Exercise root scan / oopMap
 180         testActiveGc(); // Brute force
 181     }
 182 
 183     /**
 184      * Test ClassFileParser can load inline types with reference fields
 185      */
 186     public static void testClassLoad() {
 187         String s = Person.class.toString();
 188         new Bar();
 189         new BarWithValue();
 190         s = BarValue.class.toString();
 191         s = ObjectWithObjectValue.class.toString();
 192         s = ObjectWithObjectValues.class.toString();
 193     }
 194 
 195 
 196     static class Couple {
 197         public Person onePerson;
 198         public Person otherPerson;
 199     }
 200 
 201     static final inline class Composition {
 202         public final Person onePerson;
 203         public final Person otherPerson;
 204 
 205         private Composition() {
 206             onePerson   = Person.create(0, null, null);
 207             otherPerson = Person.create(0, null, null);
 208         }
 209 
 210         public static Composition create(Person onePerson, Person otherPerson) {
 211             Composition comp = Composition.default;
 212             comp = __WithField(comp.onePerson, onePerson);
 213             comp = __WithField(comp.otherPerson, otherPerson);
 214             return comp;
 215         }
 216     }
 217 
 218     /**
 219      * Check inline type operations with "Valhalla Inline Types" (VVT)
 220      */
 221     public static void testValues() {
 222         // Exercise creation, getfield, vreturn with null refs
 223         validateDefaultPerson(createDefaultPerson());
 224 
 225         // anewarray, aaload, aastore
 226         int index = 7;
 227         Person[] array =  new Person[NOF_PEOPLE];
 228         validateDefaultPerson(array[index]);
 229 
 230         // Now with refs...
 231         validateIndexedPerson(createIndexedPerson(index), index);
 232         array[index] = createIndexedPerson(index);
 233         validateIndexedPerson(array[index], index);
 234 
 235         // Check the neighbours
 236         validateDefaultPerson(array[index - 1]);
 237         validateDefaultPerson(array[index + 1]);
 238 
 239         // getfield/putfield
 240         Couple couple = new Couple();
 241         validateDefaultPerson(couple.onePerson);
 242         validateDefaultPerson(couple.otherPerson);
 243 
 244         couple.onePerson = createIndexedPerson(index);
 245         validateIndexedPerson(couple.onePerson, index);
 246 
 247         Composition composition = Composition.create(couple.onePerson, couple.onePerson);
 248         validateIndexedPerson(composition.onePerson, index);
 249         validateIndexedPerson(composition.otherPerson, index);
 250     }
 251 
 252     /**
 253      * Check oop map generation for klass layout and frame...
 254      */
 255     public static void testOopMaps() {
 256         Object[] objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(new Couple());
 257         assertTrue(objects.length == 4, "Expected 4 oops");
 258         for (int i = 0; i < objects.length; i++) {
 259             assertTrue(objects[i] == null, "not-null");
 260         }
 261 
 262         String fn1 = "Sam";
 263         String ln1 = "Smith";
 264         String fn2 = "Jane";
 265         String ln2 = "Jones";
 266         Couple couple = new Couple();
 267         couple.onePerson = Person.create(0, fn1, ln1);
 268         couple.otherPerson = Person.create(1, fn2, ln2);
 269         objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(couple);
 270         assertTrue(objects.length == 4, "Expected 4 oops");
 271         assertTrue(objects[0] == fn1, "Bad oop fn1");
 272         assertTrue(objects[1] == ln1, "Bad oop ln1");
 273         assertTrue(objects[2] == fn2, "Bad oop fn2");
 274         assertTrue(objects[3] == ln2, "Bad oop ln2");
 275 
 276         objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(couple);
 277         assertTrue(objects.length == 4, "Expected 4 oops");
 278         assertTrue(objects[0] == fn1, "Bad oop fn1");
 279         assertTrue(objects[1] == ln1, "Bad oop ln1");
 280         assertTrue(objects[2] == fn2, "Bad oop fn2");
 281         assertTrue(objects[3] == ln2, "Bad oop ln2");
 282 
 283         // Array..
 284         objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(createPeople());
 285         assertTrue(objects.length == NOF_PEOPLE * 2, "Unexpected length: " + objects.length);
 286         int o = 0;
 287         for (int i = 0; i < NOF_PEOPLE; i++) {
 288             assertTrue(objects[o++].equals(firstName(i)), "Bad firstName");
 289             assertTrue(objects[o++].equals(lastName(i)), "Bad lastName");
 290         }
 291 
 292         // Sanity check, FixMe need more test cases
 293         objects = testFrameOops(couple);
 294         //assertTrue(objects.length == 5, "Number of frame oops incorrect = " + objects.length);
 295         //assertTrue(objects[0] == couple, "Bad oop 0");
 296         //assertTrue(objects[1] == fn1, "Bad oop 1");
 297         //assertTrue(objects[2] == ln1, "Bad oop 2");
 298         //assertTrue(objects[3] == TEST_STRING1, "Bad oop 3");
 299         //assertTrue(objects[4] == TEST_STRING2, "Bad oop 4");
 300 
 301         //testFrameOopsVBytecodes();
 302     }
 303 
 304     static final String GET_OOP_MAP_NAME = "getOopMap";
 305     static final String GET_OOP_MAP_DESC = "()[Ljava/lang/Object;";
 306 
 307     public static Object[] getOopMap() {
 308         Object[] oopMap = WhiteBox.getWhiteBox().getObjectsViaFrameOopIterator(2);
 309         /* Remove this frame (class mirror for this method), and above class mirror */
 310         Object[] trimmedOopMap = new Object[oopMap.length - 2];
 311         System.arraycopy(oopMap, 2, trimmedOopMap, 0, trimmedOopMap.length);
 312         return trimmedOopMap;
 313     }
 314 
 315     // Expecting Couple couple, Person couple.onePerson, and Person (created here)
 316     public static Object[] testFrameOops(Couple couple) {
 317         int someId = 89898;
 318         Person person = couple.onePerson;
 319         assertTrue(person.getId() == 0, "Bad Person");
 320         Person anotherPerson = Person.create(someId, TEST_STRING1, TEST_STRING2);
 321         assertTrue(anotherPerson.getId() == someId, "Bad Person");
 322         return getOopMap();
 323     }
 324 
 325     // Debug...
 326     static void dumpOopMap(Object[] oopMap) {
 327         System.out.println("Oop Map len: " + oopMap.length);
 328         for (int i = 0; i < oopMap.length; i++) {
 329             System.out.println("[" + i + "] = " + oopMap[i]);
 330         }
 331     }
 332 
 333     /**
 334      * Just some check sanity checks with defaultvalue, withfield, astore and aload
 335      *
 336      * Changes to javac slot usage may well break this test
 337      */
 338     public static void testFrameOopsVBytecodes() {
 339         int nofOopMaps = 4;
 340         Object[][] oopMaps = new Object[nofOopMaps][];
 341         String[] inputArgs = new String[] { "aName", "aDescription", "someNotes" };
 342 
 343         FooValue.testFrameOopsDefault(oopMaps);
 344 
 345         // Test-D0 Slots=R Stack=Q(RRR)RV
 346         assertTrue(oopMaps[0].length == 5 &&
 347                 oopMaps[0][1] == null &&
 348                 oopMaps[0][2] == null &&
 349                 oopMaps[0][3] == null, "Test-D0 incorrect");
 350 
 351         // Test-D1 Slots=R Stack=RV
 352         assertTrue(oopMaps[1].length == 2, "Test-D1 incorrect");
 353 
 354         // Test-D2 Slots=RQ(RRR) Stack=RV
 355         assertTrue(oopMaps[2].length == 5 &&
 356                 oopMaps[2][1] == null &&
 357                 oopMaps[2][2] == null &&
 358                 oopMaps[2][3] == null, "Test-D2 incorrect");
 359 
 360         // Test-D3 Slots=R Stack=Q(RRR)RV
 361         assertTrue(oopMaps[3].length == 6 &&
 362                 oopMaps[3][1] == null &&
 363                 oopMaps[3][2] == null &&
 364                 oopMaps[3][3] == null &&
 365                 oopMaps[3][4] == null, "Test-D3 incorrect");
 366 
 367         // With ref fields...
 368         String name = "TestName";
 369         String desc = "TestDesc";
 370         String note = "TestNotes";
 371         FooValue.testFrameOopsRefs(name, desc, note, oopMaps);
 372 
 373         // Test-R0 Slots=RR Stack=Q(RRR)RV
 374         assertTrue(oopMaps[0].length == 6 &&
 375                 oopMaps[0][2] == name &&
 376                 oopMaps[0][3] == desc &&
 377                 oopMaps[0][4] == note, "Test-R0 incorrect");
 378 
 379         /**
 380          * TODO: vwithfield from method handle cooked from anonymous class within the inline class
 381          *       even with "MethodHandles.privateLookupIn()" will fail final putfield rules
 382          */
 383     }
 384 
 385     /**
 386      * Check forcing GC for combination of VT on stack/LVT etc works
 387      */
 388     public static void testOverGc() {
 389         try {
 390             Class<?> vtClass = Person.class;
 391 
 392             System.out.println("vtClass="+vtClass);
 393 
 394             doGc();
 395 
 396             // VT on stack and lvt, null refs, see if GC flies
 397             MethodHandle moveValueThroughStackAndLvt = MethodHandleBuilder.loadCode(
 398                     LOOKUP,
 399                     "gcOverPerson",
 400                     MethodType.methodType(vtClass, vtClass),
 401                     CODE->{
 402                         CODE
 403                         .aload(0)
 404                         .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack
 405                         .astore(0)
 406                         .invokestatic(ValueOops.class, "doGc", "()V", false) // LVT
 407                         .aload(0)
 408                         .astore(1024) // LVT wide index
 409                         .aload(1024)
 410                         .iconst_1()  // push a litte further down
 411                         .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack,LVT
 412                         .pop()
 413                         .areturn();
 414                     });
 415             Person person = (Person) moveValueThroughStackAndLvt.invokeExact(createDefaultPerson());
 416             validateDefaultPerson(person);
 417             doGc();
 418 
 419             int index = 4711;
 420             person = (Person) moveValueThroughStackAndLvt.invokeExact(createIndexedPerson(index));
 421             validateIndexedPerson(person, index);
 422             doGc();
 423             person = createDefaultPerson();
 424             doGc();
 425         }
 426         catch (Throwable t) { fail("testOverGc", t); }
 427     }
 428 
 429     static void submitNewWork(ForkJoinPool fjPool, int size) {
 430         for (int i = 0; i < size; i++) {
 431             for (int j = 0; j < 100; j++) {
 432                 fjPool.execute(ValueOops::testValues);
 433             }
 434         }
 435     }
 436 
 437     static void sleepNoThrow(long ms) {
 438         try {
 439             Thread.sleep(ms);
 440         }
 441         catch (Throwable t) {}
 442     }
 443 
 444     /**
 445      * Run some workloads with different object/value life times...
 446      */
 447     public static void testActiveGc() {
 448         try {
 449             int nofThreads = 7;
 450             int workSize = nofThreads * 10;
 451 
 452             Object longLivedObjects = createLongLived();
 453             Object longLivedPeople = createPeople();
 454 
 455             Object medLivedObjects = createLongLived();
 456             Object medLivedPeople = createPeople();
 457 
 458             doGc();
 459 
 460             ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
 461 
 462             // submit work until we see some GC
 463             Reference ref = createRef();
 464             submitNewWork(fjPool, workSize);
 465             while (ref.get() != null) {
 466                 if (fjPool.hasQueuedSubmissions()) {
 467                     sleepNoThrow(1L);
 468                 }
 469                 else {
 470                     workSize *= 2; // Grow the submission size
 471                     submitNewWork(fjPool, workSize);
 472                 }
 473             }
 474 
 475             // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT
 476             int nofActiveGc = 1;
 477             ref = createRef();
 478             while (nofActiveGc < MIN_ACTIVE_GC_COUNT) {
 479                 if (ref.get() == null) {
 480                     nofActiveGc++;
 481                     ref = createRef();
 482                     if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) {
 483                         validateLongLived(medLivedObjects);
 484                         validatePeople(medLivedPeople);
 485 
 486                         medLivedObjects = createLongLived();
 487                         medLivedPeople = createPeople();
 488                     }
 489                 }
 490                 else if (fjPool.hasQueuedSubmissions()) {
 491                     sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000));
 492                     doGc();
 493                 }
 494                 else {
 495                     submitNewWork(fjPool, workSize);
 496                 }
 497             }
 498             fjPool.shutdown();
 499 
 500             validateLongLived(medLivedObjects);
 501             validatePeople(medLivedPeople);
 502             medLivedObjects = null;
 503             medLivedPeople = null;
 504 
 505             validateLongLived(longLivedObjects);
 506             validatePeople(longLivedPeople);
 507 
 508             longLivedObjects = null;
 509             longLivedPeople = null;
 510 
 511             doGc();
 512         }
 513         catch (Throwable t) { fail("testActiveGc", t); }
 514     }
 515 
 516     static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>();
 517 
 518     public static void doGc() {
 519         // Create Reference, wait until it clears...
 520         Reference ref = createRef();
 521         while (ref.get() != null) {
 522             System.gc();
 523         }
 524     }
 525 
 526     static Reference createRef() {
 527         return new WeakReference<Object>(new Object(), REFQ);
 528     }
 529 
 530     static void validatePerson(Person person, int id, String fn, String ln, boolean equals) {
 531         assertTrue(person.id == id);
 532         if (equals) {
 533             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 534             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 535         }
 536         else {
 537             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 538             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 539         }
 540     }
 541 
 542     static Person createIndexedPerson(int i) {
 543         return Person.create(i, firstName(i), lastName(i));
 544     }
 545 
 546     static void validateIndexedPerson(Person person, int i) {
 547         validatePerson(person, i, firstName(i), lastName(i), true);
 548     }
 549 
 550     static Person createDefaultPerson() {
 551         return Person.create(0, null, null);
 552     }
 553 
 554     static void validateDefaultPerson(Person person) {
 555         validatePerson(person, 0, null, null, false);
 556     }
 557 
 558     static String firstName(int i) {
 559         return "FirstName-" + i;
 560     }
 561 
 562     static String lastName(int i) {
 563         return "LastName-" + i;
 564     }
 565 
 566     static Object createLongLived()  throws Throwable {
 567         Object[] population = new Object[1];
 568         population[0] = createPeople();
 569         return population;
 570     }
 571 
 572     static void validateLongLived(Object pop) throws Throwable {
 573         Object[] population = (Object[]) pop;
 574         validatePeople(population[0]);
 575     }
 576 
 577     static Object createPeople() {
 578         int arrayLength = NOF_PEOPLE;
 579         Person[] people = new Person[arrayLength];
 580         for (int i = 0; i < arrayLength; i++) {
 581             people[i] = createIndexedPerson(i);
 582         }
 583         return people;
 584     }
 585 
 586     static void validatePeople(Object array) {
 587         Person[] people = (Person[]) array;
 588         int arrayLength = people.length;
 589         assertTrue(arrayLength == NOF_PEOPLE);
 590         for (int i = 0; i < arrayLength; i++) {
 591             validateIndexedPerson(people[i], i);
 592         }
 593     }
 594 
 595     // Various field layouts...sanity testing, see MVTCombo testing for full-set
 596 
 597     static final inline class ObjectValue {
 598         final Object object;
 599 
 600         private ObjectValue(Object obj) {
 601             object = obj;
 602         }
 603     }
 604 
 605     static class ObjectWithObjectValue {
 606         ObjectValue value1;
 607         Object      ref1;
 608     }
 609 
 610     static class ObjectWithObjectValues {
 611         ObjectValue value1;
 612         ObjectValue value2;
 613         Object      ref1;
 614     }
 615 
 616     static class Foo {
 617         int id;
 618         String name;
 619         String description;
 620         long timestamp;
 621         String notes;
 622     }
 623 
 624     static class Bar extends Foo {
 625         long extendedId;
 626         String moreNotes;
 627         int count;
 628         String otherStuff;
 629     }
 630 
 631     public static final inline class FooValue {
 632         public final int id;
 633         public final String name;
 634         public final String description;
 635         public final long timestamp;
 636         public final String notes;
 637 
 638         private FooValue() {
 639             id          = 0;
 640             name        = null;
 641             description = null;
 642             timestamp   = 0L;
 643             notes       = null;
 644         }
 645 
 646         public static FooValue create(int id, String name, String description, long timestamp, String notes) {
 647             FooValue f = FooValue.default;
 648             f = __WithField(f.id, id);
 649             f = __WithField(f.name, name);
 650             f = __WithField(f.description, description);
 651             f = __WithField(f.timestamp, timestamp);
 652             f = __WithField(f.notes, notes);
 653             return f;
 654         }
 655 
 656         public static void testFrameOopsDefault(Object[][] oopMaps) {
 657             MethodType mt = MethodType.methodType(Void.TYPE, oopMaps.getClass());
 658             int oopMapsSlot   = 0;
 659             int vtSlot        = 1;
 660 
 661             // Slots 1=oopMaps
 662             // OopMap Q=RRR (.name .description .someNotes)
 663             try {
 664                 MethodHandleBuilder.loadCode(
 665                         LOOKUP, "exerciseVBytecodeExprStackWithDefault", mt,
 666                         CODE->{
 667                             CODE
 668                             .defaultvalue(FooValue.class)
 669                             .aload(oopMapsSlot)
 670                             .iconst_0()  // Test-D0 Slots=R Stack=Q(RRR)RV
 671                             .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 672                             .aastore()
 673                             .pop()
 674                             .aload(oopMapsSlot)
 675                             .iconst_1()  // Test-D1 Slots=R Stack=RV
 676                             .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 677                             .aastore()
 678                             .defaultvalue(FooValue.class)
 679                             .astore(vtSlot)
 680                             .aload(oopMapsSlot)
 681                             .iconst_2()  // Test-D2 Slots=RQ(RRR) Stack=RV
 682                             .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 683                             .aastore()
 684                             .aload(vtSlot)
 685                             .aconst_null()
 686                             .astore(vtSlot) // Storing null over the Q slot won't remove the ref, but should be single null ref
 687                             .aload(oopMapsSlot)
 688                             .iconst_3()  // Test-D3 Slots=R Stack=Q(RRR)RV
 689                             .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 690                             .aastore()
 691                             .pop()
 692                             .return_();
 693                         }).invoke(oopMaps);
 694             } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); }
 695         }
 696 
 697         public static void testFrameOopsRefs(String name, String description, String notes, Object[][] oopMaps) {
 698             FooValue f = create(4711, name, description, 9876543231L, notes);
 699             FooValue[] fa = new FooValue[] { f };
 700             MethodType mt = MethodType.methodType(Void.TYPE, fa.getClass(), oopMaps.getClass());
 701             int fooArraySlot  = 0;
 702             int oopMapsSlot   = 1;
 703             try {
 704                 MethodHandleBuilder.loadCode(LOOKUP, "exerciseVBytecodeExprStackWithRefs", mt,
 705                         CODE->{
 706                             CODE
 707                             .aload(fooArraySlot)
 708                             .iconst_0()
 709                             .aaload()
 710                             .aload(oopMapsSlot)
 711                             .iconst_0()  // Test-R0 Slots=RR Stack=Q(RRR)RV
 712                             .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false)
 713                             .aastore()
 714                             .pop()
 715                             .return_();
 716                         }).invoke(fa, oopMaps);
 717             } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithRefs", t); }
 718         }
 719     }
 720 
 721     static class BarWithValue {
 722         FooValue foo;
 723         long extendedId;
 724         String moreNotes;
 725         int count;
 726         String otherStuff;
 727     }
 728 
 729     static final inline class BarValue {
 730         final FooValue foo;
 731         final long extendedId;
 732         final String moreNotes;
 733         final int count;
 734         final String otherStuff;
 735 
 736         private BarValue(FooValue f, long extId, String mNotes, int c, String other) {
 737             foo = f;
 738             extendedId = extId;
 739             moreNotes = mNotes;
 740             count = c;
 741             otherStuff = other;
 742         }
 743     }
 744 
 745 }
 746