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