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