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 private static final String COMPILER_GENERATED_MODEL_FIELD_NAME = "COMPILER_GENERATED_MODEL"; 94 95 static { 96 // To dump the lambda proxy classes, set this system property: 97 // -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles 98 // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true 99 final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles"; 100 lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, "DUMP_LAMBDA_PROXY_CLASS_FILES"); 101 102 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization"; 103 disableEagerInitialization = Boolean.getBoolean(disableEagerInitializationKey); 104 } 105 106 // See context values in AbstractValidatingLambdaMetafactory 107 private final ClassDesc implMethodClassDesc; // Name of type containing implementation "CC" 108 private final String implMethodName; // Name of implementation method "impl" 109 private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" 110 private final MethodType constructorType; // Generated class constructor type "(CC)void" 111 private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void" 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 */ 155 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 156 MethodType factoryType, 157 String interfaceMethodName, 158 MethodType interfaceMethodType, 159 MethodHandle implementation, 160 MethodType dynamicMethodType, 161 boolean isSerializable, 162 Class<?>[] altInterfaces, 163 MethodType[] altMethods, 164 MethodHandle reflectiveField) 165 throws LambdaConversionException { 166 super(caller, factoryType, interfaceMethodName, interfaceMethodType, 167 implementation, dynamicMethodType, 168 isSerializable, altInterfaces, altMethods, reflectiveField); 169 implMethodClassDesc = implClassDesc(implClass); 170 implMethodName = implInfo.getName(); 171 implMethodDesc = methodDesc(implInfo.getMethodType()); 172 constructorType = factoryType.changeReturnType(Void.TYPE); 173 lambdaClassName = lambdaClassName(targetClass); 174 lambdaClassEntry = pool.classEntry(ConstantUtils.internalNameToDesc(lambdaClassName)); 175 // If the target class invokes a protected method inherited from a 176 // superclass in a different package, or does 'invokespecial', the 177 // lambda class has no access to the resolved method, or does 178 // 'invokestatic' on a hidden class which cannot be resolved by name. 179 // Instead, we need to pass the live implementation method handle to 180 // the proxy class to invoke directly. (javac prefers to avoid this 181 // situation by generating bridges in the target class) 182 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) && 183 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) || 184 implKind == MethodHandleInfo.REF_invokeSpecial || 185 implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden(); 186 int parameterCount = factoryType.parameterCount(); 187 ClassDesc[] argDescs; 188 MethodTypeDesc constructorTypeDesc; 189 if (parameterCount > 0) { 190 argDescs = new ClassDesc[parameterCount]; 191 for (int i = 0; i < parameterCount; i++) { 192 argDescs[i] = classDesc(factoryType.parameterType(i)); 193 } 194 constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); 195 } else { 196 argDescs = EMPTY_CLASSDESC_ARRAY; 197 constructorTypeDesc = MTD_void; 198 } 199 this.argDescs = argDescs; 200 this.constructorTypeDesc = constructorTypeDesc; 201 } 202 203 private static String argName(int i) { 204 return i < ARG_NAME_CACHE.length ? ARG_NAME_CACHE[i] : "arg$" + (i + 1); 205 } 206 207 private static String sanitizedTargetClassName(Class<?> targetClass) { 208 String name = targetClass.getName(); 209 if (targetClass.isHidden()) { 210 // use the original class name 211 name = name.replace('/', '_'); 212 } 213 return name.replace('.', '/'); 214 } 215 216 private static String lambdaClassName(Class<?> targetClass) { 217 return sanitizedTargetClassName(targetClass).concat("$$Lambda"); 218 } 219 220 /** 221 * Build the CallSite. Generate a class file which implements the functional 222 * interface, define the class, if there are no parameters create an instance 223 * of the class which the CallSite will return, otherwise, generate handles 224 * which will call the class' constructor. 225 * 226 * @return a CallSite, which, when invoked, will return an instance of the 227 * functional interface 228 * @throws LambdaConversionException If properly formed functional interface 229 * is not found 230 */ 231 @Override 232 CallSite buildCallSite() throws LambdaConversionException { 233 final Class<?> innerClass = spinInnerClass(); 234 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 235 try { 236 return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, 237 factoryType.returnType())); 238 } catch (ReflectiveOperationException e) { 239 throw new LambdaConversionException( 240 "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e); 241 } 242 } else { 243 try { 244 MethodHandle mh = caller.findConstructor(innerClass, constructorType); 245 if (factoryType.parameterCount() == 0) { 246 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance 247 Object inst = mh.invokeBasic(); 248 return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst)); 249 } else { 250 return new ConstantCallSite(mh.asType(factoryType)); 251 } 252 } catch (ReflectiveOperationException e) { 253 throw new LambdaConversionException("Exception finding constructor", e); 254 } catch (Throwable e) { 255 throw new LambdaConversionException("Exception instantiating lambda object", e); 256 } 257 } 258 } 259 260 /** 261 * Spins the lambda proxy class. 262 * 263 * This first checks if a lambda proxy class can be loaded from CDS archive. 264 * Otherwise, generate the lambda proxy class. If CDS dumping is enabled, it 265 * registers the lambda proxy class for including into the CDS archive. 266 */ 267 private Class<?> spinInnerClass() throws LambdaConversionException { 268 // CDS does not handle disableEagerInitialization or useImplMethodHandle 269 if (!disableEagerInitialization && !useImplMethodHandle) { 270 if (CDS.isUsingArchive()) { 271 // load from CDS archive if present 272 Class<?> innerClass = LambdaProxyClassArchive.find(targetClass, 273 interfaceMethodName, 274 factoryType, 275 interfaceMethodType, 276 implementation, 277 dynamicMethodType, 278 isSerializable, 279 altInterfaces, 280 altMethods); 281 if (innerClass != null) return innerClass; 282 } 283 284 // include lambda proxy class in CDS archive at dump time 285 if (CDS.isDumpingArchive()) { 286 Class<?> innerClass = generateInnerClass(); 287 LambdaProxyClassArchive.register(targetClass, 288 interfaceMethodName, 289 factoryType, 290 interfaceMethodType, 291 implementation, 292 dynamicMethodType, 293 isSerializable, 294 altInterfaces, 295 altMethods, 296 innerClass); 297 return innerClass; 298 } 299 300 } 301 return generateInnerClass(); 302 } 303 304 /** 305 * Generate a class file which implements the functional 306 * interface, define and return the class. 307 * 308 * @return a Class which implements the functional interface 309 * @throws LambdaConversionException If properly formed functional interface 310 * is not found 311 */ 312 private Class<?> generateInnerClass() throws LambdaConversionException { 313 List<ClassDesc> interfaces; 314 ClassDesc interfaceDesc = classDesc(interfaceClass); 315 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass); 316 if (altInterfaces.length == 0) { 317 interfaces = List.of(interfaceDesc); 318 } else { 319 // Assure no duplicate interfaces (ClassFormatError) 320 Set<ClassDesc> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1); 321 itfs.add(interfaceDesc); 322 for (Class<?> i : altInterfaces) { 323 itfs.add(classDesc(i)); 324 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i); 325 } 326 interfaces = List.copyOf(itfs); 327 } 328 final boolean finalAccidentallySerializable = accidentallySerializable; 329 final byte[] classBytes = ClassFile.of().build(lambdaClassEntry, pool, new Consumer<ClassBuilder>() { 330 @Override 331 public void accept(ClassBuilder clb) { 332 clb.withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC) 333 .withInterfaceSymbols(interfaces); 334 // Generate final fields to be filled in by constructor 335 for (int i = 0; i < argDescs.length; i++) { 336 clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL); 337 } 338 339 // if quotable, generate the field that will hold the value of quoted 340 if (quotableOpGetter != null) { 341 clb.withField(quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted, ACC_PRIVATE + ACC_FINAL); 342 } 343 344 generateConstructor(clb); 345 346 generateClassInitializationMethod(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 private void generateClassInitializationMethod(ClassBuilder clb) { 397 if (!(factoryType.parameterCount() == 0 && disableEagerInitialization) && quotableOpGetter == null) { 398 return; 399 } 400 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<CodeBuilder>() { 401 @Override 402 public void accept(CodeBuilder cob) { 403 if (factoryType.parameterCount() == 0 && disableEagerInitialization) { 404 ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType()); 405 // Generate the static final field that holds the lambda singleton 406 clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 407 cob.new_(lambdaClassEntry) 408 .dup() 409 .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc))) 410 .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor))); 411 } 412 413 if (quotableOpGetter != null) { 414 // if we visit a callsite twice, we will use the same class 415 // if the lambda doesn't capture values we only have one instance, model shared anyway 416 // if it captures values, each visit result in a creation of new instance of the class 417 // those instances have the same code model generated by the compiler 418 // they may differ in captured values 419 // as first step let's share the compiler generated code model 420 ClassDesc funcOpClassDesc = CodeReflectionSupport.FUNC_OP_CLASS.describeConstable().get(); 421 clb.withField(COMPILER_GENERATED_MODEL_FIELD_NAME, funcOpClassDesc, 422 ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 423 424 ConstantPoolBuilder cp = pool; 425 MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT); 426 NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle); 427 // load quotableOpGetter 428 cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(1))), natMH)); 429 MethodType mtype = quotableOpGetterInfo.getMethodType(); 430 if (quotableOpGetterInfo.getReferenceKind() != MethodHandleInfo.REF_invokeStatic) { 431 mtype = mtype.insertParameterTypes(0, implClass); 432 } 433 // load arguments to quotableOpGetter: ExtendedOp.FACTORY and CORE_TYPE_FACTORY 434 cob.fieldAccess(Opcode.GETSTATIC, CodeReflectionSupport.EXTENDED_OP_CLASS.describeConstable().get(), 435 "FACTORY", CodeReflectionSupport.OP_FACTORY_CLASS.describeConstable().get()); 436 cob.fieldAccess(Opcode.GETSTATIC, CodeReflectionSupport.CORE_TYPE_FACTORY_CLASS.describeConstable().get(), 437 "CORE_TYPE_FACTORY", 438 CodeReflectionSupport.TYPE_ELEMENT_FACTORY_CLASS.describeConstable().get()); 439 cob.invokevirtual(CD_MethodHandle, "invokeExact", mtype.describeConstable().get()); 440 cob.checkcast(funcOpClassDesc); 441 cob.putstatic(lambdaClassEntry.asSymbol(), COMPILER_GENERATED_MODEL_FIELD_NAME, funcOpClassDesc); 442 } 443 cob.return_(); 444 } 445 }); 446 } 447 448 /** 449 * Generate the constructor for the class 450 */ 451 private void generateConstructor(ClassBuilder clb) { 452 // Generate constructor 453 clb.withMethodBody(INIT_NAME, constructorTypeDesc, ACC_PRIVATE, 454 new Consumer<>() { 455 @Override 456 public void accept(CodeBuilder cob) { 457 cob.aload(0) 458 .invokespecial(CD_Object, INIT_NAME, MTD_void); 459 int parameterCount = factoryType.parameterCount(); 460 for (int i = 0; i < parameterCount; i++) { 461 cob.aload(0) 462 .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) 463 .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 464 } 465 if (quotableOpGetter != null) { 466 generateQuotedFieldInitializer(cob); 467 } 468 cob.return_(); 469 } 470 }); 471 } 472 473 private void generateQuotedFieldInitializer(CodeBuilder cob) { 474 ConstantPoolBuilder cp = cob.constantPool(); 475 MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT); 476 NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle); 477 // push the receiver on the stack for operand of put field instruction 478 cob.aload(0) 479 // load class data: CodeReflectionSupport.HANDLE_MAKE_QUOTED 480 .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(2))), natMH)) 481 .getstatic(lambdaClassEntry.asSymbol(), COMPILER_GENERATED_MODEL_FIELD_NAME, 482 CodeReflectionSupport.FUNC_OP_CLASS.describeConstable().get()); 483 484 485 // load captured args in array 486 487 int capturedArity = factoryType.parameterCount(); 488 cob.loadConstant(capturedArity) 489 .anewarray(CD_Object); 490 // initialize quoted captures 491 for (int i = 0; i < capturedArity; i++) { 492 cob.dup() 493 .loadConstant(i) 494 .aload(0) 495 .getfield(lambdaClassEntry.asSymbol(), argName(i), argDescs[i]); 496 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 497 cob.aastore(); 498 } 499 500 // Create a Quoted from FuncOp and captured args Object[] 501 502 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(CodeReflectionSupport.HANDLE_MAKE_QUOTED.type())) 503 .putfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted); 504 } 505 506 static class CodeReflectionSupport { 507 static final Class<?> QUOTED_CLASS; 508 static final Class<?> QUOTABLE_CLASS; 509 static final MethodHandle HANDLE_MAKE_QUOTED; 510 static final Class<?> EXTENDED_OP_CLASS; 511 static final Class<?> OP_FACTORY_CLASS; 512 static final Class<?> CORE_TYPE_FACTORY_CLASS; 513 static final Class<?> TYPE_ELEMENT_FACTORY_CLASS; 514 static final Class<?> FUNC_OP_CLASS; 515 516 static { 517 try { 518 ModuleLayer layer = codeLayer(); 519 ClassLoader cl = layer.findLoader("jdk.incubator.code"); 520 QUOTED_CLASS = cl.loadClass("jdk.incubator.code.Quoted"); 521 QUOTABLE_CLASS = cl.loadClass("jdk.incubator.code.Quotable"); 522 Class<?> quotedHelper = cl.loadClass("jdk.incubator.code.internal.QuotedHelper"); 523 FUNC_OP_CLASS = cl.loadClass("jdk.incubator.code.op.CoreOp$FuncOp"); 524 MethodHandle makeQuoted = Lookup.IMPL_LOOKUP.findStatic(quotedHelper, "makeQuoted", 525 MethodType.methodType(QUOTED_CLASS, MethodHandles.Lookup.class, FUNC_OP_CLASS, Object[].class)); 526 HANDLE_MAKE_QUOTED = makeQuoted.bindTo(Lookup.IMPL_LOOKUP); 527 EXTENDED_OP_CLASS = cl.loadClass("jdk.incubator.code.op.ExtendedOp"); 528 OP_FACTORY_CLASS = cl.loadClass("jdk.incubator.code.op.OpFactory"); 529 CORE_TYPE_FACTORY_CLASS = cl.loadClass("jdk.incubator.code.type.CoreTypeFactory"); 530 TYPE_ELEMENT_FACTORY_CLASS = cl.loadClass("jdk.incubator.code.type.TypeElementFactory"); 531 } catch (Throwable ex) { 532 throw new ExceptionInInitializerError(ex); 533 } 534 } 535 536 static ModuleLayer codeLayer() { 537 final ModuleLayer codeLayer; 538 if (ModuleLayer.boot().findModule("jdk.incubator.code").isPresent()) { 539 // we are in an exploded build, so just use the boot layer 540 return ModuleLayer.boot(); 541 } else if (java.lang.module.ModuleFinder.ofSystem().find("jdk.incubator.code").isPresent()) { 542 // the code module is installed, but not in the boot layer, create a new layer which contains it 543 ModuleLayer parent = ModuleLayer.boot(); 544 Configuration cf = parent.configuration() 545 .resolve(ModuleFinder.of(), ModuleFinder.ofSystem(), Set.of("jdk.incubator.code")); 546 ClassLoader scl = ClassLoader.getSystemClassLoader(); 547 return parent.defineModulesWithOneLoader(cf, scl); 548 } else { 549 throw new IllegalStateException("jdk.incubator.code module not found"); 550 } 551 } 552 553 static final ClassDesc CD_Quoted = QUOTED_CLASS.describeConstable().get(); 554 static final MethodTypeDesc MTD_Quoted = MethodTypeDescImpl.ofValidated(CD_Quoted); 555 } 556 557 private static class SerializationSupport { 558 // Serialization support 559 private static final ClassDesc CD_SerializedLambda = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;"); 560 private static final ClassDesc CD_ObjectOutputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectOutputStream;"); 561 private static final ClassDesc CD_ObjectInputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectInputStream;"); 562 private static final MethodTypeDesc MTD_Object = MethodTypeDescImpl.ofValidated(CD_Object); 563 private static final MethodTypeDesc MTD_void_ObjectOutputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectOutputStream); 564 private static final MethodTypeDesc MTD_void_ObjectInputStream = MethodTypeDescImpl.ofValidated(CD_void, CD_ObjectInputStream); 565 566 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; 567 private static final String NAME_METHOD_READ_OBJECT = "readObject"; 568 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; 569 570 static final ClassDesc CD_NotSerializableException = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/NotSerializableException;"); 571 static final MethodTypeDesc MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION = MethodTypeDescImpl.ofValidated(CD_void, CD_String); 572 static final MethodTypeDesc MTD_CTOR_SERIALIZED_LAMBDA = MethodTypeDescImpl.ofValidated(CD_void, 573 CD_Class, CD_String, CD_String, CD_String, CD_int, CD_String, CD_String, CD_String, CD_String, ConstantUtils.CD_Object_array); 574 575 } 576 577 /** 578 * Generate a writeReplace method that supports serialization 579 */ 580 private void generateSerializationFriendlyMethods(ClassBuilder clb) { 581 clb.withMethodBody(SerializationSupport.NAME_METHOD_WRITE_REPLACE, SerializationSupport.MTD_Object, ACC_PRIVATE | ACC_FINAL, 582 new Consumer<>() { 583 @Override 584 public void accept(CodeBuilder cob) { 585 cob.new_(SerializationSupport.CD_SerializedLambda) 586 .dup() 587 .ldc(ClassDesc.ofInternalName(sanitizedTargetClassName(targetClass))) 588 .ldc(factoryType.returnType().getName().replace('.', '/')) 589 .ldc(interfaceMethodName) 590 .ldc(interfaceMethodType.toMethodDescriptorString()) 591 .ldc(implInfo.getReferenceKind()) 592 .ldc(implInfo.getDeclaringClass().getName().replace('.', '/')) 593 .ldc(implInfo.getName()) 594 .ldc(implInfo.getMethodType().toMethodDescriptorString()) 595 .ldc(dynamicMethodType.toMethodDescriptorString()) 596 .loadConstant(argDescs.length) 597 .anewarray(CD_Object); 598 for (int i = 0; i < argDescs.length; i++) { 599 cob.dup() 600 .loadConstant(i) 601 .aload(0) 602 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 603 TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); 604 cob.aastore(); 605 } 606 cob.invokespecial(SerializationSupport.CD_SerializedLambda, INIT_NAME, 607 SerializationSupport.MTD_CTOR_SERIALIZED_LAMBDA) 608 .areturn(); 609 } 610 }); 611 } 612 613 /** 614 * Generate method #quoted() 615 */ 616 private void generateQuotedMethod(ClassBuilder clb) { 617 clb.withMethod(NAME_METHOD_QUOTED, CodeReflectionSupport.MTD_Quoted, ACC_PUBLIC + ACC_FINAL, new MethodBody(new Consumer<CodeBuilder>() { 618 @Override 619 public void accept(CodeBuilder cob) { 620 cob.aload(0) 621 .getfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted) 622 .areturn(); 623 } 624 })); 625 } 626 627 /** 628 * Generate a readObject/writeObject method that is hostile to serialization 629 */ 630 private void generateSerializationHostileMethods(ClassBuilder clb) { 631 var hostileMethod = new Consumer<MethodBuilder>() { 632 @Override 633 public void accept(MethodBuilder mb) { 634 ConstantPoolBuilder cp = mb.constantPool(); 635 ClassEntry nseCE = cp.classEntry(SerializationSupport.CD_NotSerializableException); 636 mb.with(ExceptionsAttribute.of(nseCE)) 637 .withCode(new Consumer<CodeBuilder>() { 638 @Override 639 public void accept(CodeBuilder cob) { 640 cob.new_(nseCE) 641 .dup() 642 .ldc("Non-serializable lambda") 643 .invokespecial(cp.methodRefEntry(nseCE, cp.nameAndTypeEntry(INIT_NAME, 644 SerializationSupport.MTD_CTOR_NOT_SERIALIZABLE_EXCEPTION))) 645 .athrow(); 646 } 647 }); 648 } 649 }; 650 clb.withMethod(SerializationSupport.NAME_METHOD_WRITE_OBJECT, SerializationSupport.MTD_void_ObjectOutputStream, 651 ACC_PRIVATE + ACC_FINAL, hostileMethod); 652 clb.withMethod(SerializationSupport.NAME_METHOD_READ_OBJECT, SerializationSupport.MTD_void_ObjectInputStream, 653 ACC_PRIVATE + ACC_FINAL, hostileMethod); 654 } 655 656 /** 657 * This method generates a method body which calls the lambda implementation 658 * method, converting arguments, as needed. 659 */ 660 Consumer<CodeBuilder> forwardingMethod(MethodType methodType) { 661 return new Consumer<>() { 662 @Override 663 public void accept(CodeBuilder cob) { 664 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 665 cob.new_(implMethodClassDesc) 666 .dup(); 667 } 668 if (useImplMethodHandle) { 669 ConstantPoolBuilder cp = cob.constantPool(); 670 cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA_AT), List.of(cp.intEntry(0))), 671 cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle))); 672 } 673 for (int i = 0; i < argDescs.length; i++) { 674 cob.aload(0) 675 .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); 676 } 677 678 convertArgumentTypes(cob, methodType); 679 680 if (useImplMethodHandle) { 681 MethodType mtype = implInfo.getMethodType(); 682 if (implKind != MethodHandleInfo.REF_invokeStatic) { 683 mtype = mtype.insertParameterTypes(0, implClass); 684 } 685 cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(mtype)); 686 } else { 687 // Invoke the method we want to forward to 688 cob.invoke(invocationOpcode(), implMethodClassDesc, implMethodName, implMethodDesc, implClass.isInterface()); 689 } 690 // Convert the return value (if any) and return it 691 // Note: if adapting from non-void to void, the 'return' 692 // instruction will pop the unneeded result 693 Class<?> implReturnClass = implMethodType.returnType(); 694 Class<?> samReturnClass = methodType.returnType(); 695 TypeConvertingMethodAdapter.convertType(cob, implReturnClass, samReturnClass, samReturnClass); 696 cob.return_(TypeKind.from(samReturnClass)); 697 } 698 }; 699 } 700 701 private void convertArgumentTypes(CodeBuilder cob, MethodType samType) { 702 int samParametersLength = samType.parameterCount(); 703 int captureArity = factoryType.parameterCount(); 704 for (int i = 0; i < samParametersLength; i++) { 705 Class<?> argType = samType.parameterType(i); 706 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); 707 TypeConvertingMethodAdapter.convertType(cob, argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i)); 708 } 709 } 710 711 private Opcode invocationOpcode() throws InternalError { 712 return switch (implKind) { 713 case MethodHandleInfo.REF_invokeStatic -> Opcode.INVOKESTATIC; 714 case MethodHandleInfo.REF_newInvokeSpecial -> Opcode.INVOKESPECIAL; 715 case MethodHandleInfo.REF_invokeVirtual -> Opcode.INVOKEVIRTUAL; 716 case MethodHandleInfo.REF_invokeInterface -> Opcode.INVOKEINTERFACE; 717 case MethodHandleInfo.REF_invokeSpecial -> Opcode.INVOKESPECIAL; 718 default -> throw new InternalError("Unexpected invocation kind: " + implKind); 719 }; 720 } 721 722 static ClassDesc implClassDesc(Class<?> cls) { 723 return cls.isHidden() ? null : ConstantUtils.referenceClassDesc(cls.descriptorString()); 724 } 725 726 static ClassDesc classDesc(Class<?> cls) { 727 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 728 : ConstantUtils.referenceClassDesc(cls.descriptorString()); 729 } 730 731 static MethodTypeDesc methodDesc(MethodType mt) { 732 var params = new ClassDesc[mt.parameterCount()]; 733 for (int i = 0; i < params.length; i++) { 734 params[i] = classDesc(mt.parameterType(i)); 735 } 736 return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params); 737 } 738 }