Repository: groovy Updated Branches: refs/heads/native-lambda 0b8beb93c -> c24c0b7e6
Support callable native lambda Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/c24c0b7e Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/c24c0b7e Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/c24c0b7e Branch: refs/heads/native-lambda Commit: c24c0b7e6a67dcdf277207d4261cfa6f2b55031f Parents: 0b8beb9 Author: sunlan <[email protected]> Authored: Wed Jan 31 10:12:36 2018 +0800 Committer: sunlan <[email protected]> Committed: Wed Jan 31 10:12:36 2018 +0800 ---------------------------------------------------------------------- .../org/codehaus/groovy/ast/ClassHelper.java | 4 ++ .../groovy/classgen/asm/InvocationWriter.java | 29 ++++++++ .../asm/sc/StaticTypesLambdaWriter.java | 6 +- .../stc/StaticTypeCheckingVisitor.java | 10 ++- src/test/groovy/transform/stc/LambdaTest.groovy | 69 +++++++++++++++++++- 5 files changed, 108 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/c24c0b7e/src/main/java/org/codehaus/groovy/ast/ClassHelper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 9fc8643..ed984ce 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -396,6 +396,10 @@ public class ClassHelper { return findSAM(type) != null; } + public static boolean isFunctionInterface(ClassNode type) { + return type.isInterface() && !type.getAnnotations(FunctionalInterface_Type).isEmpty() && isSAMType(type); + } + /** * Returns the single abstract method of a class node, if it is a SAM type, or null otherwise. * http://git-wip-us.apache.org/repos/asf/groovy/blob/c24c0b7e/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index 10a82ed..0d8cc1b 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -479,11 +479,25 @@ public class InvocationWriter { return methodName; } + private boolean isFunctionInterfaceCall(MethodCallExpression call) { + ClassNode type = call.getObjectExpression().getType(); + + if (ClassHelper.isFunctionInterface(type)) { + return true; + } + + return false; + } + public void writeInvokeMethod(MethodCallExpression call) { if (isClosureCall(call)) { // let's invoke the closure method invokeClosure(call.getArguments(), call.getMethodAsString()); } else { + if (isFunctionInterfaceCall(call)) { + call = transformToRealMethodCall(call); + } + boolean isSuperMethodCall = usesSuper(call); MethodCallerMultiAdapter adapter = invokeMethod; if (isSuperMethodCall && call.isSafe()) { @@ -498,6 +512,21 @@ public class InvocationWriter { } } + private MethodCallExpression transformToRealMethodCall(MethodCallExpression call) { + ClassNode type = call.getObjectExpression().getType(); + final MethodNode methodNode = ClassHelper.findSAM(type); + + call = (MethodCallExpression) call.transformExpression(expression -> { + if (!(expression instanceof ConstantExpression)) { + return expression; + } + + return new ConstantExpression(methodNode.getName()); + }); + call.setMethodTarget(methodNode); + return call; + } + private boolean isClosureCall(MethodCallExpression call) { // are we a local variable? // it should not be an explicitly "this" qualified method call http://git-wip-us.apache.org/repos/asf/groovy/blob/c24c0b7e/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java ---------------------------------------------------------------------- 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 7f44c90..2d877ec 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 @@ -102,7 +102,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter { .filter(MethodNode::isAbstract) .collect(Collectors.toList()); - if (!(isFunctionInterface(lambdaType) && abstractMethodNodeList.size() == 1)) { + if (!(ClassHelper.isFunctionInterface(lambdaType.redirect()) && abstractMethodNodeList.size() == 1)) { // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; @@ -302,10 +302,6 @@ public class StaticTypesLambdaWriter extends LambdaWriter { ); } - private boolean isFunctionInterface(ClassNode parameterType) { - return parameterType.redirect().isInterface() && !parameterType.redirect().getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty(); - } - public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode lambdaClass = lambdaClassMap.get(expression); if (lambdaClass == null) { http://git-wip-us.apache.org/repos/asf/groovy/blob/c24c0b7e/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 4688e3b..d2e9bb2 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -133,7 +133,6 @@ import static org.codehaus.groovy.ast.ClassHelper.GROOVY_OBJECT_TYPE; import static org.codehaus.groovy.ast.ClassHelper.GSTRING_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Iterator_TYPE; -import static org.codehaus.groovy.ast.ClassHelper.LAMBDA_TYPE; import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE; import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE; @@ -4100,8 +4099,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { collectAllInterfaceMethodsByName(receiver, name, methods); methods.addAll(OBJECT_TYPE.getMethods(name)); - if (!receiver.getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty()) { - methods.addAll(LAMBDA_TYPE.getDeclaredMethods("call")); + if (ClassHelper.isFunctionInterface(receiver)) { + MethodNode sam = ClassHelper.findSAM(receiver); + MethodNode callMethodNode = new MethodNode("call", sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); + callMethodNode.setDeclaringClass(sam.getDeclaringClass()); + callMethodNode.setSourcePosition(sam); + + methods.addAll(Collections.singletonList(callMethodNode)); } } // TODO: investigate the trait exclusion a bit further, needed otherwise http://git-wip-us.apache.org/repos/asf/groovy/blob/c24c0b7e/src/test/groovy/transform/stc/LambdaTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 1a0b1ab..560ab77 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -403,8 +403,6 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } void testFunctionCall() { - if (SKIP_ERRORS) return - assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -425,6 +423,73 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + void testFunctionCall2() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + import java.util.function.Function + + @CompileStatic + public class Test1 { + public static void main(String[] args) { + new Test1().p(); + } + + public void p() { + Function<Integer, Integer> f = (Integer e) -> (Integer) (e + 1) // Casting is required... [Static type checking] - Incompatible generic argument types. Cannot assign java.util.function.Function <java.lang.Integer, int> to: java.util.function.Function <Integer, Integer> + assert 2 == f(1) + } + } + ''' + } + + void testConsumerCall() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + import java.util.function.Consumer + + @CompileStatic + public class Test1 { + public static void main(String[] args) { + p(); + } + + public static void p() { + int r = 1 + Consumer<Integer> c = (Integer e) -> { r += e } + c(2) + assert 3 == r + } + } + ''' + } + + void testConsumerCall2() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + import java.util.function.Consumer + + @CompileStatic + public class Test1 { + public static void main(String[] args) { + new Test1().p(); + } + + public void p() { + int r = 1 + Consumer<Integer> c = (Integer e) -> { r += e } + c(2) + assert 3 == r + } + } + ''' + } + void testFunctionWithUpdatingLocalVariable() { assertScript ''' import groovy.transform.CompileStatic
