You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/10/13 00:24:20 UTC

[groovy] 02/02: GROOVY-8258: generate unique param name for each invocation of lambda

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

sunlan pushed a commit to branch GROOVY-8258
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit d1f754bf8315cfb598337f970d5ebfa1053d1f02
Author: Daniel Sun <su...@apache.org>
AuthorDate: Tue Oct 13 08:23:22 2020 +0800

    GROOVY-8258: generate unique param name for each invocation of lambda
---
 .../linq/provider/collection/GinqAstWalker.groovy  | 23 ++++++--
 .../groovy/org/apache/groovy/linq/GinqTest.groovy  | 64 +++++++++++-----------
 2 files changed, 50 insertions(+), 37 deletions(-)

diff --git a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/collection/GinqAstWalker.groovy b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/collection/GinqAstWalker.groovy
index e6c3850..db33cff 100644
--- a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/collection/GinqAstWalker.groovy
+++ b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/collection/GinqAstWalker.groovy
@@ -365,6 +365,11 @@ class GinqAstWalker implements GinqVisitor<Object>, SyntaxErrorReportable {
     private Expression correctVariablesOfGinqExpression(DataSourceExpression dataSourceExpression, Expression expr) {
         boolean isGroup = isGroupByVisited()
         boolean isJoin = dataSourceExpression instanceof JoinExpression
+        String lambdaParamName = expr.getNodeMetaData(__LAMBDA_PARAM_NAME)
+
+        if (null == lambdaParamName) {
+            throw new GroovyBugError("lambdaParamName is null. dataSourceExpression:${dataSourceExpression}, expr:${expr}")
+        }
 
         def correctVarsForJoin = { Expression expression ->
             if (!isJoin) return expression
@@ -387,7 +392,7 @@ class GinqAstWalker implements GinqVisitor<Object>, SyntaxErrorReportable {
                      *
                      * The following code shows how to construct the access path for variables
                      */
-            Expression prop = isGroup ? propX(new VariableExpression(__T), 'v1') : new VariableExpression(__T)
+            Expression prop = isGroup ? propX(new VariableExpression(lambdaParamName), 'v1') : new VariableExpression(lambdaParamName)
             for (DataSourceExpression dse = dataSourceExpression;
                  null == transformedExpression && dse instanceof JoinExpression;
                  dse = dse.getNodeMetaData(__DATA_SOURCE_EXPRESSION)) {
@@ -414,9 +419,9 @@ class GinqAstWalker implements GinqVisitor<Object>, SyntaxErrorReportable {
                 if (isGroup) { //  groupby
                     // in #1, we will correct receiver of built-in aggregate functions
                     // the correct receiver is `__t.v2`, so we should not replace `__t` here
-                    if (__T != expression.text) {
+                    if (lambdaParamName != expression.text) {
                         // replace `gk` in the groupby with `__t.v1.gk`, note: __t.v1 stores the group key
-                        transformedExpression = propX(propX(new VariableExpression(__T), 'v1'), expression.text)
+                        transformedExpression = propX(propX(new VariableExpression(lambdaParamName), 'v1'), expression.text)
                     }
                 } else if (isJoin) {
                     transformedExpression = correctVarsForJoin(expression)
@@ -427,7 +432,7 @@ class GinqAstWalker implements GinqVisitor<Object>, SyntaxErrorReportable {
                     if (expression.implicitThis) {
                         String methodName = expression.methodAsString
                         if ('count' == methodName && ((TupleExpression) expression.arguments).getExpressions().isEmpty()) {
-                            expression.objectExpression = propX(new VariableExpression(__T), 'v2')
+                            expression.objectExpression = propX(new VariableExpression(lambdaParamName), 'v2')
                             transformedExpression = expression
                         }
                     }
@@ -479,15 +484,21 @@ class GinqAstWalker implements GinqVisitor<Object>, SyntaxErrorReportable {
         )
     }
 
+    private static String generateLambdaParamName() {
+        "__t_${System.nanoTime()}_${new Random().nextInt(1000)}"
+    }
+
     private Tuple2<String, Expression> correctVariablesOfLambdaExpression(DataSourceExpression dataSourceExpression, Expression lambdaCode) {
         boolean isGroup = isGroupByVisited()
 
         String lambdaParamName
         if (dataSourceExpression instanceof JoinExpression || isGroup) {
-            lambdaParamName = __T
+            lambdaParamName = generateLambdaParamName()
+            lambdaCode.putNodeMetaData(__LAMBDA_PARAM_NAME, lambdaParamName)
             lambdaCode = correctVariablesOfGinqExpression(dataSourceExpression, lambdaCode)
         } else {
             lambdaParamName = dataSourceExpression.aliasExpr.text
+            lambdaCode.putNodeMetaData(__LAMBDA_PARAM_NAME, lambdaParamName)
         }
 
         return Tuple.tuple(lambdaParamName, lambdaCode)
@@ -516,6 +527,6 @@ class GinqAstWalker implements GinqVisitor<Object>, SyntaxErrorReportable {
 
     private static final String __DATA_SOURCE_EXPRESSION = "__dataSourceExpression"
     private static final String __METHOD_CALL_RECEIVER = "__methodCallReceiver"
-    private static final String __T = "__t"
     private static final String __GROUP_BY = "__GROUP_BY"
+    private static final String __LAMBDA_PARAM_NAME = "__LAMBDA_PARAM_NAME"
 }
diff --git a/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy b/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
index 9a22d19..86a4d9c 100644
--- a/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
+++ b/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
@@ -18,7 +18,6 @@
  */
 package org.apache.groovy.linq
 
-import groovy.json.JsonSlurper
 import groovy.transform.CompileDynamic
 import groovy.transform.CompileStatic
 import org.junit.Test
@@ -1585,36 +1584,39 @@ class GinqTest {
     @CompileDynamic
     @Test
     void "testGinq - query json - 1"() {
-        def parser = new JsonSlurper()
-        def json = parser.parseText('''
-            {
-                "persons": [
-                    {"id": 1, "name": "Daniel"},
-                    {"id": 2, "name": "Paul"},
-                    {"id": 3, "name": "Eric"}
-                ],
-                "tasks": [
-                    {"id": 1, "assignee": 1, "content": "task1", "manDay": 6},
-                    {"id": 2, "assignee": 1, "content": "task2", "manDay": 1},
-                    {"id": 3, "assignee": 2, "content": "task3", "manDay": 3},
-                    {"id": 4, "assignee": 3, "content": "task4", "manDay": 5}
-                ]
-            }
-        ''')
-
-        def expected = [
-                [taskId: 1, taskContent: 'task1', assignee: 'Daniel', manDay: 6],
-                [taskId: 4, taskContent: 'task4', assignee: 'Eric', manDay: 5],
-                [taskId: 3, taskContent: 'task3', assignee: 'Paul', manDay: 3]
-        ]
-
-        assert expected == GINQ {
-            from p in json.persons
-            innerjoin t in json.tasks on t.assignee == p.id
-            where t.id in [1, 3, 4]
-            orderby t.manDay in desc
-            select (taskId: t.id, taskContent: t.content, assignee: p.name, manDay: t.manDay)
-        }.toList()
+        assertScript """
+            import groovy.json.JsonSlurper
+            def parser = new JsonSlurper()
+            def json = parser.parseText('''
+                {
+                    "persons": [
+                        {"id": 1, "name": "Daniel"},
+                        {"id": 2, "name": "Paul"},
+                        {"id": 3, "name": "Eric"}
+                    ],
+                    "tasks": [
+                        {"id": 1, "assignee": 1, "content": "task1", "manDay": 6},
+                        {"id": 2, "assignee": 1, "content": "task2", "manDay": 1},
+                        {"id": 3, "assignee": 2, "content": "task3", "manDay": 3},
+                        {"id": 4, "assignee": 3, "content": "task4", "manDay": 5}
+                    ]
+                }
+            ''')
+    
+            def expected = [
+                    [taskId: 1, taskContent: 'task1', assignee: 'Daniel', manDay: 6],
+                    [taskId: 4, taskContent: 'task4', assignee: 'Eric', manDay: 5],
+                    [taskId: 3, taskContent: 'task3', assignee: 'Paul', manDay: 3]
+            ]
+    
+            assert expected == GINQ {
+                from p in json.persons
+                innerjoin t in json.tasks on t.assignee == p.id
+                where t.id in [1, 3, 4]
+                orderby t.manDay in desc
+                select (taskId: t.id, taskContent: t.content, assignee: p.name, manDay: t.manDay)
+            }.toList()
+        """
     }
 
     @CompileDynamic