Repository: groovy
Updated Branches:
  refs/heads/native-lambda 621cd4a4c -> f14131ec0


Support updating non-final local variables in lambda


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

Branch: refs/heads/native-lambda
Commit: f14131ec07bf850979daac804d71f4cffb141e8d
Parents: 621cd4a
Author: sunlan <[email protected]>
Authored: Fri Jan 19 19:41:44 2018 +0800
Committer: sunlan <[email protected]>
Committed: Fri Jan 19 19:41:44 2018 +0800

----------------------------------------------------------------------
 .../groovy/classgen/asm/ClosureWriter.java      |  78 +++++++++-----
 .../asm/sc/StaticTypesLambdaWriter.java         | 106 ++++++++-----------
 src/test/groovy/transform/stc/LambdaTest.groovy |  42 ++++++++
 3 files changed, 135 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/f14131ec/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java 
b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index defc7bf..343b714 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -19,10 +19,9 @@
 package org.codehaus.groovy.classgen.asm;
 
 import org.codehaus.groovy.GroovyBugError;
-import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.CodeVisitorSupport;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.FieldNode;
 import org.codehaus.groovy.ast.InnerClassNode;
@@ -45,6 +44,7 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
 import org.codehaus.groovy.ast.stmt.ReturnStatement;
 import org.codehaus.groovy.classgen.AsmClassGenerator;
 import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.SourceUnit;
 import org.objectweb.asm.MethodVisitor;
 
 import java.util.HashMap;
@@ -254,6 +254,28 @@ public class ClosureWriter {
         BlockStatement block = createBlockStatementForConstructor(expression);
 
         // let's assign all the parameter fields from the outer context
+        addFieldsAndGettersForLocalVariables(answer, localVariableParams);
+
+        addConstructor(expression, localVariableParams, answer, block);
+        
+        correctAccessedVariable(answer,expression);
+        
+        return answer;
+    }
+
+    protected ConstructorNode addConstructor(ClosureExpression expression, 
Parameter[] localVariableParams, InnerClassNode answer, BlockStatement block) {
+        Parameter[] params = new Parameter[2 + localVariableParams.length];
+        params[0] = new Parameter(ClassHelper.OBJECT_TYPE, OUTER_INSTANCE);
+        params[1] = new Parameter(ClassHelper.OBJECT_TYPE, THIS_OBJECT);
+        System.arraycopy(localVariableParams, 0, params, 2, 
localVariableParams.length);
+
+        ConstructorNode constructorNode = answer.addConstructor(ACC_PUBLIC, 
params, ClassNode.EMPTY_ARRAY, block);
+        constructorNode.setSourcePosition(expression);
+
+        return constructorNode;
+    }
+
+    protected void addFieldsAndGettersForLocalVariables(InnerClassNode answer, 
Parameter[] localVariableParams) {
         for (Parameter param : localVariableParams) {
             String paramName = param.getName();
             ClassNode type = param.getType();
@@ -280,18 +302,6 @@ public class ClosureWriter {
                         new ReturnStatement(fieldExp));
             }
         }
-
-        Parameter[] params = new Parameter[2 + localVariableParams.length];
-        params[0] = new Parameter(ClassHelper.OBJECT_TYPE, OUTER_INSTANCE);
-        params[1] = new Parameter(ClassHelper.OBJECT_TYPE, THIS_OBJECT);
-        System.arraycopy(localVariableParams, 0, params, 2, 
localVariableParams.length);
-
-        ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, 
ClassNode.EMPTY_ARRAY, block);
-        sn.setSourcePosition(expression);
-        
-        correctAccessedVariable(answer,expression);
-        
-        return answer;
     }
 
     protected BlockStatement 
createBlockStatementForConstructor(ClosureExpression expression) {
@@ -322,21 +332,33 @@ public class ClosureWriter {
                 + controller.getContext().getNextClosureInnerName(outerClass, 
classNode, methodNode);
     }
 
+    protected static class CorrectAccessedVariableVisitor extends 
ClassCodeVisitorSupport {
+        private InnerClassNode icn;
+
+        public CorrectAccessedVariableVisitor(InnerClassNode icn) {
+            this.icn = icn;
+        }
+
+        @Override
+        public void visitVariableExpression(VariableExpression expression) {
+            Variable v = expression.getAccessedVariable();
+            if (v == null) return;
+            if (!(v instanceof FieldNode)) return;
+            String name = expression.getName();
+            FieldNode fn = icn.getDeclaredField(name);
+            if (fn != null) { // only overwrite if we find something more 
specific
+                expression.setAccessedVariable(fn);
+            }
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+    }
+
     private static void correctAccessedVariable(final InnerClassNode 
closureClass, ClosureExpression ce) {
-        CodeVisitorSupport visitor = new CodeVisitorSupport() {
-            @Override
-            public void visitVariableExpression(VariableExpression expression) 
{
-                Variable v = expression.getAccessedVariable(); 
-                if (v==null) return;
-                if (!(v instanceof FieldNode)) return;
-                String name = expression.getName();
-                FieldNode fn = closureClass.getDeclaredField(name);
-                if (fn != null) { // only overwrite if we find something more 
specific
-                    expression.setAccessedVariable(fn);
-                }
-            }  
-        };
-        visitor.visitClosureExpression(ce);
+        new 
CorrectAccessedVariableVisitor(closureClass).visitClosureExpression(ce);
     }
 
     /*

http://git-wip-us.apache.org/repos/asf/groovy/blob/f14131ec/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 78848be..3e36628 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
@@ -20,9 +20,9 @@
 package org.codehaus.groovy.classgen.asm.sc;
 
 import org.codehaus.groovy.GroovyBugError;
-import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
@@ -33,7 +33,6 @@ import org.codehaus.groovy.ast.expr.LambdaExpression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
-import org.codehaus.groovy.classgen.asm.BytecodeVariable;
 import org.codehaus.groovy.classgen.asm.CompileStack;
 import org.codehaus.groovy.classgen.asm.LambdaWriter;
 import org.codehaus.groovy.classgen.asm.OperandStack;
@@ -52,7 +51,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import static 
org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.PARAMETER_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -72,6 +70,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
     private static final String ENCLOSING_THIS = "__enclosing_this";
     private static final String LAMBDA_THIS = "__lambda_this";
     public static final String INIT = "<init>";
+    public static final String IS_GENERATED_CONSTRUCTOR = 
"__IS_GENERATED_CONSTRUCTOR";
     private StaticTypesClosureWriter staticTypesClosureWriter;
     private WriterController controller;
     private WriterControllerFactory factory;
@@ -111,20 +110,21 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
{
         ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC 
| (isInterface ? ACC_STATIC : 0));
         MethodNode syntheticLambdaMethodNode = 
lambdaClassNode.getMethods(DO_CALL).get(0);
 
-        newAndLoadGroovyLambdaInstance(lambdaClassNode);
+        newGroovyLambdaInstanceAndLoad(lambdaClassNode, 
syntheticLambdaMethodNode);
+
         loadEnclosingClassInstance();
-        Parameter[] lambdaSharedVariableParameters = 
loadSharedVariables(syntheticLambdaMethodNode);
+
 
         MethodVisitor mv = controller.getMethodVisitor();
         OperandStack operandStack = controller.getOperandStack();
 
         mv.visitInvokeDynamicInsn(
                 abstractMethodNode.getName(),
-                createAbstractMethodDesc(syntheticLambdaMethodNode, 
parameterType, lambdaClassNode),
+                createAbstractMethodDesc(parameterType, lambdaClassNode),
                 createBootstrapMethod(isInterface),
                 createBootstrapMethodArguments(abstractMethodDesc, 
lambdaClassNode, syntheticLambdaMethodNode)
         );
-        operandStack.replace(parameterType.redirect(), 
lambdaSharedVariableParameters.length + 1);
+        operandStack.replace(parameterType.redirect(), 1);
     }
 
     private void loadEnclosingClassInstance() {
@@ -140,7 +140,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
         }
     }
 
-    private void newAndLoadGroovyLambdaInstance(ClassNode lambdaClassNode) {
+    private void newGroovyLambdaInstanceAndLoad(ClassNode lambdaClassNode, 
MethodNode syntheticLambdaMethodNode) {
         MethodVisitor mv = controller.getMethodVisitor();
         String lambdaClassInternalName = 
BytecodeHelper.getClassInternalName(lambdaClassNode);
         mv.visitTypeInsn(NEW, lambdaClassInternalName);
@@ -149,32 +149,38 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
{
         loadEnclosingClassInstance();
         loadEnclosingClassInstance();
 
-        Parameter[] lambdaClassConstructorParameters = 
createConstructorParameters();
+        loadSharedVariables(syntheticLambdaMethodNode);
+
+        List<ConstructorNode> constructorNodeList =
+                lambdaClassNode.getDeclaredConstructors().stream()
+                        .filter(e -> 
Boolean.TRUE.equals(e.getNodeMetaData(IS_GENERATED_CONSTRUCTOR)))
+                        .collect(Collectors.toList());
+
+        if (constructorNodeList.size() == 0) {
+            throw new GroovyBugError("Failed to find the generated 
constructor");
+        }
+
+        ConstructorNode constructorNode = constructorNodeList.get(0);
+        Parameter[] lambdaClassConstructorParameters = 
constructorNode.getParameters();
         mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, INIT, 
BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, 
lambdaClassConstructorParameters), lambdaClassNode.isInterface());
         controller.getOperandStack().replace(ClassHelper.LAMBDA_TYPE, 
lambdaClassConstructorParameters.length);
     }
 
     private Parameter[] loadSharedVariables(MethodNode 
syntheticLambdaMethodNode) {
-        OperandStack operandStack = controller.getOperandStack();
-        CompileStack compileStack = controller.getCompileStack();
-
         Parameter[] lambdaSharedVariableParameters = 
syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
         for (Parameter parameter : lambdaSharedVariableParameters) {
             String parameterName = parameter.getName();
-//            loadReference(parameterName, controller);
-//            if 
(parameter.getNodeMetaData(LambdaWriter.UseExistingReference.class)==null) {
-//                
parameter.setNodeMetaData(LambdaWriter.UseExistingReference.class,Boolean.TRUE);
-//            }
-
-            BytecodeVariable variable = 
compileStack.getVariable(parameterName, true);
-            operandStack.loadOrStoreVariable(variable, false);
+            loadReference(parameterName, controller);
+            if 
(parameter.getNodeMetaData(LambdaWriter.UseExistingReference.class) == null) {
+                
parameter.setNodeMetaData(LambdaWriter.UseExistingReference.class, 
Boolean.TRUE);
+            }
         }
 
         return lambdaSharedVariableParameters;
     }
 
-    private String createAbstractMethodDesc(MethodNode 
syntheticLambdaMethodNode, ClassNode parameterType, ClassNode lambdaClassNode) {
-        List<Parameter> lambdaSharedVariableList = new 
LinkedList<Parameter>(Arrays.asList(syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES)));
+    private String createAbstractMethodDesc(ClassNode parameterType, ClassNode 
lambdaClassNode) {
+        List<Parameter> lambdaSharedVariableList = new LinkedList<>();
 
         prependEnclosingThis(lambdaSharedVariableList);
         prependParameter(lambdaSharedVariableList, LAMBDA_THIS, 
lambdaClassNode);
@@ -250,19 +256,17 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
{
             answer.setScriptBody(true);
         }
 
-        Parameter[] constructorParameters = createConstructorParameters();
-        answer.addConstructor(ACC_PUBLIC, constructorParameters, 
ClassNode.EMPTY_ARRAY, super.createBlockStatementForConstructor(expression));
+        MethodNode syntheticLambdaMethodNode = 
addSyntheticLambdaMethodNode(expression, answer);
+        Parameter[] localVariableParameters = 
syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
 
-        addSyntheticLambdaMethodNode(expression, answer);
+        addFieldsAndGettersForLocalVariables(answer, localVariableParameters);
+        ConstructorNode constructorNode = addConstructor(expression, 
localVariableParameters, answer, 
createBlockStatementForConstructor(expression));
+        constructorNode.putNodeMetaData(IS_GENERATED_CONSTRUCTOR, 
Boolean.TRUE);
 
-        return answer;
-    }
+        Parameter enclosingThisParameter = 
syntheticLambdaMethodNode.getParameters()[0];
+        new TransformationVisitor(answer, 
enclosingThisParameter).visitMethod(syntheticLambdaMethodNode);
 
-    private Parameter[] createConstructorParameters() {
-        Parameter[] params = new Parameter[2];
-        params[0] = new Parameter(ClassHelper.OBJECT_TYPE, OUTER_INSTANCE);
-        params[1] = new Parameter(ClassHelper.OBJECT_TYPE, THIS_OBJECT);
-        return params;
+        return answer;
     }
 
     private String genLambdaClassName() {
@@ -274,21 +278,19 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
{
                 + controller.getContext().getNextLambdaInnerName(outerClass, 
classNode, methodNode);
     }
 
-    private void addSyntheticLambdaMethodNode(LambdaExpression expression, 
InnerClassNode answer) {
+    private MethodNode addSyntheticLambdaMethodNode(LambdaExpression 
expression, InnerClassNode answer) {
         Parameter[] parametersWithExactType = 
createParametersWithExactType(expression); // expression.getParameters();
         ClassNode returnType = 
expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); 
//abstractMethodNode.getReturnType();
         Parameter[] localVariableParameters = 
getLambdaSharedVariables(expression);
         removeInitialValues(localVariableParameters);
-        Parameter[] methodParameters = 
Stream.concat(Arrays.stream(localVariableParameters), 
Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new);
 
-        List<Parameter> methodParameterList = new 
LinkedList<Parameter>(Arrays.asList(methodParameters));
-
-        Parameter thisParameter = prependEnclosingThis(methodParameterList);
+        List<Parameter> methodParameterList = new 
LinkedList<Parameter>(Arrays.asList(parametersWithExactType));
+        prependEnclosingThis(methodParameterList);
 
         MethodNode methodNode =
                 answer.addMethod(
                         DO_CALL,
-                        Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
+                        Opcodes.ACC_PUBLIC,
                         returnType,
                         methodParameterList.toArray(Parameter.EMPTY_ARRAY),
                         ClassNode.EMPTY_ARRAY,
@@ -298,7 +300,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
         methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, 
localVariableParameters);
         methodNode.setSourcePosition(expression);
 
-        new TransformationVisitor(methodNode, 
thisParameter).visitMethod(methodNode);
+        return methodNode;
     }
 
     private Parameter prependEnclosingThis(List<Parameter> 
methodParameterList) {
@@ -336,12 +338,11 @@ public class StaticTypesLambdaWriter extends LambdaWriter 
{
         return staticTypesClosureWriter.createClosureClass(expression, mods);
     }
 
-    private static final class TransformationVisitor extends 
ClassCodeVisitorSupport {
-        private MethodNode methodNode;
+    private static final class TransformationVisitor extends 
CorrectAccessedVariableVisitor {
         private Parameter thisParameter;
 
-        public TransformationVisitor(MethodNode methodNode, Parameter 
thisParameter) {
-            this.methodNode = methodNode;
+        public TransformationVisitor(InnerClassNode icn, Parameter 
thisParameter) {
+            super(icn);
             this.thisParameter = thisParameter;
         }
 
@@ -351,27 +352,6 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
         }
 
         @Override
-        public void visitVariableExpression(VariableExpression expression) {
-            if (expression.isClosureSharedVariable()) {
-                final String variableName = expression.getName();
-                Parameter[] parametersWithSameVariableName =
-                        Arrays.stream(methodNode.getParameters())
-                                .filter(e -> variableName.equals(e.getName()))
-                                .toArray(Parameter[]::new);
-
-                if (parametersWithSameVariableName.length != 1) {
-                    throw new 
GroovyBugError(parametersWithSameVariableName.length + " parameters with same 
name " + variableName + " found(Expect only one matched).");
-                }
-
-                
expression.setAccessedVariable(parametersWithSameVariableName[0]);
-                expression.setClosureSharedVariable(false);
-
-            }
-
-            super.visitVariableExpression(expression);
-        }
-
-        @Override
         public void visitMethodCallExpression(MethodCallExpression call) {
             if (!call.getMethodTarget().isStatic()) {
                 Expression objectExpression = call.getObjectExpression();

http://git-wip-us.apache.org/repos/asf/groovy/blob/f14131ec/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 356dffa..136fbe2 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -428,4 +428,46 @@ TestScript0.groovy: 14: [Static type checking] - Cannot 
find matching method jav
         }
         '''
     }
+
+    void testFunctionWithUpdatingLocalVariable() {
+        assertScript '''
+        import groovy.transform.CompileStatic
+        import java.util.stream.Collectors
+        import java.util.stream.Stream
+        
+        @CompileStatic
+        public class Test1 {
+            public static void main(String[] args) {
+                p();
+            }
+        
+            public static void p() {
+                int i = 1
+                assert [2, 4, 7] == Stream.of(1, 2, 3).map(e -> i += 
e).collect(Collectors.toList())
+                assert 7 == i
+            }
+        }
+        '''
+    }
+
+    void testFunctionWithUpdatingLocalVariable2() {
+        assertScript '''
+        import groovy.transform.CompileStatic
+        import java.util.stream.Collectors
+        import java.util.stream.Stream
+        
+        @CompileStatic
+        public class Test1 {
+            public static void main(String[] args) {
+                new Test1().p();
+            }
+        
+            public void p() {
+                int i = 1
+                assert [2, 4, 7] == Stream.of(1, 2, 3).map(e -> i += 
e).collect(Collectors.toList())
+                assert 7 == i
+            }
+        }
+        '''
+    }
 }

Reply via email to