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

[groovy] branch GROOVY_3_0_X updated: GROOVY-9853: SC: method reference to interface abstract / default method

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

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


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new a79b3077e7 GROOVY-9853: SC: method reference to interface abstract / default method
a79b3077e7 is described below

commit a79b3077e72185a5a6efcbff386ed27fc6ea05a4
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat May 21 14:07:09 2022 -0500

    GROOVY-9853: SC: method reference to interface abstract / default method
---
 .../asm/sc/AbstractFunctionalInterfaceWriter.java  | 36 +++++-------
 ...StaticTypesMethodReferenceExpressionWriter.java | 13 ++++-
 .../transform/stc/MethodReferenceTest.groovy       | 64 ++++++++++++++++++++++
 3 files changed, 90 insertions(+), 23 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
index 654b54af7f..d8e1542f20 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
@@ -30,7 +30,6 @@ import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 
 import java.util.Arrays;
-import java.util.LinkedList;
 import java.util.List;
 
 import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
@@ -83,28 +82,23 @@ public interface AbstractFunctionalInterfaceWriter {
         );
     }
 
-    default Object[] createBootstrapMethodArguments(String abstractMethodDesc, int insn, ClassNode methodOwnerClassNode, MethodNode methodNode, boolean serializable) {
-        Parameter[] parameters = methodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE);
-        List<Object> argumentList = new LinkedList<>();
-
-        argumentList.add(Type.getType(abstractMethodDesc));
-        argumentList.add(
-                new Handle(
-                        insn,
-                        BytecodeHelper.getClassInternalName(methodOwnerClassNode.getName()),
-                        methodNode.getName(),
-                        BytecodeHelper.getMethodDescriptor(methodNode),
-                        methodOwnerClassNode.isInterface()
-                )
-        );
-        argumentList.add(Type.getType(BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(), parameters)));
+    default Object[] createBootstrapMethodArguments(final String abstractMethodDesc, final int insn, final ClassNode methodOwner, final MethodNode methodNode, final boolean serializable) {
+        Object[] arguments = !serializable ? new Object[3] : new Object[]{null, null, null, 5, 0};
 
-        if (serializable) {
-            argumentList.add(5);
-            argumentList.add(0);
-        }
+        arguments[0] = Type.getType(abstractMethodDesc);
+
+        arguments[1] = new Handle(
+                insn, // H_INVOKESTATIC or H_INVOKEVIRTUAL or H_INVOKEINTERFACE (GROOVY-9853)
+                BytecodeHelper.getClassInternalName(methodOwner.getName()),
+                methodNode.getName(),
+                BytecodeHelper.getMethodDescriptor(methodNode),
+                methodOwner.isInterface());
+
+        arguments[2] = Type.getType(
+                BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(),
+                (Parameter[]) methodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE)));
 
-        return argumentList.toArray();
+        return arguments;
     }
 
     default ClassNode convertParameterType(ClassNode targetType, ClassNode parameterType, ClassNode inferredType) {
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 eefac9c212..536b53c5c9 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
@@ -134,14 +134,23 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             }
         }
 
+        int referenceKind;
+        if (isConstructorReference || methodRefMethod.isStatic()) {
+            referenceKind = Opcodes.H_INVOKESTATIC;
+        } else if (methodRefMethod.getDeclaringClass().isInterface()) {
+            referenceKind = Opcodes.H_INVOKEINTERFACE; // GROOVY-9853
+        } else {
+            referenceKind = Opcodes.H_INVOKEVIRTUAL;
+        }
+
         controller.getMethodVisitor().visitInvokeDynamicInsn(
                 abstractMethod.getName(),
                 createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
                 createBootstrapMethod(classNode.isInterface(), false),
                 createBootstrapMethodArguments(
                         abstractMethodDesc,
-                        methodRefMethod.isStatic() || isConstructorReference ? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL,
-                        isConstructorReference ? controller.getClassNode() : typeOrTargetRefType,
+                        referenceKind,
+                        isConstructorReference ? classNode : typeOrTargetRefType,
                         methodRefMethod,
                         false
                 )
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 7afcd0ed2f..d7f14b24db 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -20,6 +20,8 @@ package groovy.transform.stc
 
 import groovy.test.GroovyTestCase
 
+import static groovy.test.GroovyAssert.isAtLeastJdk
+
 final class MethodReferenceTest extends GroovyTestCase {
 
     // class::instanceMethod
@@ -131,6 +133,68 @@ final class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
+    // class::instanceMethod -- GROOVY-9853
+    void testFunctionCI6() {
+        assertScript '''
+            import java.util.function.*
+            @groovy.transform.CompileStatic
+            void test() {
+                ToIntFunction<CharSequence> f = CharSequence::size
+                int size = f.applyAsInt("")
+                assert size == 0
+            }
+            test()
+        '''
+
+        assertScript '''
+            import java.util.function.*
+            @groovy.transform.CompileStatic
+            void test() {
+                ToIntFunction<CharSequence> f = CharSequence::length
+                int length = f.applyAsInt("")
+                assert length == 0
+            }
+            test()
+        '''
+
+        assertScript '''
+            import java.util.function.*
+            @groovy.transform.CompileStatic
+            void test() {
+                Function<CharSequence,Integer> f = CharSequence::length
+                Integer length = f.apply("")
+                assert length == 0
+            }
+            test()
+        '''
+
+        assertScript '''
+            import java.util.function.*
+            import java.util.stream.IntStream
+
+            @groovy.transform.CompileStatic
+            void test() {
+                Function<CharSequence,IntStream> f = CharSequence::chars // default method
+                IntStream chars = f.apply("")
+                assert chars.count() == 0
+            }
+            test()
+        '''
+
+        if (!isAtLeastJdk('11.0')) return
+
+        assertScript '''
+            import java.util.function.*
+            @groovy.transform.CompileStatic
+            void test() {
+                ToIntBiFunction<CharSequence,CharSequence> f = CharSequence::compare // static method
+                int result = f.applyAsInt("","")
+                assert result == 0
+            }
+            test()
+        '''
+    }
+
     // class::instanceMethod -- GROOVY-9974
     void testPredicateCI() {
         assertScript '''