< prev index next >

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

Print this page

  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.misc.CDS;
 29 import jdk.internal.org.objectweb.asm.*;
 30 import jdk.internal.util.ClassFileDumper;
 31 import sun.invoke.util.BytecodeDescriptor;
 32 import sun.invoke.util.VerifyAccess;
 33 import sun.security.action.GetBooleanAction;
 34 
 35 import java.io.Serializable;
 36 import java.lang.constant.ConstantDescs;

 37 import java.lang.reflect.Modifier;

 38 import java.util.LinkedHashSet;
 39 import java.util.Set;
 40 
 41 import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
 42 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
 43 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
 44 import static java.lang.invoke.MethodType.methodType;
 45 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 46 
 47 /**
 48  * Lambda metafactory implementation which dynamically creates an
 49  * inner-class-like class per lambda callsite.
 50  *
 51  * @see LambdaMetafactory
 52  */
 53 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
 54     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
 55     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
 56     private static final String NAME_CTOR = "<init>";
 57     private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";

297      * @throws LambdaConversionException If properly formed functional interface
298      * is not found
299      */
300     private Class<?> generateInnerClass() throws LambdaConversionException {
301         String[] interfaceNames;
302         String interfaceName = interfaceClass.getName().replace('.', '/');
303         boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass);
304         if (altInterfaces.length == 0) {
305             interfaceNames = new String[]{interfaceName};
306         } else {
307             // Assure no duplicate interfaces (ClassFormatError)
308             Set<String> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1);
309             itfs.add(interfaceName);
310             for (Class<?> i : altInterfaces) {
311                 itfs.add(i.getName().replace('.', '/'));
312                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
313             }
314             interfaceNames = itfs.toArray(new String[itfs.size()]);
315         }
316 
317         cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,

318                  lambdaClassName, null,
319                  JAVA_LANG_OBJECT, interfaceNames);
320 
321         // Generate final fields to be filled in by constructor
322         for (int i = 0; i < argDescs.length; i++) {
323             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
324                                             argNames[i],
325                                             argDescs[i],
326                                             null, null);
327             fv.visitEnd();
328         }
329 
330         generateConstructor();
331 
332         if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
333             generateClassInitializer();
334         }
335 
336         // Forward the SAM method
337         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
338                                           interfaceMethodType.toMethodDescriptorString(), null, null);
339         new ForwardingMethodGenerator(mv).generate(interfaceMethodType);
340 
341         // Forward the altMethods
342         if (altMethods != null) {
343             for (MethodType mt : altMethods) {
344                 mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
345                                     mt.toMethodDescriptorString(), null, null);
346                 new ForwardingMethodGenerator(mv).generate(mt);
347             }
348         }
349 
350         if (isSerializable)
351             generateSerializationFriendlyMethods();
352         else if (accidentallySerializable)
353             generateSerializationHostileMethods();
354 












355         cw.visitEnd();
356 
357         // Define the generated class in this VM.
358 
359         final byte[] classBytes = cw.toByteArray();
360         try {
361             // this class is linked at the indy callsite; so define a hidden nestmate
362             var classdata = useImplMethodHandle? implementation : null;
363             return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
364                          .defineClass(!disableEagerInitialization, classdata);
365 
366         } catch (Throwable t) {
367             throw new InternalError(t);
368         }
369     }
370 
371     /**
372      * Generate a static field and a static initializer that sets this field to an instance of the lambda
373      */
374     private void generateClassInitializer() {

549             for (int i = 0; i < samParametersLength; i++) {
550                 Class<?> argType = samType.parameterType(i);
551                 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
552                 lvIndex += getParameterSize(argType);
553                 convertType(argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
554             }
555         }
556 
557         private int invocationOpcode() throws InternalError {
558             return switch (implKind) {
559                 case MethodHandleInfo.REF_invokeStatic     -> INVOKESTATIC;
560                 case MethodHandleInfo.REF_newInvokeSpecial -> INVOKESPECIAL;
561                 case MethodHandleInfo.REF_invokeVirtual    -> INVOKEVIRTUAL;
562                 case MethodHandleInfo.REF_invokeInterface  -> INVOKEINTERFACE;
563                 case MethodHandleInfo.REF_invokeSpecial    -> INVOKESPECIAL;
564                 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
565             };
566         }
567     }
568 






























































569     static int getParameterSize(Class<?> c) {
570         if (c == Void.TYPE) {
571             return 0;
572         } else if (c == Long.TYPE || c == Double.TYPE) {
573             return 2;
574         }
575         return 1;
576     }
577 
578     static int getLoadOpcode(Class<?> c) {
579         if(c == Void.TYPE) {
580             throw new InternalError("Unexpected void type of load opcode");
581         }
582         return ILOAD + getOpcodeOffset(c);
583     }
584 
585     static int getReturnOpcode(Class<?> c) {
586         if(c == Void.TYPE) {
587             return RETURN;
588         }

  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.misc.PreviewFeatures;
 29 import jdk.internal.misc.CDS;
 30 import jdk.internal.org.objectweb.asm.*;
 31 import jdk.internal.util.ClassFileDumper;
 32 import sun.invoke.util.BytecodeDescriptor;
 33 import sun.invoke.util.VerifyAccess;
 34 import sun.security.action.GetBooleanAction;
 35 
 36 import java.io.Serializable;
 37 import java.lang.constant.ConstantDescs;
 38 import java.lang.reflect.AccessFlag;
 39 import java.lang.reflect.Modifier;
 40 import java.util.HashSet;
 41 import java.util.LinkedHashSet;
 42 import java.util.Set;
 43 
 44 import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
 45 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
 46 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
 47 import static java.lang.invoke.MethodType.methodType;
 48 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 49 
 50 /**
 51  * Lambda metafactory implementation which dynamically creates an
 52  * inner-class-like class per lambda callsite.
 53  *
 54  * @see LambdaMetafactory
 55  */
 56 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
 57     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
 58     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
 59     private static final String NAME_CTOR = "<init>";
 60     private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";

300      * @throws LambdaConversionException If properly formed functional interface
301      * is not found
302      */
303     private Class<?> generateInnerClass() throws LambdaConversionException {
304         String[] interfaceNames;
305         String interfaceName = interfaceClass.getName().replace('.', '/');
306         boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass);
307         if (altInterfaces.length == 0) {
308             interfaceNames = new String[]{interfaceName};
309         } else {
310             // Assure no duplicate interfaces (ClassFormatError)
311             Set<String> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1);
312             itfs.add(interfaceName);
313             for (Class<?> i : altInterfaces) {
314                 itfs.add(i.getName().replace('.', '/'));
315                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
316             }
317             interfaceNames = itfs.toArray(new String[itfs.size()]);
318         }
319 
320         int version = CLASSFILE_VERSION | (PreviewFeatures.isEnabled() ? Opcodes.V_PREVIEW : 0);
321         cw.visit(version, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
322                  lambdaClassName, null,
323                  JAVA_LANG_OBJECT, interfaceNames);
324 
325         // Generate final fields to be filled in by constructor
326         for (int i = 0; i < argDescs.length; i++) {
327             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
328                                             argNames[i],
329                                             argDescs[i],
330                                             null, null);
331             fv.visitEnd();
332         }
333 
334         generateConstructor();
335 
336         if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
337             generateClassInitializer();
338         }
339 
340         // Forward the SAM method
341         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
342                                           interfaceMethodType.toMethodDescriptorString(), null, null);
343         new ForwardingMethodGenerator(mv).generate(interfaceMethodType);
344 
345         // Forward the altMethods
346         if (altMethods != null) {
347             for (MethodType mt : altMethods) {
348                 mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
349                                     mt.toMethodDescriptorString(), null, null);
350                 new ForwardingMethodGenerator(mv).generate(mt);
351             }
352         }
353 
354         if (isSerializable)
355             generateSerializationFriendlyMethods();
356         else if (accidentallySerializable)
357             generateSerializationHostileMethods();
358 
359         // generate LoadableDescriptors attribute if it references any value class
360         if (PreviewFeatures.isEnabled()) {
361             LoadableDescriptorsAttributeBuilder builder = new LoadableDescriptorsAttributeBuilder(targetClass);
362             builder.add(factoryType)
363                     .add(interfaceMethodType)
364                     .add(implMethodType)
365                     .add(dynamicMethodType)
366                     .add(altMethods);
367             if (!builder.isEmpty())
368                 cw.visitAttribute(builder.build());
369         }
370 
371         cw.visitEnd();
372 
373         // Define the generated class in this VM.
374 
375         final byte[] classBytes = cw.toByteArray();
376         try {
377             // this class is linked at the indy callsite; so define a hidden nestmate
378             var classdata = useImplMethodHandle? implementation : null;
379             return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
380                          .defineClass(!disableEagerInitialization, classdata);
381 
382         } catch (Throwable t) {
383             throw new InternalError(t);
384         }
385     }
386 
387     /**
388      * Generate a static field and a static initializer that sets this field to an instance of the lambda
389      */
390     private void generateClassInitializer() {

565             for (int i = 0; i < samParametersLength; i++) {
566                 Class<?> argType = samType.parameterType(i);
567                 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
568                 lvIndex += getParameterSize(argType);
569                 convertType(argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
570             }
571         }
572 
573         private int invocationOpcode() throws InternalError {
574             return switch (implKind) {
575                 case MethodHandleInfo.REF_invokeStatic     -> INVOKESTATIC;
576                 case MethodHandleInfo.REF_newInvokeSpecial -> INVOKESPECIAL;
577                 case MethodHandleInfo.REF_invokeVirtual    -> INVOKEVIRTUAL;
578                 case MethodHandleInfo.REF_invokeInterface  -> INVOKEINTERFACE;
579                 case MethodHandleInfo.REF_invokeSpecial    -> INVOKESPECIAL;
580                 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
581             };
582         }
583     }
584 
585     /*
586      * LoadableDescriptors attribute builder
587      */
588     static class LoadableDescriptorsAttributeBuilder {
589         private final Set<String> loadableDescriptors = new HashSet<>();
590         LoadableDescriptorsAttributeBuilder(Class<?> targetClass) {
591             if (requiresLoadableDescriptors(targetClass)) {
592                 loadableDescriptors.add(Type.getDescriptor(targetClass));
593             }
594         }
595 
596         /*
597          * Add the value types referenced in the given MethodType.
598          */
599         LoadableDescriptorsAttributeBuilder add(MethodType mt) {
600             // parameter types
601             for (Class<?> paramType : mt.ptypes()) {
602                 if (requiresLoadableDescriptors(paramType)) {
603                     loadableDescriptors.add(Type.getDescriptor(paramType));
604                 }
605             }
606             // return type
607             if (requiresLoadableDescriptors(mt.returnType())) {
608                 loadableDescriptors.add(Type.getDescriptor(mt.returnType()));
609             }
610             return this;
611         }
612 
613         LoadableDescriptorsAttributeBuilder add(MethodType... mtypes) {
614             for (MethodType mt : mtypes) {
615                 add(mt);
616             }
617             return this;
618         }
619 
620         boolean requiresLoadableDescriptors(Class<?> cls) {
621             return cls.isValue() && cls.accessFlags().contains(AccessFlag.FINAL);
622         }
623 
624         boolean isEmpty() {
625             return loadableDescriptors.isEmpty();
626         }
627 
628         Attribute build() {
629             return new Attribute("LoadableDescriptors") {
630                 @Override
631                 protected ByteVector write(ClassWriter cw,
632                                            byte[] code,
633                                            int len,
634                                            int maxStack,
635                                            int maxLocals) {
636                     ByteVector attr = new ByteVector();
637                     attr.putShort(loadableDescriptors.size());
638                     for (String s : loadableDescriptors) {
639                         attr.putShort(cw.newUTF8(s));
640                     }
641                     return attr;
642                 }
643             };
644         }
645     }
646 
647     static int getParameterSize(Class<?> c) {
648         if (c == Void.TYPE) {
649             return 0;
650         } else if (c == Long.TYPE || c == Double.TYPE) {
651             return 2;
652         }
653         return 1;
654     }
655 
656     static int getLoadOpcode(Class<?> c) {
657         if(c == Void.TYPE) {
658             throw new InternalError("Unexpected void type of load opcode");
659         }
660         return ILOAD + getOpcodeOffset(c);
661     }
662 
663     static int getReturnOpcode(Class<?> c) {
664         if(c == Void.TYPE) {
665             return RETURN;
666         }
< prev index next >