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