1 /* 2 * Copyright (c) 2008, 2025, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import jdk.internal.misc.CDS; 29 import jdk.internal.misc.Unsafe; 30 import jdk.internal.value.ValueClass; 31 import jdk.internal.vm.annotation.AOTSafeClassInitializer; 32 import jdk.internal.vm.annotation.ForceInline; 33 import jdk.internal.vm.annotation.Stable; 34 import sun.invoke.util.ValueConversions; 35 import sun.invoke.util.VerifyAccess; 36 import sun.invoke.util.Wrapper; 37 38 import java.util.Arrays; 39 import java.util.Objects; 40 import java.util.function.Function; 41 42 import static java.lang.invoke.LambdaForm.*; 43 import static java.lang.invoke.LambdaForm.Kind.*; 44 import static java.lang.invoke.MethodHandleNatives.Constants.*; 45 import static java.lang.invoke.MethodHandleStatics.UNSAFE; 46 import static java.lang.invoke.MethodHandleStatics.newInternalError; 47 import static java.lang.invoke.MethodTypeForm.*; 48 49 /** 50 * The flavor of method handle which implements a constant reference 51 * to a class member. 52 * @author jrose 53 */ 54 @AOTSafeClassInitializer 55 sealed class DirectMethodHandle extends MethodHandle { 56 final MemberName member; 57 final boolean crackable; 58 59 // Constructors and factory methods in this class *must* be package scoped or private. 60 private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member, boolean crackable) { 61 super(mtype, form); 62 if (!member.isResolved()) throw new InternalError(); 63 64 if (member.getDeclaringClass().isInterface() && 65 member.getReferenceKind() == REF_invokeInterface && 66 member.isMethod() && !member.isAbstract()) { 67 // Check for corner case: invokeinterface of Object method 68 MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind()); 69 m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null, LM_TRUSTED); 70 if (m != null && m.isPublic()) { 71 assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong 72 member = m; 73 } 74 } 75 76 this.member = member; 77 this.crackable = crackable; 78 } 79 80 // Factory methods: 81 static DirectMethodHandle make(byte refKind, Class<?> refc, MemberName member, Class<?> callerClass) { 82 MethodType mtype = member.getMethodOrFieldType(); 83 if (!member.isStatic()) { 84 if (!member.getDeclaringClass().isAssignableFrom(refc) || member.isConstructor()) 85 throw new InternalError(member.toString()); 86 mtype = mtype.insertParameterTypes(0, refc); 87 } 88 if (!member.isField()) { 89 // refKind reflects the original type of lookup via findSpecial or 90 // findVirtual etc. 91 return switch (refKind) { 92 case REF_invokeSpecial -> { 93 member = member.asSpecial(); 94 // if caller is an interface we need to adapt to get the 95 // receiver check inserted 96 if (callerClass == null) { 97 throw new InternalError("callerClass must not be null for REF_invokeSpecial"); 98 } 99 LambdaForm lform = preparedLambdaForm(member, callerClass.isInterface()); 100 yield new Special(mtype, lform, member, true, callerClass); 101 } 102 case REF_invokeInterface -> { 103 // for interfaces we always need the receiver typecheck, 104 // so we always pass 'true' to ensure we adapt if needed 105 // to include the REF_invokeSpecial case 106 LambdaForm lform = preparedLambdaForm(member, true); 107 yield new Interface(mtype, lform, member, true, refc); 108 } 109 default -> { 110 LambdaForm lform = preparedLambdaForm(member); 111 yield new DirectMethodHandle(mtype, lform, member, true); 112 } 113 }; 114 } else { 115 LambdaForm lform = preparedFieldLambdaForm(member); 116 if (member.isStatic()) { 117 long offset = MethodHandleNatives.staticFieldOffset(member); 118 Object base = MethodHandleNatives.staticFieldBase(member); 119 return new StaticAccessor(mtype, lform, member, true, base, offset); 120 } else { 121 long offset = MethodHandleNatives.objectFieldOffset(member); 122 assert(offset == (int)offset); 123 return new Accessor(mtype, lform, member, true, (int)offset); 124 } 125 } 126 } 127 static DirectMethodHandle make(Class<?> refc, MemberName member) { 128 byte refKind = member.getReferenceKind(); 129 if (refKind == REF_invokeSpecial) 130 refKind = REF_invokeVirtual; 131 return make(refKind, refc, member, null /* no callerClass context */); 132 } 133 static DirectMethodHandle make(MemberName member) { 134 if (member.isConstructor()) 135 return makeAllocator(member.getDeclaringClass(), member); 136 return make(member.getDeclaringClass(), member); 137 } 138 static DirectMethodHandle makeAllocator(Class<?> instanceClass, MemberName ctor) { 139 assert(ctor.isConstructor()) : ctor; 140 ctor = ctor.asConstructor(); 141 assert(ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor; 142 MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass); 143 LambdaForm lform = preparedLambdaForm(ctor); 144 MemberName init = ctor.asSpecial(); 145 assert(init.getMethodType().returnType() == void.class); 146 return new Constructor(mtype, lform, ctor, true, init, instanceClass); 147 } 148 149 @Override 150 BoundMethodHandle rebind() { 151 return BoundMethodHandle.makeReinvoker(this); 152 } 153 154 @Override 155 MethodHandle copyWith(MethodType mt, LambdaForm lf) { 156 assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses 157 return new DirectMethodHandle(mt, lf, member, crackable); 158 } 159 160 @Override 161 MethodHandle viewAsType(MethodType newType, boolean strict) { 162 // No actual conversions, just a new view of the same method. 163 // However, we must not expose a DMH that is crackable into a 164 // MethodHandleInfo, so we return a cloned, uncrackable DMH 165 assert(viewAsTypeChecks(newType, strict)); 166 assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses 167 return new DirectMethodHandle(newType, form, member, false); 168 } 169 170 @Override 171 boolean isCrackable() { 172 return crackable; 173 } 174 175 @Override 176 String internalProperties(int indentLevel) { 177 return "\n" + debugPrefix(indentLevel) + "& DMH.MN=" + internalMemberName(); 178 } 179 180 //// Implementation methods. 181 @Override 182 @ForceInline 183 MemberName internalMemberName() { 184 return member; 185 } 186 187 private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); 188 189 /** 190 * Create a LF which can invoke the given method. 191 * Cache and share this structure among all methods with 192 * the same basicType and refKind. 193 */ 194 private static LambdaForm preparedLambdaForm(MemberName m, boolean adaptToSpecialIfc) { 195 assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead 196 MethodType mtype = m.getInvocationType().basicType(); 197 assert(!m.isMethodHandleInvoke()) : m; 198 // MemberName.getReferenceKind represents the JVM optimized form of the call 199 // as distinct from the "kind" passed to DMH.make which represents the original 200 // bytecode-equivalent request. Specifically private/final methods that use a direct 201 // call have getReferenceKind adapted to REF_invokeSpecial, even though the actual 202 // invocation mode may be invokevirtual or invokeinterface. 203 int which = switch (m.getReferenceKind()) { 204 case REF_invokeVirtual -> LF_INVVIRTUAL; 205 case REF_invokeStatic -> LF_INVSTATIC; 206 case REF_invokeSpecial -> LF_INVSPECIAL; 207 case REF_invokeInterface -> LF_INVINTERFACE; 208 case REF_newInvokeSpecial -> LF_NEWINVSPECIAL; 209 default -> throw new InternalError(m.toString()); 210 }; 211 if (which == LF_INVSTATIC && shouldBeInitialized(m)) { 212 // precompute the barrier-free version: 213 preparedLambdaForm(mtype, which); 214 which = LF_INVSTATIC_INIT; 215 } 216 if (which == LF_INVSPECIAL && adaptToSpecialIfc) { 217 which = LF_INVSPECIAL_IFC; 218 } 219 LambdaForm lform = preparedLambdaForm(mtype, which); 220 assert(lform.methodType().dropParameterTypes(0, 1) 221 .equals(m.getInvocationType().basicType())) 222 : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType()); 223 return lform; 224 } 225 226 private static LambdaForm preparedLambdaForm(MemberName m) { 227 return preparedLambdaForm(m, false); 228 } 229 230 private static LambdaForm preparedLambdaForm(MethodType mtype, int which) { 231 LambdaForm lform = mtype.form().cachedLambdaForm(which); 232 if (lform != null) return lform; 233 lform = makePreparedLambdaForm(mtype, which); 234 return mtype.form().setCachedLambdaForm(which, lform); 235 } 236 237 static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) { 238 boolean needsInit = (which == LF_INVSTATIC_INIT); 239 boolean doesAlloc = (which == LF_NEWINVSPECIAL); 240 boolean needsReceiverCheck = (which == LF_INVINTERFACE || 241 which == LF_INVSPECIAL_IFC); 242 243 String linkerName; 244 LambdaForm.Kind kind; 245 switch (which) { 246 case LF_INVVIRTUAL: linkerName = "linkToVirtual"; kind = DIRECT_INVOKE_VIRTUAL; break; 247 case LF_INVSTATIC: linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC; break; 248 case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC_INIT; break; 249 case LF_INVSPECIAL_IFC:linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL_IFC; break; 250 case LF_INVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL; break; 251 case LF_INVINTERFACE: linkerName = "linkToInterface"; kind = DIRECT_INVOKE_INTERFACE; break; 252 case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_NEW_INVOKE_SPECIAL; break; 253 default: throw new InternalError("which="+which); 254 } 255 256 MethodType mtypeWithArg; 257 if (doesAlloc) { 258 var ptypes = mtype.ptypes(); 259 var newPtypes = new Class<?>[ptypes.length + 2]; 260 newPtypes[0] = Object.class; // insert newly allocated obj 261 System.arraycopy(ptypes, 0, newPtypes, 1, ptypes.length); 262 newPtypes[newPtypes.length - 1] = MemberName.class; 263 mtypeWithArg = MethodType.methodType(void.class, newPtypes, true); 264 } else { 265 mtypeWithArg = mtype.appendParameterTypes(MemberName.class); 266 } 267 MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic); 268 try { 269 linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, LM_TRUSTED, 270 NoSuchMethodException.class); 271 } catch (ReflectiveOperationException ex) { 272 throw newInternalError(ex); 273 } 274 final int DMH_THIS = 0; 275 final int ARG_BASE = 1; 276 final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); 277 int nameCursor = ARG_LIMIT; 278 final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1); 279 final int GET_MEMBER = nameCursor++; 280 final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1); 281 final int LINKER_CALL = nameCursor++; 282 Name[] names = invokeArguments(nameCursor - ARG_LIMIT, mtype); 283 assert(names.length == nameCursor); 284 if (doesAlloc) { 285 // names = { argx,y,z,... new C, init method } 286 names[NEW_OBJ] = new Name(getFunction(NF_allocateInstance), names[DMH_THIS]); 287 names[GET_MEMBER] = new Name(getFunction(NF_constructorMethod), names[DMH_THIS]); 288 } else if (needsInit) { 289 names[GET_MEMBER] = new Name(getFunction(NF_internalMemberNameEnsureInit), names[DMH_THIS]); 290 } else { 291 names[GET_MEMBER] = new Name(getFunction(NF_internalMemberName), names[DMH_THIS]); 292 } 293 assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]); 294 Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class); 295 if (needsReceiverCheck) { 296 names[CHECK_RECEIVER] = new Name(getFunction(NF_checkReceiver), names[DMH_THIS], names[ARG_BASE]); 297 outArgs[0] = names[CHECK_RECEIVER]; 298 } 299 assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args! 300 int result = LAST_RESULT; 301 if (doesAlloc) { 302 assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one 303 System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2); 304 outArgs[0] = names[NEW_OBJ]; 305 result = NEW_OBJ; 306 } 307 names[LINKER_CALL] = new Name(linker, outArgs); 308 LambdaForm lform = LambdaForm.create(ARG_LIMIT, names, result, kind); 309 310 // This is a tricky bit of code. Don't send it through the LF interpreter. 311 lform.compileToBytecode(); 312 return lform; 313 } 314 315 /* assert */ static Object findDirectMethodHandle(Name name) { 316 if (name.function.equals(getFunction(NF_internalMemberName)) || 317 name.function.equals(getFunction(NF_internalMemberNameEnsureInit)) || 318 name.function.equals(getFunction(NF_constructorMethod))) { 319 assert(name.arguments.length == 1); 320 return name.arguments[0]; 321 } 322 return null; 323 } 324 325 /** Static wrapper for DirectMethodHandle.internalMemberName. */ 326 @ForceInline 327 /*non-public*/ 328 static Object internalMemberName(Object mh) { 329 return ((DirectMethodHandle)mh).member; 330 } 331 332 /** Static wrapper for DirectMethodHandle.internalMemberName. 333 * This one also forces initialization. 334 */ 335 /*non-public*/ 336 static Object internalMemberNameEnsureInit(Object mh) { 337 DirectMethodHandle dmh = (DirectMethodHandle)mh; 338 dmh.ensureInitialized(); 339 return dmh.member; 340 } 341 342 /*non-public*/ 343 static boolean shouldBeInitialized(MemberName member) { 344 switch (member.getReferenceKind()) { 345 case REF_invokeStatic: 346 case REF_getStatic: 347 case REF_putStatic: 348 case REF_newInvokeSpecial: 349 break; 350 default: 351 // No need to initialize the class on this kind of member. 352 return false; 353 } 354 Class<?> cls = member.getDeclaringClass(); 355 if (cls == ValueConversions.class || 356 cls == MethodHandleImpl.class || 357 cls == Invokers.class) { 358 // These guys have lots of <clinit> DMH creation but we know 359 // the MHs will not be used until the system is booted. 360 return false; 361 } 362 if (VerifyAccess.isSamePackage(MethodHandle.class, cls) || 363 VerifyAccess.isSamePackage(ValueConversions.class, cls)) { 364 // It is a system class. It is probably in the process of 365 // being initialized, but we will help it along just to be safe. 366 UNSAFE.ensureClassInitialized(cls); 367 return CDS.needsClassInitBarrier(cls); 368 } 369 return UNSAFE.shouldBeInitialized(cls) || CDS.needsClassInitBarrier(cls); 370 } 371 372 private void ensureInitialized() { 373 if (checkInitialized()) { 374 // The coast is clear. Delete the <clinit> barrier. 375 updateForm(new Function<>() { 376 public LambdaForm apply(LambdaForm oldForm) { 377 return (member.isField() ? preparedFieldLambdaForm(member) 378 : preparedLambdaForm(member)); 379 } 380 }); 381 } 382 } 383 private boolean checkInitialized() { 384 Class<?> defc = member.getDeclaringClass(); 385 UNSAFE.ensureClassInitialized(defc); 386 // Once we get here either defc was fully initialized by another thread, or 387 // defc was already being initialized by the current thread. In the latter case 388 // the barrier must remain. We can detect this simply by checking if initialization 389 // is still needed. 390 boolean initializingStill = UNSAFE.shouldBeInitialized(defc); 391 if (initializingStill && member.isStrict()) { 392 // while <clinit> is running, we track access to strict static fields 393 UNSAFE.notifyStrictStaticAccess(defc, staticOffset(this), member.isSetter()); 394 } 395 return !initializingStill; 396 } 397 398 /*non-public*/ 399 static void ensureInitialized(Object mh) { 400 ((DirectMethodHandle)mh).ensureInitialized(); 401 } 402 403 /** This subclass represents invokespecial instructions. */ 404 static final class Special extends DirectMethodHandle { 405 private final Class<?> caller; 406 private Special(MethodType mtype, LambdaForm form, MemberName member, boolean crackable, Class<?> caller) { 407 super(mtype, form, member, crackable); 408 this.caller = caller; 409 } 410 @Override 411 boolean isInvokeSpecial() { 412 return true; 413 } 414 @Override 415 MethodHandle copyWith(MethodType mt, LambdaForm lf) { 416 return new Special(mt, lf, member, crackable, caller); 417 } 418 @Override 419 MethodHandle viewAsType(MethodType newType, boolean strict) { 420 assert(viewAsTypeChecks(newType, strict)); 421 return new Special(newType, form, member, false, caller); 422 } 423 Object checkReceiver(Object recv) { 424 if (!caller.isInstance(recv)) { 425 if (recv != null) { 426 String msg = String.format("Receiver class %s is not a subclass of caller class %s", 427 recv.getClass().getName(), caller.getName()); 428 throw new IncompatibleClassChangeError(msg); 429 } else { 430 String msg = String.format("Cannot invoke %s with null receiver", member); 431 throw new NullPointerException(msg); 432 } 433 } 434 return recv; 435 } 436 } 437 438 /** This subclass represents invokeinterface instructions. */ 439 static final class Interface extends DirectMethodHandle { 440 private final Class<?> refc; 441 private Interface(MethodType mtype, LambdaForm form, MemberName member, boolean crackable, Class<?> refc) { 442 super(mtype, form, member, crackable); 443 assert(refc.isInterface()) : refc; 444 this.refc = refc; 445 } 446 @Override 447 MethodHandle copyWith(MethodType mt, LambdaForm lf) { 448 return new Interface(mt, lf, member, crackable, refc); 449 } 450 @Override 451 MethodHandle viewAsType(MethodType newType, boolean strict) { 452 assert(viewAsTypeChecks(newType, strict)); 453 return new Interface(newType, form, member, false, refc); 454 } 455 @Override 456 Object checkReceiver(Object recv) { 457 if (!refc.isInstance(recv)) { 458 if (recv != null) { 459 String msg = String.format("Receiver class %s does not implement the requested interface %s", 460 recv.getClass().getName(), refc.getName()); 461 throw new IncompatibleClassChangeError(msg); 462 } else { 463 String msg = String.format("Cannot invoke %s with null receiver", member); 464 throw new NullPointerException(msg); 465 } 466 } 467 return recv; 468 } 469 } 470 471 /** Used for interface receiver type checks, by Interface and Special modes. */ 472 Object checkReceiver(Object recv) { 473 throw new InternalError("Should only be invoked on a subclass"); 474 } 475 476 /** This subclass handles constructor references. */ 477 @AOTSafeClassInitializer 478 static final class Constructor extends DirectMethodHandle { 479 final MemberName initMethod; 480 final Class<?> instanceClass; 481 482 private Constructor(MethodType mtype, LambdaForm form, MemberName constructor, 483 boolean crackable, MemberName initMethod, Class<?> instanceClass) { 484 super(mtype, form, constructor, crackable); 485 this.initMethod = initMethod; 486 this.instanceClass = instanceClass; 487 assert(initMethod.isResolved()); 488 } 489 @Override 490 MethodHandle copyWith(MethodType mt, LambdaForm lf) { 491 return new Constructor(mt, lf, member, crackable, initMethod, instanceClass); 492 } 493 @Override 494 MethodHandle viewAsType(MethodType newType, boolean strict) { 495 assert(viewAsTypeChecks(newType, strict)); 496 return new Constructor(newType, form, member, false, initMethod, instanceClass); 497 } 498 } 499 500 /*non-public*/ 501 static Object constructorMethod(Object mh) { 502 Constructor dmh = (Constructor)mh; 503 return dmh.initMethod; 504 } 505 506 /*non-public*/ 507 static Object allocateInstance(Object mh) throws InstantiationException { 508 Constructor dmh = (Constructor)mh; 509 return UNSAFE.allocateInstance(dmh.instanceClass); 510 } 511 512 /** This subclass handles non-static field references. */ 513 static final class Accessor extends DirectMethodHandle { 514 final Class<?> fieldType; 515 final int fieldOffset; 516 final int layout; 517 private Accessor(MethodType mtype, LambdaForm form, MemberName member, 518 boolean crackable, int fieldOffset) { 519 super(mtype, form, member, crackable); 520 this.fieldType = member.getFieldType(); 521 this.fieldOffset = fieldOffset; 522 this.layout = member.getLayout(); 523 } 524 525 @Override Object checkCast(Object obj) { 526 return fieldType.cast(obj); 527 } 528 @Override 529 MethodHandle copyWith(MethodType mt, LambdaForm lf) { 530 return new Accessor(mt, lf, member, crackable, fieldOffset); 531 } 532 @Override 533 MethodHandle viewAsType(MethodType newType, boolean strict) { 534 assert(viewAsTypeChecks(newType, strict)); 535 return new Accessor(newType, form, member, false, fieldOffset); 536 } 537 } 538 539 @ForceInline 540 /*non-public*/ 541 static long fieldOffset(Object accessorObj) { 542 // Note: We return a long because that is what Unsafe.getObject likes. 543 // We store a plain int because it is more compact. 544 return ((Accessor)accessorObj).fieldOffset; 545 } 546 547 @ForceInline 548 /*non-public*/ 549 static Object checkBase(Object obj) { 550 // Note that the object's class has already been verified, 551 // since the parameter type of the Accessor method handle 552 // is either member.getDeclaringClass or a subclass. 553 // This was verified in DirectMethodHandle.make. 554 // Therefore, the only remaining check is for null. 555 // Since this check is *not* guaranteed by Unsafe.getInt 556 // and its siblings, we need to make an explicit one here. 557 return Objects.requireNonNull(obj); 558 } 559 560 /** This subclass handles static field references. */ 561 static final class StaticAccessor extends DirectMethodHandle { 562 private final Class<?> fieldType; 563 private final Object staticBase; 564 private final long staticOffset; 565 566 private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member, 567 boolean crackable, Object staticBase, long staticOffset) { 568 super(mtype, form, member, crackable); 569 this.fieldType = member.getFieldType(); 570 this.staticBase = staticBase; 571 this.staticOffset = staticOffset; 572 } 573 574 @Override Object checkCast(Object obj) { 575 return fieldType.cast(obj); 576 } 577 @Override 578 MethodHandle copyWith(MethodType mt, LambdaForm lf) { 579 return new StaticAccessor(mt, lf, member, crackable, staticBase, staticOffset); 580 } 581 @Override 582 MethodHandle viewAsType(MethodType newType, boolean strict) { 583 assert(viewAsTypeChecks(newType, strict)); 584 return new StaticAccessor(newType, form, member, false, staticBase, staticOffset); 585 } 586 } 587 588 @ForceInline 589 /*non-public*/ 590 static Object nullCheck(Object obj) { 591 return Objects.requireNonNull(obj); 592 } 593 594 @ForceInline 595 /*non-public*/ 596 static Object staticBase(Object accessorObj) { 597 return ((StaticAccessor)accessorObj).staticBase; 598 } 599 600 @ForceInline 601 /*non-public*/ 602 static long staticOffset(Object accessorObj) { 603 return ((StaticAccessor)accessorObj).staticOffset; 604 } 605 606 @ForceInline 607 /*non-public*/ 608 static Object checkCast(Object mh, Object obj) { 609 return ((DirectMethodHandle) mh).checkCast(obj); 610 } 611 612 @ForceInline 613 /*non-public*/ static Class<?> fieldType(Object accessorObj) { 614 return ((Accessor) accessorObj).fieldType; 615 } 616 617 @ForceInline 618 static int fieldLayout(Object accessorObj) { 619 return ((Accessor) accessorObj).layout; 620 } 621 622 @ForceInline 623 /*non-public*/ static Class<?> staticFieldType(Object accessorObj) { 624 return ((StaticAccessor) accessorObj).fieldType; 625 } 626 627 Object checkCast(Object obj) { 628 return member.getMethodType().returnType().cast(obj); 629 } 630 631 // Caching machinery for field accessors: 632 static final byte 633 AF_GETFIELD = 0, 634 AF_PUTFIELD = 1, 635 AF_GETSTATIC = 2, 636 AF_PUTSTATIC = 3, 637 AF_GETSTATIC_INIT = 4, 638 AF_PUTSTATIC_INIT = 5, 639 AF_LIMIT = 6; 640 // Enumerate the different field kinds using Wrapper, 641 // with an extra case added for checked references and value field access 642 static final int 643 FT_FIRST_REFERENCE = 8, 644 // Any oop, same sig (Runnable?) 645 FT_UNCHECKED_REF = FT_FIRST_REFERENCE, 646 // Oop with type checks (Number?) 647 FT_CHECKED_REF = FT_FIRST_REFERENCE + 1, 648 // Oop with null checks, (Runnable!) 649 FT_UNCHECKED_NR_REF = FT_FIRST_REFERENCE + 2, 650 // Oop with null and type checks, (Number!) 651 FT_CHECKED_NR_REF = FT_FIRST_REFERENCE + 3, 652 FT_FIRST_FLAT = FT_FIRST_REFERENCE + 4, 653 // nullable flat (must check type), (Integer?) 654 FT_NULLABLE_FLAT = FT_FIRST_FLAT, 655 // Null restricted flat (must check type), (Integer!) 656 FT_NR_FLAT = FT_FIRST_FLAT + 1, 657 FT_LIMIT = FT_FIRST_FLAT + 2; 658 659 static { 660 assert FT_FIRST_REFERENCE == Wrapper.OBJECT.ordinal(); 661 } 662 663 private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) { 664 return ((formOp * FT_LIMIT * 2) 665 + (isVolatile ? FT_LIMIT : 0) 666 + ftypeKind); 667 } 668 @Stable 669 private static final LambdaForm[] ACCESSOR_FORMS 670 = new LambdaForm[afIndex(AF_LIMIT, false, 0)]; 671 static int ftypeKind(Class<?> ftype, boolean isFlat, boolean isNullRestricted) { 672 if (ftype.isPrimitive()) { 673 assert !isFlat && !isNullRestricted : ftype; 674 return Wrapper.forPrimitiveType(ftype).ordinal(); 675 } else if (ftype.isInterface() || ftype.isAssignableFrom(Object.class)) { 676 assert !isFlat : ftype; 677 // retyping can be done without a cast 678 return isNullRestricted ? FT_UNCHECKED_NR_REF : FT_UNCHECKED_REF; 679 } 680 if (isFlat) { 681 assert ValueClass.isConcreteValueClass(ftype) : ftype; 682 return isNullRestricted ? FT_NR_FLAT : FT_NULLABLE_FLAT; 683 } 684 return isNullRestricted ? FT_CHECKED_NR_REF : FT_CHECKED_REF; 685 } 686 687 /** 688 * Create a LF which can access the given field. 689 * Cache and share this structure among all fields with 690 * the same basicType and refKind. 691 */ 692 private static LambdaForm preparedFieldLambdaForm(MemberName m) { 693 Class<?> ftype = m.getFieldType(); 694 byte formOp = switch (m.getReferenceKind()) { 695 case REF_getField -> AF_GETFIELD; 696 case REF_putField -> AF_PUTFIELD; 697 case REF_getStatic -> AF_GETSTATIC; 698 case REF_putStatic -> AF_PUTSTATIC; 699 default -> throw new InternalError(m.toString()); 700 }; 701 if (shouldBeInitialized(m)) { 702 // precompute the barrier-free version: 703 preparedFieldLambdaForm(formOp, m.isVolatile(), m.isFlat(), m.isNullRestricted(), ftype); 704 assert((AF_GETSTATIC_INIT - AF_GETSTATIC) == 705 (AF_PUTSTATIC_INIT - AF_PUTSTATIC)); 706 formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC); 707 } 708 LambdaForm lform = preparedFieldLambdaForm(formOp, m.isVolatile(), m.isFlat(), m.isNullRestricted(), ftype); 709 assert(lform.methodType().dropParameterTypes(0, 1) 710 .equals(m.getInvocationType().basicType())) 711 : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType()); 712 return lform; 713 } 714 715 private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, 716 boolean isFlat, boolean isNullRestricted, Class<?> ftype) { 717 int ftypeKind = ftypeKind(ftype, isFlat, isNullRestricted); 718 return preparedFieldLambdaForm(formOp, isVolatile, ftypeKind); 719 } 720 721 private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) { 722 int afIndex = afIndex(formOp, isVolatile, ftypeKind); 723 LambdaForm lform = ACCESSOR_FORMS[afIndex]; 724 if (lform != null) return lform; 725 lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind); 726 ACCESSOR_FORMS[afIndex] = lform; // don't bother with a CAS 727 return lform; 728 } 729 730 private static final @Stable Wrapper[] ALL_WRAPPERS = Wrapper.values(); 731 732 // Names in kind may overload but differ from their basic type 733 private static Kind getFieldKind(boolean isGetter, 734 boolean isVolatile, 735 boolean needsInit, 736 boolean needsCast, 737 boolean isFlat, 738 boolean isNullRestricted, 739 Wrapper wrapper) { 740 if (!wrapper.isOther()) { 741 // primitives 742 assert !isFlat && !isNullRestricted && !needsCast; 743 return switch (wrapper) { 744 case BYTE -> isVolatile 745 ? (needsInit ? VOLATILE_FIELD_ACCESS_INIT_B : VOLATILE_FIELD_ACCESS_B) 746 : (needsInit ? FIELD_ACCESS_INIT_B : FIELD_ACCESS_B); 747 case CHAR -> isVolatile 748 ? (needsInit ? VOLATILE_FIELD_ACCESS_INIT_C : VOLATILE_FIELD_ACCESS_C) 749 : (needsInit ? FIELD_ACCESS_INIT_C : FIELD_ACCESS_C); 750 case SHORT -> isVolatile 751 ? (needsInit ? VOLATILE_FIELD_ACCESS_INIT_S : VOLATILE_FIELD_ACCESS_S) 752 : (needsInit ? FIELD_ACCESS_INIT_S : FIELD_ACCESS_S); 753 case BOOLEAN -> isVolatile 754 ? (needsInit ? VOLATILE_FIELD_ACCESS_INIT_Z : VOLATILE_FIELD_ACCESS_Z) 755 : (needsInit ? FIELD_ACCESS_INIT_Z : FIELD_ACCESS_Z); 756 // basic types 757 default -> isVolatile 758 ? (needsInit ? VOLATILE_FIELD_ACCESS_INIT : VOLATILE_FIELD_ACCESS) 759 : (needsInit ? FIELD_ACCESS_INIT : FIELD_ACCESS); 760 }; 761 } 762 763 assert !(isGetter && isNullRestricted); 764 if (isVolatile) { 765 if (isFlat) { 766 assert !needsInit && needsCast; 767 return isNullRestricted ? VOLATILE_PUT_NULL_RESTRICTED_FLAT_VALUE : VOLATILE_FIELD_ACCESS_FLAT; 768 } else if (needsCast) { 769 if (needsInit) { 770 return isNullRestricted ? VOLATILE_PUT_NULL_RESTRICTED_REFERENCE_CAST_INIT : VOLATILE_FIELD_ACCESS_INIT_CAST; 771 } else { 772 return isNullRestricted ? VOLATILE_PUT_NULL_RESTRICTED_REFERENCE_CAST : VOLATILE_FIELD_ACCESS_CAST; 773 } 774 } else { 775 if (needsInit) { 776 return isNullRestricted ? VOLATILE_PUT_NULL_RESTRICTED_REFERENCE_INIT : VOLATILE_FIELD_ACCESS_INIT; 777 } else { 778 return isNullRestricted ? VOLATILE_PUT_NULL_RESTRICTED_REFERENCE : VOLATILE_FIELD_ACCESS; 779 } 780 } 781 } else { 782 if (isFlat) { 783 assert !needsInit && needsCast; 784 return isNullRestricted ? PUT_NULL_RESTRICTED_FLAT_VALUE : FIELD_ACCESS_FLAT; 785 } else if (needsCast) { 786 if (needsInit) { 787 return isNullRestricted ? PUT_NULL_RESTRICTED_REFERENCE_CAST_INIT : FIELD_ACCESS_INIT_CAST; 788 } else { 789 return isNullRestricted ? PUT_NULL_RESTRICTED_REFERENCE_CAST : FIELD_ACCESS_CAST; 790 } 791 } else { 792 if (needsInit) { 793 return isNullRestricted ? PUT_NULL_RESTRICTED_REFERENCE_INIT : FIELD_ACCESS_INIT; 794 } else { 795 return isNullRestricted ? PUT_NULL_RESTRICTED_REFERENCE : FIELD_ACCESS; 796 } 797 } 798 } 799 } 800 801 private static String unsafeMethodName(boolean isGetter, 802 boolean isVolatile, 803 Wrapper wrapper) { 804 var name = switch (wrapper) { 805 case BOOLEAN -> "Boolean"; 806 case BYTE -> "Byte"; 807 case CHAR -> "Char"; 808 case SHORT -> "Short"; 809 case INT -> "Int"; 810 case FLOAT -> "Float"; 811 case LONG -> "Long"; 812 case DOUBLE -> "Double"; 813 case OBJECT -> "Reference"; 814 case VOID -> "FlatValue"; 815 }; 816 var sb = new StringBuilder(3 + name.length() + (isVolatile ? 8 : 0)) 817 .append(isGetter ? "get" : "put") 818 .append(name); 819 if (isVolatile) { 820 sb.append("Volatile"); 821 } 822 return sb.toString(); 823 } 824 825 static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) { 826 boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1); 827 boolean isStatic = (formOp >= AF_GETSTATIC); 828 boolean needsInit = (formOp >= AF_GETSTATIC_INIT); 829 boolean isFlat = (ftypeKind >= FT_FIRST_FLAT); 830 boolean isNullRestricted = (ftypeKind == FT_NR_FLAT || ftypeKind == FT_CHECKED_NR_REF || ftypeKind == FT_UNCHECKED_NR_REF); 831 boolean needsCast = (isFlat || ftypeKind == FT_CHECKED_REF || ftypeKind == FT_CHECKED_NR_REF); 832 833 if (isGetter && isNullRestricted) { 834 int newKind = switch (ftypeKind) { 835 case FT_NR_FLAT -> FT_NULLABLE_FLAT; 836 case FT_CHECKED_NR_REF -> FT_CHECKED_REF; 837 case FT_UNCHECKED_NR_REF -> FT_UNCHECKED_REF; 838 default -> throw new InternalError(); 839 }; 840 return preparedFieldLambdaForm(formOp, isVolatile, newKind); 841 } 842 843 if (isFlat && isStatic) 844 throw new InternalError("Static flat not supported yet"); 845 846 // primitives, reference, and void for flat 847 Wrapper fw = ftypeKind < FT_FIRST_REFERENCE ? ALL_WRAPPERS[ftypeKind] : 848 isFlat ? Wrapper.VOID : Wrapper.OBJECT; 849 850 // getObject, putIntVolatile, etc. 851 String unsafeMethodName = unsafeMethodName(isGetter, isVolatile, fw); 852 // isGetter and isStatic is reflected in field type; 853 // flat, NR distinguished 854 // basic type clash for subwords 855 Kind kind = getFieldKind(isGetter, isVolatile, needsInit, needsCast, isFlat, isNullRestricted, fw); 856 857 Class<?> ft = ftypeKind < FT_FIRST_REFERENCE ? fw.primitiveType() : Object.class; 858 MethodType linkerType; 859 if (isGetter) { 860 linkerType = isFlat 861 ? MethodType.methodType(ft, Object.class, long.class, int.class, Class.class) 862 : MethodType.methodType(ft, Object.class, long.class); 863 } else { 864 linkerType = isFlat 865 ? MethodType.methodType(void.class, Object.class, long.class, int.class, Class.class, ft) 866 : MethodType.methodType(void.class, Object.class, long.class, ft); 867 } 868 MemberName linker = new MemberName(Unsafe.class, unsafeMethodName, linkerType, REF_invokeVirtual); 869 try { 870 linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, LM_TRUSTED, 871 NoSuchMethodException.class); 872 } catch (ReflectiveOperationException ex) { 873 throw newInternalError(ex); 874 } 875 876 // What is the external type of the lambda form? 877 MethodType mtype; 878 if (isGetter) 879 mtype = MethodType.methodType(ft); 880 else 881 mtype = MethodType.methodType(void.class, ft); 882 mtype = mtype.basicType(); // erase short to int, etc. 883 if (!isStatic) 884 mtype = mtype.insertParameterTypes(0, Object.class); 885 final int DMH_THIS = 0; 886 final int ARG_BASE = 1; 887 final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); 888 // if this is for non-static access, the base pointer is stored at this index: 889 final int OBJ_BASE = isStatic ? -1 : ARG_BASE; 890 // if this is for write access, the value to be written is stored at this index: 891 final int SET_VALUE = isGetter ? -1 : ARG_LIMIT - 1; 892 int nameCursor = ARG_LIMIT; 893 final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any 894 final int F_OFFSET = nameCursor++; // Either static offset or field offset. 895 final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1); 896 final int U_HOLDER = nameCursor++; // UNSAFE holder 897 final int INIT_BAR = (needsInit ? nameCursor++ : -1); 898 final int LAYOUT = (isFlat ? nameCursor++ : -1); // field must be instance 899 final int VALUE_TYPE = (isFlat ? nameCursor++ : -1); 900 final int NULL_CHECK = (isNullRestricted && !isGetter ? nameCursor++ : -1); 901 final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1); 902 final int LINKER_CALL = nameCursor++; 903 final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1); 904 final int RESULT = nameCursor-1; // either the call, or the cast 905 Name[] names = invokeArguments(nameCursor - ARG_LIMIT, mtype); 906 if (needsInit) 907 names[INIT_BAR] = new Name(getFunction(NF_ensureInitialized), names[DMH_THIS]); 908 if (!isGetter) { 909 if (isNullRestricted) 910 names[NULL_CHECK] = new Name(getFunction(NF_nullCheck), names[SET_VALUE]); 911 if (needsCast) 912 names[PRE_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[SET_VALUE]); 913 } 914 Object[] outArgs = new Object[1 + linkerType.parameterCount()]; 915 assert (outArgs.length == (isGetter ? 3 : 4) + (isFlat ? 2 : 0)); 916 outArgs[0] = names[U_HOLDER] = new Name(getFunction(NF_UNSAFE)); 917 if (isStatic) { 918 outArgs[1] = names[F_HOLDER] = new Name(getFunction(NF_staticBase), names[DMH_THIS]); 919 outArgs[2] = names[F_OFFSET] = new Name(getFunction(NF_staticOffset), names[DMH_THIS]); 920 } else { 921 outArgs[1] = names[OBJ_CHECK] = new Name(getFunction(NF_checkBase), names[OBJ_BASE]); 922 outArgs[2] = names[F_OFFSET] = new Name(getFunction(NF_fieldOffset), names[DMH_THIS]); 923 } 924 int x = 3; 925 if (isFlat) { 926 outArgs[x++] = names[LAYOUT] = new Name(getFunction(NF_fieldLayout), names[DMH_THIS]); 927 outArgs[x++] = names[VALUE_TYPE] = isStatic ? new Name(getFunction(NF_staticFieldType), names[DMH_THIS]) 928 : new Name(getFunction(NF_fieldType), names[DMH_THIS]); 929 } 930 if (!isGetter) { 931 outArgs[x] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]); 932 } 933 for (Object a : outArgs) assert(a != null); 934 names[LINKER_CALL] = new Name(linker, outArgs); 935 if (needsCast && isGetter) 936 names[POST_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[LINKER_CALL]); 937 for (Name n : names) assert(n != null); 938 939 LambdaForm form = LambdaForm.create(ARG_LIMIT, names, RESULT, kind); 940 941 if (LambdaForm.debugNames()) { 942 // add some detail to the lambdaForm debugname, 943 // significant only for debugging 944 StringBuilder nameBuilder = new StringBuilder(unsafeMethodName); 945 if (isStatic) { 946 nameBuilder.append("Static"); 947 } else { 948 nameBuilder.append("Field"); 949 } 950 if (isNullRestricted) { 951 nameBuilder.append("NullRestricted"); 952 } 953 if (needsCast) { 954 nameBuilder.append("Cast"); 955 } 956 if (needsInit) { 957 nameBuilder.append("Init"); 958 } 959 LambdaForm.associateWithDebugName(form, nameBuilder.toString()); 960 } 961 962 // NF_UNSAFE uses field form, avoid circular dependency in interpreter 963 form.compileToBytecode(); 964 return form; 965 } 966 967 /** 968 * Pre-initialized NamedFunctions for bootstrapping purposes. 969 */ 970 static final byte NF_internalMemberName = 0, 971 NF_internalMemberNameEnsureInit = 1, 972 NF_ensureInitialized = 2, 973 NF_fieldOffset = 3, 974 NF_checkBase = 4, 975 NF_staticBase = 5, 976 NF_staticOffset = 6, 977 NF_checkCast = 7, 978 NF_allocateInstance = 8, 979 NF_constructorMethod = 9, 980 NF_UNSAFE = 10, 981 NF_checkReceiver = 11, 982 NF_fieldType = 12, 983 NF_staticFieldType = 13, 984 NF_fieldLayout = 14, 985 NF_nullCheck = 15, 986 NF_LIMIT = 16; 987 988 private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT]; 989 990 private static NamedFunction getFunction(byte func) { 991 NamedFunction nf = NFS[func]; 992 if (nf != null) { 993 return nf; 994 } 995 // Each nf must be statically invocable or we get tied up in our bootstraps. 996 nf = NFS[func] = createFunction(func); 997 assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf)); 998 return nf; 999 } 1000 1001 private static final MethodType CLS_OBJ_TYPE = MethodType.methodType(Class.class, Object.class); 1002 private static final MethodType INT_OBJ_TYPE = MethodType.methodType(int.class, Object.class); 1003 1004 private static final MethodType OBJ_OBJ_TYPE = MethodType.methodType(Object.class, Object.class); 1005 1006 private static final MethodType LONG_OBJ_TYPE = MethodType.methodType(long.class, Object.class); 1007 1008 private static NamedFunction createFunction(byte func) { 1009 try { 1010 switch (func) { 1011 case NF_internalMemberName: 1012 return getNamedFunction("internalMemberName", OBJ_OBJ_TYPE); 1013 case NF_internalMemberNameEnsureInit: 1014 return getNamedFunction("internalMemberNameEnsureInit", OBJ_OBJ_TYPE); 1015 case NF_ensureInitialized: 1016 return getNamedFunction("ensureInitialized", MethodType.methodType(void.class, Object.class)); 1017 case NF_fieldOffset: 1018 return getNamedFunction("fieldOffset", LONG_OBJ_TYPE); 1019 case NF_checkBase: 1020 return getNamedFunction("checkBase", OBJ_OBJ_TYPE); 1021 case NF_staticBase: 1022 return getNamedFunction("staticBase", OBJ_OBJ_TYPE); 1023 case NF_staticOffset: 1024 return getNamedFunction("staticOffset", LONG_OBJ_TYPE); 1025 case NF_checkCast: 1026 return getNamedFunction("checkCast", MethodType.methodType(Object.class, Object.class, Object.class)); 1027 case NF_allocateInstance: 1028 return getNamedFunction("allocateInstance", OBJ_OBJ_TYPE); 1029 case NF_constructorMethod: 1030 return getNamedFunction("constructorMethod", OBJ_OBJ_TYPE); 1031 case NF_UNSAFE: 1032 MemberName member = new MemberName(MethodHandleStatics.class, "UNSAFE", Unsafe.class, REF_getStatic); 1033 return new NamedFunction( 1034 MemberName.getFactory().resolveOrFail(REF_getStatic, member, 1035 DirectMethodHandle.class, LM_TRUSTED, 1036 NoSuchFieldException.class)); 1037 case NF_checkReceiver: 1038 member = new MemberName(DirectMethodHandle.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual); 1039 return new NamedFunction( 1040 MemberName.getFactory().resolveOrFail(REF_invokeVirtual, member, 1041 DirectMethodHandle.class, LM_TRUSTED, 1042 NoSuchMethodException.class)); 1043 case NF_fieldType: 1044 return getNamedFunction("fieldType", CLS_OBJ_TYPE); 1045 case NF_staticFieldType: 1046 return getNamedFunction("staticFieldType", CLS_OBJ_TYPE); 1047 case NF_nullCheck: 1048 return getNamedFunction("nullCheck", OBJ_OBJ_TYPE); 1049 case NF_fieldLayout: 1050 return getNamedFunction("fieldLayout", INT_OBJ_TYPE); 1051 default: 1052 throw newInternalError("Unknown function: " + func); 1053 } 1054 } catch (ReflectiveOperationException ex) { 1055 throw newInternalError(ex); 1056 } 1057 } 1058 1059 private static NamedFunction getNamedFunction(String name, MethodType type) 1060 throws ReflectiveOperationException 1061 { 1062 MemberName member = new MemberName(DirectMethodHandle.class, name, type, REF_invokeStatic); 1063 return new NamedFunction( 1064 MemberName.getFactory().resolveOrFail(REF_invokeStatic, member, 1065 DirectMethodHandle.class, LM_TRUSTED, 1066 NoSuchMethodException.class)); 1067 } 1068 1069 static { 1070 // The Holder class will contain pre-generated DirectMethodHandles resolved 1071 // speculatively using MemberName.getFactory().resolveOrNull. However, that 1072 // doesn't initialize the class, which subtly breaks inlining etc. By forcing 1073 // initialization of the Holder class we avoid these issues. 1074 UNSAFE.ensureClassInitialized(Holder.class); 1075 } 1076 1077 /* Placeholder class for DirectMethodHandles generated ahead of time */ 1078 @AOTSafeClassInitializer 1079 final class Holder {} 1080 }