< prev index next >

src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java

Print this page

  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 
< prev index next >