groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pa...@apache.org
Subject [groovy] 11/20: Support class::new and arrayClass::new
Date Mon, 08 Apr 2019 12:59:03 GMT
This is an automated email from the ASF dual-hosted git repository.

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

commit df80aa30d4c0cbae9f667ad022d9aa25adbab91d
Author: Daniel Sun <sunlan@apache.org>
AuthorDate: Sat Mar 16 20:29:19 2019 +0800

    Support class::new and arrayClass::new
---
 .../java/org/codehaus/groovy/ast/ClassNode.java    |  16 +-
 .../codehaus/groovy/classgen/GeneratorContext.java |   9 +-
 ...StaticTypesMethodReferenceExpressionWriter.java |  70 ++++++++-
 .../transform/stc/MethodReferenceTest.groovy       | 162 +++++++++++++++++++++
 4 files changed, 250 insertions(+), 7 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 3a8fc35..8099c8c 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -1136,9 +1136,23 @@ public class ClassNode extends AnnotatedNode implements Opcodes {
             visitor.visitConstructor(cn);
         }
 
-        for (MethodNode mn : getMethods()) {
+        visitMethods(visitor);
+    }
+
+    private void visitMethods(GroovyClassVisitor visitor) {
+        List<MethodNode> methodList = new ArrayList<>(getMethods());
+        for (MethodNode mn : methodList) {
             visitor.visitMethod(mn);
         }
+
+        // visit the method node added while iterating the above methodList, e.g. synthetic
method for constructor reference
+        List<MethodNode> changedMethodList = new ArrayList<>(getMethods());
+        boolean changed = changedMethodList.removeAll(methodList);
+        if (changed) {
+            for (MethodNode mn : changedMethodList) {
+                visitor.visitMethod(mn);
+            }
+        }
     }
 
     public MethodNode getGetterMethod(String getterName) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java
index 7cf688f..c80a8dd 100644
--- a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java
+++ b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java
@@ -32,7 +32,7 @@ public class GeneratorContext {
 
     private int innerClassIdx = 1;
     private int closureClassIdx = 1;
-    private int lambdaClassIdx = 1;
+    private int syntheticMethodIdx = 0;
     private final CompileUnit compileUnit;
     
     public GeneratorContext(CompileUnit compileUnit) {
@@ -75,6 +75,13 @@ public class GeneratorContext {
         return methodName + "_" + classifier + closureClassIdx++;
     }
 
+    public String getNextConstructorReferenceSyntheticMethodName(MethodNode enclosingMethodNode)
{
+        return "constructorReference$"
+                + (null == enclosingMethodNode
+                        ? ""
+                        : enclosingMethodNode.getName().replace("<", "").replace(">",
"") + "$" )
+                + syntheticMethodIdx++;
+    }
 
     private static final int MIN_ENCODING = ' ';
     private static final int MAX_ENCODING = ']';
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 8ccc619..b3fb945 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
@@ -24,6 +24,8 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
 import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
@@ -36,6 +38,7 @@ import org.codehaus.groovy.classgen.asm.CompileStack;
 import org.codehaus.groovy.classgen.asm.MethodReferenceExpressionWriter;
 import org.codehaus.groovy.classgen.asm.OperandStack;
 import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.runtime.ArrayTypeUtils;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -45,6 +48,10 @@ import java.util.LinkedList;
 import java.util.List;
 
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.filterMethodsByVisibility;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.CLOSURE_ARGUMENTS;
 
@@ -75,10 +82,16 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         ClassNode mrExprType = mrExpr.getType();
         String mrMethodName = methodReferenceExpression.getMethodName().getText();
 
-
         ClassNode[] methodReferenceParamTypes = methodReferenceExpression.getNodeMetaData(CLOSURE_ARGUMENTS);
         Parameter[] parametersWithExactType = createParametersWithExactType(abstractMethodNode,
methodReferenceParamTypes);
-        MethodNode mrMethodNode = findMrMethodNode(mrMethodName, parametersWithExactType,
mrExpr);
+
+        boolean isConstructorReference = isConstructorReference(mrMethodName);
+        if (isConstructorReference) {
+            mrMethodName = createSyntheticMethodForConstructorReference();
+            addSyntheticMethodForConstructorReference(mrMethodName, mrExprType, parametersWithExactType);
+        }
+
+        MethodNode mrMethodNode = findMrMethodNode(mrMethodName, parametersWithExactType,
mrExpr, isConstructorReference);
 
         if (null == mrMethodNode) {
             throw new GroovyRuntimeException("Failed to find the expected method["
@@ -89,7 +102,13 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         MethodVisitor mv = controller.getMethodVisitor();
 
         boolean isClassExpr = isClassExpr(mrExpr);
+
         if (!isClassExpr) {
+            if (isConstructorReference) {
+                // TODO move the checking code to the Parrot parser
+                throw new GroovyRuntimeException("Constructor reference must be className::new");
+            }
+
             if (mrMethodNode.isStatic()) {
                 ClassExpression classExpression = new ClassExpression(mrExprType);
                 classExpression.setSourcePosition(mrExpr);
@@ -115,7 +134,12 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
                 abstractMethodNode.getName(),
                 createAbstractMethodDesc(functionalInterfaceType, mrExpr),
                 createBootstrapMethod(isInterface),
-                createBootstrapMethodArguments(abstractMethodDesc, mrMethodNode.isStatic()
? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL, mrExprType, mrMethodNode));
+                createBootstrapMethodArguments(
+                        abstractMethodDesc,
+                        mrMethodNode.isStatic() || isConstructorReference ? Opcodes.H_INVOKESTATIC
: Opcodes.H_INVOKEVIRTUAL,
+                        isConstructorReference ? controller.getClassNode() : mrExprType,
+                        mrMethodNode)
+        );
 
         if (isClassExpr) {
             controller.getOperandStack().push(redirect);
@@ -124,6 +148,38 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         }
     }
 
+    private void addSyntheticMethodForConstructorReference(String syntheticMethodName, ClassNode
returnType, Parameter[] parametersWithExactType) {
+        ArgumentListExpression ctorArgs = args(parametersWithExactType);
+
+        controller.getClassNode().addSyntheticMethod(
+                syntheticMethodName,
+                Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
+                returnType,
+                parametersWithExactType,
+                ClassNode.EMPTY_ARRAY,
+                block(
+                        returnS(
+                                returnType.isArray()
+                                        ?   new ArrayExpression(
+                                                ClassHelper.make(ArrayTypeUtils.elementType(returnType.getTypeClass())),
+                                                null,
+                                                ctorArgs.getExpressions()
+                                            )
+                                        :   ctorX(returnType, ctorArgs)
+                        )
+                )
+        );
+
+    }
+
+    private String createSyntheticMethodForConstructorReference() {
+        return controller.getContext().getNextConstructorReferenceSyntheticMethodName(controller.getMethodNode());
+    }
+
+    private boolean isConstructorReference(String mrMethodName) {
+        return "new".equals(mrMethodName);
+    }
+
     private boolean isClassExpr(Expression mrExpr) {
         return mrExpr instanceof ClassExpression;
     }
@@ -167,7 +223,11 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         return parameters;
     }
 
-    private MethodNode findMrMethodNode(String mrMethodName, Parameter[] abstractMethodParameters,
Expression mrExpr) {
+    private MethodNode findMrMethodNode(String mrMethodName, Parameter[] abstractMethodParameters,
Expression mrExpr, boolean isConstructorReference) {
+        if (isConstructorReference) {
+            return controller.getClassNode().getMethod(mrMethodName, abstractMethodParameters);
+        }
+
         ClassNode mrExprType = mrExpr.getType();
         List<MethodNode> methodNodeList = mrExprType.getMethods(mrMethodName);
         ClassNode classNode = controller.getClassNode();
@@ -204,4 +264,4 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
         return mrMethodNode;
     }
-}
+}
\ No newline at end of file
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 07a073f..f0a90d0 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -38,6 +38,22 @@ class MethodReferenceTest extends GroovyTestCase {
     }
 
     // class::instanceMethod
+    void testFunctionCI_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Collectors
+            
+            @groovy.transform.CompileStatic
+            static void p() {
+                def result = [1, 2, 3].stream().map(Object::toString).collect(Collectors.toList())
+                assert 3 == result.size()
+                assert ['1', '2', '3'] == result
+            }
+            
+            p()
+        '''
+    }
+
+    // class::instanceMethod
     void testBinaryOperatorCI() {
         if (true) return
 
@@ -55,6 +71,22 @@ class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
+    // class::instanceMethod
+    void testBinaryOperatorCI_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Stream
+
+            @groovy.transform.CompileStatic
+            static void p() {
+                def result = [new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)].stream().reduce(new
BigDecimal(0), BigDecimal::add)
+
+                assert new BigDecimal(6) == result
+            }
+            
+            p()
+        '''
+    }
+
     // class::staticMethod
     void testFunctionCS() {
         assertScript '''
@@ -72,6 +104,23 @@ class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
+    // class::staticMethod
+    void testFunctionCS_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Stream
+            import java.util.stream.Collectors
+
+            @groovy.transform.CompileStatic
+            static void p() {
+                def result = [1, -2, 3].stream().map(Math::abs).collect(Collectors.toList())
+
+                assert [1, 2, 3] == result
+            }
+            
+            p()
+        '''
+    }
+
     // instance::instanceMethod
     void testBinaryOperatorII() {
         assertScript '''
@@ -97,6 +146,31 @@ class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
+    // instance::instanceMethod
+    void testBinaryOperatorII_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Stream
+            import java.util.stream.Collectors
+
+            @groovy.transform.CompileStatic
+            static void p() {
+                Adder adder = new Adder()
+                def result = [new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)].stream().reduce(new
BigDecimal(0), adder::add)
+
+                assert new BigDecimal(6) == result
+            }
+            
+            p()
+            
+            @groovy.transform.CompileStatic
+            class Adder {
+                public BigDecimal add(BigDecimal a, BigDecimal b) {
+                    return a.add(b)
+                }
+            }
+        '''
+    }
+
     // instance::staticMethod
     void testBinaryOperatorIS() {
         assertScript '''
@@ -121,4 +195,92 @@ class MethodReferenceTest extends GroovyTestCase {
             }
         '''
     }
+
+    // instance::staticMethod
+    void testBinaryOperatorIS_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Stream
+            import java.util.stream.Collectors
+
+            @groovy.transform.CompileStatic
+            static void p() {
+                Adder adder = new Adder()
+                def result = [new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)].stream().reduce(new
BigDecimal(0), adder::add)
+
+                assert new BigDecimal(6) == result
+            }
+            
+            p()
+            
+            @groovy.transform.CompileStatic
+            class Adder {
+                public static BigDecimal add(BigDecimal a, BigDecimal b) {
+                    return a.add(b)
+                }
+            }
+        '''
+    }
+
+    // arrayClass::new
+    void testIntFunctionCN() {
+        assertScript '''
+            import java.util.stream.Stream
+
+            @groovy.transform.CompileStatic
+            void p() {
+                assert new Integer[] { 1, 2, 3 } == [1, 2, 3].stream().toArray(Integer[]::new)
+            }
+            
+            p()
+
+        '''
+    }
+
+    // arrayClass::new
+    void testIntFunctionCN_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Stream
+
+            @groovy.transform.CompileStatic
+            static void p() {
+                assert new Integer[] { 1, 2, 3 } == [1, 2, 3].stream().toArray(Integer[]::new)
+            }
+            
+            p()
+
+        '''
+    }
+
+    // class::new
+    void testFunctionCN() {
+        assertScript '''
+            import java.util.stream.Stream
+            import java.util.stream.Collectors
+
+            @groovy.transform.CompileStatic
+            void p() {
+                assert [1, 2, 3] == ["1", "2", "3"].stream().map(Integer::new).collect(Collectors.toList())
+            }
+            
+            p()
+
+        '''
+    }
+
+    // class::new
+    void testFunctionCN_STATICMETHOD() {
+        assertScript '''
+            import java.util.stream.Stream
+            import java.util.stream.Collectors
+
+            @groovy.transform.CompileStatic
+            static void p() {
+                assert [1, 2, 3] == ["1", "2", "3"].stream().map(Integer::new).collect(Collectors.toList())
+            }
+            
+            p()
+
+        '''
+    }
+
 }


Mime
View raw message