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