Repository: groovy Updated Branches: refs/heads/native-lambda b711a68d9 -> 6bf63025e
Support invoking instance method in lambda Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/6bf63025 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/6bf63025 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/6bf63025 Branch: refs/heads/native-lambda Commit: 6bf63025ea0478f92ae7075990e90f435cf656fc Parents: b711a68 Author: sunlan <[email protected]> Authored: Wed Jan 17 14:45:42 2018 +0800 Committer: sunlan <[email protected]> Committed: Wed Jan 17 14:45:42 2018 +0800 ---------------------------------------------------------------------- .../asm/sc/StaticTypesLambdaWriter.java | 78 +++++++++++++++++--- src/test/groovy/transform/stc/LambdaTest.groovy | 23 ++++++ 2 files changed, 91 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/6bf63025/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 f3679b3..892927e 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 @@ -27,8 +27,10 @@ import org.codehaus.groovy.ast.InnerClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; 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.VariableExpression; import org.codehaus.groovy.classgen.asm.BytecodeHelper; import org.codehaus.groovy.classgen.asm.BytecodeVariable; @@ -46,6 +48,7 @@ import org.objectweb.asm.Type; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -54,15 +57,16 @@ import java.util.stream.Stream; import static org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.PARAMETER_TYPE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ALOAD; /** * Writer responsible for generating lambda classes in statically compiled mode. - * */ public class StaticTypesLambdaWriter extends LambdaWriter { public static final String DO_CALL = "doCall"; public static final String ORIGINAL_PARAMETERS_WITH_EXACT_TYPE = "__ORIGINAL_PARAMETERS_WITH_EXACT_TYPE"; public static final String LAMBDA_SHARED_VARIABLES = "__LAMBDA_SHARED_VARIABLES"; + public static final String THIS = "__this"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -97,11 +101,22 @@ public class StaticTypesLambdaWriter extends LambdaWriter { MethodNode abstractMethodNode = abstractMethodNodeList.get(0); String abstractMethodDesc = createMethodDescriptor(abstractMethodNode); - boolean isInterface = controller.getClassNode().isInterface(); + ClassNode classNode = controller.getClassNode(); + boolean isInterface = classNode.isInterface(); ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0)); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); MethodVisitor mv = controller.getMethodVisitor(); + + OperandStack operandStack = controller.getOperandStack(); + + if (controller.getMethodNode().isStatic()) { + operandStack.pushConstant(ConstantExpression.NULL); + } else { + mv.visitVarInsn(ALOAD, 0); + operandStack.push(classNode); + } + Parameter[] lambdaSharedVariableParameters = loadSharedVariables(syntheticLambdaMethodNode); mv.visitInvokeDynamicInsn( @@ -110,7 +125,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter { createBootstrapMethod(isInterface), createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) ); - controller.getOperandStack().replace(parameterType.redirect(), lambdaSharedVariableParameters.length); + operandStack.replace(parameterType.redirect(), lambdaSharedVariableParameters.length + 1); } private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) { @@ -132,10 +147,11 @@ public class StaticTypesLambdaWriter extends LambdaWriter { } private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, ClassNode parameterType) { - Parameter[] lambdaSharedVariables = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); - String methodDescriptor = BytecodeHelper.getMethodDescriptor(parameterType.redirect(), lambdaSharedVariables); + List<Parameter> lambdaSharedVariableList = new LinkedList<Parameter>(Arrays.asList(syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES))); + + prependThis(lambdaSharedVariableList); - return methodDescriptor; + return BytecodeHelper.getMethodDescriptor(parameterType.redirect(), lambdaSharedVariableList.toArray(Parameter.EMPTY_ARRAY)); } private Handle createBootstrapMethod(boolean isInterface) { @@ -227,12 +243,16 @@ public class StaticTypesLambdaWriter extends LambdaWriter { 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 = prependThis(methodParameterList); + MethodNode methodNode = answer.addMethod( DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, returnType, - methodParameters, + methodParameterList.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, expression.getCode() ); @@ -240,7 +260,23 @@ public class StaticTypesLambdaWriter extends LambdaWriter { methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters); methodNode.setSourcePosition(expression); - new LocalVariableReplacementVisitor(methodNode).visitMethod(methodNode); + new TransformationVisitor(methodNode, thisParameter).visitMethod(methodNode); + } + + private Parameter prependThis(List<Parameter> methodParameterList) { + ClassNode classNode = controller.getClassNode(); + + // FIXME the following code `classNode.setUsingGenerics(false)` is used to avoid the error: + // ERROR MESSAGE: A transform used a generics containing ClassNode Test1 for the method public static int doCall(Test1 __this, java.lang.Integer e) { ... } directly. You are not supposed to do this. Please create a new ClassNode referring to the old ClassNode and use the new ClassNode instead of the old one. Otherwise the compiler will create wrong descriptors and a potential NullPointerException in TypeResolver in the OpenJDK. If this is not your own doing, please report this bug to the writer of the transform. + classNode.setUsingGenerics(false); + + Parameter thisParameter = new Parameter(classNode, THIS); + thisParameter.setOriginType(classNode); + thisParameter.setClosureSharedVariable(false); + + methodParameterList.add(0, thisParameter); + + return thisParameter; } private Parameter[] createParametersWithExactType(LambdaExpression expression) { @@ -263,11 +299,13 @@ public class StaticTypesLambdaWriter extends LambdaWriter { return staticTypesClosureWriter.createClosureClass(expression, mods); } - private static final class LocalVariableReplacementVisitor extends ClassCodeVisitorSupport { + private static final class TransformationVisitor extends ClassCodeVisitorSupport { private MethodNode methodNode; + private Parameter thisParameter; - public LocalVariableReplacementVisitor(MethodNode methodNode) { + public TransformationVisitor(MethodNode methodNode, Parameter thisParameter) { this.methodNode = methodNode; + this.thisParameter = thisParameter; } @Override @@ -295,5 +333,25 @@ public class StaticTypesLambdaWriter extends LambdaWriter { super.visitVariableExpression(expression); } + + @Override + public void visitMethodCallExpression(MethodCallExpression call) { + if (!call.getMethodTarget().isStatic()) { + Expression objectExpression = call.getObjectExpression(); + + if (objectExpression instanceof VariableExpression) { + VariableExpression originalObjectExpression = (VariableExpression) objectExpression; + if (null == originalObjectExpression.getAccessedVariable()) { + VariableExpression thisVariable = new VariableExpression(thisParameter); + thisVariable.setSourcePosition(originalObjectExpression); + + call.setObjectExpression(thisVariable); + call.setImplicitThis(false); + } + } + } + + super.visitMethodCallExpression(call); + } } } http://git-wip-us.apache.org/repos/asf/groovy/blob/6bf63025/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 ad5c9f2..79cb71f 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -282,4 +282,27 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } ''' } + + void testFunctionWithLocalVariables5() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + + @CompileStatic + public class Test4 { + public static void main(String[] args) { + new Test4().p(); + } + + public void p() { + assert ['Hello Jochen', 'Hello Daniel'] == Stream.of("Jochen", "Daniel").map(e -> hello() + e).collect(Collectors.toList()); + } + + public String hello() { + return "Hello "; + } + } + ''' + } }
