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