# HG changeset patch # User vjovanovic # Date 1572271416 -3600 # Node ID 27c2d2a4b6953a15ee86c1d8ca5edf74524d543e # Parent f547a06da806bfc1c853e44aad9806b193b534cd 8232806: Introduce a system property to disable eager lambda initialization Reviewed-by: briangoetz, mr, psandoz, forax diff -r f547a06da806 -r 27c2d2a4b695 src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.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(); } diff -r f547a06da806 -r 27c2d2a4b695 test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java --- 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 allowedMethods() { + Set 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 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 c1 = la.getClass(); Method[] methods = c1.getDeclaredMethods(); + assertTrue(matchingMethodNames(methods)); Set 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 c2 = km.getClass(); Method[] methods = c2.getDeclaredMethods(); + assertTrue(matchingMethodNames(methods)); Set 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 c3 = na.getClass(); Method[] methods = c3.getDeclaredMethods(); + assertTrue(matchingMethodNames(methods)); Set 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 } diff -r f547a06da806 -r 27c2d2a4b695 test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java --- 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 allowedMethods() { + Set 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 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 c1 = la.getClass(); Method[] methods = c1.getDeclaredMethods(); + assertTrue(matchingMethodNames(methods)); Set 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 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());