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