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