< prev index next >

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

Print this page
rev 56749 : Initialize lambdas when executing generated bytecode

Changes the InnerClassLambdaMetaFactory to postpone lambda initialization
until the generated bytecode is executed. This is achieved by generating a
static field containing the singleton instance of the non-capturing lambda.
rev 56750 : Eagerly initialize lambdas by default

But leave an option (jdk.internal.lambda.disableEagerInitialization) to disable
this optimization.

@@ -26,15 +26,15 @@
 package java.lang.invoke;
 
 import jdk.internal.org.objectweb.asm.*;
 import sun.invoke.util.BytecodeDescriptor;
 import jdk.internal.misc.Unsafe;
+import sun.security.action.GetBooleanAction;
 import sun.security.action.GetPropertyAction;
 
 import java.io.FilePermission;
 import java.io.Serializable;
-import java.lang.reflect.Constructor;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.LinkedHashSet;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.PropertyPermission;

@@ -54,10 +54,11 @@
     private static final int CLASSFILE_VERSION = 52;
     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
     private static final String NAME_CTOR = "<init>";
     private static final String NAME_FACTORY = "get$Lambda";
+    private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
 
     //Serialization support
     private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
     private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";

@@ -85,14 +86,20 @@
     private static final AtomicInteger counter = new AtomicInteger(0);
 
     // For dumping generated classes to disk, for debugging purposes
     private static final ProxyClassesDumper dumper;
 
+    // disable optimization that eagerly initializes lambdas
+    private static final boolean disableEagerInitialization;
+
     static {
-        final String key = "jdk.internal.lambda.dumpProxyClasses";
-        String path = GetPropertyAction.privilegedGetProperty(key);
+        final String dumpKey = "jdk.internal.lambda.dumpProxyClasses";
+        String path = GetPropertyAction.privilegedGetProperty(dumpKey);
         dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
+
+        final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
+        disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
     }
 
     // See context values in AbstractValidatingLambdaMetafactory
     private final String implMethodClassName;        // Name of type containing implementation "CC"
     private final String implMethodName;             // Name of implementation method "impl"

@@ -178,53 +185,28 @@
      * of the class which the CallSite will return, otherwise, generate handles
      * which will call the class' constructor.
      *
      * @return a CallSite, which, when invoked, will return an instance of the
      * functional interface
-     * @throws ReflectiveOperationException
      * @throws LambdaConversionException If properly formed functional interface
      * is not found
      */
     @Override
     CallSite buildCallSite() throws LambdaConversionException {
         final Class<?> innerClass = spinInnerClass();
-        if (invokedType.parameterCount() == 0) {
-            final Constructor<?>[] ctrs = AccessController.doPrivileged(
-                    new PrivilegedAction<>() {
-                @Override
-                public Constructor<?>[] run() {
-                    Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
-                    if (ctrs.length == 1) {
-                        // The lambda implementing inner class constructor is private, set
-                        // it accessible (by us) before creating the constant sole instance
-                        ctrs[0].setAccessible(true);
-                    }
-                    return ctrs;
-                }
-                    });
-            if (ctrs.length != 1) {
-                throw new LambdaConversionException("Expected one lambda constructor for "
-                        + innerClass.getCanonicalName() + ", got " + ctrs.length);
-            }
-
-            try {
-                Object inst = ctrs[0].newInstance();
-                return new ConstantCallSite(MethodHandles.constant(samBase, inst));
-            }
-            catch (ReflectiveOperationException e) {
-                throw new LambdaConversionException("Exception instantiating lambda object", e);
-            }
-        } else {
             try {
+            if (!disableEagerInitialization) {
                 UNSAFE.ensureClassInitialized(innerClass);
-                return new ConstantCallSite(
-                        MethodHandles.Lookup.IMPL_LOOKUP
-                             .findStatic(innerClass, NAME_FACTORY, invokedType));
             }
-            catch (ReflectiveOperationException e) {
-                throw new LambdaConversionException("Exception finding constructor", e);
+            MethodHandle lambdaHandle = invokedType.parameterCount() == 0
+                    ? MethodHandles.Lookup.IMPL_LOOKUP.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, invokedType.returnType())
+                    : MethodHandles.Lookup.IMPL_LOOKUP.findStatic(innerClass, NAME_FACTORY, invokedType);
+
+            return new ConstantCallSite(lambdaHandle);
             }
+        catch (ReflectiveOperationException e) {
+            throw new LambdaConversionException("Exception finding lambda ", e);
         }
     }
 
     /**
      * Generate a class file which implements the functional

@@ -270,12 +252,13 @@
                                             null, null);
             fv.visitEnd();
         }
 
         generateConstructor();
-
-        if (invokedType.parameterCount() != 0) {
+        if (invokedType.parameterCount() == 0) {
+            generateStaticField();
+        } else {
             generateFactory();
         }
 
         // Forward the SAM method
         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,

@@ -320,27 +303,54 @@
 
         return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
     }
 
     /**
+     * Generate a static field that contains the singleton instance of the lambda
+     */
+    private void generateStaticField() {
+        String lambdaTypeDescriptor = BytecodeDescriptor.unparse(invokedType.returnType());
+
+        // Generate the static final field that holds the lambda singleton
+        FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null);
+        fv.visitEnd();
+
+        // Instantiate the lambda and store it to the static final field
+        MethodVisitor clinit = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+        clinit.visitCode();
+
+        instantiateLambda(clinit);
+        clinit.visitFieldInsn(PUTSTATIC, lambdaClassName, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor);
+
+        clinit.visitInsn(RETURN);
+        clinit.visitMaxs(-1, -1);
+        clinit.visitEnd();
+    }
+
+    /**
      * Generate the factory method for the class
      */
     private void generateFactory() {
         MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
         m.visitCode();
+
+        instantiateLambda(m);
+        m.visitInsn(ARETURN);
+
+        m.visitMaxs(-1, -1);
+        m.visitEnd();
+    }
+
+    private void instantiateLambda(MethodVisitor m) {
         m.visitTypeInsn(NEW, lambdaClassName);
         m.visitInsn(Opcodes.DUP);
-        int parameterCount = invokedType.parameterCount();
-        for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
+        for (int typeIndex = 0, varIndex = 0; typeIndex < invokedType.parameterCount(); typeIndex++) {
             Class<?> argType = invokedType.parameterType(typeIndex);
             m.visitVarInsn(getLoadOpcode(argType), varIndex);
             varIndex += getParameterSize(argType);
         }
         m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
-        m.visitInsn(ARETURN);
-        m.visitMaxs(-1, -1);
-        m.visitEnd();
     }
 
     /**
      * Generate the constructor for the class
      */
< prev index next >