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