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 static java.lang.invoke.MethodType.methodType; 62 import jdk.internal.constant.ConstantUtils; 63 import jdk.internal.constant.MethodTypeDescImpl; 64 import jdk.internal.vm.annotation.Stable; 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 @Stable String[] ARG_NAME_CACHE = {"arg$1", "arg$2", "arg$3", "arg$4", "arg$5", "arg$6", "arg$7", "arg$8"}; 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 = 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 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 346 generateClassInitializer(clb); 347 } 348 349 // Forward the SAM method 350 clb.withMethodBody(interfaceMethodName, 351 methodDesc(interfaceMethodType), 352 ACC_PUBLIC, 353 forwardingMethod(interfaceMethodType)); 354 355 // Forward the bridges 356 if (altMethods != null) { 357 for (MethodType mt : altMethods) { 358 clb.withMethodBody(interfaceMethodName, 359 methodDesc(mt), 360 ACC_PUBLIC | ACC_BRIDGE, 361 forwardingMethod(mt)); 362 } 363 } 364 365 if (isSerializable) 366 generateSerializationFriendlyMethods(clb); 367 else if (finalAccidentallySerializable) 368 generateSerializationHostileMethods(clb); 369 370 if (quotableOpGetter != null) { 371 generateQuotedMethod(clb); 372 } 373 } 374 }); 375 376 // Define the generated class in this VM. 377 378 try { 379 // this class is linked at the indy callsite; so define a hidden nestmate 380 List<?> classdata; 381 if (useImplMethodHandle || quotableOpGetter != null) { 382 classdata = quotableOpGetter == null ? 383 List.of(implementation) : 384 List.of(implementation, quotableOpGetter, CodeReflectionSupport.HANDLE_MAKE_QUOTED); 385 } else { 386 classdata = null; 387 } 388 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK) 389 .defineClass(!disableEagerInitialization, classdata); 390 391 } catch (Throwable t) { 392 throw new InternalError(t); 393 } 394 } 395 396 /** 397 * Generate a static field and a static initializer that sets this field to an instance of the lambda 398 */ 399 private void generateClassInitializer(ClassBuilder clb) { 400 ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType()); 401 402 // Generate the static final field that holds the lambda singleton 403 clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 404 405 // Instantiate the lambda and store it to the static final field 406 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() { 407 @Override 408 public void accept(CodeBuilder cob) { 409 assert factoryType.parameterCount() == 0; 410 cob.new_(lambdaClassEntry) 411 .dup() 412 .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc))) 413 .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor))) 414 .return_(); 415 } 416 }); 417 } 418 419 /** 420 * Generate the constructor for the class 421 */ 422 private void generateConstructor(ClassBuilder clb) { 423 // Generate constructor 424 clb.withMethodBody(INIT_NAME, constructorTypeDesc, ACC_PRIVATE, 425 new Consumer<>() { 426 @Override 427 public void accept(CodeBuilder cob) { 428 cob.aload(0) 429 .invokespecial(CD_Object, INIT_NAME, MTD_void); 430 int parameterCount = factoryType.parameterCount(); 431 for (int i = 0; i < parameterCount; i++) { 432 cob.aload(0) 433 .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) 434 .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 435 } 436 if (quotableOpGetter != null) { 437 generateQuotedFieldInitializer(cob); 438 } 439 cob.return_(); 440 } 441 }); 442 } 443 444 private void generateQuotedFieldInitializer(CodeBuilder cob) { 445 ConstantPoolBuilder cp = cob.constantPool(); 446 MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT); 447 NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle); 448 // push the receiver on the stack for operand of put field instruction 449 cob.aload(0) 450 // load class data: CodeReflectionSupport.HANDLE_MAKE_QUOTED and quotableOpGetter 451 .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(2))), natMH)) 452 .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(1))), natMH)); 453 MethodType mtype = quotableOpGetterInfo.getMethodType(); 454 if (quotableOpGetterInfo.getReferenceKind() != MethodHandleInfo.REF_invokeStatic) { 455 mtype = mtype.insertParameterTypes(0, implClass); 456 } 457 // load arguments to quotableOpGetter: ExtendedOp.FACTORY and CORE_TYPE_FACTORY 458 cob.fieldAccess(Opcode.GETSTATIC, CodeReflectionSupport.EXTENDED_OP_CLASS.describeConstable().get(), 459 "FACTORY", CodeReflectionSupport.OP_FACTORY_CLASS.describeConstable().get()); 460 cob.fieldAccess(Opcode.GETSTATIC, CodeReflectionSupport.CORE_TYPE_FACTORY_CLASS.describeConstable().get(), 461 "CORE_TYPE_FACTORY", 462 CodeReflectionSupport.TYPE_ELEMENT_FACTORY_CLASS.describeConstable().get()); 463 cob.invokevirtual(CD_MethodHandle, "invokeExact", mtype.describeConstable().get()); 464 cob.checkcast(CodeReflectionSupport.FUNC_OP_CLASS.describeConstable().get()); 465 466 // load captured args in array 467 468 int capturedArity = factoryType.parameterCount(); 469 cob.loadConstant(capturedArity) 470 .anewarray(CD_Object); 471 // initialize quoted captures 472 for (int i = 0; i < capturedArity; i++) { 473 cob.dup() 474 .loadConstant(i) 475 .aload(0) 476 .getfield(lambdaClassEntry.asSymbol(), argName(i), argDescs[i]); 477 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 478 cob.aastore(); 479 } 480 481 // Create a Quoted from FuncOp and captured args Object[] 482 483 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(CodeReflectionSupport.HANDLE_MAKE_QUOTED.type())) 484 .putfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted); 485 } 486 487 static class CodeReflectionSupport { 488 static final Class<?> QUOTED_CLASS; 489 static final Class<?> QUOTABLE_CLASS; 490 static final MethodHandle HANDLE_MAKE_QUOTED; 491 static final Class<?> EXTENDED_OP_CLASS; 492 static final Class<?> OP_FACTORY_CLASS; 493 static final Class<?> CORE_TYPE_FACTORY_CLASS; 494 static final Class<?> TYPE_ELEMENT_FACTORY_CLASS; 495 static final Class<?> FUNC_OP_CLASS; 496 497 static { 498 try { 499 ModuleLayer layer = codeLayer(); 500 ClassLoader cl = layer.findLoader("jdk.incubator.code"); 501 QUOTED_CLASS = cl.loadClass("jdk.incubator.code.Quoted"); 502 QUOTABLE_CLASS = cl.loadClass("jdk.incubator.code.Quotable"); 503 Class<?> quotedHelper = cl.loadClass("jdk.incubator.code.internal.QuotedHelper"); 504 FUNC_OP_CLASS = cl.loadClass("jdk.incubator.code.op.CoreOp$FuncOp"); 505 MethodHandle makeQuoted = Lookup.IMPL_LOOKUP.findStatic(quotedHelper, "makeQuoted", 506 MethodType.methodType(QUOTED_CLASS, MethodHandles.Lookup.class, FUNC_OP_CLASS, Object[].class)); 507 HANDLE_MAKE_QUOTED = makeQuoted.bindTo(Lookup.IMPL_LOOKUP); 508 EXTENDED_OP_CLASS = cl.loadClass("jdk.incubator.code.op.ExtendedOp"); 509 OP_FACTORY_CLASS = cl.loadClass("jdk.incubator.code.op.OpFactory"); 510 CORE_TYPE_FACTORY_CLASS = cl.loadClass("jdk.incubator.code.type.CoreTypeFactory"); 511 TYPE_ELEMENT_FACTORY_CLASS = cl.loadClass("jdk.incubator.code.type.TypeElementFactory"); 512 } catch (Throwable ex) { 513 throw new ExceptionInInitializerError(ex); 514 } 515 } 516 517 static ModuleLayer codeLayer() { 518 final ModuleLayer codeLayer; 519 if (ModuleLayer.boot().findModule("jdk.incubator.code").isPresent()) { 520 // we are in an exploded build, so just use the boot layer 521 return ModuleLayer.boot(); 522 } else if (java.lang.module.ModuleFinder.ofSystem().find("jdk.incubator.code").isPresent()) { 523 // the code module is installed, but not in the boot layer, create a new layer which contains it 524 ModuleLayer parent = ModuleLayer.boot(); 525 Configuration cf = parent.configuration() 526 .resolve(ModuleFinder.of(), ModuleFinder.ofSystem(), Set.of("jdk.incubator.code")); 527 ClassLoader scl = ClassLoader.getSystemClassLoader(); 528 return parent.defineModulesWithOneLoader(cf, scl); 529 } else { 530 throw new IllegalStateException("jdk.incubator.code module not found"); 531 } 532 } 533 534 static final ClassDesc CD_Quoted = QUOTED_CLASS.describeConstable().get(); 535 static final MethodTypeDesc MTD_Quoted = MethodTypeDescImpl.ofValidated(CD_Quoted); 536 } 537 538 private static class SerializationSupport { 539 // Serialization support 540 private static final ClassDesc CD_SerializedLambda = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;"); 541 private static final ClassDesc CD_ObjectOutputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectOutputStream;"); 542 private static final ClassDesc CD_ObjectInputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectInputStream;"); 543 private static final MethodTypeDesc MTD_Object = MethodTypeDescImpl.ofValidated(CD_Object); 544 private static final MethodTypeDesc MTD_void_ObjectOutputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectOutputStream); 545 private static final MethodTypeDesc MTD_void_ObjectInputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectInputStream); 546 547 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; 548 private static final String NAME_METHOD_READ_OBJECT = "readObject"; 549 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; 550 551 static final ClassDesc CD_NotSerializableException = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/NotSerializableException;"); 552 static final MethodTypeDesc MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION = MethodTypeDescImpl.ofValidated(CD_void, CD_String); 553 static final MethodTypeDesc MTD_CTOR_SERIALIZED_LAMBDA = MethodTypeDescImpl.ofValidated(CD_void, 554 CD_Class, CD_String, CD_String, CD_String, CD_int, CD_String, CD_String, CD_String, CD_String, ConstantUtils.CD_Object_array); 555 556 } 557 558 /** 559 * Generate a writeReplace method that supports serialization 560 */ 561 private void generateSerializationFriendlyMethods(ClassBuilder clb) { 562 clb.withMethodBody(SerializationSupport.NAME_METHOD_WRITE_REPLACE, SerializationSupport.MTD_Object, ACC_PRIVATE | ACC_FINAL, 563 new Consumer<>() { 564 @Override 565 public void accept(CodeBuilder cob) { 566 cob.new_(SerializationSupport.CD_SerializedLambda) 567 .dup() 568 .ldc(ClassDesc.ofInternalName(sanitizedTargetClassName(targetClass))) 569 .ldc(factoryType.returnType().getName().replace('.', '/')) 570 .ldc(interfaceMethodName) 571 .ldc(interfaceMethodType.toMethodDescriptorString()) 572 .ldc(implInfo.getReferenceKind()) 573 .ldc(implInfo.getDeclaringClass().getName().replace('.', '/')) 574 .ldc(implInfo.getName()) 575 .ldc(implInfo.getMethodType().toMethodDescriptorString()) 576 .ldc(dynamicMethodType.toMethodDescriptorString()) 577 .loadConstant(argDescs.length) 578 .anewarray(CD_Object); 579 for (int i = 0; i < argDescs.length; i++) { 580 cob.dup() 581 .loadConstant(i) 582 .aload(0) 583 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 584 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 585 cob.aastore(); 586 } 587 cob.invokespecial(SerializationSupport.CD_SerializedLambda, INIT_NAME, 588 SerializationSupport.MTD_CTOR_SERIALIZED_LAMBDA) 589 .areturn(); 590 } 591 }); 592 } 593 594 /** 595 * Generate method #quoted() 596 */ 597 private void generateQuotedMethod(ClassBuilder clb) { 598 clb.withMethod(NAME_METHOD_QUOTED, CodeReflectionSupport.MTD_Quoted, ACC_PUBLIC + ACC_FINAL, new MethodBody(new Consumer<CodeBuilder>() { 599 @Override 600 public void accept(CodeBuilder cob) { 601 cob.aload(0) 602 .getfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted) 603 .areturn(); 604 } 605 })); 606 } 607 608 /** 609 * Generate a readObject/writeObject method that is hostile to serialization 610 */ 611 private void generateSerializationHostileMethods(ClassBuilder clb) { 612 var hostileMethod = new Consumer<MethodBuilder>() { 613 @Override 614 public void accept(MethodBuilder mb) { 615 ConstantPoolBuilder cp = mb.constantPool(); 616 ClassEntry nseCE = cp.classEntry(SerializationSupport.CD_NotSerializableException); 617 mb.with(ExceptionsAttribute.of(nseCE)) 618 .withCode(new Consumer<CodeBuilder>() { 619 @Override 620 public void accept(CodeBuilder cob) { 621 cob.new_(nseCE) 622 .dup() 623 .ldc("Non-serializable lambda") 624 .invokespecial(cp.methodRefEntry(nseCE, cp.nameAndTypeEntry(INIT_NAME, 625 SerializationSupport.MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION))) 626 .athrow(); 627 } 628 }); 629 } 630 }; 631 clb.withMethod(SerializationSupport.NAME_METHOD_WRITE_OBJECT, SerializationSupport.MTD_void_ObjectOutputStream, 632 ACC_PRIVATE + ACC_FINAL, hostileMethod); 633 clb.withMethod(SerializationSupport.NAME_METHOD_READ_OBJECT, SerializationSupport.MTD_void_ObjectInputStream, 634 ACC_PRIVATE + ACC_FINAL, hostileMethod); 635 } 636 637 /** 638 * This method generates a method body which calls the lambda implementation 639 * method, converting arguments, as needed. 640 */ 641 Consumer<CodeBuilder> forwardingMethod(MethodType methodType) { 642 return new Consumer<>() { 643 @Override 644 public void accept(CodeBuilder cob) { 645 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 646 cob.new_(implMethodClassDesc) 647 .dup(); 648 } 649 if (useImplMethodHandle) { 650 ConstantPoolBuilder cp = cob.constantPool(); 651 cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA_AT), List.of(cp.intEntry(0))), 652 cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle))); 653 } 654 for (int i = 0; i < argDescs.length; i++) { 655 cob.aload(0) 656 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 657 } 658 659 convertArgumentTypes(cob, methodType); 660 661 if (useImplMethodHandle) { 662 MethodType mtype = implInfo.getMethodType(); 663 if (implKind != MethodHandleInfo.REF_invokeStatic) { 664 mtype = mtype.insertParameterTypes(0, implClass); 665 } 666 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(mtype)); 667 } else { 668 // Invoke the method we want to forward to 669 cob.invoke(invocationOpcode(), implMethodClassDesc, implMethodName, implMethodDesc, implClass.isInterface()); 670 } 671 // Convert the return value (if any) and return it 672 // Note: if adapting from non-void to void, the 'return' 673 // instruction will pop the unneeded result 674 Class<?> implReturnClass = implMethodType.returnType(); 675 Class<?> samReturnClass = methodType.returnType(); 676 TypeConvertingMethodAdapter.convertType(cob, implReturnClass, samReturnClass, samReturnClass); 677 cob.return_(TypeKind.from(samReturnClass)); 678 } 679 }; 680 } 681 682 private void convertArgumentTypes(CodeBuilder cob, MethodType samType) { 683 int samParametersLength = samType.parameterCount(); 684 int captureArity = factoryType.parameterCount(); 685 for (int i = 0; i < samParametersLength; i++) { 686 Class<?> argType = samType.parameterType(i); 687 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); 688 TypeConvertingMethodAdapter.convertType(cob, argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i)); 689 } 690 } 691 692 private Opcode invocationOpcode() throws InternalError { 693 return switch (implKind) { 694 case MethodHandleInfo.REF_invokeStatic -> Opcode.INVOKESTATIC; 695 case MethodHandleInfo.REF_newInvokeSpecial -> Opcode.INVOKESPECIAL; 696 case MethodHandleInfo.REF_invokeVirtual -> Opcode.INVOKEVIRTUAL; 697 case MethodHandleInfo.REF_invokeInterface -> Opcode.INVOKEINTERFACE; 698 case MethodHandleInfo.REF_invokeSpecial -> Opcode.INVOKESPECIAL; 699 default -> throw new InternalError("Unexpected invocation kind: " + implKind); 700 }; 701 } 702 703 static ClassDesc implClassDesc(Class<?> cls) { 704 return cls.isHidden() ? null : ConstantUtils.referenceClassDesc(cls.descriptorString()); 705 } 706 707 static ClassDesc classDesc(Class<?> cls) { 708 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 709 : ConstantUtils.referenceClassDesc(cls.descriptorString()); 710 } 711 712 static MethodTypeDesc methodDesc(MethodType mt) { 713 var params = new ClassDesc[mt.parameterCount()]; 714 for (int i = 0; i < params.length; i++) { 715 params[i] = classDesc(mt.parameterType(i)); 716 } 717 return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params); 718 } 719 }