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