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