Skip to content

Commit 1b01ffe

Browse files
committed
GROOVY-11905: Optimize non-capturing lambdas
1 parent 3bc4500 commit 1b01ffe

2 files changed

Lines changed: 29 additions & 10 deletions

File tree

src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import static org.objectweb.asm.Opcodes.ALOAD;
7373
import static org.objectweb.asm.Opcodes.CHECKCAST;
7474
import static org.objectweb.asm.Opcodes.DUP;
75+
import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
7576
import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
7677
import static org.objectweb.asm.Opcodes.ICONST_0;
7778
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
@@ -113,27 +114,45 @@ public void writeLambda(final LambdaExpression expression) {
113114
ClassNode lambdaClass = getOrAddLambdaClass(expression, abstractMethod);
114115
MethodNode lambdaMethod = lambdaClass.getMethods(DO_CALL).get(0);
115116

116-
boolean canDeserialize = controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams());
117-
if (!canDeserialize) {
118-
if (expression.isSerializable()) {
119-
addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass);
120-
addDeserializeLambdaMethod();
117+
Parameter[] lambdaSharedVariables = expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
118+
boolean accessingInstanceMembers = isAccessingInstanceMembersOfEnclosingClass(lambdaMethod);
119+
// For non-capturing lambdas: make doCall static and use a capture-free invokedynamic,
120+
// so LambdaMetafactory creates a singleton instance — just like Java non-capturing lambdas.
121+
boolean isNonCapturing = lambdaSharedVariables.length == 0 && !accessingInstanceMembers && !expression.isSerializable();
122+
123+
if (isNonCapturing) {
124+
lambdaMethod.setModifiers(lambdaMethod.getModifiers() | ACC_STATIC);
125+
} else {
126+
boolean canDeserialize = controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams());
127+
if (!canDeserialize) {
128+
if (expression.isSerializable()) {
129+
addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass);
130+
addDeserializeLambdaMethod();
131+
}
132+
newGroovyLambdaWrapperAndLoad(lambdaClass, expression, accessingInstanceMembers);
121133
}
122-
newGroovyLambdaWrapperAndLoad(lambdaClass, expression, isAccessingInstanceMembersOfEnclosingClass(lambdaMethod));
123134
}
124135

125136
MethodVisitor mv = controller.getMethodVisitor();
126137
mv.visitInvokeDynamicInsn(
127138
abstractMethod.getName(),
128-
createAbstractMethodDesc(functionalType.redirect(), lambdaClass),
139+
isNonCapturing
140+
? BytecodeHelper.getMethodDescriptor(functionalType.redirect(), Parameter.EMPTY_ARRAY)
141+
: createAbstractMethodDesc(functionalType.redirect(), lambdaClass),
129142
createBootstrapMethod(controller.getClassNode().isInterface(), expression.isSerializable()),
130-
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable())
143+
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod),
144+
isNonCapturing ? H_INVOKESTATIC : H_INVOKEVIRTUAL,
145+
lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable())
131146
);
132147
if (expression.isSerializable()) {
133148
mv.visitTypeInsn(CHECKCAST, "java/io/Serializable");
134149
}
135150

136-
controller.getOperandStack().replace(functionalType.redirect(), 1);
151+
if (isNonCapturing) {
152+
controller.getOperandStack().push(functionalType.redirect());
153+
} else {
154+
controller.getOperandStack().replace(functionalType.redirect(), 1);
155+
}
137156
}
138157

139158
private static Parameter[] createDeserializeLambdaMethodParams() {

src/test/groovy/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ final class TypeAnnotationsTest extends AbstractBytecodeTestCase {
299299
}
300300
''')
301301
assert bytecode.hasStrictSequence([
302-
'public doCall(I)I',
302+
'public static doCall(I)I',
303303
'@LTypeAnno1;() : METHOD_FORMAL_PARAMETER 0, null',
304304
'L0'
305305
])

0 commit comments

Comments
 (0)