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()); + } + } + + ''' + } }
