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