1 /* 2 * Copyright (c) 2012, 2023, 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. Instead, we need 179 // to pass the live implementation method handle to the proxy class 180 // to invoke directly. (javac prefers to avoid this situation by 181 // generating bridges in the target class) 182 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) && 183 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) || 184 implKind == H_INVOKESPECIAL; 185 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 186 int parameterCount = factoryType.parameterCount(); 187 if (parameterCount > 0) { 188 argNames = new String[parameterCount]; 189 argDescs = new String[parameterCount]; 190 for (int i = 0; i < parameterCount; i++) { 191 argNames[i] = "arg$" + (i + 1); 192 argDescs[i] = BytecodeDescriptor.unparse(factoryType.parameterType(i)); 193 } 194 } else { 195 argNames = argDescs = EMPTY_STRING_ARRAY; 196 } 197 } 198 199 private static String lambdaClassName(Class<?> targetClass) { 200 String name = targetClass.getName(); 201 if (targetClass.isHidden()) { 202 // use the original class name 203 name = name.replace('/', '_'); 204 } 205 return name.replace('.', '/') + "$$Lambda"; 206 } 207 208 /** 209 * Build the CallSite. Generate a class file which implements the functional 210 * interface, define the class, if there are no parameters create an instance 211 * of the class which the CallSite will return, otherwise, generate handles 212 * which will call the class' constructor. 213 * 214 * @return a CallSite, which, when invoked, will return an instance of the 215 * functional interface 216 * @throws LambdaConversionException If properly formed functional interface 217 * is not found 218 */ 219 @Override 220 CallSite buildCallSite() throws LambdaConversionException { 221 final Class<?> innerClass = spinInnerClass(); 222 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 223 try { 224 return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, 225 factoryType.returnType())); 226 } catch (ReflectiveOperationException e) { 227 throw new LambdaConversionException( 228 "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e); 229 } 230 } else { 231 try { 232 MethodHandle mh = caller.findConstructor(innerClass, constructorType); 233 if (factoryType.parameterCount() == 0) { 234 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance 235 Object inst = mh.asType(methodType(Object.class)).invokeExact(); 236 return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst)); 237 } else { 238 return new ConstantCallSite(mh.asType(factoryType)); 239 } 240 } catch (ReflectiveOperationException e) { 241 throw new LambdaConversionException("Exception finding constructor", e); 242 } catch (Throwable e) { 243 throw new LambdaConversionException("Exception instantiating lambda object", e); 244 } 245 } 246 } 247 248 /** 249 * Spins the lambda proxy class. 250 * 251 * This first checks if a lambda proxy class can be loaded from CDS archive. 252 * Otherwise, generate the lambda proxy class. If CDS dumping is enabled, it 253 * registers the lambda proxy class for including into the CDS archive. 254 */ 255 private Class<?> spinInnerClass() throws LambdaConversionException { 256 // CDS does not handle disableEagerInitialization or useImplMethodHandle 257 if (!disableEagerInitialization && !useImplMethodHandle) { 258 if (CDS.isSharingEnabled()) { 259 // load from CDS archive if present 260 Class<?> innerClass = LambdaProxyClassArchive.find(targetClass, 261 interfaceMethodName, 262 factoryType, 263 interfaceMethodType, 264 implementation, 265 dynamicMethodType, 266 isSerializable, 267 altInterfaces, 268 altMethods); 269 if (innerClass != null) return innerClass; 270 } 271 272 // include lambda proxy class in CDS archive at dump time 273 if (CDS.isDumpingArchive()) { 274 Class<?> innerClass = generateInnerClass(); 275 LambdaProxyClassArchive.register(targetClass, 276 interfaceMethodName, 277 factoryType, 278 interfaceMethodType, 279 implementation, 280 dynamicMethodType, 281 isSerializable, 282 altInterfaces, 283 altMethods, 284 innerClass); 285 return innerClass; 286 } 287 288 } 289 return generateInnerClass(); 290 } 291 292 /** 293 * Generate a class file which implements the functional 294 * interface, define and return the class. 295 * 296 * @return a Class which implements the functional interface 297 * @throws LambdaConversionException If properly formed functional interface 298 * is not found 299 */ 300 private Class<?> generateInnerClass() throws LambdaConversionException { 301 String[] interfaceNames; 302 String interfaceName = interfaceClass.getName().replace('.', '/'); 303 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass); 304 if (altInterfaces.length == 0) { 305 interfaceNames = new String[]{interfaceName}; 306 } else { 307 // Assure no duplicate interfaces (ClassFormatError) 308 Set<String> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1); 309 itfs.add(interfaceName); 310 for (Class<?> i : altInterfaces) { 311 itfs.add(i.getName().replace('.', '/')); 312 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i); 313 } 314 interfaceNames = itfs.toArray(new String[itfs.size()]); 315 } 316 317 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, 318 lambdaClassName, null, 319 JAVA_LANG_OBJECT, interfaceNames); 320 321 // Generate final fields to be filled in by constructor 322 for (int i = 0; i < argDescs.length; i++) { 323 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, 324 argNames[i], 325 argDescs[i], 326 null, null); 327 fv.visitEnd(); 328 } 329 330 generateConstructor(); 331 332 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 333 generateClassInitializer(); 334 } 335 336 // Forward the SAM method 337 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName, 338 interfaceMethodType.toMethodDescriptorString(), null, null); 339 new ForwardingMethodGenerator(mv).generate(interfaceMethodType); 340 341 // Forward the altMethods 342 if (altMethods != null) { 343 for (MethodType mt : altMethods) { 344 mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName, 345 mt.toMethodDescriptorString(), null, null); 346 new ForwardingMethodGenerator(mv).generate(mt); 347 } 348 } 349 350 if (isSerializable) 351 generateSerializationFriendlyMethods(); 352 else if (accidentallySerializable) 353 generateSerializationHostileMethods(); 354 355 cw.visitEnd(); 356 357 // Define the generated class in this VM. 358 359 final byte[] classBytes = cw.toByteArray(); 360 try { 361 // this class is linked at the indy callsite; so define a hidden nestmate 362 var classdata = useImplMethodHandle? implementation : null; 363 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper) 364 .defineClass(!disableEagerInitialization, classdata); 365 366 } catch (Throwable t) { 367 throw new InternalError(t); 368 } 369 } 370 371 /** 372 * Generate a static field and a static initializer that sets this field to an instance of the lambda 373 */ 374 private void generateClassInitializer() { 375 String lambdaTypeDescriptor = factoryType.returnType().descriptorString(); 376 377 // Generate the static final field that holds the lambda singleton 378 FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, 379 LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null); 380 fv.visitEnd(); 381 382 // Instantiate the lambda and store it to the static final field 383 MethodVisitor clinit = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 384 clinit.visitCode(); 385 386 clinit.visitTypeInsn(NEW, lambdaClassName); 387 clinit.visitInsn(Opcodes.DUP); 388 assert factoryType.parameterCount() == 0; 389 clinit.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false); 390 clinit.visitFieldInsn(PUTSTATIC, lambdaClassName, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor); 391 392 clinit.visitInsn(RETURN); 393 clinit.visitMaxs(-1, -1); 394 clinit.visitEnd(); 395 } 396 397 /** 398 * Generate the constructor for the class 399 */ 400 private void generateConstructor() { 401 // Generate constructor 402 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, 403 constructorType.toMethodDescriptorString(), null, null); 404 ctor.visitCode(); 405 ctor.visitVarInsn(ALOAD, 0); 406 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR, 407 METHOD_DESCRIPTOR_VOID, false); 408 int parameterCount = factoryType.parameterCount(); 409 for (int i = 0, lvIndex = 0; i < parameterCount; i++) { 410 ctor.visitVarInsn(ALOAD, 0); 411 Class<?> argType = factoryType.parameterType(i); 412 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1); 413 lvIndex += getParameterSize(argType); 414 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]); 415 } 416 ctor.visitInsn(RETURN); 417 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored 418 ctor.visitMaxs(-1, -1); 419 ctor.visitEnd(); 420 } 421 422 /** 423 * Generate a writeReplace method that supports serialization 424 */ 425 private void generateSerializationFriendlyMethods() { 426 TypeConvertingMethodAdapter mv 427 = new TypeConvertingMethodAdapter( 428 cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 429 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, 430 null, null)); 431 432 mv.visitCode(); 433 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); 434 mv.visitInsn(DUP); 435 mv.visitLdcInsn(Type.getType(targetClass)); 436 mv.visitLdcInsn(factoryType.returnType().getName().replace('.', '/')); 437 mv.visitLdcInsn(interfaceMethodName); 438 mv.visitLdcInsn(interfaceMethodType.toMethodDescriptorString()); 439 mv.visitLdcInsn(implInfo.getReferenceKind()); 440 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/')); 441 mv.visitLdcInsn(implInfo.getName()); 442 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString()); 443 mv.visitLdcInsn(dynamicMethodType.toMethodDescriptorString()); 444 mv.iconst(argDescs.length); 445 mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT); 446 for (int i = 0; i < argDescs.length; i++) { 447 mv.visitInsn(DUP); 448 mv.iconst(i); 449 mv.visitVarInsn(ALOAD, 0); 450 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); 451 mv.boxIfTypePrimitive(Type.getType(argDescs[i])); 452 mv.visitInsn(AASTORE); 453 } 454 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR, 455 DESCR_CTOR_SERIALIZED_LAMBDA, false); 456 mv.visitInsn(ARETURN); 457 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored 458 mv.visitMaxs(-1, -1); 459 mv.visitEnd(); 460 } 461 462 /** 463 * Generate a readObject/writeObject method that is hostile to serialization 464 */ 465 private void generateSerializationHostileMethods() { 466 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 467 NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT, 468 null, SER_HOSTILE_EXCEPTIONS); 469 mv.visitCode(); 470 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); 471 mv.visitInsn(DUP); 472 mv.visitLdcInsn("Non-serializable lambda"); 473 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, 474 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false); 475 mv.visitInsn(ATHROW); 476 mv.visitMaxs(-1, -1); 477 mv.visitEnd(); 478 479 mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 480 NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT, 481 null, SER_HOSTILE_EXCEPTIONS); 482 mv.visitCode(); 483 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); 484 mv.visitInsn(DUP); 485 mv.visitLdcInsn("Non-serializable lambda"); 486 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, 487 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false); 488 mv.visitInsn(ATHROW); 489 mv.visitMaxs(-1, -1); 490 mv.visitEnd(); 491 } 492 493 /** 494 * This class generates a method body which calls the lambda implementation 495 * method, converting arguments, as needed. 496 */ 497 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { 498 499 ForwardingMethodGenerator(MethodVisitor mv) { 500 super(mv); 501 } 502 503 void generate(MethodType methodType) { 504 visitCode(); 505 506 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 507 visitTypeInsn(NEW, implMethodClassName); 508 visitInsn(DUP); 509 } 510 if (useImplMethodHandle) { 511 visitLdcInsn(implMethodCondy); 512 } 513 for (int i = 0; i < argNames.length; i++) { 514 visitVarInsn(ALOAD, 0); 515 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); 516 } 517 518 convertArgumentTypes(methodType); 519 520 if (useImplMethodHandle) { 521 MethodType mtype = implInfo.getMethodType(); 522 if (implKind != MethodHandleInfo.REF_invokeStatic) { 523 mtype = mtype.insertParameterTypes(0, implClass); 524 } 525 visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", 526 "invokeExact", mtype.descriptorString(), false); 527 } else { 528 // Invoke the method we want to forward to 529 visitMethodInsn(invocationOpcode(), implMethodClassName, 530 implMethodName, implMethodDesc, 531 implClass.isInterface()); 532 } 533 // Convert the return value (if any) and return it 534 // Note: if adapting from non-void to void, the 'return' 535 // instruction will pop the unneeded result 536 Class<?> implReturnClass = implMethodType.returnType(); 537 Class<?> samReturnClass = methodType.returnType(); 538 convertType(implReturnClass, samReturnClass, samReturnClass); 539 visitInsn(getReturnOpcode(samReturnClass)); 540 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored 541 visitMaxs(-1, -1); 542 visitEnd(); 543 } 544 545 private void convertArgumentTypes(MethodType samType) { 546 int lvIndex = 0; 547 int samParametersLength = samType.parameterCount(); 548 int captureArity = factoryType.parameterCount(); 549 for (int i = 0; i < samParametersLength; i++) { 550 Class<?> argType = samType.parameterType(i); 551 visitVarInsn(getLoadOpcode(argType), lvIndex + 1); 552 lvIndex += getParameterSize(argType); 553 convertType(argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i)); 554 } 555 } 556 557 private int invocationOpcode() throws InternalError { 558 return switch (implKind) { 559 case MethodHandleInfo.REF_invokeStatic -> INVOKESTATIC; 560 case MethodHandleInfo.REF_newInvokeSpecial -> INVOKESPECIAL; 561 case MethodHandleInfo.REF_invokeVirtual -> INVOKEVIRTUAL; 562 case MethodHandleInfo.REF_invokeInterface -> INVOKEINTERFACE; 563 case MethodHandleInfo.REF_invokeSpecial -> INVOKESPECIAL; 564 default -> throw new InternalError("Unexpected invocation kind: " + implKind); 565 }; 566 } 567 } 568 569 static int getParameterSize(Class<?> c) { 570 if (c == Void.TYPE) { 571 return 0; 572 } else if (c == Long.TYPE || c == Double.TYPE) { 573 return 2; 574 } 575 return 1; 576 } 577 578 static int getLoadOpcode(Class<?> c) { 579 if(c == Void.TYPE) { 580 throw new InternalError("Unexpected void type of load opcode"); 581 } 582 return ILOAD + getOpcodeOffset(c); 583 } 584 585 static int getReturnOpcode(Class<?> c) { 586 if(c == Void.TYPE) { 587 return RETURN; 588 } 589 return IRETURN + getOpcodeOffset(c); 590 } 591 592 private static int getOpcodeOffset(Class<?> c) { 593 if (c.isPrimitive()) { 594 if (c == Long.TYPE) { 595 return 1; 596 } else if (c == Float.TYPE) { 597 return 2; 598 } else if (c == Double.TYPE) { 599 return 3; 600 } 601 return 0; 602 } else { 603 return 4; 604 } 605 } 606 607 }