From 29f2d7417326cb7105c79c46278ec3d1180bbbeb Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Mon, 28 Oct 2019 15:03:36 +0100 Subject: [PATCH] 8232806: Introduce a system property to disable eager lambda initialization Reviewed-by: briangoetz, mr, psandoz, forax --- .../invoke/InnerClassLambdaMetafactory.java | 22 ++++++--- .../lambda/lambdaExpression/LambdaTest6.java | 48 ++++++++++++++----- .../lambda/methodReference/BridgeMethod.java | 45 +++++++++++++---- 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 70db5c9c7a..1c386e181b 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -29,6 +29,7 @@ import jdk.internal.org.objectweb.asm.*; 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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; // 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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; @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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; } } 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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; generateConstructor(); - if (invokedType.parameterCount() != 0) { + if (invokedType.parameterCount() != 0 || disableEagerInitialization) { generateFactory(); } diff --git a/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java b/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java index 76bc2ba34a..a0513860ff 100644 --- a/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java +++ b/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java @@ -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 @@ public class LambdaTest6 { 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 @@ public class LambdaTest6 { //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 @@ public class LambdaTest6 { 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 --git a/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java b/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java index eb68d16276..dd9dd2582a 100644 --- a/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java +++ b/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java @@ -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 @@ public class BridgeMethod { 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 @@ public class BridgeMethod { //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());