Repository: groovy
Updated Branches:
  refs/heads/native-lambda ea17e1836 -> 411fb6d48


Support native lambda on the RHS of variable declaration


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/411fb6d4
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/411fb6d4
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/411fb6d4

Branch: refs/heads/native-lambda
Commit: 411fb6d48b680abcdadcaf9478e1e1e4fc13734a
Parents: ea17e18
Author: sunlan <[email protected]>
Authored: Mon Jan 22 21:49:20 2018 +0800
Committer: sunlan <[email protected]>
Committed: Mon Jan 22 21:49:20 2018 +0800

----------------------------------------------------------------------
 ...ypesBinaryExpressionMultiTypeDispatcher.java | 14 ++++--
 .../asm/sc/StaticTypesLambdaWriter.java         | 50 ++++++++++++++++++--
 .../groovy/transform/stc/StaticTypesMarker.java |  3 +-
 src/test/groovy/transform/stc/LambdaTest.groovy | 50 ++++++++++++++++++++
 4 files changed, 108 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/411fb6d4/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
index 116fd16..a5c514d 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -33,6 +33,7 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
 import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.LambdaExpression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.PropertyExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
@@ -71,6 +72,8 @@ import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
 import static 
org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_ADD_METHOD;
 import static 
org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CLASSNODE;
 import static 
org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CONSTRUCTOR;
+import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_LAMBDA_TYPE;
+import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
 
 /**
  * A specialized version of the multi type binary expression dispatcher which 
is aware of static compilation.
@@ -146,8 +149,8 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher 
extends BinaryExpres
 
     @Override
     public void evaluateEqual(final BinaryExpression expression, final boolean 
defineVariable) {
+        Expression leftExpression = expression.getLeftExpression();
         if (!defineVariable) {
-            Expression leftExpression = expression.getLeftExpression();
             if (leftExpression instanceof PropertyExpression) {
                 PropertyExpression pexp = (PropertyExpression) leftExpression;
                 if (makeSetProperty(
@@ -159,10 +162,15 @@ public class 
StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
                         pexp.isImplicitThis(),
                         pexp instanceof AttributeExpression)) return;
             }
+        } else {
+            Expression rightExpression = expression.getRightExpression();
+            if (rightExpression instanceof LambdaExpression) {
+                rightExpression.putNodeMetaData(INFERRED_LAMBDA_TYPE, 
leftExpression.getNodeMetaData(INFERRED_TYPE));
+            }
         }
         // GROOVY-5620: Spread safe/Null safe operator on LHS is not supported
-        if (expression.getLeftExpression() instanceof PropertyExpression
-                && ((PropertyExpression) 
expression.getLeftExpression()).isSpreadSafe()
+        if (leftExpression instanceof PropertyExpression
+                && ((PropertyExpression) leftExpression).isSpreadSafe()
                 && 
StaticTypeCheckingSupport.isAssignment(expression.getOperation().getType())) {
             // rewrite it so that it can be statically compiled
             transformSpreadOnLHS(expression);

http://git-wip-us.apache.org/repos/asf/groovy/blob/411fb6d4/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 68ad4ad..7014a99 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
@@ -52,6 +52,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_LAMBDA_TYPE;
 import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_PARAMETER_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ACC_STATIC;
@@ -89,14 +90,14 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
 
     @Override
     public void writeLambda(LambdaExpression expression) {
-        ClassNode parameterType = 
expression.getNodeMetaData(INFERRED_PARAMETER_TYPE);
+        ClassNode inferedType = getInferredType(expression);
 
         List<MethodNode> abstractMethodNodeList =
-                parameterType.redirect().getMethods().stream()
+                inferedType.redirect().getMethods().stream()
                         .filter(MethodNode::isAbstract)
                         .collect(Collectors.toList());
 
-        if (!(isFunctionInterface(parameterType) && 
abstractMethodNodeList.size() == 1)) {
+        if (!(isFunctionInterface(inferedType) && 
abstractMethodNodeList.size() == 1)) {
             // if the parameter type is not real FunctionInterface, generate 
the default bytecode, which is actually a closure
             super.writeLambda(expression);
             return;
@@ -120,11 +121,45 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
{
 
         mv.visitInvokeDynamicInsn(
                 abstractMethodNode.getName(),
-                createAbstractMethodDesc(parameterType, lambdaClassNode),
+                createAbstractMethodDesc(inferedType, lambdaClassNode),
                 createBootstrapMethod(isInterface),
                 createBootstrapMethodArguments(abstractMethodDesc, 
lambdaClassNode, syntheticLambdaMethodNode)
         );
-        operandStack.replace(parameterType.redirect(), 1);
+        operandStack.replace(inferedType.redirect(), 1);
+
+        if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) {
+            // FIXME declaring variable whose initial value is a lambda, e.g. 
`Function<Integer, String> f = (Integer e) -> 'a' + e`
+            //       Groovy will `POP` twice...(expecting `POP` only once), as 
a hack, `DUP` to duplicate the element of operand stack:
+            /*
+                INVOKEDYNAMIC 
apply(LTest1$_p_lambda1;LTest1;)Ljava/util/function/Function; [
+                  // handle kind 0x6 : INVOKESTATIC
+                  
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+                  // arguments:
+                  (Ljava/lang/Object;)Ljava/lang/Object;,
+                  // handle kind 0x5 : INVOKEVIRTUAL
+                  
Test1$_p_lambda1.doCall(LTest1;Ljava/lang/Integer;)Ljava/lang/String;,
+                  (Ljava/lang/Integer;)Ljava/lang/String;
+                ]
+                DUP           <-------------- FIXME ADDED ON PURPOSE, WE 
SHOULD REMOVE IT AFTER FIND BETTER SOLUTION
+                ASTORE 0
+               L2
+                ALOAD 0
+                POP
+                POP
+            */
+
+            mv.visitInsn(DUP);
+        }
+
+    }
+
+    private ClassNode getInferredType(LambdaExpression expression) {
+        ClassNode inferedType = 
expression.getNodeMetaData(INFERRED_PARAMETER_TYPE);
+
+        if (null == inferedType) {
+            inferedType = expression.getNodeMetaData(INFERRED_LAMBDA_TYPE);
+        }
+        return inferedType;
     }
 
     private void loadEnclosingClassInstance() {
@@ -326,6 +361,11 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
 
         for (int i = 0; i < parameters.length; i++) {
             ClassNode inferredType = 
parameters[i].getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+
+            if (null == inferredType) {
+                continue;
+            }
+
             parameters[i].setType(inferredType);
             parameters[i].setOriginType(inferredType);
         }

http://git-wip-us.apache.org/repos/asf/groovy/blob/411fb6d4/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
index 7411308..fcde258 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
@@ -38,5 +38,6 @@ public enum StaticTypesMarker {
     PV_METHODS_ACCESS, // set of private methods that are accessed from 
closures or inner classes
     DYNAMIC_RESOLUTION, // call recognized by a type checking extension as a 
dynamic method call
     SUPER_MOP_METHOD_REQUIRED, // used to store the list of MOP methods that 
still have to be generated
-    INFERRED_PARAMETER_TYPE // used to store the parameter type information of 
method invocation
+    INFERRED_PARAMETER_TYPE, // used to store the parameter type information 
of method invocation on an expression
+    INFERRED_LAMBDA_TYPE // used to store the lambda type information on a 
lambda expression
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/411fb6d4/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 136fbe2..f928756 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -470,4 +470,54 @@ TestScript0.groovy: 14: [Static type checking] - Cannot 
find matching method jav
         }
         '''
     }
+
+    void testFunctionWithVariableDeclaration() {
+        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) {
+                p()
+            }
+        
+            public static void p() {
+                Function<Integer, String> f = (Integer e) -> 'a' + e
+                assert ['a1', 'a2', 'a3'] == Stream.of(1, 2, 
3).map(f).collect(Collectors.toList())
+            }
+        }
+        
+        '''
+    }
+
+    void testFunctionWithMixingVariableDeclarationAndMethodInvocation() {
+        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) {
+                p()
+            }
+        
+            public static void p() {
+                String x = "#"
+                Integer y = 23
+                assert ['23#1', '23#2', '23#3'] == Stream.of(1, 2, 3).map(e -> 
'' + y + x + e).collect(Collectors.toList())
+            
+                Function<Integer, String> f = (Integer e) -> 'a' + e
+                assert ['a1', 'a2', 'a3'] == Stream.of(1, 2, 
3).map(f).collect(Collectors.toList())
+                
+                assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 
1).collect(Collectors.toList());
+            }
+        }
+        
+        '''
+    }
 }

Reply via email to