8232806: Introduce a system property to disable eager lambda initialization
Reviewed-by: briangoetz, mr, psandoz, forax
--- 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());