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