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