This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new 2f31b58af4 GROOVY-9853: SC: method reference to interface abstract /
default method
2f31b58af4 is described below
commit 2f31b58af40d7889d502b5135a814811e852f7a9
Author: Eric Milles <[email protected]>
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 | 90 ++++++++++++++++------
3 files changed, 93 insertions(+), 46 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 0223e397ec..d264d19542 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 222e9ee6fe..aeba6a9ebc 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 da8a61f2bf..b73012ba40 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -18,8 +18,6 @@
*/
package groovy.transform.stc
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.junit.Test
import static groovy.test.GroovyAssert.assertScript
@@ -27,10 +25,13 @@ import static groovy.test.GroovyAssert.shouldFail
final class MethodReferenceTest {
- private final GroovyShell shell = new GroovyShell(new
CompilerConfiguration().tap {
- addCompilationCustomizers(new
ImportCustomizer().addStarImports('java.util.function')
- .addImports('java.util.stream.Collectors',
'groovy.transform.CompileStatic'))
- })
+ private final GroovyShell shell = GroovyShell.withConfig {
+ imports {
+ normal 'groovy.transform.CompileStatic'
+ normal 'java.util.stream.Collectors'
+ star 'java.util.function'
+ }
+ }
@Test // class::instanceMethod
void testFunctionCI() {
@@ -61,13 +62,11 @@ final class MethodReferenceTest {
@Test // class::instanceMethod -- GROOVY-10047
void testFunctionCI3() {
assertScript shell, '''
- import static java.util.stream.Collectors.toMap
-
@CompileStatic
void p() {
List<String> list = ['a','bc','def']
Function<String,String> self = str -> str // help for toMap
- def map = list.stream().collect(toMap(self, String::length))
+ def map = list.stream().collect(Collectors.toMap(self,
String::length))
assert map == [a: 1, bc: 2, 'def': 3]
}
@@ -75,13 +74,11 @@ final class MethodReferenceTest {
'''
assertScript shell, '''
- import static java.util.stream.Collectors.toMap
-
@CompileStatic
void p() {
List<String> list = ['a','bc','def']
// TODO: inference for T in toMap(Function<? super T,...>,
Function<? super T,...>)
- def map =
list.stream().collect(toMap(Function.<String>identity(), String::length))
+ def map =
list.stream().collect(Collectors.toMap(Function.<String>identity(),
String::length))
assert map == [a: 1, bc: 2, 'def': 3]
}
@@ -99,7 +96,6 @@ final class MethodReferenceTest {
p()
'''
-
assert err =~ /Invalid receiver type: java.lang.Integer is not
compatible with java.lang.String/
}
@@ -129,6 +125,61 @@ final class MethodReferenceTest {
'''
}
+ @Test // class::instanceMethod -- GROOVY-9853
+ void testFunctionCI6() {
+ assertScript shell, '''
+ @CompileStatic
+ void test() {
+ ToIntFunction<CharSequence> f = CharSequence::size
+ int size = f.applyAsInt("")
+ assert size == 0
+ }
+ test()
+ '''
+
+ assertScript shell, '''
+ @CompileStatic
+ void test() {
+ ToIntFunction<CharSequence> f = CharSequence::length
+ int length = f.applyAsInt("")
+ assert length == 0
+ }
+ test()
+ '''
+
+ assertScript shell, '''
+ @CompileStatic
+ void test() {
+ Function<CharSequence,Integer> f = CharSequence::length
+ Integer length = f.apply("")
+ assert length == 0
+ }
+ test()
+ '''
+
+ assertScript shell, '''
+ import java.util.stream.IntStream
+
+ @CompileStatic
+ void test() {
+ Function<CharSequence,IntStream> f = CharSequence::chars //
default method
+ IntStream chars = f.apply("")
+ assert chars.count() == 0
+ }
+ test()
+ '''
+
+ assertScript shell, '''
+ @CompileStatic
+ void test() {
+ ToIntBiFunction<CharSequence,CharSequence> f =
CharSequence::compare // static method
+ int result = f.applyAsInt("","")
+ assert result == 0
+ }
+ test()
+ '''
+ }
+
@Test // class::instanceMethod -- GROOVY-9974
void testPredicateCI() {
assertScript shell, '''
@@ -145,12 +196,11 @@ final class MethodReferenceTest {
void testBinaryOperatorCI() {
assertScript shell, '''
@CompileStatic
- void p() {
+ void test() {
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G,
BigDecimal::add)
assert 6.0G == result
}
-
- p()
+ test()
'''
}
@@ -456,12 +506,10 @@ final class MethodReferenceTest {
@Test // class::staticMethod
void testFunctionCS2() {
assertScript shell, '''
- import static java.util.stream.Collectors.toMap
-
@CompileStatic
void p() {
List<String> list = ['x','y','z']
- def map = list.stream().collect(toMap(Function.identity(),
Collections::singletonList))
+ def map =
list.stream().collect(Collectors.toMap(Function.identity(),
Collections::singletonList))
assert map == [x: ['x'], y: ['y'], z: ['z']]
}
@@ -611,7 +659,6 @@ final class MethodReferenceTest {
[1.0G, 2.0G, 3.0G].stream().reduce(0.0G, BigDecimal::addx)
}
'''
-
assert err.message.contains('Failed to find the expected
method[addx(java.math.BigDecimal,java.math.BigDecimal)] in the
type[java.math.BigDecimal]')
}
@@ -623,7 +670,6 @@ final class MethodReferenceTest {
Function<String,String> reference = String::toLowerCaseX
}
'''
-
assert err.message.contains('Failed to find the expected
method[toLowerCaseX(java.lang.String)] in the type[java.lang.String]')
}
@@ -641,7 +687,6 @@ final class MethodReferenceTest {
baz(this::foo) // not yet supported!
}
'''
-
assert err =~ /The argument is a method reference, but the parameter
type is not a functional interface/
}
@@ -659,7 +704,6 @@ final class MethodReferenceTest {
}
}
'''
-
assert err =~ /The argument is a method reference, but the parameter
type is not a functional interface/
}
}