8232806: Introduce a system property to disable eager lambda initialization
authorvjovanovic
Mon, 28 Oct 2019 15:03:36 +0100
changeset 58871 27c2d2a4b695
parent 58868 f547a06da806
child 58872 ca70299778b9
8232806: Introduce a system property to disable eager lambda initialization Reviewed-by: briangoetz, mr, psandoz, forax
src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java
test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Oct 31 10:37:03 2019 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Mon Oct 28 15:03:36 2019 +0100
@@ -29,6 +29,7 @@
 import sun.invoke.util.BytecodeDescriptor;
 import jdk.internal.misc.Unsafe;
 import sun.security.action.GetPropertyAction;
+import sun.security.action.GetBooleanAction;
 
 import java.io.FilePermission;
 import java.io.Serializable;
@@ -87,10 +88,15 @@
     // For dumping generated classes to disk, for debugging purposes
     private static final ProxyClassesDumper dumper;
 
+    private static final boolean disableEagerInitialization;
+
     static {
-        final String key = "jdk.internal.lambda.dumpProxyClasses";
-        String path = GetPropertyAction.privilegedGetProperty(key);
-        dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
+        final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
+        String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
+        dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
+
+        final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
+        disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
     }
 
     // See context values in AbstractValidatingLambdaMetafactory
@@ -187,7 +193,9 @@
     @Override
     CallSite buildCallSite() throws LambdaConversionException {
         final Class<?> innerClass = spinInnerClass();
-        if (invokedType.parameterCount() == 0) {
+        if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
+            // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
+            // unless we've suppressed eager initialization
             final Constructor<?>[] ctrs = AccessController.doPrivileged(
                     new PrivilegedAction<>() {
                 @Override
@@ -215,7 +223,9 @@
             }
         } else {
             try {
-                UNSAFE.ensureClassInitialized(innerClass);
+                if (!disableEagerInitialization) {
+                    UNSAFE.ensureClassInitialized(innerClass);
+                }
                 return new ConstantCallSite(
                         MethodHandles.Lookup.IMPL_LOOKUP
                              .findStatic(innerClass, NAME_FACTORY, invokedType));
@@ -273,7 +283,7 @@
 
         generateConstructor();
 
-        if (invokedType.parameterCount() != 0) {
+        if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
             generateFactory();
         }
 
--- a/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java	Thu Oct 31 10:37:03 2019 +0100
+++ b/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java	Mon Oct 28 15:03:36 2019 +0100
@@ -26,8 +26,11 @@
  * @bug 8003280
  * @summary Add lambda tests
  *   Test bridge methods for certain SAM conversions
+ *   Tests that jdk.internal.lambda.disableEagerInitialization=true creates a
+ *   get$Lambda method for non-capturing lambdas
  * @compile LambdaTest6.java
  * @run main LambdaTest6
+ * @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true LambdaTest6
  */
 
 import java.lang.reflect.Method;
@@ -60,18 +63,37 @@
         return s;
     }
 
+    private static Set<String> allowedMethods() {
+        Set<String> s = new HashSet<>();
+        s.add("m");
+        if (Boolean.getBoolean("jdk.internal.lambda.disableEagerInitialization")) {
+            s.add("get$Lambda");
+        }
+        return s;
+    }
+
+    private static boolean matchingMethodNames(Method[] methods) {
+        Set<String> methodNames = new HashSet<>();
+        for (Method m : methods) {
+            methodNames.add(m.getName());
+        }
+        return methodNames.equals(allowedMethods());
+    }
+
     private void test1()
     {
         L la = s -> { };
         la.m("hi");
         Class<? extends L> c1 = la.getClass();
         Method[] methods = c1.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         for(Method m : methods) {
-            assertTrue(m.getName().equals("m"));
-            Class[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if ("m".equals(m.getName())) {
+                Class[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty() || (types.size() == 1 && types.contains("java.lang.String")));
     }
@@ -82,12 +104,14 @@
         //km.m("hi");
         Class<? extends KM> c2 = km.getClass();
         Method[] methods = c2.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         for(Method m : methods) {
-            assertTrue(m.getName().equals("m"));
-            Class[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if ("m".equals(m.getName())) {
+                Class[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty());
     }
@@ -99,11 +123,13 @@
         assertTrue( ((H)na).m().equals("hi") );
         Class<? extends N> c3 = na.getClass();
         Method[] methods = c3.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         for(Method m : methods) {
-            assertTrue(m.getName().equals("m"));
-            Class returnType = m.getReturnType();
-            assertTrue(types.remove(returnType.getName()));
+            if ("m".equals(m.getName())) {
+                Class returnType = m.getReturnType();
+                assertTrue(types.remove(returnType.getName()));
+            }
         }
         assertTrue(types.size() == 1); //there's a bridge
     }
--- a/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java	Thu Oct 31 10:37:03 2019 +0100
+++ b/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java	Mon Oct 28 15:03:36 2019 +0100
@@ -26,11 +26,15 @@
  * @bug 8003280
  * @summary Add lambda tests
  *   Test bridge methods in certain SAM conversion
+ *   Tests that jdk.internal.lambda.disableEagerInitialization=true creates a
+ *   get$Lambda method for non-capturing lambdas
  * @compile BridgeMethod.java
  * @run main BridgeMethod
+ * @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true BridgeMethod
  */
 
 import java.lang.reflect.Method;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -68,19 +72,38 @@
         return s;
     }
 
+    private static Set<String> allowedMethods() {
+        Set<String> s = new HashSet<>();
+        s.add("m");
+        if (Boolean.getBoolean("jdk.internal.lambda.disableEagerInitialization")) {
+            s.add("get$Lambda");
+        }
+        return s;
+    }
+
+    private static boolean matchingMethodNames(Method[] methods) {
+        Set<String> methodNames = new HashSet<>();
+        for (Method m : methods) {
+            methodNames.add(m.getName());
+        }
+        return methodNames.equals(allowedMethods());
+    }
+
     public static void main(String[] args) {
         L la = BridgeMethod::bar; //static reference
         la.m("hi");
         Class<? extends L> c1 = la.getClass();
         Method[] methods = c1.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         System.out.println("methods in SAM conversion of L:");
         for(Method m : methods) {
-            System.out.println(m.toGenericString());
-            assertTrue(m.getName().equals("m"));
-            Class[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if (m.getName().equals("m")) {
+                System.out.println(m.toGenericString());
+                Class[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty() || (types.size() == 1 && types.contains("java.lang.String")));
 
@@ -88,14 +111,16 @@
         //km.m("hi"); //will be uncommented when CR7028808 fixed
         Class<? extends KM> c2 = km.getClass();
         methods = c2.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         types = setOfStringObject();
         System.out.println("methods in SAM conversion of KM:");
         for(Method m : methods) {
-            System.out.println(m.toGenericString());
-            assertTrue(m.getName().equals("m"));
-            Class<?>[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if (m.getName().equals("m")) {
+                System.out.println(m.toGenericString());
+                Class<?>[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty());