# HG changeset patch # User Vojin Jovanovic # Date 1572383363 -3600 # Tue Oct 29 22:09:23 2019 +0100 # Node ID 4b8b35f722b06631b7d12e040c94c704c4fdd14f # Parent 67009d58dd7024e353693fec14f26300e9c9ddac 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. diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -32,7 +32,6 @@ 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; @@ -56,6 +55,7 @@ private static final String JAVA_LANG_OBJECT = "java/lang/Object"; private static final String NAME_CTOR = ""; 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"; @@ -180,49 +180,21 @@ * * @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 { + 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); - 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 { - 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); - } + return new ConstantCallSite(lambdaHandle); + } + catch (ReflectiveOperationException e) { + throw new LambdaConversionException("Exception finding lambda ", e); } } @@ -272,8 +244,9 @@ } generateConstructor(); - - if (invokedType.parameterCount() != 0) { + if (invokedType.parameterCount() == 0) { + generateStaticField(); + } else { generateFactory(); } @@ -322,23 +295,50 @@ } /** + * 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, "", "()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(); } /** # HG changeset patch # User Vojin Jovanovic # Date 1572383636 -3600 # Tue Oct 29 22:13:56 2019 +0100 # Node ID 11205395c222da07d0e060f392c510729b401e3b # Parent 4b8b35f722b06631b7d12e040c94c704c4fdd14f Eagerly initialize lambdas by default But leave an option (jdk.internal.lambda.disableEagerInitialization) to disable this optimization. diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -28,6 +28,7 @@ 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; @@ -87,10 +88,16 @@ // 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 @@ -187,6 +194,9 @@ CallSite buildCallSite() throws LambdaConversionException { final Class innerClass = spinInnerClass(); try { + if (!disableEagerInitialization) { + UNSAFE.ensureClassInitialized(innerClass); + } 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);