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

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

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