This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY-11905
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 1b01ffee6d70278218a6ef9264c4deb8686c341f
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Apr 5 22:28:54 2026 +0900

    GROOVY-11905: Optimize non-capturing lambdas
---
 .../classgen/asm/sc/StaticTypesLambdaWriter.java   | 37 ++++++++++++++++------
 .../groovy/classgen/asm/TypeAnnotationsTest.groovy |  2 +-
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
index b804702754..7843c8504e 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
@@ -72,6 +72,7 @@ import static org.objectweb.asm.Opcodes.ACC_STATIC;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.CHECKCAST;
 import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
 import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
 import static org.objectweb.asm.Opcodes.ICONST_0;
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
@@ -113,27 +114,45 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
implements AbstractFun
         ClassNode lambdaClass = getOrAddLambdaClass(expression, 
abstractMethod);
         MethodNode lambdaMethod = lambdaClass.getMethods(DO_CALL).get(0);
 
-        boolean canDeserialize = 
controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass),
 createDeserializeLambdaMethodParams());
-        if (!canDeserialize) {
-            if (expression.isSerializable()) {
-                addDeserializeLambdaMethodForEachLambdaExpression(expression, 
lambdaClass);
-                addDeserializeLambdaMethod();
+        Parameter[] lambdaSharedVariables = 
expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
+        boolean accessingInstanceMembers = 
isAccessingInstanceMembersOfEnclosingClass(lambdaMethod);
+        // For non-capturing lambdas: make doCall static and use a 
capture-free invokedynamic,
+        // so LambdaMetafactory creates a singleton instance — just like Java 
non-capturing lambdas.
+        boolean isNonCapturing = lambdaSharedVariables.length == 0 && 
!accessingInstanceMembers && !expression.isSerializable();
+
+        if (isNonCapturing) {
+            lambdaMethod.setModifiers(lambdaMethod.getModifiers() | 
ACC_STATIC);
+        } else {
+            boolean canDeserialize = 
controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass),
 createDeserializeLambdaMethodParams());
+            if (!canDeserialize) {
+                if (expression.isSerializable()) {
+                    
addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass);
+                    addDeserializeLambdaMethod();
+                }
+                newGroovyLambdaWrapperAndLoad(lambdaClass, expression, 
accessingInstanceMembers);
             }
-            newGroovyLambdaWrapperAndLoad(lambdaClass, expression, 
isAccessingInstanceMembersOfEnclosingClass(lambdaMethod));
         }
 
         MethodVisitor mv = controller.getMethodVisitor();
         mv.visitInvokeDynamicInsn(
                 abstractMethod.getName(),
-                createAbstractMethodDesc(functionalType.redirect(), 
lambdaClass),
+                isNonCapturing
+                        ? 
BytecodeHelper.getMethodDescriptor(functionalType.redirect(), 
Parameter.EMPTY_ARRAY)
+                        : createAbstractMethodDesc(functionalType.redirect(), 
lambdaClass),
                 createBootstrapMethod(controller.getClassNode().isInterface(), 
expression.isSerializable()),
-                
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), 
H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), 
expression.isSerializable())
+                
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod),
+                        isNonCapturing ? H_INVOKESTATIC : H_INVOKEVIRTUAL,
+                        lambdaClass, lambdaMethod, 
lambdaMethod.getParameters(), expression.isSerializable())
         );
         if (expression.isSerializable()) {
             mv.visitTypeInsn(CHECKCAST, "java/io/Serializable");
         }
 
-        controller.getOperandStack().replace(functionalType.redirect(), 1);
+        if (isNonCapturing) {
+            controller.getOperandStack().push(functionalType.redirect());
+        } else {
+            controller.getOperandStack().replace(functionalType.redirect(), 1);
+        }
     }
 
     private static Parameter[] createDeserializeLambdaMethodParams() {
diff --git 
a/src/test/groovy/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy 
b/src/test/groovy/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
index c5270ddec5..cdcf626338 100644
--- 
a/src/test/groovy/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
+++ 
b/src/test/groovy/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
@@ -299,7 +299,7 @@ final class TypeAnnotationsTest extends 
AbstractBytecodeTestCase {
             }
         ''')
         assert bytecode.hasStrictSequence([
-                'public doCall(I)I',
+                'public static doCall(I)I',
                 '@LTypeAnno1;() : METHOD_FORMAL_PARAMETER 0, null',
                 'L0'
         ])

Reply via email to