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.FieldBuilder; 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.DynamicConstantDesc; 43 import java.lang.constant.MethodTypeDesc; 44 import java.lang.reflect.Modifier; 45 import java.util.LinkedHashSet; 46 import java.util.List; 47 import java.util.Set; 48 import java.util.function.Consumer; 49 50 import static java.lang.classfile.ClassFile.*; 51 import java.lang.classfile.attribute.ExceptionsAttribute; 52 import java.lang.classfile.constantpool.ClassEntry; 53 import java.lang.classfile.constantpool.ConstantPoolBuilder; 54 import java.lang.classfile.constantpool.MethodRefEntry; 55 import static java.lang.constant.ConstantDescs.*; 56 import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS; 57 import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK; 58 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; 59 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; 60 import static java.lang.invoke.MethodType.methodType; 61 import jdk.internal.constant.ConstantUtils; 62 import jdk.internal.constant.MethodTypeDescImpl; 63 import jdk.internal.constant.ReferenceClassDescImpl; 64 import sun.invoke.util.Wrapper; 65 66 /** 67 * Lambda metafactory implementation which dynamically creates an 68 * inner-class-like class per lambda callsite. 69 * 70 * @see LambdaMetafactory 71 */ 72 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { 73 private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$"; 74 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 75 private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC; 76 77 // For dumping generated classes to disk, for debugging purposes 78 private static final ClassFileDumper lambdaProxyClassFileDumper; 79 80 private static final boolean disableEagerInitialization; 81 82 static { 83 // To dump the lambda proxy classes, set this system property: 84 // -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles 85 // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true 86 final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles"; 87 lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, "DUMP_LAMBDA_PROXY_CLASS_FILES"); 88 89 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization"; 90 disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey); 91 } 92 93 // See context values in AbstractValidatingLambdaMetafactory 94 private final ClassDesc implMethodClassDesc; // Name of type containing implementation "CC" 95 private final String implMethodName; // Name of implementation method "impl" 96 private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" 97 private final MethodType constructorType; // Generated class constructor type "(CC)void" 98 private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void" 99 private final String[] argNames; // Generated names for the constructor arguments 100 private final ClassDesc[] argDescs; // Type descriptors for the constructor arguments 101 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" 102 private final ConstantPoolBuilder pool = ConstantPoolBuilder.of(); 103 private final ClassEntry lambdaClassEntry; // Class entry for the generated class "X$$Lambda$1" 104 private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation 105 106 /** 107 * General meta-factory constructor, supporting both standard cases and 108 * allowing for uncommon options such as serialization or bridging. 109 * 110 * @param caller Stacked automatically by VM; represents a lookup context 111 * with the accessibility privileges of the caller. 112 * @param factoryType Stacked automatically by VM; the signature of the 113 * invoked method, which includes the expected static 114 * type of the returned lambda object, and the static 115 * types of the captured arguments for the lambda. In 116 * the event that the implementation method is an 117 * instance method, the first argument in the invocation 118 * signature will correspond to the receiver. 119 * @param interfaceMethodName Name of the method in the functional interface to 120 * which the lambda or method reference is being 121 * converted, represented as a String. 122 * @param interfaceMethodType Type of the method in the functional interface to 123 * which the lambda or method reference is being 124 * converted, represented as a MethodType. 125 * @param implementation The implementation method which should be called (with 126 * suitable adaptation of argument types, return types, 127 * and adjustment for captured arguments) when methods of 128 * the resulting functional interface instance are invoked. 129 * @param dynamicMethodType The signature of the primary functional 130 * interface method after type variables are 131 * substituted with their instantiation from 132 * the capture site 133 * @param isSerializable Should the lambda be made serializable? If set, 134 * either the target type or one of the additional SAM 135 * types must extend {@code Serializable}. 136 * @param altInterfaces Additional interfaces which the lambda object 137 * should implement. 138 * @param altMethods Method types for additional signatures to be 139 * implemented by invoking the implementation method 140 * @throws LambdaConversionException If any of the meta-factory protocol 141 * invariants are violated 142 * @throws SecurityException If a security manager is present, and it 143 * <a href="MethodHandles.Lookup.html#secmgr">denies access</a> 144 * from {@code caller} to the package of {@code implementation}. 145 */ 146 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 147 MethodType factoryType, 148 String interfaceMethodName, 149 MethodType interfaceMethodType, 150 MethodHandle implementation, 151 MethodType dynamicMethodType, 152 boolean isSerializable, 153 Class<?>[] altInterfaces, 154 MethodType[] altMethods) 155 throws LambdaConversionException { 156 super(caller, factoryType, interfaceMethodName, interfaceMethodType, 157 implementation, dynamicMethodType, 158 isSerializable, altInterfaces, altMethods); 159 implMethodClassDesc = implClassDesc(implClass); 160 implMethodName = implInfo.getName(); 161 implMethodDesc = methodDesc(implInfo.getMethodType()); 162 constructorType = factoryType.changeReturnType(Void.TYPE); 163 lambdaClassName = lambdaClassName(targetClass); 164 lambdaClassEntry = pool.classEntry(ReferenceClassDescImpl.ofValidated(ConstantUtils.concat("L", lambdaClassName, ";"))); 165 // If the target class invokes a protected method inherited from a 166 // superclass in a different package, or does 'invokespecial', the 167 // lambda class has no access to the resolved method, or does 168 // 'invokestatic' on a hidden class which cannot be resolved by name. 169 // Instead, we need to pass the live implementation method handle to 170 // the proxy class to invoke directly. (javac prefers to avoid this 171 // situation by generating bridges in the target class) 172 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) && 173 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) || 174 implKind == MethodHandleInfo.REF_invokeSpecial || 175 implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden(); 176 int parameterCount = factoryType.parameterCount(); 177 if (parameterCount > 0) { 178 argNames = new String[parameterCount]; 179 argDescs = new ClassDesc[parameterCount]; 180 for (int i = 0; i < parameterCount; i++) { 181 argNames[i] = "arg$" + (i + 1); 182 argDescs[i] = classDesc(factoryType.parameterType(i)); 183 } 184 } else { 185 argNames = EMPTY_STRING_ARRAY; 186 argDescs = EMPTY_CLASSDESC_ARRAY; 187 } 188 constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); 189 } 190 191 private static String lambdaClassName(Class<?> targetClass) { 192 String name = targetClass.getName(); 193 if (targetClass.isHidden()) { 194 // use the original class name 195 name = name.replace('/', '_'); 196 } 197 return name.replace('.', '/').concat("$$Lambda"); 198 } 199 200 /** 201 * Build the CallSite. Generate a class file which implements the functional 202 * interface, define the class, if there are no parameters create an instance 203 * of the class which the CallSite will return, otherwise, generate handles 204 * which will call the class' constructor. 205 * 206 * @return a CallSite, which, when invoked, will return an instance of the 207 * functional interface 208 * @throws LambdaConversionException If properly formed functional interface 209 * is not found 210 */ 211 @Override 212 CallSite buildCallSite() throws LambdaConversionException { 213 final Class<?> innerClass = spinInnerClass(); 214 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 215 try { 216 return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, 217 factoryType.returnType())); 218 } catch (ReflectiveOperationException e) { 219 throw new LambdaConversionException( 220 "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e); 221 } 222 } else { 223 try { 224 MethodHandle mh = caller.findConstructor(innerClass, constructorType); 225 if (factoryType.parameterCount() == 0) { 226 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance 227 Object inst = mh.invokeBasic(); 228 return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst)); 229 } else { 230 return new ConstantCallSite(mh.asType(factoryType)); 231 } 232 } catch (ReflectiveOperationException e) { 233 throw new LambdaConversionException("Exception finding constructor", e); 234 } catch (Throwable e) { 235 throw new LambdaConversionException("Exception instantiating lambda object", e); 236 } 237 } 238 } 239 240 /** 241 * Spins the lambda proxy class. 242 * 243 * This first checks if a lambda proxy class can be loaded from CDS archive. 244 * Otherwise, generate the lambda proxy class. If CDS dumping is enabled, it 245 * registers the lambda proxy class for including into the CDS archive. 246 */ 247 private Class<?> spinInnerClass() throws LambdaConversionException { 248 // CDS does not handle disableEagerInitialization or useImplMethodHandle 249 if (!disableEagerInitialization && !useImplMethodHandle) { 250 if (CDS.isUsingArchive()) { 251 // load from CDS archive if present 252 Class<?> innerClass = LambdaProxyClassArchive.find(targetClass, 253 interfaceMethodName, 254 factoryType, 255 interfaceMethodType, 256 implementation, 257 dynamicMethodType, 258 isSerializable, 259 altInterfaces, 260 altMethods); 261 if (innerClass != null) return innerClass; 262 } 263 264 // include lambda proxy class in CDS archive at dump time 265 if (CDS.isDumpingArchive()) { 266 Class<?> innerClass = generateInnerClass(); 267 LambdaProxyClassArchive.register(targetClass, 268 interfaceMethodName, 269 factoryType, 270 interfaceMethodType, 271 implementation, 272 dynamicMethodType, 273 isSerializable, 274 altInterfaces, 275 altMethods, 276 innerClass); 277 return innerClass; 278 } 279 280 } 281 return generateInnerClass(); 282 } 283 284 /** 285 * Generate a class file which implements the functional 286 * interface, define and return the class. 287 * 288 * @return a Class which implements the functional interface 289 * @throws LambdaConversionException If properly formed functional interface 290 * is not found 291 */ 292 private Class<?> generateInnerClass() throws LambdaConversionException { 293 List<ClassDesc> interfaces; 294 ClassDesc interfaceDesc = classDesc(interfaceClass); 295 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass); 296 if (altInterfaces.length == 0) { 297 interfaces = List.of(interfaceDesc); 298 } else { 299 // Assure no duplicate interfaces (ClassFormatError) 300 Set<ClassDesc> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1); 301 itfs.add(interfaceDesc); 302 for (Class<?> i : altInterfaces) { 303 itfs.add(classDesc(i)); 304 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i); 305 } 306 interfaces = List.copyOf(itfs); 307 } 308 final boolean finalAccidentallySerializable = accidentallySerializable; 309 final byte[] classBytes = ClassFile.of().build(lambdaClassEntry, pool, new Consumer<ClassBuilder>() { 310 @Override 311 public void accept(ClassBuilder clb) { 312 clb.withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC) 313 .withInterfaceSymbols(interfaces); 314 // Generate final fields to be filled in by constructor 315 for (int i = 0; i < argDescs.length; i++) { 316 clb.withField(argNames[i], argDescs[i], ACC_PRIVATE | ACC_FINAL); 317 } 318 319 generateConstructor(clb); 320 321 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 322 generateClassInitializer(clb); 323 } 324 325 // Forward the SAM method 326 clb.withMethodBody(interfaceMethodName, 327 methodDesc(interfaceMethodType), 328 ACC_PUBLIC, 329 forwardingMethod(interfaceMethodType)); 330 331 // Forward the bridges 332 if (altMethods != null) { 333 for (MethodType mt : altMethods) { 334 clb.withMethodBody(interfaceMethodName, 335 methodDesc(mt), 336 ACC_PUBLIC | ACC_BRIDGE, 337 forwardingMethod(mt)); 338 } 339 } 340 341 if (isSerializable) 342 generateSerializationFriendlyMethods(clb); 343 else if (finalAccidentallySerializable) 344 generateSerializationHostileMethods(clb); 345 } 346 }); 347 348 // Define the generated class in this VM. 349 350 try { 351 // this class is linked at the indy callsite; so define a hidden nestmate 352 var classdata = useImplMethodHandle? implementation : null; 353 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK) 354 .defineClass(!disableEagerInitialization, classdata); 355 356 } catch (Throwable t) { 357 throw new InternalError(t); 358 } 359 } 360 361 /** 362 * Generate a static field and a static initializer that sets this field to an instance of the lambda 363 */ 364 private void generateClassInitializer(ClassBuilder clb) { 365 ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType()); 366 367 // Generate the static final field that holds the lambda singleton 368 clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 369 370 // Instantiate the lambda and store it to the static final field 371 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() { 372 @Override 373 public void accept(CodeBuilder cob) { 374 assert factoryType.parameterCount() == 0; 375 cob.new_(lambdaClassEntry) 376 .dup() 377 .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc))) 378 .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor))) 379 .return_(); 380 } 381 }); 382 } 383 384 /** 385 * Generate the constructor for the class 386 */ 387 private void generateConstructor(ClassBuilder clb) { 388 // Generate constructor 389 clb.withMethodBody(INIT_NAME, constructorTypeDesc, ACC_PRIVATE, 390 new Consumer<>() { 391 @Override 392 public void accept(CodeBuilder cob) { 393 cob.aload(0) 394 .invokespecial(CD_Object, INIT_NAME, MTD_void); 395 int parameterCount = factoryType.parameterCount(); 396 for (int i = 0; i < parameterCount; i++) { 397 cob.aload(0); 398 Class<?> argType = factoryType.parameterType(i); 399 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); 400 cob.putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[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 = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;"); 410 private static final ClassDesc CD_ObjectOutputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectOutputStream;"); 411 private static final ClassDesc CD_ObjectInputStream = ReferenceClassDescImpl.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 = ReferenceClassDescImpl.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, ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;")); 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(argNames[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 < argNames.length; i++) { 510 cob.aload(0) 511 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[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 : ReferenceClassDescImpl.ofValidated(cls.descriptorString()); 560 } 561 562 static ClassDesc classDesc(Class<?> cls) { 563 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 564 : ReferenceClassDescImpl.ofValidated(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 }