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 2021/07/25 09:49:33 UTC

[groovy] branch master updated: Support GINQ methods

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 53db604  Support GINQ methods
53db604 is described below

commit 53db604641b8f4b56343ece679a374c797828b41
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Jul 25 14:32:09 2021 +0800

    Support GINQ methods
---
 .../src/main/groovy/groovy/ginq/transform/GQ.java  |  26 ++++
 .../apache/groovy/ginq/GinqGroovyMethods.groovy    |   2 +-
 .../ginq/provider/collection/GinqAstWalker.groovy  |   2 +-
 .../ginq/transform/GinqASTTransformation.java      |  94 +++++++++++++++
 .../test/org/apache/groovy/ginq/GinqTest.groovy    | 134 +++++++++++++++++++++
 5 files changed, 256 insertions(+), 2 deletions(-)

diff --git a/subprojects/groovy-ginq/src/main/groovy/groovy/ginq/transform/GQ.java b/subprojects/groovy-ginq/src/main/groovy/groovy/ginq/transform/GQ.java
new file mode 100644
index 0000000..80373cd
--- /dev/null
+++ b/subprojects/groovy-ginq/src/main/groovy/groovy/ginq/transform/GQ.java
@@ -0,0 +1,26 @@
+package groovy.ginq.transform;
+
+import org.apache.groovy.lang.annotation.Incubating;
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Method annotation to make a method call returning GINQ result
+ *
+ * @since 4.0.0
+ */
+@Incubating
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@GroovyASTTransformationClass("org.apache.groovy.ginq.transform.GinqASTTransformation")
+public @interface GQ {
+    boolean optimize() default true;
+    boolean parallel() default false;
+    String astWalker() default "org.apache.groovy.ginq.provider.collection.GinqAstWalker";
+}
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy
index 5517bbb..0e41754 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy
@@ -146,10 +146,10 @@ class GinqGroovyMethods {
 
     private GinqGroovyMethods() {}
 
+    public static final List<String> CONF_LIST = [CONF_PARALLEL, CONF_AST_WALKER, CONF_OPTIMIZE]
     public static final String CONF_PARALLEL = 'parallel'
     private static final String CONF_AST_WALKER = 'astWalker'
     private static final String CONF_OPTIMIZE = 'optimize'
-    private static final List<String> CONF_LIST = [CONF_PARALLEL, CONF_AST_WALKER, CONF_OPTIMIZE]
     private static final String DEFAULT_AST_WALKER_CLASS_NAME = GinqAstWalker.class.name
     private static final String TRUE_STR = 'true'
 }
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 6f33278..235af22 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -217,7 +217,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
         }
         statementList << returnS(varX(resultName))
 
-        def resultLambda = lambdaX(block(statementList as Statement[]))
+        def resultLambda = lambdaX(null, block(statementList as Statement[]))
         def result = parallelEnabled
                         ? callX(callX(QUERYABLE_HELPER_TYPE, 'submit', args(resultLambda)), "get")
                         : callX(resultLambda, "call")
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/transform/GinqASTTransformation.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/transform/GinqASTTransformation.java
new file mode 100644
index 0000000..845fc48
--- /dev/null
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/transform/GinqASTTransformation.java
@@ -0,0 +1,94 @@
+package org.apache.groovy.ginq.transform;
+
+import groovy.ginq.transform.GQ;
+import org.apache.groovy.ginq.GinqGroovyMethods;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.classgen.VariableScopeVisitor;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.apache.groovy.ginq.GinqGroovyMethods.transformGinqCode;
+import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.mapX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+
+/**
+ * Handles generation of code for the {@code @GQ} annotation.
+ * @since 4.0.0
+ */
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+public class GinqASTTransformation extends AbstractASTTransformation {
+    private static final ClassNode GQ_CLASS_NODE = make(GQ.class);
+
+    @Override
+    public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+        ModuleNode moduleNode = sourceUnit.getAST();
+        List<ClassNode> classNodeList = moduleNode.getClasses();
+        if (classNodeList == null) return;
+
+        classNodeList.stream().flatMap(c -> c.getMethods().stream())
+                .filter(m -> !m.getAnnotations(GQ_CLASS_NODE).isEmpty())
+                .map(m -> {
+                    if (m.isAbstract()) {
+                        addError("Error during " + GQ_CLASS_NODE.getName() + " processing: annotation not allowed on abstract method '" + m.getName() + "'", m);
+                        return m.getDeclaringClass();
+                    }
+                    BlockStatement origCode = (BlockStatement) m.getCode();
+                    MapExpression ginqConfigurationMapExpression = makeGinqConfigurationMapExpression(m);
+                    BlockStatement newCode = block(
+                            returnS(transformGinqCode(sourceUnit, ginqConfigurationMapExpression, origCode))
+                    );
+                    newCode.setSourcePosition(origCode);
+                    m.setCode(newCode);
+                    return m.getDeclaringClass();
+                }).distinct()
+                .forEach(c -> {
+                    VariableScopeVisitor variableScopeVisitor = new VariableScopeVisitor(sourceUnit);
+                    variableScopeVisitor.visitClass(c);
+                });
+    }
+
+    private MapExpression makeGinqConfigurationMapExpression(MethodNode m) {
+        Map<String, Expression> resultMembers = new HashMap<>();
+        Map<String, Expression> defaultMembers = GinqGroovyMethods.CONF_LIST.stream().collect(Collectors.toMap(
+                c -> c,
+                c -> {
+                    try {
+                        return constX(GQ_CLASS_NODE.getTypeClass().getMethod(c).getDefaultValue());
+                    } catch (NoSuchMethodException e) {
+                        throw new GroovyBugError("Unknown GINQ option: " + c, e);
+                    }
+                }
+        ));
+        resultMembers.putAll(defaultMembers);
+
+        AnnotationNode gqAnnotationNode = m.getAnnotations(GQ_CLASS_NODE).get(0);
+        Map<String, Expression> members = gqAnnotationNode.getMembers();
+        resultMembers.putAll(members);
+
+        MapExpression ginqConfigurationMapExpression =
+                        mapX(resultMembers.entrySet().stream()
+                                .map(e -> new MapEntryExpression(constX(e.getKey()), constX(e.getValue().getText())))
+                                .collect(Collectors.toList()));
+
+        return ginqConfigurationMapExpression;
+    }
+}
diff --git a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
index 1577535..1b0a3d0 100644
--- a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
+++ b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
@@ -6112,6 +6112,140 @@ class GinqTest {
         '''
     }
 
+    @Test
+    void "testGinqMethod - GQ - 1"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            @GQ
+            def ginq(x) {
+                from n in [1, 2, 3]
+                where n < x
+                select n
+            }
+            
+            assert [1] == ginq(2).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 2"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            class GinqClass {
+                @GQ
+                def ginq(x) {
+                    from n in [1, 2, 3]
+                    where n < x
+                    select n
+                }
+            }
+            
+            assert [1, 2] == new GinqClass().ginq(3).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 3"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            class GinqClass {
+                static class Holder {
+                    @GQ
+                    def ginq(x) {
+                        from n in [1, 2, 3]
+                        where n < x
+                        select n
+                    }
+                }
+            }
+            
+            assert [1, 2] == new GinqClass.Holder().ginq(3).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 4"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            @GQ(optimize=false)
+            def ginq(x) {
+                from n in [1, 2, 3]
+                where n < x
+                select n
+            }
+            
+            assert [1] == ginq(2).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 5"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            @GQ(parallel=true)
+            def ginq(x) {
+                from n in [1, 2, 3]
+                where n < x
+                select n
+            }
+            
+            assert [1] == ginq(2).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 6"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            @GQ(astWalker='org.apache.groovy.ginq.provider.collection.GinqAstWalker')
+            def ginq(x) {
+                from n in [1, 2, 3]
+                where n < x
+                select n
+            }
+            
+            assert [1] == ginq(2).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 7"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            @GQ(optimize=false, parallel=true)
+            def ginq(x) {
+                from n in [1, 2, 3]
+                where n < x
+                select n
+            }
+            
+            assert [1] == ginq(2).toList()
+        '''
+    }
+
+    @Test
+    void "testGinqMethod - GQ - 8"() {
+        assertScript '''
+            import groovy.ginq.transform.GQ
+            
+            @GQ(optimize=false, parallel=true, astWalker='org.apache.groovy.ginq.provider.collection.GinqAstWalker')
+            def ginq(x) {
+                from n in [1, 2, 3]
+                where n < x
+                select n
+            }
+            
+            assert [1] == ginq(2).toList()
+        '''
+    }
+
     @AfterClass
     static void "testGinq - shutdown - 0"() {
         assertScript '''