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