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