1 /* 2 * Copyright (c) 2012, 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.constant.ClassOrInterfaceDescImpl; 29 import jdk.internal.misc.PreviewFeatures; 30 import jdk.internal.misc.CDS; 31 import jdk.internal.util.ClassFileDumper; 32 import jdk.internal.value.ValueClass; 33 import sun.invoke.util.VerifyAccess; 34 35 import java.io.Serializable; 36 import java.lang.classfile.ClassBuilder; 37 import java.lang.classfile.ClassFile; 38 import java.lang.classfile.CodeBuilder; 39 import java.lang.classfile.MethodBuilder; 40 import java.lang.classfile.Opcode; 41 import java.lang.classfile.TypeKind; 42 43 import java.lang.constant.ClassDesc; 44 import java.lang.constant.MethodTypeDesc; 45 import java.lang.reflect.ClassFileFormatVersion; 46 import java.lang.reflect.Modifier; 47 import java.util.ArrayList; 48 import java.util.HashSet; 49 import java.util.LinkedHashSet; 50 import java.util.List; 51 import java.util.Set; 52 import java.util.function.Consumer; 53 54 import static java.lang.classfile.ClassFile.*; 55 import java.lang.classfile.attribute.ExceptionsAttribute; 56 import java.lang.classfile.attribute.LoadableDescriptorsAttribute; 57 import java.lang.classfile.constantpool.ClassEntry; 58 import java.lang.classfile.constantpool.ConstantPoolBuilder; 59 import java.lang.classfile.constantpool.Utf8Entry; 60 61 import static java.lang.constant.ConstantDescs.*; 62 import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS; 63 import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK; 64 import jdk.internal.constant.ConstantUtils; 65 import jdk.internal.constant.MethodTypeDescImpl; 66 import jdk.internal.vm.annotation.Stable; 67 import sun.invoke.util.Wrapper; 68 69 /** 70 * Lambda metafactory implementation which dynamically creates an 71 * inner-class-like class per lambda callsite. 72 * 73 * @see LambdaMetafactory 74 */ 75 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { 76 private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$"; 77 private static final @Stable String[] ARG_NAME_CACHE = {"arg$1", "arg$2", "arg$3", "arg$4", "arg$5", "arg$6", "arg$7", "arg$8"}; 78 private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC; 79 80 // For dumping generated classes to disk, for debugging purposes 81 private static final ClassFileDumper lambdaProxyClassFileDumper; 82 83 private static final boolean disableEagerInitialization; 84 85 static { 86 // To dump the lambda proxy classes, set this system property: 87 // -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles 88 // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true 89 final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles"; 90 lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, "DUMP_LAMBDA_PROXY_CLASS_FILES"); 91 92 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization"; 93 disableEagerInitialization = Boolean.getBoolean(disableEagerInitializationKey); 94 } 95 96 // See context values in AbstractValidatingLambdaMetafactory 97 private final ClassDesc implMethodClassDesc; // Name of type containing implementation "CC" 98 private final String implMethodName; // Name of implementation method "impl" 99 private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" 100 private final MethodType constructorType; // Generated class constructor type "(CC)void" 101 private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void" 102 private final ClassDesc[] argDescs; // Type descriptors for the constructor arguments 103 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" 104 private final ConstantPoolBuilder pool = ConstantPoolBuilder.of(); 105 private final ClassEntry lambdaClassEntry; // Class entry for the generated class "X$$Lambda$1" 106 private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation 107 108 /** 109 * General meta-factory constructor, supporting both standard cases and 110 * allowing for uncommon options such as serialization or bridging. 111 * 112 * @param caller Stacked automatically by VM; represents a lookup context 113 * with the accessibility privileges of the caller. 114 * @param factoryType Stacked automatically by VM; the signature of the 115 * invoked method, which includes the expected static 116 * type of the returned lambda object, and the static 117 * types of the captured arguments for the lambda. In 118 * the event that the implementation method is an 119 * instance method, the first argument in the invocation 120 * signature will correspond to the receiver. 121 * @param interfaceMethodName Name of the method in the functional interface to 122 * which the lambda or method reference is being 123 * converted, represented as a String. 124 * @param interfaceMethodType Type of the method in the functional interface to 125 * which the lambda or method reference is being 126 * converted, represented as a MethodType. 127 * @param implementation The implementation method which should be called (with 128 * suitable adaptation of argument types, return types, 129 * and adjustment for captured arguments) when methods of 130 * the resulting functional interface instance are invoked. 131 * @param dynamicMethodType The signature of the primary functional 132 * interface method after type variables are 133 * substituted with their instantiation from 134 * the capture site 135 * @param isSerializable Should the lambda be made serializable? If set, 136 * either the target type or one of the additional SAM 137 * types must extend {@code Serializable}. 138 * @param altInterfaces Additional interfaces which the lambda object 139 * should implement. 140 * @param altMethods Method types for additional signatures to be 141 * implemented by invoking the implementation method 142 * @throws LambdaConversionException If any of the meta-factory protocol 143 * invariants are violated 144 */ 145 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 146 MethodType factoryType, 147 String interfaceMethodName, 148 MethodType interfaceMethodType, 149 MethodHandle implementation, 150 MethodType dynamicMethodType, 151 boolean isSerializable, 152 Class<?>[] altInterfaces, 153 MethodType[] altMethods) 154 throws LambdaConversionException { 155 super(caller, factoryType, interfaceMethodName, interfaceMethodType, 156 implementation, dynamicMethodType, 157 isSerializable, altInterfaces, altMethods); 158 implMethodClassDesc = implClassDesc(implClass); 159 implMethodName = implInfo.getName(); 160 implMethodDesc = methodDesc(implInfo.getMethodType()); 161 constructorType = factoryType.changeReturnType(Void.TYPE); 162 lambdaClassName = lambdaClassName(targetClass); 163 lambdaClassEntry = pool.classEntry(ConstantUtils.internalNameToDesc(lambdaClassName)); 164 // If the target class invokes a protected method inherited from a 165 // superclass in a different package, or does 'invokespecial', the 166 // lambda class has no access to the resolved method, or does 167 // 'invokestatic' on a hidden class which cannot be resolved by name. 168 // Instead, we need to pass the live implementation method handle to 169 // the proxy class to invoke directly. (javac prefers to avoid this 170 // situation by generating bridges in the target class) 171 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) && 172 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) || 173 implKind == MethodHandleInfo.REF_invokeSpecial || 174 implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden(); 175 int parameterCount = factoryType.parameterCount(); 176 ClassDesc[] argDescs; 177 MethodTypeDesc constructorTypeDesc; 178 if (parameterCount > 0) { 179 argDescs = new ClassDesc[parameterCount]; 180 for (int i = 0; i < parameterCount; i++) { 181 argDescs[i] = classDesc(factoryType.parameterType(i)); 182 } 183 constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); 184 } else { 185 argDescs = EMPTY_CLASSDESC_ARRAY; 186 constructorTypeDesc = MTD_void; 187 } 188 this.argDescs = argDescs; 189 this.constructorTypeDesc = constructorTypeDesc; 190 } 191 192 private static String argName(int i) { 193 return i < ARG_NAME_CACHE.length ? ARG_NAME_CACHE[i] : "arg$" + (i + 1); 194 } 195 196 private static String sanitizedTargetClassName(Class<?> targetClass) { 197 String name = targetClass.getName(); 198 if (targetClass.isHidden()) { 199 // use the original class name 200 name = name.replace('/', '_'); 201 } 202 return name.replace('.', '/'); 203 } 204 205 private static String lambdaClassName(Class<?> targetClass) { 206 return sanitizedTargetClassName(targetClass).concat("$$Lambda"); 207 } 208 209 /** 210 * Build the CallSite. Generate a class file which implements the functional 211 * interface, define the class, if there are no parameters create an instance 212 * of the class which the CallSite will return, otherwise, generate handles 213 * which will call the class' constructor. 214 * 215 * @return a CallSite, which, when invoked, will return an instance of the 216 * functional interface 217 * @throws LambdaConversionException If properly formed functional interface 218 * is not found 219 */ 220 @Override 221 CallSite buildCallSite() throws LambdaConversionException { 222 final Class<?> innerClass = spinInnerClass(); 223 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 224 try { 225 return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, 226 factoryType.returnType())); 227 } catch (ReflectiveOperationException e) { 228 throw new LambdaConversionException( 229 "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e); 230 } 231 } else { 232 try { 233 MethodHandle mh = caller.findConstructor(innerClass, constructorType); 234 if (factoryType.parameterCount() == 0) { 235 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance 236 Object inst = mh.invokeBasic(); 237 return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst)); 238 } else { 239 return new ConstantCallSite(mh.asType(factoryType)); 240 } 241 } catch (ReflectiveOperationException e) { 242 throw new LambdaConversionException("Exception finding constructor", e); 243 } catch (Throwable e) { 244 throw new LambdaConversionException("Exception instantiating lambda object", e); 245 } 246 } 247 } 248 249 /** 250 * Spins the lambda proxy class. 251 * 252 * This first checks if a lambda proxy class can be loaded from CDS archive. 253 * Otherwise, generate the lambda proxy class. If CDS dumping is enabled, it 254 * registers the lambda proxy class for including into the CDS archive. 255 */ 256 private Class<?> spinInnerClass() throws LambdaConversionException { 257 // CDS does not handle disableEagerInitialization or useImplMethodHandle 258 if (!disableEagerInitialization && !useImplMethodHandle) { 259 if (CDS.isUsingArchive()) { 260 // load from CDS archive if present 261 Class<?> innerClass = LambdaProxyClassArchive.find(targetClass, 262 interfaceMethodName, 263 factoryType, 264 interfaceMethodType, 265 implementation, 266 dynamicMethodType, 267 isSerializable, 268 altInterfaces, 269 altMethods); 270 if (innerClass != null) return innerClass; 271 } 272 273 // include lambda proxy class in CDS archive at dump time 274 if (CDS.isDumpingArchive()) { 275 Class<?> innerClass = generateInnerClass(); 276 LambdaProxyClassArchive.register(targetClass, 277 interfaceMethodName, 278 factoryType, 279 interfaceMethodType, 280 implementation, 281 dynamicMethodType, 282 isSerializable, 283 altInterfaces, 284 altMethods, 285 innerClass); 286 return innerClass; 287 } 288 289 } 290 return generateInnerClass(); 291 } 292 293 /** 294 * Generate a class file which implements the functional 295 * interface, define and return the class. 296 * 297 * @return a Class which implements the functional interface 298 * @throws LambdaConversionException If properly formed functional interface 299 * is not found 300 */ 301 private Class<?> generateInnerClass() throws LambdaConversionException { 302 List<ClassDesc> interfaces; 303 ClassDesc interfaceDesc = classDesc(interfaceClass); 304 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass); 305 if (altInterfaces.length == 0) { 306 interfaces = List.of(interfaceDesc); 307 } else { 308 // Assure no duplicate interfaces (ClassFormatError) 309 Set<ClassDesc> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1); 310 itfs.add(interfaceDesc); 311 for (Class<?> i : altInterfaces) { 312 itfs.add(classDesc(i)); 313 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i); 314 } 315 interfaces = List.copyOf(itfs); 316 } 317 final boolean finalAccidentallySerializable = accidentallySerializable; 318 final byte[] classBytes = ClassFile.of().build(lambdaClassEntry, pool, new Consumer<ClassBuilder>() { 319 @Override 320 public void accept(ClassBuilder clb) { 321 clb.withVersion(ClassFileFormatVersion.latest().major(), (PreviewFeatures.isEnabled() ? 0xFFFF0000 : 0)) 322 .withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC) 323 .withInterfaceSymbols(interfaces); 324 325 // generate LoadableDescriptors attribute if it references any value class 326 if (PreviewFeatures.isEnabled()) { 327 generateLoadableDescriptors(clb); 328 } 329 330 // Generate final fields to be filled in by constructor 331 for (int i = 0; i < argDescs.length; i++) { 332 clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL); 333 } 334 335 generateConstructor(clb); 336 337 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 338 generateClassInitializer(clb); 339 } 340 341 // Forward the SAM method 342 clb.withMethodBody(interfaceMethodName, 343 methodDesc(interfaceMethodType), 344 ACC_PUBLIC, 345 forwardingMethod(interfaceMethodType)); 346 347 // Forward the bridges 348 if (altMethods != null) { 349 for (MethodType mt : altMethods) { 350 clb.withMethodBody(interfaceMethodName, 351 methodDesc(mt), 352 ACC_PUBLIC | ACC_BRIDGE, 353 forwardingMethod(mt)); 354 } 355 } 356 357 if (isSerializable) 358 generateSerializationFriendlyMethods(clb); 359 else if (finalAccidentallySerializable) 360 generateSerializationHostileMethods(clb); 361 } 362 }); 363 364 // Define the generated class in this VM. 365 366 try { 367 // this class is linked at the indy callsite; so define a hidden nestmate 368 var classdata = useImplMethodHandle? implementation : null; 369 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK) 370 .defineClass(!disableEagerInitialization, classdata); 371 372 } catch (Throwable t) { 373 throw new InternalError(t); 374 } 375 } 376 377 /** 378 * Generate a static field and a static initializer that sets this field to an instance of the lambda 379 */ 380 private void generateClassInitializer(ClassBuilder clb) { 381 ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType()); 382 383 // Generate the static final field that holds the lambda singleton 384 clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 385 386 // Instantiate the lambda and store it to the static final field 387 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() { 388 @Override 389 public void accept(CodeBuilder cob) { 390 assert factoryType.parameterCount() == 0; 391 cob.new_(lambdaClassEntry) 392 .dup() 393 .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc))) 394 .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor))) 395 .return_(); 396 } 397 }); 398 } 399 400 /** 401 * Generate the constructor for the class 402 */ 403 private void generateConstructor(ClassBuilder clb) { 404 // Generate constructor 405 clb.withMethodBody(INIT_NAME, constructorTypeDesc, ACC_PRIVATE, 406 new Consumer<>() { 407 @Override 408 public void accept(CodeBuilder cob) { 409 cob.aload(0) 410 .invokespecial(CD_Object, INIT_NAME, MTD_void); 411 int parameterCount = factoryType.parameterCount(); 412 for (int i = 0; i < parameterCount; i++) { 413 cob.aload(0) 414 .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) 415 .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 416 } 417 cob.return_(); 418 } 419 }); 420 } 421 422 private static class SerializationSupport { 423 // Serialization support 424 private static final ClassDesc CD_SerializedLambda = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;"); 425 private static final ClassDesc CD_ObjectOutputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectOutputStream;"); 426 private static final ClassDesc CD_ObjectInputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectInputStream;"); 427 private static final MethodTypeDesc MTD_Object = MethodTypeDescImpl.ofValidated(CD_Object); 428 private static final MethodTypeDesc MTD_void_ObjectOutputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectOutputStream); 429 private static final MethodTypeDesc MTD_void_ObjectInputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectInputStream); 430 431 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; 432 private static final String NAME_METHOD_READ_OBJECT = "readObject"; 433 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; 434 435 static final ClassDesc CD_NotSerializableException = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/NotSerializableException;"); 436 static final MethodTypeDesc MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION = MethodTypeDescImpl.ofValidated(CD_void, CD_String); 437 static final MethodTypeDesc MTD_CTOR_SERIALIZED_LAMBDA = MethodTypeDescImpl.ofValidated(CD_void, 438 CD_Class, CD_String, CD_String, CD_String, CD_int, CD_String, CD_String, CD_String, CD_String, ConstantUtils.CD_Object_array); 439 440 } 441 442 /** 443 * Generate a writeReplace method that supports serialization 444 */ 445 private void generateSerializationFriendlyMethods(ClassBuilder clb) { 446 clb.withMethodBody(SerializationSupport.NAME_METHOD_WRITE_REPLACE, SerializationSupport.MTD_Object, ACC_PRIVATE | ACC_FINAL, 447 new Consumer<>() { 448 @Override 449 public void accept(CodeBuilder cob) { 450 cob.new_(SerializationSupport.CD_SerializedLambda) 451 .dup() 452 .ldc(ClassDesc.ofInternalName(sanitizedTargetClassName(targetClass))) 453 .ldc(factoryType.returnType().getName().replace('.', '/')) 454 .ldc(interfaceMethodName) 455 .ldc(interfaceMethodType.toMethodDescriptorString()) 456 .ldc(implInfo.getReferenceKind()) 457 .ldc(implInfo.getDeclaringClass().getName().replace('.', '/')) 458 .ldc(implInfo.getName()) 459 .ldc(implInfo.getMethodType().toMethodDescriptorString()) 460 .ldc(dynamicMethodType.toMethodDescriptorString()) 461 .loadConstant(argDescs.length) 462 .anewarray(CD_Object); 463 for (int i = 0; i < argDescs.length; i++) { 464 cob.dup() 465 .loadConstant(i) 466 .aload(0) 467 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 468 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 469 cob.aastore(); 470 } 471 cob.invokespecial(SerializationSupport.CD_SerializedLambda, INIT_NAME, 472 SerializationSupport.MTD_CTOR_SERIALIZED_LAMBDA) 473 .areturn(); 474 } 475 }); 476 } 477 478 /** 479 * Generate a readObject/writeObject method that is hostile to serialization 480 */ 481 private void generateSerializationHostileMethods(ClassBuilder clb) { 482 var hostileMethod = new Consumer<MethodBuilder>() { 483 @Override 484 public void accept(MethodBuilder mb) { 485 ConstantPoolBuilder cp = mb.constantPool(); 486 ClassEntry nseCE = cp.classEntry(SerializationSupport.CD_NotSerializableException); 487 mb.with(ExceptionsAttribute.of(nseCE)) 488 .withCode(new Consumer<CodeBuilder>() { 489 @Override 490 public void accept(CodeBuilder cob) { 491 cob.new_(nseCE) 492 .dup() 493 .ldc("Non-serializable lambda") 494 .invokespecial(cp.methodRefEntry(nseCE, cp.nameAndTypeEntry(INIT_NAME, 495 SerializationSupport.MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION))) 496 .athrow(); 497 } 498 }); 499 } 500 }; 501 clb.withMethod(SerializationSupport.NAME_METHOD_WRITE_OBJECT, SerializationSupport.MTD_void_ObjectOutputStream, 502 ACC_PRIVATE + ACC_FINAL, hostileMethod); 503 clb.withMethod(SerializationSupport.NAME_METHOD_READ_OBJECT, SerializationSupport.MTD_void_ObjectInputStream, 504 ACC_PRIVATE + ACC_FINAL, hostileMethod); 505 } 506 507 /** 508 * This method generates a method body which calls the lambda implementation 509 * method, converting arguments, as needed. 510 */ 511 Consumer<CodeBuilder> forwardingMethod(MethodType methodType) { 512 return new Consumer<>() { 513 @Override 514 public void accept(CodeBuilder cob) { 515 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 516 cob.new_(implMethodClassDesc) 517 .dup(); 518 } 519 if (useImplMethodHandle) { 520 ConstantPoolBuilder cp = cob.constantPool(); 521 cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA), List.of()), 522 cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle))); 523 } 524 for (int i = 0; i < argDescs.length; i++) { 525 cob.aload(0) 526 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 527 } 528 529 convertArgumentTypes(cob, methodType); 530 531 if (useImplMethodHandle) { 532 MethodType mtype = implInfo.getMethodType(); 533 if (implKind != MethodHandleInfo.REF_invokeStatic) { 534 mtype = mtype.insertParameterTypes(0, implClass); 535 } 536 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(mtype)); 537 } else { 538 // Invoke the method we want to forward to 539 cob.invoke(invocationOpcode(), implMethodClassDesc, implMethodName, implMethodDesc, implClass.isInterface()); 540 } 541 // Convert the return value (if any) and return it 542 // Note: if adapting from non-void to void, the 'return' 543 // instruction will pop the unneeded result 544 Class<?> implReturnClass = implMethodType.returnType(); 545 Class<?> samReturnClass = methodType.returnType(); 546 TypeConvertingMethodAdapter.convertType(cob, implReturnClass, samReturnClass, samReturnClass); 547 cob.return_(TypeKind.from(samReturnClass)); 548 } 549 }; 550 } 551 552 private void convertArgumentTypes(CodeBuilder cob, MethodType samType) { 553 int samParametersLength = samType.parameterCount(); 554 int captureArity = factoryType.parameterCount(); 555 for (int i = 0; i < samParametersLength; i++) { 556 Class<?> argType = samType.parameterType(i); 557 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); 558 TypeConvertingMethodAdapter.convertType(cob, argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i)); 559 } 560 } 561 562 /* 563 * LoadableDescriptors attribute builder 564 */ 565 static class LoadableDescriptorsAttributeBuilder { 566 private final Set<String> loadableDescriptors = new HashSet<>(); 567 LoadableDescriptorsAttributeBuilder(Class<?> targetClass) { 568 if (requiresLoadableDescriptors(targetClass)) { 569 loadableDescriptors.add(targetClass.descriptorString()); 570 } 571 } 572 573 /* 574 * Add the value types referenced in the given MethodType. 575 */ 576 LoadableDescriptorsAttributeBuilder add(MethodType mt) { 577 // parameter types 578 for (Class<?> paramType : mt.ptypes()) { 579 if (requiresLoadableDescriptors(paramType)) { 580 loadableDescriptors.add(paramType.descriptorString()); 581 } 582 } 583 // return type 584 if (requiresLoadableDescriptors(mt.returnType())) { 585 loadableDescriptors.add(mt.returnType().descriptorString()); 586 } 587 return this; 588 } 589 590 LoadableDescriptorsAttributeBuilder add(MethodType... mtypes) { 591 for (MethodType mt : mtypes) { 592 add(mt); 593 } 594 return this; 595 } 596 597 boolean requiresLoadableDescriptors(Class<?> cls) { 598 return ValueClass.isConcreteValueClass(cls); 599 } 600 601 boolean isEmpty() { 602 return loadableDescriptors.isEmpty(); 603 } 604 605 void build(ClassBuilder clb) { 606 if (!isEmpty()) { 607 List<Utf8Entry> lds = new ArrayList<Utf8Entry>(loadableDescriptors.size()); 608 for (String ld : loadableDescriptors) { 609 lds.add(clb.constantPool().utf8Entry(ld)); 610 } 611 clb.with(LoadableDescriptorsAttribute.of(lds)); 612 } 613 } 614 } 615 616 /** 617 * Generate LoadableDescriptors attribute if it references any value class 618 */ 619 private void generateLoadableDescriptors(ClassBuilder clb) { 620 LoadableDescriptorsAttributeBuilder builder = new LoadableDescriptorsAttributeBuilder(targetClass); 621 builder.add(factoryType) 622 .add(interfaceMethodType) 623 .add(implMethodType) 624 .add(dynamicMethodType) 625 .add(altMethods) 626 .build(clb); 627 } 628 629 private Opcode invocationOpcode() throws InternalError { 630 return switch (implKind) { 631 case MethodHandleInfo.REF_invokeStatic -> Opcode.INVOKESTATIC; 632 case MethodHandleInfo.REF_newInvokeSpecial -> Opcode.INVOKESPECIAL; 633 case MethodHandleInfo.REF_invokeVirtual -> Opcode.INVOKEVIRTUAL; 634 case MethodHandleInfo.REF_invokeInterface -> Opcode.INVOKEINTERFACE; 635 case MethodHandleInfo.REF_invokeSpecial -> Opcode.INVOKESPECIAL; 636 default -> throw new InternalError("Unexpected invocation kind: " + implKind); 637 }; 638 } 639 640 static ClassDesc implClassDesc(Class<?> cls) { 641 return cls.isHidden() ? null : ConstantUtils.referenceClassDesc(cls.descriptorString()); 642 } 643 644 static ClassDesc classDesc(Class<?> cls) { 645 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 646 : ConstantUtils.referenceClassDesc(cls.descriptorString()); 647 } 648 649 static MethodTypeDesc methodDesc(MethodType mt) { 650 var params = new ClassDesc[mt.parameterCount()]; 651 for (int i = 0; i < params.length; i++) { 652 params[i] = classDesc(mt.parameterType(i)); 653 } 654 return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params); 655 } 656 }