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.util.ClassFileDumper; 30 import sun.invoke.util.VerifyAccess; 31 import sun.security.action.GetBooleanAction; 32 33 import java.io.Serializable; 34 import java.lang.classfile.ClassBuilder; 35 import java.lang.classfile.ClassFile; 36 import java.lang.classfile.CodeBuilder; 37 import java.lang.classfile.MethodBuilder; 38 import java.lang.classfile.Opcode; 39 import java.lang.classfile.TypeKind; 40 import java.lang.classfile.constantpool.MethodHandleEntry; 41 import java.lang.classfile.constantpool.NameAndTypeEntry; 42 import java.lang.constant.ClassDesc; 43 import java.lang.constant.MethodTypeDesc; 44 import java.lang.invoke.MethodHandles.Lookup; 45 import java.lang.module.Configuration; 46 import java.lang.module.ModuleFinder; 47 import java.lang.reflect.Modifier; 48 import java.util.LinkedHashSet; 49 import java.util.List; 50 import java.util.Set; 51 import java.util.function.Consumer; 52 53 import static java.lang.classfile.ClassFile.*; 54 import java.lang.classfile.attribute.ExceptionsAttribute; 55 import java.lang.classfile.constantpool.ClassEntry; 56 import java.lang.classfile.constantpool.ConstantPoolBuilder; 57 58 import static java.lang.constant.ConstantDescs.*; 59 import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS; 60 import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK; 61 import static java.lang.invoke.MethodType.methodType; 62 import jdk.internal.constant.ConstantUtils; 63 import jdk.internal.constant.MethodTypeDescImpl; 64 import jdk.internal.constant.ReferenceClassDescImpl; 65 import sun.invoke.util.Wrapper; 66 67 /** 68 * Lambda metafactory implementation which dynamically creates an 69 * inner-class-like class per lambda callsite. 70 * 71 * @see LambdaMetafactory 72 */ 73 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { 74 private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$"; 75 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 76 private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC; 77 78 // Static builders to avoid lambdas 79 record MethodBody(Consumer<CodeBuilder> code) implements Consumer<MethodBuilder> { 80 @Override 81 public void accept(MethodBuilder mb) { 82 mb.withCode(code); 83 } 84 }; 85 86 // For dumping generated classes to disk, for debugging purposes 87 private static final ClassFileDumper lambdaProxyClassFileDumper; 88 89 private static final boolean disableEagerInitialization; 90 91 private static final String NAME_METHOD_QUOTED = "__internal_quoted"; 92 private static final String quotedInstanceFieldName = "quoted"; 93 94 static { 95 // To dump the lambda proxy classes, set this system property: 96 // -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles 97 // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true 98 final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles"; 99 lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, "DUMP_LAMBDA_PROXY_CLASS_FILES"); 100 101 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization"; 102 disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey); 103 } 104 105 // See context values in AbstractValidatingLambdaMetafactory 106 private final ClassDesc implMethodClassDesc; // Name of type containing implementation "CC" 107 private final String implMethodName; // Name of implementation method "impl" 108 private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" 109 private final MethodType constructorType; // Generated class constructor type "(CC)void" 110 private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void" 111 private final String[] argNames; // Generated names for the constructor arguments 112 private final ClassDesc[] argDescs; // Type descriptors for the constructor arguments 113 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" 114 private final ConstantPoolBuilder pool = ConstantPoolBuilder.of(); 115 private final ClassEntry lambdaClassEntry; // Class entry for the generated class "X$$Lambda$1" 116 private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation 117 118 /** 119 * General meta-factory constructor, supporting both standard cases and 120 * allowing for uncommon options such as serialization or bridging. 121 * 122 * @param caller Stacked automatically by VM; represents a lookup context 123 * with the accessibility privileges of the caller. 124 * @param factoryType Stacked automatically by VM; the signature of the 125 * invoked method, which includes the expected static 126 * type of the returned lambda object, and the static 127 * types of the captured arguments for the lambda. In 128 * the event that the implementation method is an 129 * instance method, the first argument in the invocation 130 * signature will correspond to the receiver. 131 * @param interfaceMethodName Name of the method in the functional interface to 132 * which the lambda or method reference is being 133 * converted, represented as a String. 134 * @param interfaceMethodType Type of the method in the functional interface to 135 * which the lambda or method reference is being 136 * converted, represented as a MethodType. 137 * @param implementation The implementation method which should be called (with 138 * suitable adaptation of argument types, return types, 139 * and adjustment for captured arguments) when methods of 140 * the resulting functional interface instance are invoked. 141 * @param dynamicMethodType The signature of the primary functional 142 * interface method after type variables are 143 * substituted with their instantiation from 144 * the capture site 145 * @param isSerializable Should the lambda be made serializable? If set, 146 * either the target type or one of the additional SAM 147 * types must extend {@code Serializable}. 148 * @param altInterfaces Additional interfaces which the lambda object 149 * should implement. 150 * @param altMethods Method types for additional signatures to be 151 * implemented by invoking the implementation method 152 * @throws LambdaConversionException If any of the meta-factory protocol 153 * invariants are violated 154 * @throws SecurityException If a security manager is present, and it 155 * <a href="MethodHandles.Lookup.html#secmgr">denies access</a> 156 * from {@code caller} to the package of {@code implementation}. 157 */ 158 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 159 MethodType factoryType, 160 String interfaceMethodName, 161 MethodType interfaceMethodType, 162 MethodHandle implementation, 163 MethodType dynamicMethodType, 164 boolean isSerializable, 165 Class<?>[] altInterfaces, 166 MethodType[] altMethods, 167 MethodHandle reflectiveField) 168 throws LambdaConversionException { 169 super(caller, factoryType, interfaceMethodName, interfaceMethodType, 170 implementation, dynamicMethodType, 171 isSerializable, altInterfaces, altMethods, reflectiveField); 172 implMethodClassDesc = implClassDesc(implClass); 173 implMethodName = implInfo.getName(); 174 implMethodDesc = methodDesc(implInfo.getMethodType()); 175 constructorType = factoryType.changeReturnType(Void.TYPE); 176 lambdaClassName = lambdaClassName(targetClass); 177 lambdaClassEntry = pool.classEntry(ReferenceClassDescImpl.ofValidated(ConstantUtils.concat("L", lambdaClassName, ";"))); 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, or does 181 // 'invokestatic' on a hidden class which cannot be resolved by name. 182 // Instead, we need to pass the live implementation method handle to 183 // the proxy class to invoke directly. (javac prefers to avoid this 184 // situation by generating bridges in the target class) 185 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) && 186 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) || 187 implKind == MethodHandleInfo.REF_invokeSpecial || 188 implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden(); 189 int parameterCount = factoryType.parameterCount(); 190 if (parameterCount > 0) { 191 argNames = new String[parameterCount]; 192 argDescs = new ClassDesc[parameterCount]; 193 for (int i = 0; i < parameterCount; i++) { 194 argNames[i] = "arg$" + (i + 1); 195 argDescs[i] = classDesc(factoryType.parameterType(i)); 196 } 197 } else { 198 argNames = EMPTY_STRING_ARRAY; 199 argDescs = EMPTY_CLASSDESC_ARRAY; 200 } 201 constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); 202 } 203 204 private static String lambdaClassName(Class<?> targetClass) { 205 String name = targetClass.getName(); 206 if (targetClass.isHidden()) { 207 // use the original class name 208 name = name.replace('/', '_'); 209 } 210 return name.replace('.', '/').concat("$$Lambda"); 211 } 212 213 /** 214 * Build the CallSite. Generate a class file which implements the functional 215 * interface, define the class, if there are no parameters create an instance 216 * of the class which the CallSite will return, otherwise, generate handles 217 * which will call the class' constructor. 218 * 219 * @return a CallSite, which, when invoked, will return an instance of the 220 * functional interface 221 * @throws LambdaConversionException If properly formed functional interface 222 * is not found 223 */ 224 @Override 225 CallSite buildCallSite() throws LambdaConversionException { 226 final Class<?> innerClass = spinInnerClass(); 227 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 228 try { 229 return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, 230 factoryType.returnType())); 231 } catch (ReflectiveOperationException e) { 232 throw new LambdaConversionException( 233 "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e); 234 } 235 } else { 236 try { 237 MethodHandle mh = caller.findConstructor(innerClass, constructorType); 238 if (factoryType.parameterCount() == 0) { 239 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance 240 Object inst = mh.invokeBasic(); 241 return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst)); 242 } else { 243 return new ConstantCallSite(mh.asType(factoryType)); 244 } 245 } catch (ReflectiveOperationException e) { 246 throw new LambdaConversionException("Exception finding constructor", e); 247 } catch (Throwable e) { 248 throw new LambdaConversionException("Exception instantiating lambda object", e); 249 } 250 } 251 } 252 253 /** 254 * Spins the lambda proxy class. 255 * 256 * This first checks if a lambda proxy class can be loaded from CDS archive. 257 * Otherwise, generate the lambda proxy class. If CDS dumping is enabled, it 258 * registers the lambda proxy class for including into the CDS archive. 259 */ 260 private Class<?> spinInnerClass() throws LambdaConversionException { 261 // CDS does not handle disableEagerInitialization or useImplMethodHandle 262 if (!disableEagerInitialization && !useImplMethodHandle) { 263 if (CDS.isUsingArchive()) { 264 // load from CDS archive if present 265 Class<?> innerClass = LambdaProxyClassArchive.find(targetClass, 266 interfaceMethodName, 267 factoryType, 268 interfaceMethodType, 269 implementation, 270 dynamicMethodType, 271 isSerializable, 272 altInterfaces, 273 altMethods); 274 if (innerClass != null) return innerClass; 275 } 276 277 // include lambda proxy class in CDS archive at dump time 278 if (CDS.isDumpingArchive()) { 279 Class<?> innerClass = generateInnerClass(); 280 LambdaProxyClassArchive.register(targetClass, 281 interfaceMethodName, 282 factoryType, 283 interfaceMethodType, 284 implementation, 285 dynamicMethodType, 286 isSerializable, 287 altInterfaces, 288 altMethods, 289 innerClass); 290 return innerClass; 291 } 292 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 private Class<?> generateInnerClass() throws LambdaConversionException { 306 List<ClassDesc> interfaces; 307 ClassDesc interfaceDesc = classDesc(interfaceClass); 308 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass); 309 if (altInterfaces.length == 0) { 310 interfaces = List.of(interfaceDesc); 311 } else { 312 // Assure no duplicate interfaces (ClassFormatError) 313 Set<ClassDesc> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1); 314 itfs.add(interfaceDesc); 315 for (Class<?> i : altInterfaces) { 316 itfs.add(classDesc(i)); 317 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i); 318 } 319 interfaces = List.copyOf(itfs); 320 } 321 final boolean finalAccidentallySerializable = accidentallySerializable; 322 final byte[] classBytes = ClassFile.of().build(lambdaClassEntry, pool, new Consumer<ClassBuilder>() { 323 @Override 324 public void accept(ClassBuilder clb) { 325 clb.withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC) 326 .withInterfaceSymbols(interfaces); 327 // Generate final fields to be filled in by constructor 328 for (int i = 0; i < argDescs.length; i++) { 329 clb.withField(argNames[i], argDescs[i], ACC_PRIVATE | ACC_FINAL); 330 } 331 332 // if quotable, generate the field that will hold the value of quoted 333 if (quotableOpGetter != null) { 334 clb.withField(quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted, ACC_PRIVATE + ACC_FINAL); 335 } 336 337 generateConstructor(clb); 338 339 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 340 generateClassInitializer(clb); 341 } 342 343 // Forward the SAM method 344 clb.withMethodBody(interfaceMethodName, 345 methodDesc(interfaceMethodType), 346 ACC_PUBLIC, 347 forwardingMethod(interfaceMethodType)); 348 349 // Forward the bridges 350 if (altMethods != null) { 351 for (MethodType mt : altMethods) { 352 clb.withMethodBody(interfaceMethodName, 353 methodDesc(mt), 354 ACC_PUBLIC | ACC_BRIDGE, 355 forwardingMethod(mt)); 356 } 357 } 358 359 if (isSerializable) 360 generateSerializationFriendlyMethods(clb); 361 else if (finalAccidentallySerializable) 362 generateSerializationHostileMethods(clb); 363 364 if (quotableOpGetter != null) { 365 generateQuotedMethod(clb); 366 } 367 } 368 }); 369 370 // Define the generated class in this VM. 371 372 try { 373 // this class is linked at the indy callsite; so define a hidden nestmate 374 List<?> classdata; 375 if (useImplMethodHandle || quotableOpGetter != null) { 376 classdata = quotableOpGetter == null ? 377 List.of(implementation) : 378 List.of(implementation, quotableOpGetter, CodeReflectionSupport.HANDLE_MAKE_QUOTED); 379 } else { 380 classdata = null; 381 } 382 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK) 383 .defineClass(!disableEagerInitialization, classdata); 384 385 } catch (Throwable t) { 386 throw new InternalError(t); 387 } 388 } 389 390 /** 391 * Generate a static field and a static initializer that sets this field to an instance of the lambda 392 */ 393 private void generateClassInitializer(ClassBuilder clb) { 394 ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType()); 395 396 // Generate the static final field that holds the lambda singleton 397 clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 398 399 // Instantiate the lambda and store it to the static final field 400 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() { 401 @Override 402 public void accept(CodeBuilder cob) { 403 assert factoryType.parameterCount() == 0; 404 cob.new_(lambdaClassEntry) 405 .dup() 406 .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc))) 407 .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor))) 408 .return_(); 409 } 410 }); 411 } 412 413 /** 414 * Generate the constructor for the class 415 */ 416 private void generateConstructor(ClassBuilder clb) { 417 // Generate constructor 418 clb.withMethodBody(INIT_NAME, constructorTypeDesc, ACC_PRIVATE, 419 new Consumer<>() { 420 @Override 421 public void accept(CodeBuilder cob) { 422 cob.aload(0) 423 .invokespecial(CD_Object, INIT_NAME, MTD_void); 424 int parameterCount = factoryType.parameterCount(); 425 for (int i = 0; i < parameterCount; i++) { 426 cob.aload(0); 427 Class<?> argType = factoryType.parameterType(i); 428 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); 429 cob.putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); 430 } 431 if (quotableOpGetter != null) { 432 generateQuotedFieldInitializer(cob); 433 } 434 cob.return_(); 435 } 436 }); 437 } 438 439 private void generateQuotedFieldInitializer(CodeBuilder cob) { 440 ConstantPoolBuilder cp = cob.constantPool(); 441 MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT); 442 NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle); 443 // push the receiver on the stack for operand of put field instruction 444 cob.aload(0) 445 .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(2))), natMH)) 446 // load op string from field 447 .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(1))), natMH)); 448 MethodType mtype = quotableOpGetterInfo.getMethodType(); 449 if (quotableOpGetterInfo.getReferenceKind() != MethodHandleInfo.REF_invokeStatic) { 450 mtype = mtype.insertParameterTypes(0, implClass); 451 } 452 cob.invokevirtual(CD_MethodHandle, "invokeExact", mtype.describeConstable().get()); 453 454 // load captured args in array 455 456 int capturedArity = factoryType.parameterCount(); 457 cob.loadConstant(capturedArity) 458 .anewarray(CD_Object); 459 // initialize quoted captures 460 for (int i = 0; i < capturedArity; i++) { 461 cob.dup() 462 .loadConstant(i) 463 .aload(0) 464 .getfield(lambdaClassEntry.asSymbol(), argNames[i], argDescs[i]); 465 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 466 cob.aastore(); 467 } 468 469 // Create a Quoted from FuncOp and captured args Object[] 470 471 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(CodeReflectionSupport.HANDLE_MAKE_QUOTED.type())) 472 .putfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted); 473 } 474 475 static class CodeReflectionSupport { 476 static final Class<?> QUOTED_CLASS; 477 static final Class<?> QUOTABLE_CLASS; 478 static final MethodHandle HANDLE_MAKE_QUOTED; 479 480 static { 481 try { 482 ModuleLayer layer = codeLayer(); 483 ClassLoader cl = layer.findLoader("jdk.incubator.code"); 484 QUOTED_CLASS = cl.loadClass("jdk.incubator.code.Quoted"); 485 QUOTABLE_CLASS = cl.loadClass("jdk.incubator.code.Quotable"); 486 Class<?> quotedHelper = cl.loadClass("jdk.incubator.code.internal.QuotedHelper"); 487 Class<?> funcOp = cl.loadClass("jdk.incubator.code.op.CoreOp$FuncOp"); 488 MethodHandle makeQuoted = Lookup.IMPL_LOOKUP.findStatic(quotedHelper, "makeQuoted", 489 MethodType.methodType(QUOTED_CLASS, MethodHandles.Lookup.class, funcOp, Object[].class)); 490 HANDLE_MAKE_QUOTED = makeQuoted.bindTo(Lookup.IMPL_LOOKUP); 491 } catch (Throwable ex) { 492 throw new ExceptionInInitializerError(ex); 493 } 494 } 495 496 static ModuleLayer codeLayer() { 497 final ModuleLayer codeLayer; 498 if (ModuleLayer.boot().findModule("jdk.incubator.code").isPresent()) { 499 // we are in an exploded build, so just use the boot layer 500 return ModuleLayer.boot(); 501 } else if (java.lang.module.ModuleFinder.ofSystem().find("jdk.incubator.code").isPresent()) { 502 // the code module is installed, but not in the boot layer, create a new layer which contains it 503 ModuleLayer parent = ModuleLayer.boot(); 504 Configuration cf = parent.configuration() 505 .resolve(ModuleFinder.of(), ModuleFinder.ofSystem(), Set.of("jdk.incubator.code")); 506 ClassLoader scl = ClassLoader.getSystemClassLoader(); 507 return parent.defineModulesWithOneLoader(cf, scl); 508 } else { 509 throw new IllegalStateException("jdk.incubator.code module not found"); 510 } 511 } 512 513 static final ClassDesc CD_Quoted = QUOTED_CLASS.describeConstable().get(); 514 static final MethodTypeDesc MTD_Quoted = MethodTypeDescImpl.ofValidated(CD_Quoted); 515 } 516 517 private static class SerializationSupport { 518 // Serialization support 519 private static final ClassDesc CD_SerializedLambda = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;"); 520 private static final ClassDesc CD_ObjectOutputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectOutputStream;"); 521 private static final ClassDesc CD_ObjectInputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectInputStream;"); 522 private static final MethodTypeDesc MTD_Object = MethodTypeDescImpl.ofValidated(CD_Object); 523 private static final MethodTypeDesc MTD_void_ObjectOutputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectOutputStream); 524 private static final MethodTypeDesc MTD_void_ObjectInputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectInputStream); 525 526 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; 527 private static final String NAME_METHOD_READ_OBJECT = "readObject"; 528 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; 529 530 static final ClassDesc CD_NotSerializableException = ReferenceClassDescImpl.ofValidated("Ljava/io/NotSerializableException;"); 531 static final MethodTypeDesc MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION = MethodTypeDescImpl.ofValidated(CD_void, CD_String); 532 static final MethodTypeDesc MTD_CTOR_SERIALIZED_LAMBDA = MethodTypeDescImpl.ofValidated(CD_void, 533 CD_Class, CD_String, CD_String, CD_String, CD_int, CD_String, CD_String, CD_String, CD_String, ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;")); 534 535 } 536 537 /** 538 * Generate a writeReplace method that supports serialization 539 */ 540 private void generateSerializationFriendlyMethods(ClassBuilder clb) { 541 clb.withMethodBody(SerializationSupport.NAME_METHOD_WRITE_REPLACE, SerializationSupport.MTD_Object, ACC_PRIVATE | ACC_FINAL, 542 new Consumer<>() { 543 @Override 544 public void accept(CodeBuilder cob) { 545 cob.new_(SerializationSupport.CD_SerializedLambda) 546 .dup() 547 .ldc(classDesc(targetClass)) 548 .ldc(factoryType.returnType().getName().replace('.', '/')) 549 .ldc(interfaceMethodName) 550 .ldc(interfaceMethodType.toMethodDescriptorString()) 551 .ldc(implInfo.getReferenceKind()) 552 .ldc(implInfo.getDeclaringClass().getName().replace('.', '/')) 553 .ldc(implInfo.getName()) 554 .ldc(implInfo.getMethodType().toMethodDescriptorString()) 555 .ldc(dynamicMethodType.toMethodDescriptorString()) 556 .loadConstant(argDescs.length) 557 .anewarray(CD_Object); 558 for (int i = 0; i < argDescs.length; i++) { 559 cob.dup() 560 .loadConstant(i) 561 .aload(0) 562 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); 563 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 564 cob.aastore(); 565 } 566 cob.invokespecial(SerializationSupport.CD_SerializedLambda, INIT_NAME, 567 SerializationSupport.MTD_CTOR_SERIALIZED_LAMBDA) 568 .areturn(); 569 } 570 }); 571 } 572 573 /** 574 * Generate method #quoted() 575 */ 576 private void generateQuotedMethod(ClassBuilder clb) { 577 clb.withMethod(NAME_METHOD_QUOTED, CodeReflectionSupport.MTD_Quoted, ACC_PUBLIC + ACC_FINAL, new MethodBody(new Consumer<CodeBuilder>() { 578 @Override 579 public void accept(CodeBuilder cob) { 580 cob.aload(0) 581 .getfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted) 582 .areturn(); 583 } 584 })); 585 } 586 587 /** 588 * Generate a readObject/writeObject method that is hostile to serialization 589 */ 590 private void generateSerializationHostileMethods(ClassBuilder clb) { 591 var hostileMethod = new Consumer<MethodBuilder>() { 592 @Override 593 public void accept(MethodBuilder mb) { 594 ConstantPoolBuilder cp = mb.constantPool(); 595 ClassEntry nseCE = cp.classEntry(SerializationSupport.CD_NotSerializableException); 596 mb.with(ExceptionsAttribute.of(nseCE)) 597 .withCode(new Consumer<CodeBuilder>() { 598 @Override 599 public void accept(CodeBuilder cob) { 600 cob.new_(nseCE) 601 .dup() 602 .ldc("Non-serializable lambda") 603 .invokespecial(cp.methodRefEntry(nseCE, cp.nameAndTypeEntry(INIT_NAME, 604 SerializationSupport.MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION))) 605 .athrow(); 606 } 607 }); 608 } 609 }; 610 clb.withMethod(SerializationSupport.NAME_METHOD_WRITE_OBJECT, SerializationSupport.MTD_void_ObjectOutputStream, 611 ACC_PRIVATE + ACC_FINAL, hostileMethod); 612 clb.withMethod(SerializationSupport.NAME_METHOD_READ_OBJECT, SerializationSupport.MTD_void_ObjectInputStream, 613 ACC_PRIVATE + ACC_FINAL, hostileMethod); 614 } 615 616 /** 617 * This method generates a method body which calls the lambda implementation 618 * method, converting arguments, as needed. 619 */ 620 Consumer<CodeBuilder> forwardingMethod(MethodType methodType) { 621 return new Consumer<>() { 622 @Override 623 public void accept(CodeBuilder cob) { 624 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 625 cob.new_(implMethodClassDesc) 626 .dup(); 627 } 628 if (useImplMethodHandle) { 629 ConstantPoolBuilder cp = cob.constantPool(); 630 cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA_AT), List.of(cp.intEntry(0))), 631 cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle))); 632 } 633 for (int i = 0; i < argNames.length ; i++) { 634 cob.aload(0) 635 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); 636 } 637 638 convertArgumentTypes(cob, methodType); 639 640 if (useImplMethodHandle) { 641 MethodType mtype = implInfo.getMethodType(); 642 if (implKind != MethodHandleInfo.REF_invokeStatic) { 643 mtype = mtype.insertParameterTypes(0, implClass); 644 } 645 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(mtype)); 646 } else { 647 // Invoke the method we want to forward to 648 cob.invoke(invocationOpcode(), implMethodClassDesc, implMethodName, implMethodDesc, implClass.isInterface()); 649 } 650 // Convert the return value (if any) and return it 651 // Note: if adapting from non-void to void, the 'return' 652 // instruction will pop the unneeded result 653 Class<?> implReturnClass = implMethodType.returnType(); 654 Class<?> samReturnClass = methodType.returnType(); 655 TypeConvertingMethodAdapter.convertType(cob, implReturnClass, samReturnClass, samReturnClass); 656 cob.return_(TypeKind.from(samReturnClass)); 657 } 658 }; 659 } 660 661 private void convertArgumentTypes(CodeBuilder cob, MethodType samType) { 662 int samParametersLength = samType.parameterCount(); 663 int captureArity = factoryType.parameterCount(); 664 for (int i = 0; i < samParametersLength; i++) { 665 Class<?> argType = samType.parameterType(i); 666 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); 667 TypeConvertingMethodAdapter.convertType(cob, argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i)); 668 } 669 } 670 671 private Opcode invocationOpcode() throws InternalError { 672 return switch (implKind) { 673 case MethodHandleInfo.REF_invokeStatic -> Opcode.INVOKESTATIC; 674 case MethodHandleInfo.REF_newInvokeSpecial -> Opcode.INVOKESPECIAL; 675 case MethodHandleInfo.REF_invokeVirtual -> Opcode.INVOKEVIRTUAL; 676 case MethodHandleInfo.REF_invokeInterface -> Opcode.INVOKEINTERFACE; 677 case MethodHandleInfo.REF_invokeSpecial -> Opcode.INVOKESPECIAL; 678 default -> throw new InternalError("Unexpected invocation kind: " + implKind); 679 }; 680 } 681 682 static ClassDesc implClassDesc(Class<?> cls) { 683 return cls.isHidden() ? null : ReferenceClassDescImpl.ofValidated(cls.descriptorString()); 684 } 685 686 static ClassDesc classDesc(Class<?> cls) { 687 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 688 : ReferenceClassDescImpl.ofValidated(cls.descriptorString()); 689 } 690 691 static MethodTypeDesc methodDesc(MethodType mt) { 692 var params = new ClassDesc[mt.parameterCount()]; 693 for (int i = 0; i < params.length; i++) { 694 params[i] = classDesc(mt.parameterType(i)); 695 } 696 return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params); 697 } 698 }