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.constant.ClassDesc;
41 import java.lang.constant.MethodTypeDesc;
42 import java.lang.reflect.Modifier;
43 import java.util.LinkedHashSet;
44 import java.util.List;
45 import java.util.Set;
46 import java.util.function.Consumer;
47
48 import static java.lang.classfile.ClassFile.*;
49 import java.lang.classfile.attribute.ExceptionsAttribute;
50 import java.lang.classfile.constantpool.ClassEntry;
51 import java.lang.classfile.constantpool.ConstantPoolBuilder;
52
53 import static java.lang.constant.ConstantDescs.*;
54 import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS;
55 import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK;
56 import jdk.internal.constant.ConstantUtils;
57 import jdk.internal.constant.MethodTypeDescImpl;
58 import jdk.internal.vm.annotation.Stable;
59 import sun.invoke.util.Wrapper;
60
61 /**
62 * Lambda metafactory implementation which dynamically creates an
63 * inner-class-like class per lambda callsite.
64 *
65 * @see LambdaMetafactory
66 */
67 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
68 private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
69 private static final @Stable String[] ARG_NAME_CACHE = {"arg$1", "arg$2", "arg$3", "arg$4", "arg$5", "arg$6", "arg$7", "arg$8"};
70 private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC;
71
293 private Class<?> generateInnerClass() throws LambdaConversionException {
294 List<ClassDesc> interfaces;
295 ClassDesc interfaceDesc = classDesc(interfaceClass);
296 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass);
297 if (altInterfaces.length == 0) {
298 interfaces = List.of(interfaceDesc);
299 } else {
300 // Assure no duplicate interfaces (ClassFormatError)
301 Set<ClassDesc> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1);
302 itfs.add(interfaceDesc);
303 for (Class<?> i : altInterfaces) {
304 itfs.add(classDesc(i));
305 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
306 }
307 interfaces = List.copyOf(itfs);
308 }
309 final boolean finalAccidentallySerializable = accidentallySerializable;
310 final byte[] classBytes = ClassFile.of().build(lambdaClassEntry, pool, new Consumer<ClassBuilder>() {
311 @Override
312 public void accept(ClassBuilder clb) {
313 clb.withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC)
314 .withInterfaceSymbols(interfaces);
315 // Generate final fields to be filled in by constructor
316 for (int i = 0; i < argDescs.length; i++) {
317 clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL);
318 }
319
320 generateConstructor(clb);
321
322 if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
323 generateClassInitializer(clb);
324 }
325
326 // Forward the SAM method
327 clb.withMethodBody(interfaceMethodName,
328 methodDesc(interfaceMethodType),
329 ACC_PUBLIC,
330 forwardingMethod(interfaceMethodType));
331
332 // Forward the bridges
333 if (altMethods != null) {
334 for (MethodType mt : altMethods) {
527 // Note: if adapting from non-void to void, the 'return'
528 // instruction will pop the unneeded result
529 Class<?> implReturnClass = implMethodType.returnType();
530 Class<?> samReturnClass = methodType.returnType();
531 TypeConvertingMethodAdapter.convertType(cob, implReturnClass, samReturnClass, samReturnClass);
532 cob.return_(TypeKind.from(samReturnClass));
533 }
534 };
535 }
536
537 private void convertArgumentTypes(CodeBuilder cob, MethodType samType) {
538 int samParametersLength = samType.parameterCount();
539 int captureArity = factoryType.parameterCount();
540 for (int i = 0; i < samParametersLength; i++) {
541 Class<?> argType = samType.parameterType(i);
542 cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i));
543 TypeConvertingMethodAdapter.convertType(cob, argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
544 }
545 }
546
547 private Opcode invocationOpcode() throws InternalError {
548 return switch (implKind) {
549 case MethodHandleInfo.REF_invokeStatic -> Opcode.INVOKESTATIC;
550 case MethodHandleInfo.REF_newInvokeSpecial -> Opcode.INVOKESPECIAL;
551 case MethodHandleInfo.REF_invokeVirtual -> Opcode.INVOKEVIRTUAL;
552 case MethodHandleInfo.REF_invokeInterface -> Opcode.INVOKEINTERFACE;
553 case MethodHandleInfo.REF_invokeSpecial -> Opcode.INVOKESPECIAL;
554 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
555 };
556 }
557
558 static ClassDesc implClassDesc(Class<?> cls) {
559 return cls.isHidden() ? null : ConstantUtils.referenceClassDesc(cls.descriptorString());
560 }
561
562 static ClassDesc classDesc(Class<?> cls) {
563 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
564 : ConstantUtils.referenceClassDesc(cls.descriptorString());
565 }
566
|
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
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() ? 0xFFFF0000 : 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) {
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
|