< 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$";

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

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










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

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


































































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

  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$";

296      * @throws LambdaConversionException If properly formed functional interface
297      * is not found
298      */
299     private Class<?> generateInnerClass() throws LambdaConversionException {
300         String[] interfaceNames;
301         String interfaceName = interfaceClass.getName().replace('.', '/');
302         boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass);
303         if (altInterfaces.length == 0) {
304             interfaceNames = new String[]{interfaceName};
305         } else {
306             // Assure no duplicate interfaces (ClassFormatError)
307             Set<String> itfs = LinkedHashSet.newLinkedHashSet(altInterfaces.length + 1);
308             itfs.add(interfaceName);
309             for (Class<?> i : altInterfaces) {
310                 itfs.add(i.getName().replace('.', '/'));
311                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
312             }
313             interfaceNames = itfs.toArray(new String[itfs.size()]);
314         }
315 
316         int version = CLASSFILE_VERSION | (PreviewFeatures.isEnabled() ? Opcodes.V_PREVIEW : 0);
317         cw.visit(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         // generate Preload attribute if it references any value class
356         PreloadAttributeBuilder builder = new PreloadAttributeBuilder(targetClass);
357         builder.add(factoryType)
358                .add(interfaceMethodType)
359                .add(implMethodType)
360                .add(dynamicMethodType)
361                .add(altMethods);
362         if (!builder.isEmpty())
363             cw.visitAttribute(builder.build());
364 
365         cw.visitEnd();
366 
367         // Define the generated class in this VM.
368 
369         final byte[] classBytes = cw.toByteArray();
370         try {
371             // this class is linked at the indy callsite; so define a hidden nestmate
372             var classdata = useImplMethodHandle? implementation : null;
373             return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
374                          .defineClass(!disableEagerInitialization, classdata);
375 
376         } catch (Throwable t) {
377             throw new InternalError(t);
378         }
379     }
380 
381     /**
382      * Generate a static field and a static initializer that sets this field to an instance of the lambda
383      */
384     private void generateClassInitializer() {

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