You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2022/06/11 04:04:29 UTC

[groovy] branch GROOVY_4_0_X updated (0b5e00ed45 -> 92ca7fbd79)

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

paulk pushed a change to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


    from 0b5e00ed45 Fix missleading error message in GroovyScriptEngine
     new 233d53f3d8 GROOVY-9813: SC: match functional interface to variadic method reference
     new 40c059ea29 GROOVY-9813: add test cases
     new ef7026317f GROOVY-8045: support coercion for variadic parameter
     new 92ca7fbd79 GROOVY-10653: SC: instance target is implied parameter for static method

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/codehaus/groovy/ast/MethodNode.java   |   1 +
 .../codehaus/groovy/ast/tools/ParameterUtils.java  |   8 +
 .../groovy/classgen/AsmClassGenerator.java         |   5 +-
 ...StaticTypesMethodReferenceExpressionWriter.java | 219 ++++++++++++++-------
 .../codehaus/groovy/reflection/ParameterTypes.java | 123 ++++++------
 .../transform/stc/StaticTypeCheckingSupport.java   |   4 +-
 .../groovy/transform/stc/CoercionSTCTest.groovy    |  11 ++
 src/test/groovy/transform/stc/LambdaTest.groovy    |  25 +++
 .../transform/stc/MethodReferenceTest.groovy       | 193 ++++++++++++++++--
 9 files changed, 427 insertions(+), 162 deletions(-)


[groovy] 03/04: GROOVY-8045: support coercion for variadic parameter

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit ef7026317f9b613e3fc9bfc5cee74f105e15956c
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed May 25 15:40:57 2022 -0500

    GROOVY-8045: support coercion for variadic parameter
---
 .../codehaus/groovy/reflection/ParameterTypes.java | 123 ++++++++++-----------
 .../groovy/transform/stc/CoercionSTCTest.groovy    |  11 ++
 2 files changed, 69 insertions(+), 65 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java b/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java
index 86056e578b..59a745b9a1 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java
@@ -235,19 +235,17 @@ public class ParameterTypes {
         return result;
     }
 
-    public boolean isValidMethod(Class[] arguments) {
-        if (arguments == null) return true;
-
-        final int size = arguments.length;
+    public boolean isValidMethod(Class[] argumentTypes) {
+        if (argumentTypes == null) return true;
         CachedClass[] pt = getParameterTypes();
-        final int paramMinus1 = pt.length - 1;
-
-        if (isVargsMethod && size >= paramMinus1)
-            return isValidVarargsMethod(arguments, size, pt, paramMinus1);
-        else if (pt.length == size)
-            return isValidExactMethod(arguments, pt);
-        else if (pt.length == 1 && size == 0 && !pt[0].isPrimitive)
-            return true;
+        final int nArguments = argumentTypes.length, nParameters = pt.length, nthParameter = nParameters - 1;
+
+        if (isVargsMethod && nArguments >= nthParameter)
+            return isValidVargsMethod(argumentTypes, pt, nthParameter);
+        else if (nArguments == nParameters)
+            return isValidExactMethod(argumentTypes, pt);
+        else if (nArguments == 0 && nParameters == 1 && !pt[0].isPrimitive)
+            return true; // implicit null argument
         return false;
     }
 
@@ -292,77 +290,72 @@ public class ParameterTypes {
         return true;
     }
 
-    private static boolean testComponentAssignable(Class toTestAgainst, Class toTest) {
-        Class component = toTest.getComponentType();
-        if (component == null) return false;
-        return MetaClassHelper.isAssignableFrom(toTestAgainst, component);
-    }
-
-    private static boolean isValidVarargsMethod(Class[] arguments, int size, CachedClass[] pt, int paramMinus1) {
-        // first check normal number of parameters
-        for (int i = 0; i < paramMinus1; i++) {
-            if (pt[i].isAssignableFrom(arguments[i])) continue;
-            return false;
+    private static boolean isValidVargsMethod(Class[] argumentTypes, CachedClass[] parameterTypes, int nthParameter) {
+        for (int i = 0; i < nthParameter; i += 1) {
+            if (!parameterTypes[i].isAssignableFrom(argumentTypes[i])) {
+                return false;
+            }
         }
 
+        CachedClass arrayType = parameterTypes[nthParameter];
+        CachedClass componentType = ReflectionCache.getCachedClass(arrayType.getTheClass().getComponentType());
+
         // check direct match
-        CachedClass varg = pt[paramMinus1];
-        Class clazz = varg.getTheClass().getComponentType();
-        if (size == pt.length &&
-                (varg.isAssignableFrom(arguments[paramMinus1]) ||
-                        testComponentAssignable(clazz, arguments[paramMinus1]))) {
-            return true;
+        if (argumentTypes.length == parameterTypes.length) {
+            Class argumentType = argumentTypes[nthParameter];
+            if (arrayType.isAssignableFrom(argumentType) || (argumentType.isArray()
+                    && componentType.isAssignableFrom(argumentType.getComponentType()))) {
+                return true;
+            }
         }
 
-        // check varged
-        for (int i = paramMinus1; i < size; i++) {
-            if (MetaClassHelper.isAssignableFrom(clazz, arguments[i])) continue;
-            return false;
+        // check vararg match
+        for (int i = nthParameter; i < argumentTypes.length; i += 1) {
+            if (!componentType.isAssignableFrom(argumentTypes[i])) {
+                return false;
+            }
         }
+
         return true;
     }
 
     public boolean isValidMethod(Object[] arguments) {
         if (arguments == null) return true;
-
-        final int size = arguments.length;
-        CachedClass[] paramTypes = getParameterTypes();
-        final int paramMinus1 = paramTypes.length - 1;
-
-        if (size >= paramMinus1 && paramTypes.length > 0 &&
-                paramTypes[(paramMinus1)].isArray) {
-            // first check normal number of parameters
-            for (int i = 0; i < paramMinus1; i++) {
-                if (paramTypes[i].isAssignableFrom(getArgClass(arguments[i]))) continue;
-                return false;
+        final CachedClass[] parameterTypes = getParameterTypes();
+        final int nArguments = arguments.length, nParameters = parameterTypes.length, nthParameter = nParameters - 1;
+
+        if (nParameters > 0 && parameterTypes[nthParameter].isArray && nArguments >= nthParameter) {
+            for (int i = 0; i < nthParameter; i += 1) {
+                if (!parameterTypes[i].isAssignableFrom(getArgClass(arguments[i]))) {
+                    return false;
+                }
             }
-
-
+            CachedClass arrayType = parameterTypes[nthParameter];
+            CachedClass componentType = ReflectionCache.getCachedClass(arrayType.getTheClass().getComponentType());
             // check direct match
-            CachedClass varg = paramTypes[paramMinus1];
-            Class clazz = varg.getTheClass().getComponentType();
-            if (size == paramTypes.length &&
-                    (varg.isAssignableFrom(getArgClass(arguments[paramMinus1])) ||
-                            testComponentAssignable(clazz, getArgClass(arguments[paramMinus1])))) {
-                return true;
+            if (nArguments == parameterTypes.length) {
+                Class argumentType = getArgClass(arguments[nthParameter]);
+                if (arrayType.isAssignableFrom(argumentType) || (argumentType.isArray()
+                        && componentType.isAssignableFrom(argumentType.getComponentType()))) {
+                    return true;
+                }
             }
-
-
-            // check varged
-            for (int i = paramMinus1; i < size; i++) {
-                if (MetaClassHelper.isAssignableFrom(clazz, getArgClass(arguments[i]))) continue;
-                return false;
+            // check vararg match
+            for (int i = nthParameter; i < nArguments; i += 1) {
+                if (!componentType.isAssignableFrom(getArgClass(arguments[i]))) {
+                    return false;
+                }
             }
             return true;
-        } else if (paramTypes.length == size) {
-            // lets check the parameter types match
-            for (int i = 0; i < size; i++) {
-                if (paramTypes[i].isAssignableFrom(getArgClass(arguments[i]))) continue;
-                return false;
+        } else if (nArguments == nParameters) {
+            for (int i = 0; i < nArguments; i += 1) {
+                if (!parameterTypes[i].isAssignableFrom(getArgClass(arguments[i]))) {
+                    return false;
+                }
             }
             return true;
-        } else if (paramTypes.length == 1 && size == 0 && !paramTypes[0].isPrimitive) {
-            return true;
+        } else if (nArguments == 0 && nParameters == 1 && !parameterTypes[0].isPrimitive) {
+            return true; // implicit null argument
         }
         return false;
     }
diff --git a/src/test/groovy/transform/stc/CoercionSTCTest.groovy b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
index 2c100508b7..35f6a1be2c 100644
--- a/src/test/groovy/transform/stc/CoercionSTCTest.groovy
+++ b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
@@ -158,4 +158,15 @@ class CoercionSTCTest extends StaticTypeCheckingTestCase {
             def s = (() -> ['']) as Supplier<Number>
         ''', 'Cannot return value of type java.util.List<java.lang.String> for lambda expecting java.lang.Number'
     }
+
+    // GROOVY-8045
+    void testCoerceToFunctionalInterface2() {
+        assertScript '''import java.util.function.*
+            def f(Supplier<Integer>... suppliers) {
+                suppliers*.get().sum()
+            }
+            Object result = f({->1},{->2})
+            assert result == 3
+        '''
+    }
 }


[groovy] 04/04: GROOVY-10653: SC: instance target is implied parameter for static method

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 92ca7fbd79b074e79ca1a3802ea909da06110ecc
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Jun 9 12:49:35 2022 -0500

    GROOVY-10653: SC: instance target is implied parameter for static method
---
 ...StaticTypesMethodReferenceExpressionWriter.java | 78 +++++++++++-----------
 src/test/groovy/transform/stc/LambdaTest.groovy    | 25 +++++++
 .../transform/stc/MethodReferenceTest.groovy       | 24 +++++--
 3 files changed, 82 insertions(+), 45 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index c16229448d..1e4b3fdaa3 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -39,9 +39,9 @@ import org.codehaus.groovy.syntax.RuntimeParserException;
 import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
 import org.codehaus.groovy.transform.stc.ExtensionMethodNode;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -86,6 +86,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         ClassNode classNode = controller.getClassNode();
         Expression typeOrTargetRef = methodReferenceExpression.getExpression();
         boolean isClassExpression = (typeOrTargetRef instanceof ClassExpression);
+        boolean targetIsArgument = false; // implied argument for expr::staticMethod?
         ClassNode typeOrTargetRefType = isClassExpression ? typeOrTargetRef.getType()
                 : controller.getTypeChooser().resolveType(typeOrTargetRef, classNode);
 
@@ -107,35 +108,44 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
         if (isExtensionMethod(methodRefMethod)) {
             ExtensionMethodNode extensionMethodNode = (ExtensionMethodNode) methodRefMethod;
-            methodRefMethod = extensionMethodNode.getExtensionMethodNode();
-            if (extensionMethodNode.isStaticExtension()) {
+            methodRefMethod  = extensionMethodNode.getExtensionMethodNode();
+            boolean isStatic = extensionMethodNode.isStaticExtension();
+            if (isStatic) { // create adapter method to pass extra argument
                 methodRefMethod = addSyntheticMethodForDGSM(methodRefMethod);
             }
-            typeOrTargetRef = makeClassTarget(methodRefMethod.getDeclaringClass(), typeOrTargetRef);
-            typeOrTargetRefType = typeOrTargetRef.getType();
-
+            if (isStatic || isClassExpression) {
+                // replace expression with declaring type
+                typeOrTargetRefType = methodRefMethod.getDeclaringClass();
+                typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
+            } else { // GROOVY-10653
+                targetIsArgument = true; // ex: "string"::size
+            }
         } else if (isVargs(methodRefMethod.getParameters())) {
             int mParameters = abstractMethod.getParameters().length;
             int nParameters = methodRefMethod.getParameters().length;
             if (isTypeReferringInstanceMethod(typeOrTargetRef, methodRefMethod)) nParameters += 1;
             if (mParameters > nParameters || mParameters == nParameters-1 || (mParameters == nParameters
                     && !isAssignableTo(last(parametersWithExactType).getType(), last(methodRefMethod.getParameters()).getType()))) {
-                methodRefMethod = addSyntheticMethodForVariadicReference(methodRefMethod, mParameters, isClassExpression); // GROOVY-9813
-                if (methodRefMethod.isStatic()) {
-                    typeOrTargetRef = makeClassTarget(methodRefMethod.getDeclaringClass(), typeOrTargetRef);
-                    typeOrTargetRefType = typeOrTargetRef.getType();
+                // GROOVY-9813: reference to variadic method which needs adapter method to match runtime arguments to its parameters
+                if (!isClassExpression && !methodRefMethod.isStatic() && !methodRefMethod.getDeclaringClass().equals(classNode)) {
+                    targetIsArgument = true; // GROOVY-10653: create static adapter in source class with target as first parameter
+                    mParameters += 1;
+                }
+                methodRefMethod = addSyntheticMethodForVariadicReference(methodRefMethod, mParameters, isClassExpression || targetIsArgument);
+                if (methodRefMethod.isStatic() && !targetIsArgument) {
+                    // replace expression with declaring type
+                    typeOrTargetRefType = methodRefMethod.getDeclaringClass();
+                    typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
                 }
             }
         }
 
         if (!isClassExpression) {
-            if (isConstructorReference) {
-                // TODO: move the checking code to the parser
-                addFatalError("Constructor reference must be className::new", methodReferenceExpression);
-            } else if (methodRefMethod.isStatic()) {
-                ClassExpression classExpression = classX(typeOrTargetRefType);
-                classExpression.setSourcePosition(typeOrTargetRef);
-                typeOrTargetRef = classExpression;
+            if (isConstructorReference) { // TODO: move this check to the parser
+                addFatalError("Constructor reference must be TypeName::new", methodReferenceExpression);
+            } else if (methodRefMethod.isStatic() && !targetIsArgument) {
+                // "string"::valueOf refers to static method, so instance is superflous
+                typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
                 isClassExpression = true;
             } else {
                 typeOrTargetRef.visit(controller.getAcg());
@@ -151,20 +161,21 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             referenceKind = Opcodes.H_INVOKEVIRTUAL;
         }
 
+        String methodName = abstractMethod.getName();
+        String methodDesc = BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(),
+                isClassExpression ? Parameter.EMPTY_ARRAY : new Parameter[]{new Parameter(typeOrTargetRefType, "__METHODREF_EXPR_INSTANCE")});
+
         methodRefMethod.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
         try {
-            controller.getMethodVisitor().visitInvokeDynamicInsn(
-                    abstractMethod.getName(),
-                    createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
-                    createBootstrapMethod(classNode.isInterface(), false),
-                    createBootstrapMethodArguments(
-                            abstractMethodDesc,
-                            referenceKind,
-                            isConstructorReference ? classNode : typeOrTargetRefType,
-                            methodRefMethod,
-                            false
-                    )
+            Handle bootstrapMethod = createBootstrapMethod(classNode.isInterface(), false);
+            Object[] bootstrapArgs = createBootstrapMethodArguments(
+                    abstractMethodDesc,
+                    referenceKind,
+                    methodRefMethod.getDeclaringClass(),
+                    methodRefMethod,
+                    false
             );
+            controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, methodDesc, bootstrapMethod, bootstrapArgs);
         } finally {
             methodRefMethod.removeNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE);
         }
@@ -281,17 +292,6 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             returnS(returnValue));
     }
 
-    private String createAbstractMethodDesc(final ClassNode functionalInterfaceType, final Expression methodRef) {
-        List<Parameter> methodReferenceSharedVariableList = new ArrayList<>();
-
-        if (!(methodRef instanceof ClassExpression)) {
-            prependParameter(methodReferenceSharedVariableList, "__METHODREF_EXPR_INSTANCE",
-                controller.getTypeChooser().resolveType(methodRef, controller.getClassNode()));
-        }
-
-        return BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(), methodReferenceSharedVariableList.toArray(Parameter.EMPTY_ARRAY));
-    }
-
     private Parameter[] createParametersWithExactType(final MethodNode abstractMethod, final ClassNode[] inferredParamTypes) {
         // MUST clone the parameters to avoid impacting the original parameter type of SAM
         Parameter[] parameters = GeneralUtils.cloneParams(abstractMethod.getParameters());
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 20451116df..a0165db6a7 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -130,6 +130,31 @@ final class LambdaTest {
         '''
     }
 
+    @Test
+    void testBiFunctionAndVariadicMethod() {
+        assertScript '''
+            import groovy.transform.CompileStatic
+            import java.util.function.BiFunction
+
+            class C {
+                List m(... args) {
+                    [this,*args]
+                }
+            }
+
+            @CompileStatic
+            void test(C c) {
+                BiFunction<Integer, Integer, List> f = (i, j) -> c.m(i, j)
+                def list = f.apply(1,2)
+                assert list.size() == 3
+                assert list[0] == c
+                assert list[1] == 1
+                assert list[2] == 2
+            }
+            test(new C())
+        '''
+    }
+
     @Test
     void testPredicate() {
         assertScript '''
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index e3b5f894cb..fc9f07d58c 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -294,13 +294,12 @@ final class MethodReferenceTest {
         '''
     }
 
-    @Test // instance::instanceMethod
-    @groovy.test.NotYetImplemented
+    @Test // instance::instanceMethod -- GROOVY-10653
     void testFunctionII2() {
-        assertScript shell, """
+        assertScript shell, '''
             class C {
-                def m(... args) {
-                    [this, *args]
+                List m(... args) {
+                    [this,*args]
                 }
             }
             @CompileStatic
@@ -313,7 +312,20 @@ final class MethodReferenceTest {
                 assert list[2] == 2
             }
             test(new C())
-        """
+        '''
+    }
+
+    @Test // instance::instanceMethod (DGM) -- GROOVY-10653
+    void testFunctionII3() {
+        assertScript shell, '''
+            @CompileStatic
+            int test(CharSequence chars) {
+                IntSupplier sizing = chars::size // from StringGroovyMethods
+                return sizing.getAsInt()
+            }
+            int size = test("foo")
+            assert size == 3
+        '''
     }
 
     @Test // instance::instanceMethod -- GROOVY-10057


[groovy] 02/04: GROOVY-9813: add test cases

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 40c059ea298cc0c303ecd3a7a9cfa635e2f35428
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat May 28 17:44:17 2022 -0500

    GROOVY-9813: add test cases
---
 .../java/org/codehaus/groovy/ast/MethodNode.java   |  1 +
 ...StaticTypesMethodReferenceExpressionWriter.java |  9 +--
 .../transform/stc/MethodReferenceTest.groovy       | 76 ++++++++++++++++++++++
 3 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/MethodNode.java b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
index 34f941918e..331742c46d 100644
--- a/src/main/java/org/codehaus/groovy/ast/MethodNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
@@ -110,6 +110,7 @@ public class MethodNode extends AnnotatedNode {
     public void setModifiers(int modifiers) {
         invalidateCachedData();
         this.modifiers = modifiers;
+        getVariableScope().setInStaticContext(isStatic());
     }
 
     public String getName() {
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index 8a13ac663d..c16229448d 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -211,7 +211,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
     private MethodNode addSyntheticMethodForVariadicReference(final MethodNode mn, final int samParameters, final boolean isStaticTarget) {
         Parameter[] parameters = new Parameter[samParameters];
-        Expression arguments = null, receiver = null;
+        Expression arguments, receiver;
         if (mn.isStatic()) {
             for (int i = 0, j = mn.getParameters().length-1; i < samParameters; i += 1) {
                 ClassNode t = mn.getParameters()[Math.min(i, j)].getType();
@@ -222,11 +222,11 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             receiver = classX(mn.getDeclaringClass());
         } else {
             int p = 0;
-            if (isStaticTarget) parameters[p++] = new Parameter(mn.getDeclaringClass(), "o");
-            for (int i = 0, j = mn.getParameters().length-1; i < samParameters - p; i += 1) {
+            if (isStaticTarget) parameters[p++] = new Parameter(mn.getDeclaringClass(), "target");
+            for (int i = 0, j = mn.getParameters().length-1, n = samParameters-p; i < n; i += 1) {
                 ClassNode t = mn.getParameters()[Math.min(i, j)].getType();
                 if (i >= j) t = t.getComponentType(); // targets the array
-                parameters[p++] = new Parameter(t, "p" + p);
+                parameters[p] = new Parameter(t, "p" + p); p += 1;
             }
             if (isStaticTarget) {
                 arguments = new ArgumentListExpression(removeFirstParameter(parameters));
@@ -247,6 +247,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         MethodNode delegateMethod = addSyntheticMethod(methodName, mn.getReturnType(), methodCall, parameters, mn.getExceptions());
         if (!isStaticTarget && !mn.isStatic()) delegateMethod.setModifiers(delegateMethod.getModifiers() & ~Opcodes.ACC_STATIC);
         delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
+        delegateMethod.setGenericsTypes(mn.getGenericsTypes());
         return delegateMethod;
     }
 
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index f1188942e7..e3b5f894cb 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -240,6 +240,82 @@ final class MethodReferenceTest {
         '''
     }
 
+    @Test // instance::instanceMethod -- GROOVY-9813
+    void testFunctionII() {
+        String asList = '''
+            def <T> List<T> asList(T... a) {
+                return Arrays.asList(a)
+            }
+        '''
+
+        assertScript shell, asList + '''
+            @CompileStatic
+            void test() {
+                Supplier<List> zero = this::asList
+                def list = zero.get()
+                assert list.isEmpty()
+            }
+            test()
+        '''
+
+        assertScript shell, asList + '''
+            @CompileStatic
+            void test() {
+                Function<Integer, List> one = this::asList
+                def list = one.apply(1)
+                assert list.size() == 1
+                assert list[0] == 1
+            }
+            test()
+        '''
+
+        assertScript shell, asList + '''
+            @CompileStatic
+            void test() {
+                BiFunction<Integer, Integer, List> two = this::asList
+                def list = two.apply(2,3)
+                assert list.size() == 2
+                assert list[0] == 2
+                assert list[1] == 3
+            }
+            test()
+        '''
+
+        assertScript shell, asList + '''
+            @CompileStatic
+            void test() { def that = this
+                BiFunction<Integer, Integer, List> two = that::asList
+                def list = two.apply(2,3)
+                assert list.size() == 2
+                assert list[0] == 2
+                assert list[1] == 3
+            }
+            test()
+        '''
+    }
+
+    @Test // instance::instanceMethod
+    @groovy.test.NotYetImplemented
+    void testFunctionII2() {
+        assertScript shell, """
+            class C {
+                def m(... args) {
+                    [this, *args]
+                }
+            }
+            @CompileStatic
+            void test(C c) {
+                BiFunction<Integer, Integer, List> two = c::m
+                def list = two.apply(1,2)
+                assert list.size() == 3
+                assert list[0] == c
+                assert list[1] == 1
+                assert list[2] == 2
+            }
+            test(new C())
+        """
+    }
+
     @Test // instance::instanceMethod -- GROOVY-10057
     void testPredicateII() {
         assertScript shell, '''


[groovy] 01/04: GROOVY-9813: SC: match functional interface to variadic method reference

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 233d53f3d867844145912973859b1c24797d37fc
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat May 28 13:25:54 2022 -0500

    GROOVY-9813: SC: match functional interface to variadic method reference
---
 .../codehaus/groovy/ast/tools/ParameterUtils.java  |   8 +
 .../groovy/classgen/AsmClassGenerator.java         |   5 +-
 ...StaticTypesMethodReferenceExpressionWriter.java | 180 +++++++++++++++------
 .../transform/stc/StaticTypeCheckingSupport.java   |   4 +-
 .../transform/stc/MethodReferenceTest.groovy       | 105 +++++++++---
 5 files changed, 224 insertions(+), 78 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java
index 372c4aa3f3..a7db7d998c 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java
@@ -27,6 +27,14 @@ import java.util.function.BiPredicate;
 
 public class ParameterUtils {
 
+    /**
+     * @since 5.0.0
+     */
+    public static boolean isVargs(final Parameter[] parameters) {
+        if (parameters == null || parameters.length == 0) return false;
+        return (parameters[parameters.length - 1].getType().isArray());
+    }
+
     public static boolean parametersEqual(final Parameter[] a, final Parameter[] b) {
         return parametersEqual(a, b, false);
     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 800dc38600..46193cf84e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -153,6 +153,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.maybeFallsThrough;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
+import static org.codehaus.groovy.ast.tools.ParameterUtils.isVargs;
 import static org.codehaus.groovy.transform.SealedASTTransformation.sealedNative;
 import static org.codehaus.groovy.transform.SealedASTTransformation.sealedSkipAnnotation;
 import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PROPERTY_OWNER;
@@ -2326,10 +2327,6 @@ public class AsmClassGenerator extends ClassGenerator {
         return ExpressionUtils.isNullConstant(expression);
     }
 
-    private static boolean isVargs(final Parameter[] parameters) {
-        return (parameters.length > 0 && parameters[parameters.length - 1].getType().isArray());
-    }
-
     private void loadThis(final VariableExpression thisOrSuper) {
         MethodVisitor mv = controller.getMethodVisitor();
         mv.visitVarInsn(ALOAD, 0);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index 4c5b44fff1..8a13ac663d 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -46,15 +46,15 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.ParameterUtils.isVargs;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.filterMethodsByVisibility;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsForClassNode;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isAssignableTo;
@@ -111,15 +111,23 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             if (extensionMethodNode.isStaticExtension()) {
                 methodRefMethod = addSyntheticMethodForDGSM(methodRefMethod);
             }
-
-            typeOrTargetRefType = methodRefMethod.getDeclaringClass();
-            Expression classExpression = classX(typeOrTargetRefType);
-            classExpression.setSourcePosition(typeOrTargetRef);
-            typeOrTargetRef = classExpression;
+            typeOrTargetRef = makeClassTarget(methodRefMethod.getDeclaringClass(), typeOrTargetRef);
+            typeOrTargetRefType = typeOrTargetRef.getType();
+
+        } else if (isVargs(methodRefMethod.getParameters())) {
+            int mParameters = abstractMethod.getParameters().length;
+            int nParameters = methodRefMethod.getParameters().length;
+            if (isTypeReferringInstanceMethod(typeOrTargetRef, methodRefMethod)) nParameters += 1;
+            if (mParameters > nParameters || mParameters == nParameters-1 || (mParameters == nParameters
+                    && !isAssignableTo(last(parametersWithExactType).getType(), last(methodRefMethod.getParameters()).getType()))) {
+                methodRefMethod = addSyntheticMethodForVariadicReference(methodRefMethod, mParameters, isClassExpression); // GROOVY-9813
+                if (methodRefMethod.isStatic()) {
+                    typeOrTargetRef = makeClassTarget(methodRefMethod.getDeclaringClass(), typeOrTargetRef);
+                    typeOrTargetRefType = typeOrTargetRef.getType();
+                }
+            }
         }
 
-        methodRefMethod.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
-
         if (!isClassExpression) {
             if (isConstructorReference) {
                 // TODO: move the checking code to the parser
@@ -143,18 +151,23 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             referenceKind = Opcodes.H_INVOKEVIRTUAL;
         }
 
-        controller.getMethodVisitor().visitInvokeDynamicInsn(
-                abstractMethod.getName(),
-                createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
-                createBootstrapMethod(classNode.isInterface(), false),
-                createBootstrapMethodArguments(
-                        abstractMethodDesc,
-                        referenceKind,
-                        isConstructorReference ? classNode : typeOrTargetRefType,
-                        methodRefMethod,
-                        false
-                )
-        );
+        methodRefMethod.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
+        try {
+            controller.getMethodVisitor().visitInvokeDynamicInsn(
+                    abstractMethod.getName(),
+                    createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
+                    createBootstrapMethod(classNode.isInterface(), false),
+                    createBootstrapMethodArguments(
+                            abstractMethodDesc,
+                            referenceKind,
+                            isConstructorReference ? classNode : typeOrTargetRefType,
+                            methodRefMethod,
+                            false
+                    )
+            );
+        } finally {
+            methodRefMethod.removeNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE);
+        }
 
         if (isClassExpression) {
             controller.getOperandStack().push(redirect);
@@ -181,29 +194,64 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
     private MethodNode addSyntheticMethodForDGSM(final MethodNode mn) {
         Parameter[] parameters = removeFirstParameter(mn.getParameters());
-        ArgumentListExpression args = args(parameters);
+        ArgumentListExpression args = new ArgumentListExpression(parameters);
         args.getExpressions().add(0, nullX());
 
-        MethodCallExpression returnValue = callX(classX(mn.getDeclaringClass()), mn.getName(), args);
-        returnValue.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, mn);
-        returnValue.setMethodTarget(mn);
+        MethodCallExpression methodCall = callX(classX(mn.getDeclaringClass()), mn.getName(), args);
+        methodCall.setImplicitThis(false);
+        methodCall.setMethodTarget(mn);
+        methodCall.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, mn);
 
-        MethodNode delegateMethod = addGeneratedMethod(controller.getClassNode(),
-                "dgsm$$" + mn.getParameters()[0].getType().getName().replace('.', '$') + "$$" + mn.getName(),
-                Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
-                mn.getReturnType(),
-                parameters,
-                ClassNode.EMPTY_ARRAY,
-                block(returnS(returnValue))
-        );
+        String methodName = "dgsm$$" + mn.getParameters()[0].getType().getName().replace('.', '$') + "$$" + mn.getName();
 
+        MethodNode delegateMethod = addSyntheticMethod(methodName, mn.getReturnType(), methodCall, parameters, mn.getExceptions());
         delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
+        return delegateMethod;
+    }
+
+    private MethodNode addSyntheticMethodForVariadicReference(final MethodNode mn, final int samParameters, final boolean isStaticTarget) {
+        Parameter[] parameters = new Parameter[samParameters];
+        Expression arguments = null, receiver = null;
+        if (mn.isStatic()) {
+            for (int i = 0, j = mn.getParameters().length-1; i < samParameters; i += 1) {
+                ClassNode t = mn.getParameters()[Math.min(i, j)].getType();
+                if (i >= j) t = t.getComponentType(); // targets the array
+                parameters[i] = new Parameter(t, "p" + i);
+            }
+            arguments = new ArgumentListExpression(parameters);
+            receiver = classX(mn.getDeclaringClass());
+        } else {
+            int p = 0;
+            if (isStaticTarget) parameters[p++] = new Parameter(mn.getDeclaringClass(), "o");
+            for (int i = 0, j = mn.getParameters().length-1; i < samParameters - p; i += 1) {
+                ClassNode t = mn.getParameters()[Math.min(i, j)].getType();
+                if (i >= j) t = t.getComponentType(); // targets the array
+                parameters[p++] = new Parameter(t, "p" + p);
+            }
+            if (isStaticTarget) {
+                arguments = new ArgumentListExpression(removeFirstParameter(parameters));
+                receiver = varX(parameters[0]);
+            } else {
+                arguments = new ArgumentListExpression(parameters);
+                receiver = varX("this", controller.getClassNode());
+            }
+        }
+
+        MethodCallExpression methodCall = callX(receiver, mn.getName(), arguments);
+        methodCall.setImplicitThis(false);
+        methodCall.setMethodTarget(mn);
+        methodCall.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, mn);
 
+        String methodName = "adapt$" + mn.getDeclaringClass().getNameWithoutPackage() + "$" + mn.getName() + "$" + System.nanoTime();
+
+        MethodNode delegateMethod = addSyntheticMethod(methodName, mn.getReturnType(), methodCall, parameters, mn.getExceptions());
+        if (!isStaticTarget && !mn.isStatic()) delegateMethod.setModifiers(delegateMethod.getModifiers() & ~Opcodes.ACC_STATIC);
+        delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
         return delegateMethod;
     }
 
-    private MethodNode addSyntheticMethodForConstructorReference(final String syntheticMethodName, final ClassNode returnType, final Parameter[] parametersWithExactType) {
-        ArgumentListExpression ctorArgs = args(parametersWithExactType);
+    private MethodNode addSyntheticMethodForConstructorReference(final String methodName, final ClassNode returnType, final Parameter[] parametersWithExactType) {
+        ArgumentListExpression ctorArgs = new ArgumentListExpression(parametersWithExactType);
 
         Expression returnValue;
         if (returnType.isArray()) {
@@ -214,23 +262,24 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             returnValue = ctorX(returnType, ctorArgs);
         }
 
-        MethodNode delegateMethod = addGeneratedMethod(controller.getClassNode(),
-                syntheticMethodName,
-                Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
-                returnType,
-                parametersWithExactType,
-                ClassNode.EMPTY_ARRAY,
-                block(returnS(returnValue))
-        );
-
+        MethodNode delegateMethod = addSyntheticMethod(methodName, returnType, returnValue, parametersWithExactType, ClassNode.EMPTY_ARRAY);
         // TODO: if StaticTypesMarker.DIRECT_METHOD_CALL_TARGET or
         // OptimizingStatementWriter.StatementMeta.class metadatas
         // can bet set for the ctorX above, then this can be TRUE:
         delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.FALSE);
-
         return delegateMethod;
     }
 
+    private MethodNode addSyntheticMethod(final String methodName, final ClassNode returnType, final Expression returnValue, final Parameter[] parameters, final ClassNode[] exceptions) {
+        return controller.getClassNode().addSyntheticMethod(
+            methodName,
+            Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
+            returnType,
+            parameters,
+            exceptions,
+            returnS(returnValue));
+    }
+
     private String createAbstractMethodDesc(final ClassNode functionalInterfaceType, final Expression methodRef) {
         List<Parameter> methodReferenceSharedVariableList = new ArrayList<>();
 
@@ -266,7 +315,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
     private MethodNode findMethodRefMethod(final String methodName, final Parameter[] samParameters, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType) {
         List<MethodNode> methods = findVisibleMethods(methodName, typeOrTargetRefType);
 
-        return chooseMethodRefMethodCandidate(typeOrTargetRef, methods.stream().filter(method -> {
+        methods.removeIf(method -> {
             Parameter[] parameters = method.getParameters();
             if (isTypeReferringInstanceMethod(typeOrTargetRef, method)) {
                 // there is an implicit parameter for "String::length"
@@ -279,8 +328,31 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
                 parameters = plusOne;
             }
-            return parametersCompatible(samParameters, parameters);
-        }).collect(Collectors.toList()));
+
+            // check direct match
+            if (parametersCompatible(samParameters, parameters)) return false;
+
+            // check vararg match
+            if (isVargs(parameters)) {
+                int nParameters = parameters.length;
+                if (samParameters.length == nParameters - 1) { // 0 case
+                    parameters = Arrays.copyOf(parameters, nParameters - 1);
+                    if (parametersCompatible(samParameters, parameters)) return false;
+                }
+                else if (samParameters.length >= nParameters) { // 1+ case
+                    Parameter p = new Parameter(parameters[nParameters - 1].getType().getComponentType(), "");
+                    parameters = Arrays.copyOf(parameters, samParameters.length);
+                    for (int i = nParameters - 1; i < parameters.length; i += 1){
+                        parameters[i] = p;
+                    }
+                    if (parametersCompatible(samParameters, parameters)) return false;
+                }
+            }
+
+            return true; // no match; remove method
+        });
+
+        return chooseMethodRefMethodCandidate(typeOrTargetRef, methods);
     }
 
     private List<MethodNode> findVisibleMethods(final String name, final ClassNode type) {
@@ -310,15 +382,21 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
                 || (isExtensionMethod(mn) && !((ExtensionMethodNode) mn).isStaticExtension()));
     }
 
+    private static Expression makeClassTarget(final ClassNode target, final Expression source) {
+        Expression expression = classX(target);
+        expression.setSourcePosition(source);
+        return expression;
+    }
+
     private static Parameter[] removeFirstParameter(final Parameter[] parameters) {
         return Arrays.copyOfRange(parameters, 1, parameters.length);
     }
 
     /**
-     * Choose the best method node for method reference.
+     * Chooses the best method node for method reference.
      */
     private static MethodNode chooseMethodRefMethodCandidate(final Expression methodRef, final List<MethodNode> candidates) {
-        if (1 == candidates.size()) return candidates.get(0);
+        if (candidates.size() == 1) return candidates.get(0);
 
         return candidates.stream()
                 .map(e -> Tuple.tuple(e, matchingScore(e, methodRef)))
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 336de67bac..a66471b9d8 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -491,9 +491,9 @@ public abstract class StaticTypeCheckingSupport {
         return (type.isDerivedFrom(CLOSURE_TYPE) && isSAMType(toBeAssignedTo));
     }
 
+    @Deprecated
     static boolean isVargs(final Parameter[] parameters) {
-        if (parameters == null || parameters.length == 0) return false;
-        return (parameters[parameters.length - 1].getType().isArray());
+        return ParameterUtils.isVargs(parameters);
     }
 
     public static boolean isCompareToBoolean(final int op) {
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index a3f401a1f4..f1188942e7 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -126,8 +126,41 @@ final class MethodReferenceTest {
         '''
     }
 
-    @Test // class::instanceMethod -- GROOVY-9853
+    @Test // class::instanceMethod -- GROOVY-9813
     void testFunctionCI6() {
+        String head = '''
+            @CompileStatic
+            class C {
+                def <T> List<T> asList(T... a) {
+                    return Arrays.asList(a)
+                }
+                static main(args) {
+        '''
+        String tail = '''
+                }
+            }
+        '''
+
+        shouldFail shell, head + '''
+            Supplier<List> zero = C::asList
+        ''' + tail
+
+        assertScript shell, head + '''
+            Function<C, List> one = C::asList
+            def list = one.apply(new C())
+            assert list.isEmpty()
+        ''' + tail
+
+        assertScript shell, head + '''
+            BiFunction<C, Integer, List> two = C::asList
+            def list = two.apply(new C(),1)
+            assert list.size() == 1
+            assert list[0] == 1
+        ''' + tail
+    }
+
+    @Test // class::instanceMethod -- GROOVY-9853
+    void testFunctionCI7() {
         assertScript shell, '''
             @CompileStatic
             void test() {
@@ -493,6 +526,32 @@ final class MethodReferenceTest {
         '''
     }
 
+    @Test // class::new
+    void testFunctionCN5() {
+        assertScript shell, '''
+            @CompileStatic
+            void p() {
+                Function<String, Integer> f = Integer::new
+                assert [1, 2, 3] == ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
+            }
+
+            p()
+        '''
+    }
+
+    @Test // arrayClass::new
+    void testIntFunctionCN6() {
+        assertScript shell, '''
+            @CompileStatic
+            void p() {
+                IntFunction<Integer[]> f = Integer[]::new
+                assert new Integer[] { 1, 2, 3 } == [1, 2, 3].stream().toArray(f)
+            }
+
+            p()
+        '''
+    }
+
     @Test // class::staticMethod
     void testFunctionCS() {
         assertScript shell, '''
@@ -545,7 +604,7 @@ final class MethodReferenceTest {
     }
 
     @Test // class::staticMethod
-    void testFunctionCS_RHS() {
+    void testFunctionCS4() {
         assertScript shell, '''
             @CompileStatic
             void p() {
@@ -553,13 +612,12 @@ final class MethodReferenceTest {
                 def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
-
             p()
         '''
     }
 
     @Test // class::staticMethod
-    void testFunctionCS_RHS_NOTYPE() {
+    void testFunctionCS5() {
         assertScript shell, '''
             @CompileStatic
             void p() {
@@ -567,33 +625,42 @@ final class MethodReferenceTest {
                 def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
-
             p()
         '''
     }
 
-    @Test // class::new
-    void testFunctionCN_RHS() {
+    @Test // class::staticMethod -- GROOVY-9813
+    void testFunctionCS6() {
         assertScript shell, '''
             @CompileStatic
             void p() {
-                Function<String, Integer> f = Integer::new
-                assert [1, 2, 3] == ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
+                Supplier<List> zero = Arrays::asList
+                def list = zero.get()
+                assert list.isEmpty()
             }
-
             p()
         '''
-    }
 
-    @Test // arrayClass::new
-    void testIntFunctionCN_RHS() {
         assertScript shell, '''
             @CompileStatic
             void p() {
-                IntFunction<Integer[]> f = Integer[]::new
-                assert new Integer[] { 1, 2, 3 } == [1, 2, 3].stream().toArray(f)
+                Function<Integer, List> one = Arrays::asList
+                def list = one.apply(1)
+                assert list.size() == 1
+                assert list[0] == 1
             }
+            p()
+        '''
 
+        assertScript shell, '''
+            @CompileStatic
+            void p() {
+                BiFunction<Integer, Integer, List> two = Arrays::asList
+                def list = two.apply(2,3)
+                assert list.size() == 2
+                assert list[0] == 2
+                assert list[1] == 3
+            }
             p()
         '''
     }
@@ -606,7 +673,6 @@ final class MethodReferenceTest {
                 def result = ['a', 'ab', 'abc'].stream().map(String::size).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
-
             p()
         '''
     }
@@ -619,7 +685,6 @@ final class MethodReferenceTest {
                 def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
                 assert result.every(e -> e instanceof Thread)
             }
-
             p()
         '''
     }
@@ -633,7 +698,6 @@ final class MethodReferenceTest {
                 assert 3 == result.size()
                 assert ['[a:1]', '[b:2]', '[c:3]'] == result
             }
-
             p()
         '''
     }
@@ -649,7 +713,6 @@ final class MethodReferenceTest {
                 result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
                 assert result.every(e -> e instanceof Thread)
             }
-
             p()
         '''
     }
@@ -713,8 +776,8 @@ final class MethodReferenceTest {
     @Test // GROOVY-10635
     void testRecordComponentMethodReference() {
         assertScript shell, '''
-            record Bar(String name) { }
-
+            record Bar(String name) {
+            }
             def bars = [new Bar(name: 'A'), new Bar(name: 'B')]
             assert bars.stream().map(Bar::name).map(String::toLowerCase).toList() == ['a', 'b']
         '''