From d746dd7ff222abad8e61aefd7d5b153bb9c28250 Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 11 Jan 2018 15:35:02 +0800 Subject: [PATCH 01/47] Support very basic native lambda --- .../groovy/ast/GroovyCodeVisitor.java | 5 + .../groovy/ast/expr/LambdaExpression.java | 5 + .../groovy/classgen/AsmClassGenerator.java | 10 + .../classgen/asm/DelegatingController.java | 7 +- .../groovy/classgen/asm/LambdaWriter.java | 31 ++ .../groovy/classgen/asm/WriterController.java | 6 + .../asm/sc/StaticInvocationWriter.java | 4 + .../asm/sc/StaticTypesLambdaWriter.java | 122 ++++++++ .../asm/sc/StaticTypesWriterController.java | 11 + .../groovy/transform/stc/LambdaTest.groovy | 56 ++++ .../groovy/parser/antlr4/AstBuilder.java | 273 +++++++++++++++++- 11 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java create mode 100644 src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java create mode 100644 src/test/groovy/transform/stc/LambdaTest.groovy diff --git a/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java b/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java index 12787c090c7..db793cc7c57 100644 --- a/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java +++ b/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java @@ -34,6 +34,7 @@ import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; import org.codehaus.groovy.ast.expr.FieldExpression; import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.LambdaExpression; import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapEntryExpression; import org.codehaus.groovy.ast.expr.MapExpression; @@ -138,6 +139,10 @@ public interface GroovyCodeVisitor { void visitClosureExpression(ClosureExpression expression); + default void visitLambdaExpression(LambdaExpression expression) { + visitClosureExpression(expression); + } + void visitTupleExpression(TupleExpression expression); void visitMapExpression(MapExpression expression); diff --git a/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java index f4f10012e60..ab30513d3c0 100644 --- a/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java +++ b/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java @@ -20,6 +20,7 @@ package org.codehaus.groovy.ast.expr; import org.codehaus.groovy.ast.AstToTextHelper; +import org.codehaus.groovy.ast.GroovyCodeVisitor; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.stmt.Statement; @@ -35,6 +36,10 @@ public LambdaExpression(Parameter[] parameters, Statement code) { super(parameters, code); } + public void visit(GroovyCodeVisitor visitor) { + visitor.visitLambdaExpression(this); + } + @Override public String getText() { String paramText = AstToTextHelper.getParametersText(this.getParameters()); diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java index 099bc9ce0e6..7721260d4bb 100644 --- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java +++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java @@ -55,6 +55,7 @@ import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.FieldExpression; import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.LambdaExpression; import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapEntryExpression; import org.codehaus.groovy.ast.expr.MapExpression; @@ -717,6 +718,11 @@ public void visitClosureExpression(ClosureExpression expression) { controller.getClosureWriter().writeClosure(expression); } + @Override + public void visitLambdaExpression(LambdaExpression expression) { + controller.getLambdaWriter().writeLambda(expression); + } + /** * Loads either this object or if we're inside a closure then load the top level owner */ @@ -2174,4 +2180,8 @@ public boolean addInnerClass(ClassNode innerClass) { mn.getUnit().addGeneratedInnerClass((InnerClassNode)innerClass); return innerClasses.add(innerClass); } + + public ClassVisitor getClassVisitor() { + return cv; + } } diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java b/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java index 22acbaaab58..3553a826488 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java @@ -112,7 +112,12 @@ public ClassVisitor getClassVisitor() { @Override public ClosureWriter getClosureWriter() { return delegationController.getClosureWriter(); - } + } + + @Override + public LambdaWriter getLambdaWriter() { + return delegationController.getLambdaWriter(); + } @Override public CompileStack getCompileStack() { diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java new file mode 100644 index 00000000000..3a3968892da --- /dev/null +++ b/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.classgen.asm; + +import org.codehaus.groovy.ast.expr.LambdaExpression; + +public class LambdaWriter extends ClosureWriter { + public LambdaWriter(WriterController wc) { + super(wc); + } + + public void writeLambda(LambdaExpression expression) { + super.writeClosure(expression); + } +} diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java b/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java index 3c9d843266f..fe7a21e45f9 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java @@ -55,6 +55,7 @@ public class WriterController { private CallSiteWriter callSiteWriter; private ClassVisitor cv; private ClosureWriter closureWriter; + private LambdaWriter lambdaWriter; private String internalClassName; private InvocationWriter invocationWriter; private BinaryExpressionHelper binaryExpHelper, fastPathBinaryExpHelper; @@ -120,6 +121,7 @@ public void init(AsmClassGenerator asmClassGenerator, GeneratorContext gcon, Cla this.operandStack = new OperandStack(this); this.assertionWriter = new AssertionWriter(this); this.closureWriter = new ClosureWriter(this); + this.lambdaWriter = new LambdaWriter(this); this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass()); this.acg = asmClassGenerator; this.sourceUnit = acg.getSourceUnit(); @@ -197,6 +199,10 @@ public ClosureWriter getClosureWriter() { return closureWriter; } + public LambdaWriter getLambdaWriter() { + return lambdaWriter; + } + public ClassVisitor getCv() { return cv; } diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index d2b5bc79f94..22482e51d8f 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -102,6 +102,7 @@ public class StaticInvocationWriter extends InvocationWriter { new Parameter(ClassHelper.OBJECT_TYPE, "args") } ); + public static final String PARAMETER_TYPE = "_parameter_type"; private final AtomicInteger labelCounter = new AtomicInteger(); @@ -435,6 +436,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { // first parameters as usual for (int i = 0; i < para.length - 1; i++) { Expression expression = argumentList.get(i); + expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); @@ -460,6 +462,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { } else if (argumentListSize == para.length) { for (int i = 0; i < argumentListSize; i++) { Expression expression = argumentList.get(i); + expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); @@ -491,6 +494,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { } for (int i = 0; i < arguments.length; i++) { Expression expression = arguments[i]; + expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); 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 new file mode 100644 index 00000000000..6feaba5a052 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.codehaus.groovy.classgen.asm.sc; + +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.LambdaExpression; +import org.codehaus.groovy.classgen.AsmClassGenerator; +import org.codehaus.groovy.classgen.asm.BytecodeHelper; +import org.codehaus.groovy.classgen.asm.LambdaWriter; +import org.codehaus.groovy.classgen.asm.WriterController; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.apache.groovy.parser.antlr4.AstBuilder.LAMBDA_ENCLOSING_CLASSNODE; +import static org.apache.groovy.parser.antlr4.AstBuilder.SYNTHETIC_LAMBDA_METHOD_NODE; +import static org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.PARAMETER_TYPE; +import static org.objectweb.asm.Opcodes.ACC_FINAL; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ACC_STATIC; + +/** + * Writer responsible for generating lambda classes in statically compiled mode. + * + */ +public class StaticTypesLambdaWriter extends LambdaWriter { + private StaticTypesClosureWriter staticTypesClosureWriter; + private WriterController controller; + + public StaticTypesLambdaWriter(WriterController wc) { + super(wc); + this.staticTypesClosureWriter = new StaticTypesClosureWriter(wc); + this.controller = wc; + } + + @Override + public void writeLambda(LambdaExpression expression) { + ClassNode parameterType = expression.getNodeMetaData(PARAMETER_TYPE); + + List abstractMethodNodeList = + parameterType.redirect().getMethods().stream() + .filter(MethodNode::isAbstract) + .collect(Collectors.toList()); + + if (abstractMethodNodeList.size() != 1) { + super.writeClosure(expression); + return; + } + + MethodNode abstractMethodNode = abstractMethodNodeList.get(0); + String abstractMethodName = abstractMethodNode.getName(); + String abstractMethodDesc = "()L" + parameterType.redirect().getPackageName().replace('.', '/') + "/" + parameterType.redirect().getNameWithoutPackage() + ";"; + + + AsmClassGenerator acg = controller.getAcg(); + ClassVisitor cw = acg.getClassVisitor(); + cw.visitInnerClass( + "java/lang/invoke/MethodHandles$Lookup", + "java/lang/invoke/MethodHandles", + "Lookup", + ACC_PUBLIC + ACC_FINAL + ACC_STATIC); + + MethodVisitor mv = controller.getMethodVisitor(); + MethodNode syntheticLambdaMethodNode = expression.getNodeMetaData(SYNTHETIC_LAMBDA_METHOD_NODE); + ClassNode lambdaEnclosingClassNode = expression.getNodeMetaData(LAMBDA_ENCLOSING_CLASSNODE); + + String syntheticLambdaMethodDesc = BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode); + + controller.getOperandStack().push(ClassHelper.OBJECT_TYPE); + + mv.visitInvokeDynamicInsn( + abstractMethodName, + abstractMethodDesc, + new Handle( + Opcodes.H_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;", + false), + new Object[] { + Type.getType(syntheticLambdaMethodDesc), + new Handle( + Opcodes.H_INVOKESTATIC, + lambdaEnclosingClassNode.getName(), + syntheticLambdaMethodNode.getName(), + syntheticLambdaMethodDesc, + false), + Type.getType(syntheticLambdaMethodDesc) + }); + + } + + @Override + protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) { + return staticTypesClosureWriter.createClosureClass(expression, mods); + } +} diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java index 47ef3d417af..15943e5c1ef 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java @@ -31,6 +31,7 @@ import org.codehaus.groovy.classgen.asm.ClosureWriter; import org.codehaus.groovy.classgen.asm.DelegatingController; import org.codehaus.groovy.classgen.asm.InvocationWriter; +import org.codehaus.groovy.classgen.asm.LambdaWriter; import org.codehaus.groovy.classgen.asm.StatementWriter; import org.codehaus.groovy.classgen.asm.TypeChooser; import org.codehaus.groovy.classgen.asm.UnaryExpressionHelper; @@ -60,6 +61,7 @@ public class StaticTypesWriterController extends DelegatingController { private BinaryExpressionMultiTypeDispatcher binaryExprHelper; private UnaryExpressionHelper unaryExpressionHelper; private ClosureWriter closureWriter; + private LambdaWriter lambdaWriter; public StaticTypesWriterController(WriterController normalController) { super(normalController); @@ -74,6 +76,7 @@ public void init(final AsmClassGenerator asmClassGenerator, final GeneratorConte this.typeChooser = new StaticTypesTypeChooser(); this.invocationWriter = new StaticInvocationWriter(this); this.closureWriter = new StaticTypesClosureWriter(this); + this.lambdaWriter = new StaticTypesLambdaWriter(this); this.unaryExpressionHelper = new StaticTypesUnaryExpressionHelper(this); CompilerConfiguration config = cn.getCompileUnit().getConfig(); @@ -183,4 +186,12 @@ public ClosureWriter getClosureWriter() { } return super.getClosureWriter(); } + + @Override + public LambdaWriter getLambdaWriter() { + if (isInStaticallyCheckedMethod) { + return lambdaWriter; + } + return super.getLambdaWriter(); + } } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy new file mode 100644 index 00000000000..0ee1ac36b64 --- /dev/null +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.transform.stc + +class LambdaTest extends GroovyTestCase { + void testMethodCall() { + 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) { + p1(); + // p2(); + // p3(); + } + + public static void p1() { + assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e + 1).collect(Collectors.toList()); + } + + /* + public static void p2() { + assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); + } + */ + + /* + public static void p3() { + Stream.of(1, 2, 3).forEach(e -> { System.out.println(e + 1); }); + } + */ + } + ''' + + } +} diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java index 675255bd718..f808dde609b 100644 --- a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java +++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java @@ -137,7 +137,212 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.*; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ADD; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AS; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AdditiveExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AndExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotatedQualifiedClassNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotationNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotationsOptContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnonymousInnerClassDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ArgumentsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ArrayInitializerContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AssertStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AssertStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.AssignmentExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockStatementsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockStatementsOptContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BooleanLiteralAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BreakStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BreakStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.BuiltInTypeContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CASE; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CastExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CastParExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CatchClauseContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CatchTypeContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassBodyContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassBodyDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceModifierContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceModifiersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceModifiersOptContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceTypeContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassicalForControlContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassifiedModifiersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClosureContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClosurePrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CommandArgumentContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CommandExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CommandExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CompilationUnitContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ConditionalExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ConditionalStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ConditionalStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ContinueStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ContinueStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CreatedNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.CreatorContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DEC; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DEF; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DEFAULT; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DimsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DimsOptContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DoWhileStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.DynamicMemberNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValueArrayInitializerContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValueContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValuePairContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValuePairsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValuesContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedArgumentListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedArgumentListElementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedForControlContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedStatementExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnumConstantContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnumConstantsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.EqualityExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExclusiveOrExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionInParContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionListElementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.FieldDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.FinallyBlockContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.FloatingPointLiteralAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForControlContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForInitContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForUpdateContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.FormalParameterContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.FormalParameterListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.FormalParametersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GE; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GT; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GroovyParserRuleContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringPathContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringValueContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.IN; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.INC; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.INSTANCEOF; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.IdentifierContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.IdentifierPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.IfElseStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ImportDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ImportStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.InclusiveOrExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.IndexPropertyArgsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.IntegerLiteralAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.KeywordsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LE; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LT; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LabeledStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LambdaBodyContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LambdaPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ListPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LiteralPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LocalVariableDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LocalVariableDeclarationStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LogicalAndExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LogicalOrExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.LoopStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapEntryContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapEntryLabelContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapEntryListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MemberDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodBodyContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodDeclarationStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ModifierContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ModifiersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ModifiersOptContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MultipleAssignmentExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.MultiplicativeExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NOT_IN; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NOT_INSTANCEOF; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NamePartContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NamedPropertyArgsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NewPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NonWildcardTypeArgumentsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NormalExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.NullLiteralAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PRIVATE; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PackageDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ParExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ParenPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PathElementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PathExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PostfixExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PostfixExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PowerExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.PrimitiveTypeContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedClassNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedClassNameListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedStandardClassNameContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.RegexExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.RelationalExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ResourceContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ResourceListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ResourcesContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ReturnStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ReturnTypeContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.STATIC; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.SUB; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ShiftExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.StandardLambdaExpressionContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.StandardLambdaParametersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.StatementsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.StringLiteralAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.StringLiteralContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.SuperPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.SwitchBlockStatementGroupContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.SwitchLabelContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.SwitchStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.SynchronizedStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ThisFormalParameterContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ThisPrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.ThrowStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TryCatchStatementContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TryCatchStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeArgumentContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeArgumentsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeArgumentsOrDiamondContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeBoundContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeDeclarationStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeListContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeNamePairContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeNamePairsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeParameterContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeParametersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypePrmrAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.UnaryAddExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.UnaryNotExprAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclarationContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclaratorContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclaratorIdContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclaratorsContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableInitializerContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableInitializersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableModifierContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableModifiersContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableModifiersOptContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableNamesContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.WhileStmtAltContext; import static org.apache.groovy.parser.antlr4.util.PositionConfigureUtils.configureAST; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last; @@ -149,6 +354,7 @@ * Created on 2016/08/14 */ public class AstBuilder extends GroovyParserBaseVisitor implements GroovyParserVisitor { + public AstBuilder(SourceUnit sourceUnit) { this.sourceUnit = sourceUnit; this.moduleNode = new ModuleNode(sourceUnit); @@ -1060,9 +1266,49 @@ public Void visitClassBody(ClassBodyContext ctx) { this.visitClassBodyDeclaration(e); }); + genSyntheticLambdaMethodNode(classNode); + return null; } + private void genSyntheticLambdaMethodNode(ClassNode classNode) { + if (null == classNode) { + classNode = moduleNode.getScriptClassDummy(); + } + + if (null == classNode) { + return; + } + + List lambdaExpressionList = lambdaExpressionRegistry.get(classNode); + + if (null == lambdaExpressionList) { + return; + } + + for (LambdaExpression lambdaExpression : lambdaExpressionList) { + String syntheticLambdaMethodNodeName = createSyntheticLambdaMethodNodeName(); + + Parameter[] parameters = lambdaExpression.getParameters(); + if (parameters == null) { + parameters = Parameter.EMPTY_ARRAY; + } + + MethodNode methodNode = + classNode.addMethod(syntheticLambdaMethodNodeName, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, lambdaExpression.getCode()); + + lambdaExpression.putNodeMetaData(SYNTHETIC_LAMBDA_METHOD_NODE, methodNode); + lambdaExpression.putNodeMetaData(LAMBDA_ENCLOSING_CLASSNODE, classNode); + + configureAST(methodNode, lambdaExpression); + } + } + + private int syntheticLambdaMethodNodeCount = 0; + private String createSyntheticLambdaMethodNodeName() { + return "lambda$" + syntheticLambdaMethodNodeCount++; + } + @Override public List visitEnumConstants(EnumConstantsContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); @@ -3374,7 +3620,26 @@ public Expression visitGstringPath(GstringPathContext ctx) { @Override public LambdaExpression visitStandardLambdaExpression(StandardLambdaExpressionContext ctx) { - return configureAST(this.createLambda(ctx.standardLambdaParameters(), ctx.lambdaBody()), ctx); + LambdaExpression lambdaExpression = configureAST(this.createLambda(ctx.standardLambdaParameters(), ctx.lambdaBody()), ctx); + + registerLambdaExpression(lambdaExpression); + + return lambdaExpression; + } + + private void registerLambdaExpression(LambdaExpression lambdaExpression) { + ClassNode classNode = classNodeStack.peek(); + + if (null == classNode) { + classNode = moduleNode.getScriptClassDummy(); + } + + List lambdaExpressionList = lambdaExpressionRegistry.get(classNode); + if (null == lambdaExpressionList) { + lambdaExpressionList = new LinkedList<>(); + lambdaExpressionRegistry.put(classNode, lambdaExpressionList); + } + lambdaExpressionList.add(lambdaExpression); } private LambdaExpression createLambda(StandardLambdaParametersContext standardLambdaParametersContext, LambdaBodyContext lambdaBodyContext) { @@ -4282,6 +4547,7 @@ private void configureScriptClassNode() { scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber()); } + genSyntheticLambdaMethodNode(scriptClassNode); } private boolean isTrue(NodeMetaDataHandler nodeMetaDataHandler, String key) { @@ -4447,6 +4713,7 @@ public List getDeclarationExpressions() { private final List classNodeList = new LinkedList<>(); private final Deque classNodeStack = new ArrayDeque<>(); private final Deque> anonymousInnerClassesDefinedInMethodStack = new ArrayDeque<>(); + private final Map> lambdaExpressionRegistry = new LinkedHashMap<>(); private int anonymousInnerClassCounter = 1; private Tuple2 numberFormatError; @@ -4505,4 +4772,6 @@ public List getDeclarationExpressions() { private static final String FLOATING_POINT_LITERAL_TEXT = "_FLOATING_POINT_LITERAL_TEXT"; private static final String CLASS_NAME = "_CLASS_NAME"; + public static final String LAMBDA_ENCLOSING_CLASSNODE = "_LAMBDA_ENCLOSING_CLASSNODE"; + public static final String SYNTHETIC_LAMBDA_METHOD_NODE = "_SYNTHETIC_LAMBDA_METHOD_NODE"; } From 979cab6075b298f7375c62b2c4eb3c6abea6eeb1 Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 11 Jan 2018 17:28:11 +0800 Subject: [PATCH 02/47] Exclude some Lambda tests temporarily --- .../org/apache/groovy/parser/antlr4/GroovyParserTest.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy index 9fd07a57815..cbc3a200342 100644 --- a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy +++ b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy @@ -141,9 +141,11 @@ class GroovyParserTest extends GroovyTestCase { doTest('core/Closure_10.groovy', [Parameter]) } + /* TODO uncomment void "test groovy core - Lambda"() { doRunAndTestAntlr4('core/Lambda_01x.groovy') } + */ void "test groovy core - MethodReference"() { doRunAndTestAntlr4('core/MethodReference_01x.groovy') @@ -381,7 +383,9 @@ class GroovyParserTest extends GroovyTestCase { doTest('bugs/BUG-GROOVY-8161.groovy') doRunAndTestAntlr4('bugs/GROOVY-3898.groovy') doRunAndTestAntlr4('bugs/BUG-GROOVY-8311.groovy') - doRunAndTestAntlr4('bugs/GROOVY-8228.groovy') + + // TODO uncomment + // doRunAndTestAntlr4('bugs/GROOVY-8228.groovy') doRunAndTest('bugs/BUG-GROOVY-8426.groovy') } From 97649f9d1e3b3a14c68b18891e725d02bd04e6a0 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 12 Jan 2018 11:45:15 +0800 Subject: [PATCH 03/47] Generate inner class for native lambda --- src/main/groovy/groovy/lang/Lambda.java | 56 ++++ .../org/codehaus/groovy/ast/ClassHelper.java | 14 +- .../groovy/classgen/GeneratorContext.java | 14 +- .../groovy/classgen/asm/ClosureWriter.java | 14 +- .../asm/sc/StaticTypesLambdaWriter.java | 63 +++- .../groovy/runtime/GeneratedLambda.java | 28 ++ .../groovy/transform/stc/LambdaTest.groovy | 2 +- .../groovy/parser/antlr4/AstBuilder.java | 273 +----------------- 8 files changed, 177 insertions(+), 287 deletions(-) create mode 100644 src/main/groovy/groovy/lang/Lambda.java create mode 100644 src/main/java/org/codehaus/groovy/runtime/GeneratedLambda.java diff --git a/src/main/groovy/groovy/lang/Lambda.java b/src/main/groovy/groovy/lang/Lambda.java new file mode 100644 index 00000000000..0b09ef09899 --- /dev/null +++ b/src/main/groovy/groovy/lang/Lambda.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +import java.io.Serializable; + +/** + * Represents any lambda object in Groovy. + * + * @since 3.0.0 + */ +public abstract class Lambda extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable, Serializable { + /** + * When an object implementing interface Runnable is used + * to create a thread, starting the thread causes the object's + * run method to be called in that separately executing + * thread. + *

+ * The general contract of the method run is that it may + * take any action whatsoever. + * + * @see Thread#run() + */ + @Override + public void run() { + + } + + /** + * Computes a result, or throws an exception if unable to do so. + * + * @return computed result + * @throws Exception if unable to compute a result + */ + @Override + public V call() throws Exception { + return null; + } +} diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 60eaa47f401..27bc0003947 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -24,11 +24,13 @@ import groovy.lang.GroovyInterceptable; import groovy.lang.GroovyObject; import groovy.lang.GroovyObjectSupport; +import groovy.lang.Lambda; import groovy.lang.MetaClass; import groovy.lang.Range; import groovy.lang.Reference; import groovy.lang.Script; import org.codehaus.groovy.runtime.GeneratedClosure; +import org.codehaus.groovy.runtime.GeneratedLambda; import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport; import org.codehaus.groovy.transform.trait.Traits; import org.codehaus.groovy.util.ManagedConcurrentMap; @@ -58,12 +60,12 @@ public class ClassHelper { private static final Class[] classes = new Class[]{ Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE, - Closure.class, GString.class, List.class, Map.class, Range.class, + Closure.class, Lambda.class, GString.class, List.class, Map.class, Range.class, Pattern.class, Script.class, String.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Double.class, Float.class, BigDecimal.class, BigInteger.class, Number.class, Void.class, Reference.class, Class.class, MetaClass.class, - Iterator.class, GeneratedClosure.class, GroovyObjectSupport.class + Iterator.class, GeneratedClosure.class, GeneratedLambda.class, GroovyObjectSupport.class }; private static final String[] primitiveClassNames = new String[]{ @@ -73,7 +75,8 @@ public class ClassHelper { public static final ClassNode DYNAMIC_TYPE = makeCached(Object.class), OBJECT_TYPE = DYNAMIC_TYPE, - VOID_TYPE = makeCached(Void.TYPE), CLOSURE_TYPE = makeCached(Closure.class), + VOID_TYPE = makeCached(Void.TYPE), + CLOSURE_TYPE = makeCached(Closure.class), LAMBDA_TYPE = makeCached(Lambda.class), GSTRING_TYPE = makeCached(GString.class), LIST_TYPE = makeWithoutCaching(List.class), MAP_TYPE = makeWithoutCaching(Map.class), RANGE_TYPE = makeCached(Range.class), PATTERN_TYPE = makeCached(Pattern.class), STRING_TYPE = makeCached(String.class), @@ -102,6 +105,7 @@ public class ClassHelper { // uncached constants. CLASS_Type = makeWithoutCaching(Class.class), COMPARABLE_TYPE = makeWithoutCaching(Comparable.class), GENERATED_CLOSURE_Type = makeWithoutCaching(GeneratedClosure.class), + GENERATED_LAMBDA_TYPE = makeWithoutCaching(GeneratedLambda.class), GROOVY_OBJECT_SUPPORT_TYPE = makeWithoutCaching(GroovyObjectSupport.class), GROOVY_OBJECT_TYPE = makeWithoutCaching(GroovyObject.class), GROOVY_INTERCEPTABLE_TYPE = makeWithoutCaching(GroovyInterceptable.class); @@ -110,14 +114,14 @@ public class ClassHelper { OBJECT_TYPE, boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE, int_TYPE, long_TYPE, double_TYPE, float_TYPE, - VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE, + VOID_TYPE, CLOSURE_TYPE, LAMBDA_TYPE, GSTRING_TYPE, LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE, SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE, Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE, Double_TYPE, Float_TYPE, BigDecimal_TYPE, BigInteger_TYPE, Number_TYPE, void_WRAPPER_TYPE, REFERENCE_TYPE, CLASS_Type, METACLASS_TYPE, - Iterator_TYPE, GENERATED_CLOSURE_Type, GROOVY_OBJECT_SUPPORT_TYPE, + Iterator_TYPE, GENERATED_CLOSURE_Type, GENERATED_LAMBDA_TYPE, GROOVY_OBJECT_SUPPORT_TYPE, GROOVY_OBJECT_TYPE, GROOVY_INTERCEPTABLE_TYPE, Enum_Type, Annotation_TYPE }; diff --git a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java index 8b8a510762d..dd318bd36f4 100644 --- a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java +++ b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java @@ -34,6 +34,7 @@ public class GeneratorContext { private int innerClassIdx = 1; private int closureClassIdx = 1; + private int lambdaClassIdx = 1; private final CompileUnit compileUnit; public GeneratorContext(CompileUnit compileUnit) { @@ -54,6 +55,14 @@ public CompileUnit getCompileUnit() { } public String getNextClosureInnerName(ClassNode owner, ClassNode enclosingClass, MethodNode enclosingMethod) { + return getNextInnerName(owner, enclosingClass, enclosingMethod, "closure"); + } + + public String getNextLambdaInnerName(ClassNode owner, ClassNode enclosingClass, MethodNode enclosingMethod) { + return getNextInnerName(owner, enclosingClass, enclosingMethod, "lambda"); + } + + private String getNextInnerName(ClassNode owner, ClassNode enclosingClass, MethodNode enclosingMethod, String classifier) { String methodName = ""; if (enclosingMethod != null) { methodName = enclosingMethod.getName(); @@ -61,10 +70,11 @@ public String getNextClosureInnerName(ClassNode owner, ClassNode enclosingClass, if (enclosingClass.isDerivedFrom(ClassHelper.CLOSURE_TYPE)) { methodName = ""; } else { - methodName = "_"+encodeAsValidClassName(methodName); + methodName = "_" + encodeAsValidClassName(methodName); } } - return methodName + "_closure" + closureClassIdx++; + + return methodName + "_" + classifier + closureClassIdx++; } 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 af305bab75c..33ce0359bfe 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java @@ -184,9 +184,8 @@ private static boolean classNodeUsesReferences(ClassNode classNode) { protected ClassNode createClosureClass(ClosureExpression expression, int mods) { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); - MethodNode methodNode = controller.getMethodNode(); - String name = classNode.getName() + "$" - + controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode); // add a more informative name +// MethodNode methodNode = controller.getMethodNode(); + String name = genInnerClassName(); boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass(); Parameter[] parameters = expression.getParameters(); @@ -306,6 +305,15 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { return answer; } + protected String genInnerClassName() { + ClassNode classNode = controller.getClassNode(); + ClassNode outerClass = controller.getOutermostClass(); + MethodNode methodNode = controller.getMethodNode(); + + return classNode.getName() + "$" + + controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode); + } + private static void correctAccessedVariable(final InnerClassNode closureClass, ClosureExpression ce) { CodeVisitorSupport visitor = new CodeVisitorSupport() { @Override 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 6feaba5a052..46cbe15942f 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 @@ -21,24 +21,28 @@ import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; +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.Expression; import org.codehaus.groovy.ast.expr.LambdaExpression; import org.codehaus.groovy.classgen.AsmClassGenerator; import org.codehaus.groovy.classgen.asm.BytecodeHelper; import org.codehaus.groovy.classgen.asm.LambdaWriter; import org.codehaus.groovy.classgen.asm.WriterController; +import org.codehaus.groovy.classgen.asm.WriterControllerFactory; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; -import static org.apache.groovy.parser.antlr4.AstBuilder.LAMBDA_ENCLOSING_CLASSNODE; -import static org.apache.groovy.parser.antlr4.AstBuilder.SYNTHETIC_LAMBDA_METHOD_NODE; import static org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.PARAMETER_TYPE; import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; @@ -49,13 +53,21 @@ * */ public class StaticTypesLambdaWriter extends LambdaWriter { + public static final String DO_CALL = "doCall"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; + private WriterControllerFactory factory; + private final Map lambdaClassMap = new HashMap<>(); public StaticTypesLambdaWriter(WriterController wc) { super(wc); this.staticTypesClosureWriter = new StaticTypesClosureWriter(wc); this.controller = wc; + this.factory = new WriterControllerFactory() { + public WriterController makeController(final WriterController normalController) { + return controller; + } + }; } @Override @@ -86,9 +98,8 @@ public void writeLambda(LambdaExpression expression) { ACC_PUBLIC + ACC_FINAL + ACC_STATIC); MethodVisitor mv = controller.getMethodVisitor(); - MethodNode syntheticLambdaMethodNode = expression.getNodeMetaData(SYNTHETIC_LAMBDA_METHOD_NODE); - ClassNode lambdaEnclosingClassNode = expression.getNodeMetaData(LAMBDA_ENCLOSING_CLASSNODE); - + ClassNode lambdaEnclosingClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC); + MethodNode syntheticLambdaMethodNode = lambdaEnclosingClassNode.getMethods(DO_CALL).get(0); String syntheticLambdaMethodDesc = BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode); controller.getOperandStack().push(ClassHelper.OBJECT_TYPE); @@ -112,7 +123,49 @@ public void writeLambda(LambdaExpression expression) { false), Type.getType(syntheticLambdaMethodDesc) }); + } + + public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods) { + ClassNode lambdaClass = lambdaClassMap.get(expression); + if (lambdaClass == null) { + lambdaClass = createLambdaClass(expression, mods); + lambdaClassMap.put(expression, lambdaClass); + controller.getAcg().addInnerClass(lambdaClass); + lambdaClass.addInterface(ClassHelper.GENERATED_LAMBDA_TYPE); + lambdaClass.putNodeMetaData(WriterControllerFactory.class, factory); + } + return lambdaClass; + } + + protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { + ClassNode outerClass = controller.getOutermostClass(); + ClassNode classNode = controller.getClassNode(); + String name = genInnerClassName(); + boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass(); + + InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.LAMBDA_TYPE.getPlainNodeReference()); + answer.setEnclosingMethod(controller.getMethodNode()); + answer.setSynthetic(true); + answer.setUsingGenerics(outerClass.isUsingGenerics()); + answer.setSourcePosition(expression); + + if (staticMethodOrInStaticClass) { + answer.setStaticClass(true); + } + if (controller.isInScriptBody()) { + answer.setScriptBody(true); + } + + Parameter[] parameters = expression.getParameters(); + if (parameters == null) { + parameters = Parameter.EMPTY_ARRAY; + } + + MethodNode methodNode = + answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode()); + methodNode.setSourcePosition(expression); + return answer; } @Override diff --git a/src/main/java/org/codehaus/groovy/runtime/GeneratedLambda.java b/src/main/java/org/codehaus/groovy/runtime/GeneratedLambda.java new file mode 100644 index 00000000000..0a74b990bc0 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/runtime/GeneratedLambda.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.codehaus.groovy.runtime; + +/** + * Marker interface to identify lambda generated by the groovy compiler. + * For internal use only! + * + * @since 3.0.0 + */ +public interface GeneratedLambda { } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 0ee1ac36b64..0b9e1892b54 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -30,7 +30,7 @@ class LambdaTest extends GroovyTestCase { public class Test1 { public static void main(String[] args) { p1(); - // p2(); +// p2(); // p3(); } diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java index f808dde609b..675255bd718 100644 --- a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java +++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java @@ -137,212 +137,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ADD; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AS; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AdditiveExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AndExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotatedQualifiedClassNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotationNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnnotationsOptContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AnonymousInnerClassDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ArgumentsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ArrayInitializerContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AssertStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AssertStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.AssignmentExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockStatementsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BlockStatementsOptContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BooleanLiteralAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BreakStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BreakStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.BuiltInTypeContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CASE; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CastExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CastParExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CatchClauseContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CatchTypeContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassBodyContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassBodyDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceModifierContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceModifiersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceModifiersOptContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassOrInterfaceTypeContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassicalForControlContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClassifiedModifiersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClosureContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ClosurePrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CommandArgumentContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CommandExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CommandExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CompilationUnitContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ConditionalExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ConditionalStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ConditionalStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ContinueStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ContinueStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CreatedNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.CreatorContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DEC; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DEF; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DEFAULT; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DimsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DimsOptContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DoWhileStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.DynamicMemberNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValueArrayInitializerContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValueContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValuePairContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValuePairsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ElementValuesContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedArgumentListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedArgumentListElementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedForControlContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnhancedStatementExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnumConstantContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EnumConstantsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.EqualityExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExclusiveOrExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionInParContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionListElementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ExpressionStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.FieldDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.FinallyBlockContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.FloatingPointLiteralAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForControlContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForInitContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ForUpdateContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.FormalParameterContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.FormalParameterListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.FormalParametersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GE; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GT; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GroovyParserRuleContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringPathContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.GstringValueContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.IN; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.INC; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.INSTANCEOF; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.IdentifierContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.IdentifierPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.IfElseStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ImportDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ImportStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.InclusiveOrExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.IndexPropertyArgsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.IntegerLiteralAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.KeywordsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LE; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LT; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LabeledStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LambdaBodyContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LambdaPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ListPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LiteralPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LocalVariableDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LocalVariableDeclarationStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LogicalAndExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LogicalOrExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.LoopStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapEntryContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapEntryLabelContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapEntryListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MapPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MemberDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodBodyContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodDeclarationStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MethodNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ModifierContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ModifiersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ModifiersOptContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MultipleAssignmentExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.MultiplicativeExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NOT_IN; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NOT_INSTANCEOF; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NamePartContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NamedPropertyArgsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NewPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NonWildcardTypeArgumentsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NormalExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.NullLiteralAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PRIVATE; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PackageDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ParExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ParenPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PathElementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PathExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PostfixExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PostfixExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PowerExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.PrimitiveTypeContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedClassNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedClassNameListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.QualifiedStandardClassNameContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.RegexExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.RelationalExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ResourceContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ResourceListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ResourcesContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ReturnStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ReturnTypeContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.STATIC; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.SUB; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ShiftExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.StandardLambdaExpressionContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.StandardLambdaParametersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.StatementsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.StringLiteralAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.StringLiteralContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.SuperPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.SwitchBlockStatementGroupContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.SwitchLabelContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.SwitchStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.SynchronizedStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ThisFormalParameterContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ThisPrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.ThrowStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TryCatchStatementContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TryCatchStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeArgumentContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeArgumentsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeArgumentsOrDiamondContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeBoundContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeDeclarationStmtAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeListContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeNamePairContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeNamePairsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeParameterContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypeParametersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.TypePrmrAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.UnaryAddExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.UnaryNotExprAltContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclarationContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclaratorContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclaratorIdContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableDeclaratorsContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableInitializerContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableInitializersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableModifierContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableModifiersContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableModifiersOptContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.VariableNamesContext; -import static org.apache.groovy.parser.antlr4.GroovyLangParser.WhileStmtAltContext; +import static org.apache.groovy.parser.antlr4.GroovyLangParser.*; import static org.apache.groovy.parser.antlr4.util.PositionConfigureUtils.configureAST; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last; @@ -354,7 +149,6 @@ * Created on 2016/08/14 */ public class AstBuilder extends GroovyParserBaseVisitor implements GroovyParserVisitor { - public AstBuilder(SourceUnit sourceUnit) { this.sourceUnit = sourceUnit; this.moduleNode = new ModuleNode(sourceUnit); @@ -1266,49 +1060,9 @@ public Void visitClassBody(ClassBodyContext ctx) { this.visitClassBodyDeclaration(e); }); - genSyntheticLambdaMethodNode(classNode); - return null; } - private void genSyntheticLambdaMethodNode(ClassNode classNode) { - if (null == classNode) { - classNode = moduleNode.getScriptClassDummy(); - } - - if (null == classNode) { - return; - } - - List lambdaExpressionList = lambdaExpressionRegistry.get(classNode); - - if (null == lambdaExpressionList) { - return; - } - - for (LambdaExpression lambdaExpression : lambdaExpressionList) { - String syntheticLambdaMethodNodeName = createSyntheticLambdaMethodNodeName(); - - Parameter[] parameters = lambdaExpression.getParameters(); - if (parameters == null) { - parameters = Parameter.EMPTY_ARRAY; - } - - MethodNode methodNode = - classNode.addMethod(syntheticLambdaMethodNodeName, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, lambdaExpression.getCode()); - - lambdaExpression.putNodeMetaData(SYNTHETIC_LAMBDA_METHOD_NODE, methodNode); - lambdaExpression.putNodeMetaData(LAMBDA_ENCLOSING_CLASSNODE, classNode); - - configureAST(methodNode, lambdaExpression); - } - } - - private int syntheticLambdaMethodNodeCount = 0; - private String createSyntheticLambdaMethodNodeName() { - return "lambda$" + syntheticLambdaMethodNodeCount++; - } - @Override public List visitEnumConstants(EnumConstantsContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); @@ -3620,26 +3374,7 @@ public Expression visitGstringPath(GstringPathContext ctx) { @Override public LambdaExpression visitStandardLambdaExpression(StandardLambdaExpressionContext ctx) { - LambdaExpression lambdaExpression = configureAST(this.createLambda(ctx.standardLambdaParameters(), ctx.lambdaBody()), ctx); - - registerLambdaExpression(lambdaExpression); - - return lambdaExpression; - } - - private void registerLambdaExpression(LambdaExpression lambdaExpression) { - ClassNode classNode = classNodeStack.peek(); - - if (null == classNode) { - classNode = moduleNode.getScriptClassDummy(); - } - - List lambdaExpressionList = lambdaExpressionRegistry.get(classNode); - if (null == lambdaExpressionList) { - lambdaExpressionList = new LinkedList<>(); - lambdaExpressionRegistry.put(classNode, lambdaExpressionList); - } - lambdaExpressionList.add(lambdaExpression); + return configureAST(this.createLambda(ctx.standardLambdaParameters(), ctx.lambdaBody()), ctx); } private LambdaExpression createLambda(StandardLambdaParametersContext standardLambdaParametersContext, LambdaBodyContext lambdaBodyContext) { @@ -4547,7 +4282,6 @@ private void configureScriptClassNode() { scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber()); } - genSyntheticLambdaMethodNode(scriptClassNode); } private boolean isTrue(NodeMetaDataHandler nodeMetaDataHandler, String key) { @@ -4713,7 +4447,6 @@ public List getDeclarationExpressions() { private final List classNodeList = new LinkedList<>(); private final Deque classNodeStack = new ArrayDeque<>(); private final Deque> anonymousInnerClassesDefinedInMethodStack = new ArrayDeque<>(); - private final Map> lambdaExpressionRegistry = new LinkedHashMap<>(); private int anonymousInnerClassCounter = 1; private Tuple2 numberFormatError; @@ -4772,6 +4505,4 @@ public List getDeclarationExpressions() { private static final String FLOATING_POINT_LITERAL_TEXT = "_FLOATING_POINT_LITERAL_TEXT"; private static final String CLASS_NAME = "_CLASS_NAME"; - public static final String LAMBDA_ENCLOSING_CLASSNODE = "_LAMBDA_ENCLOSING_CLASSNODE"; - public static final String SYNTHETIC_LAMBDA_METHOD_NODE = "_SYNTHETIC_LAMBDA_METHOD_NODE"; } From b0aaa1ef96c7f56efb4e7b7099fb4d61381f6d53 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 12 Jan 2018 17:42:44 +0800 Subject: [PATCH 04/47] Refine bytecode generation of native lambda --- .../org/codehaus/groovy/ast/Parameter.java | 4 ++ .../groovy/classgen/asm/ClosureWriter.java | 4 +- .../asm/sc/StaticTypesLambdaWriter.java | 65 ++++++++++++++----- .../groovy/transform/stc/LambdaTest.groovy | 45 +++++++++++-- 4 files changed, 93 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java index 1b22128ef8b..b2ddb09bf15 100644 --- a/src/main/java/org/codehaus/groovy/ast/Parameter.java +++ b/src/main/java/org/codehaus/groovy/ast/Parameter.java @@ -123,4 +123,8 @@ public void setOriginType(ClassNode cn) { public void setModifiers(int modifiers) { this.modifiers = modifiers; } + + public Expression getDefaultValue() { + return defaultValue; + } } 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 33ce0359bfe..d6e215d523a 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java @@ -185,7 +185,7 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); // MethodNode methodNode = controller.getMethodNode(); - String name = genInnerClassName(); + String name = genClosureClassName(); boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass(); Parameter[] parameters = expression.getParameters(); @@ -305,7 +305,7 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { return answer; } - protected String genInnerClassName() { + private String genClosureClassName() { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); MethodNode methodNode = controller.getMethodNode(); 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 46cbe15942f..29c0e401021 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 @@ -38,6 +38,7 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -98,9 +99,16 @@ public void writeLambda(LambdaExpression expression) { ACC_PUBLIC + ACC_FINAL + ACC_STATIC); MethodVisitor mv = controller.getMethodVisitor(); - ClassNode lambdaEnclosingClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC); + ClassNode lambdaEnclosingClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC, abstractMethodNode); MethodNode syntheticLambdaMethodNode = lambdaEnclosingClassNode.getMethods(DO_CALL).get(0); - String syntheticLambdaMethodDesc = BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode); + String syntheticLambdaMethodWithExactTypeDesc = BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode); + String syntheticLambdaMethodDesc = + BytecodeHelper.getMethodDescriptor( + abstractMethodNode.getReturnType().getTypeClass(), + Arrays.stream(abstractMethodNode.getParameters()) + .map(e -> e.getType().getTypeClass()) + .toArray(Class[]::new) + ); controller.getOperandStack().push(ClassHelper.OBJECT_TYPE); @@ -112,23 +120,26 @@ public void writeLambda(LambdaExpression expression) { "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;", - false), - new Object[] { + false + ), + new Object[]{ Type.getType(syntheticLambdaMethodDesc), new Handle( Opcodes.H_INVOKESTATIC, lambdaEnclosingClassNode.getName(), syntheticLambdaMethodNode.getName(), - syntheticLambdaMethodDesc, - false), - Type.getType(syntheticLambdaMethodDesc) - }); + syntheticLambdaMethodWithExactTypeDesc, + false + ), + Type.getType(syntheticLambdaMethodWithExactTypeDesc) + } + ); } - public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods) { + public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode lambdaClass = lambdaClassMap.get(expression); if (lambdaClass == null) { - lambdaClass = createLambdaClass(expression, mods); + lambdaClass = createLambdaClass(expression, mods, abstractMethodNode); lambdaClassMap.put(expression, lambdaClass); controller.getAcg().addInnerClass(lambdaClass); lambdaClass.addInterface(ClassHelper.GENERATED_LAMBDA_TYPE); @@ -137,10 +148,10 @@ public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods) { return lambdaClass; } - protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { + protected ClassNode createLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode outerClass = controller.getOutermostClass(); ClassNode classNode = controller.getClassNode(); - String name = genInnerClassName(); + String name = genLambdaClassName(); boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass(); InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.LAMBDA_TYPE.getPlainNodeReference()); @@ -156,20 +167,42 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { answer.setScriptBody(true); } + Parameter[] parametersWithExactType = createParametersWithExactType(expression); // expression.getParameters(); + + MethodNode methodNode = + answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, abstractMethodNode.getReturnType(), parametersWithExactType, ClassNode.EMPTY_ARRAY, expression.getCode()); + methodNode.setSourcePosition(expression); + + return answer; + } + + private Parameter[] createParametersWithExactType(LambdaExpression expression) { Parameter[] parameters = expression.getParameters(); if (parameters == null) { parameters = Parameter.EMPTY_ARRAY; } - MethodNode methodNode = - answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode()); - methodNode.setSourcePosition(expression); + ClassNode[] blockParameterTypes = expression.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.CLOSURE_ARGUMENTS); +// Parameter[] parametersWithExactType = new Parameter[parameters.length]; - return answer; + for (int i = 0; i < parameters.length; i++) { + parameters[i].setType(blockParameterTypes[i]); + parameters[i].setOriginType(blockParameterTypes[i]); + } + return parameters; } @Override protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) { return staticTypesClosureWriter.createClosureClass(expression, mods); } + + private String genLambdaClassName() { + ClassNode classNode = controller.getClassNode(); + ClassNode outerClass = controller.getOutermostClass(); + MethodNode methodNode = controller.getMethodNode(); + + return classNode.getName() + "$" + + controller.getContext().getNextLambdaInnerName(outerClass, classNode, methodNode); + } } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 0b9e1892b54..e7ae05f33b4 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -30,25 +30,56 @@ class LambdaTest extends GroovyTestCase { public class Test1 { public static void main(String[] args) { p1(); -// p2(); - // p3(); } public static void p1() { - assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e + 1).collect(Collectors.toList()); + assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 1).collect(Collectors.toList()); } + } + ''' + + } + + void testMethodCall2() { + if (true) return; + + // the test can pass only in dynamic mode now, it can not pass static type checking... + + 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) { + p2(); + } + public static void p2() { assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); } - */ + } + ''' + + } + + void testMethodCall3() { + 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) { + p3(); + } - /* public static void p3() { Stream.of(1, 2, 3).forEach(e -> { System.out.println(e + 1); }); } - */ + } ''' From 8096d278f09343b8acd9d68edf521e03b28b4062 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 12 Jan 2018 17:57:54 +0800 Subject: [PATCH 05/47] Trivial refactoring: extract method --- .../groovy/classgen/asm/sc/StaticTypesLambdaWriter.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 29c0e401021..9681ecd38b5 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 @@ -80,7 +80,7 @@ public void writeLambda(LambdaExpression expression) { .filter(MethodNode::isAbstract) .collect(Collectors.toList()); - if (abstractMethodNodeList.size() != 1) { + if (!(isFunctionInterfaceAnnotationAttached(parameterType) && abstractMethodNodeList.size() == 1)) { super.writeClosure(expression); return; } @@ -136,6 +136,10 @@ public void writeLambda(LambdaExpression expression) { ); } + private boolean isFunctionInterfaceAnnotationAttached(ClassNode parameterType) { + return !parameterType.redirect().getAnnotations(ClassHelper.makeCached(FunctionalInterface.class)).isEmpty(); + } + public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode lambdaClass = lambdaClassMap.get(expression); if (lambdaClass == null) { From 6aeaa1c5a7863af5d2c126a5bdf9da9aff2a8db6 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 12 Jan 2018 22:15:11 +0800 Subject: [PATCH 06/47] Minor refactoring: cleanup code and refine the check of FunctionInterface --- .../org/codehaus/groovy/ast/ClassHelper.java | 2 ++ .../classgen/asm/sc/StaticTypesLambdaWriter.java | 16 ++++------------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 27bc0003947..9fc8643b4cd 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -102,6 +102,8 @@ public class ClassHelper { Annotation_TYPE = makeCached(Annotation.class), ELEMENT_TYPE_TYPE = makeCached(ElementType.class), + FunctionalInterface_Type = ClassHelper.makeCached(FunctionalInterface.class), + // uncached constants. CLASS_Type = makeWithoutCaching(Class.class), COMPARABLE_TYPE = makeWithoutCaching(Comparable.class), GENERATED_CLOSURE_Type = makeWithoutCaching(GeneratedClosure.class), 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 9681ecd38b5..cdb627b6dcd 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 @@ -80,8 +80,8 @@ public void writeLambda(LambdaExpression expression) { .filter(MethodNode::isAbstract) .collect(Collectors.toList()); - if (!(isFunctionInterfaceAnnotationAttached(parameterType) && abstractMethodNodeList.size() == 1)) { - super.writeClosure(expression); + if (!(isFunctionInterface(parameterType) && abstractMethodNodeList.size() == 1)) { + super.writeLambda(expression); return; } @@ -90,14 +90,6 @@ public void writeLambda(LambdaExpression expression) { String abstractMethodDesc = "()L" + parameterType.redirect().getPackageName().replace('.', '/') + "/" + parameterType.redirect().getNameWithoutPackage() + ";"; - AsmClassGenerator acg = controller.getAcg(); - ClassVisitor cw = acg.getClassVisitor(); - cw.visitInnerClass( - "java/lang/invoke/MethodHandles$Lookup", - "java/lang/invoke/MethodHandles", - "Lookup", - ACC_PUBLIC + ACC_FINAL + ACC_STATIC); - MethodVisitor mv = controller.getMethodVisitor(); ClassNode lambdaEnclosingClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC, abstractMethodNode); MethodNode syntheticLambdaMethodNode = lambdaEnclosingClassNode.getMethods(DO_CALL).get(0); @@ -136,8 +128,8 @@ public void writeLambda(LambdaExpression expression) { ); } - private boolean isFunctionInterfaceAnnotationAttached(ClassNode parameterType) { - return !parameterType.redirect().getAnnotations(ClassHelper.makeCached(FunctionalInterface.class)).isEmpty(); + private boolean isFunctionInterface(ClassNode parameterType) { + return parameterType.redirect().isInterface() && !parameterType.redirect().getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty(); } public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { From 4172bd76428ce2c71a95dbc683323fba7385bda8 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 12 Jan 2018 22:46:59 +0800 Subject: [PATCH 07/47] Add more tests --- .../asm/sc/StaticTypesLambdaWriter.java | 4 - .../groovy/transform/stc/LambdaTest.groovy | 111 ++++++++++++++++-- 2 files changed, 100 insertions(+), 15 deletions(-) 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 cdb627b6dcd..c847c33823c 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,12 +27,10 @@ import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.LambdaExpression; -import org.codehaus.groovy.classgen.AsmClassGenerator; import org.codehaus.groovy.classgen.asm.BytecodeHelper; import org.codehaus.groovy.classgen.asm.LambdaWriter; import org.codehaus.groovy.classgen.asm.WriterController; import org.codehaus.groovy.classgen.asm.WriterControllerFactory; -import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -45,9 +43,7 @@ import java.util.stream.Collectors; import static org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.PARAMETER_TYPE; -import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.ACC_STATIC; /** * Writer responsible for generating lambda classes in statically compiled mode. diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index e7ae05f33b4..db4aa36c33e 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -20,7 +20,7 @@ package groovy.transform.stc class LambdaTest extends GroovyTestCase { - void testMethodCall() { + void testFunction() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -29,22 +29,35 @@ class LambdaTest extends GroovyTestCase { @CompileStatic public class Test1 { public static void main(String[] args) { - p1(); + p(); } - public static void p1() { + public static void p() { assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 1).collect(Collectors.toList()); } } ''' - } - void testMethodCall2() { + void testBinaryOperator() { if (true) return; // the test can pass only in dynamic mode now, it can not pass static type checking... + /* TODO +TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.util.stream.Stream#reduce(int, groovy.lang.Closure). Please check if the declared type is correct and if the method exists. + @ line 13, column 30. + assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); + ^ + +TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.lang.Object#plus(java.lang.Object). Please check if the declared type is correct and if the method exists. + @ line 13, column 69. + (1, 2, 3).reduce(7, (r, e) -> r + e); + ^ + +2 errors + */ + assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -53,18 +66,17 @@ class LambdaTest extends GroovyTestCase { @CompileStatic public class Test1 { public static void main(String[] args) { - p2(); + p(); } - public static void p2() { + public static void p() { assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); } } ''' - } - void testMethodCall3() { + void testConsumer() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -73,15 +85,92 @@ class LambdaTest extends GroovyTestCase { @CompileStatic public class Test1 { public static void main(String[] args) { - p3(); + p(); } - public static void p3() { + public static void p() { Stream.of(1, 2, 3).forEach(e -> { System.out.println(e + 1); }); } } ''' + } + + void testPredicate() { + 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() { + def list = ['ab', 'bc', 'de'] + list.removeIf(e -> e.startsWith("a")) + assert ['bc', 'de'] == list + } + } + ''' + } + + void testUnaryOperator() { + if (true) return; + + /* TODO +TestScript0.groovy: 14: [Static type checking] - Cannot find matching method java.util.List#replaceAll(groovy.lang.Closure). Please check if the declared type is correct and if the method exists. + @ line 14, column 17. + list.replaceAll(e -> e + 10) + ^ + +TestScript0.groovy: 14: [Static type checking] - Cannot find matching method java.lang.Object#plus(int). Please check if the declared type is correct and if the method exists. + @ line 14, column 38. + list.replaceAll(e -> e + 10) + ^ + +2 errors + */ + + 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() { + def list = [1, 2, 3] + list.replaceAll(e -> e + 10) + assert [11, 12, 13] == list + } + } + ''' + } + void testBiConsumer() { + 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() { + def map = [a: 1, b: 2, c: 3] + map.forEach((k, v) -> System.out.println(k + ":" + v)); + } + } + ''' } } From 083089fc35405379c4a4f27fa1d3123eb396dc67 Mon Sep 17 00:00:00 2001 From: sunlan Date: Sat, 13 Jan 2018 00:18:00 +0800 Subject: [PATCH 08/47] Minor refactoring --- .../asm/sc/StaticTypesLambdaWriter.java | 123 ++++++++++-------- .../groovy/transform/stc/LambdaTest.groovy | 17 +++ 2 files changed, 86 insertions(+), 54 deletions(-) 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 c847c33823c..bdf10d83ad0 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 @@ -31,8 +31,8 @@ import org.codehaus.groovy.classgen.asm.LambdaWriter; import org.codehaus.groovy.classgen.asm.WriterController; import org.codehaus.groovy.classgen.asm.WriterControllerFactory; +import org.codehaus.groovy.transform.stc.StaticTypesMarker; import org.objectweb.asm.Handle; -import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -77,50 +77,61 @@ public void writeLambda(LambdaExpression expression) { .collect(Collectors.toList()); if (!(isFunctionInterface(parameterType) && abstractMethodNodeList.size() == 1)) { + // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; } MethodNode abstractMethodNode = abstractMethodNodeList.get(0); - String abstractMethodName = abstractMethodNode.getName(); - String abstractMethodDesc = "()L" + parameterType.redirect().getPackageName().replace('.', '/') + "/" + parameterType.redirect().getNameWithoutPackage() + ";"; + String abstractMethodDesc = createMethodDescriptor(abstractMethodNode); - - MethodVisitor mv = controller.getMethodVisitor(); - ClassNode lambdaEnclosingClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC, abstractMethodNode); - MethodNode syntheticLambdaMethodNode = lambdaEnclosingClassNode.getMethods(DO_CALL).get(0); + ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC); + MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); String syntheticLambdaMethodWithExactTypeDesc = BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode); - String syntheticLambdaMethodDesc = - BytecodeHelper.getMethodDescriptor( - abstractMethodNode.getReturnType().getTypeClass(), - Arrays.stream(abstractMethodNode.getParameters()) - .map(e -> e.getType().getTypeClass()) - .toArray(Class[]::new) - ); - - controller.getOperandStack().push(ClassHelper.OBJECT_TYPE); - - mv.visitInvokeDynamicInsn( - abstractMethodName, - abstractMethodDesc, + + controller.getOperandStack().push(parameterType.redirect()); + controller.getMethodVisitor().visitInvokeDynamicInsn( + abstractMethodNode.getName(), + createAbstractMethodDesc(parameterType), + createBootstrapMethod(), + createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode, syntheticLambdaMethodWithExactTypeDesc) + ); + } + + private String createAbstractMethodDesc(ClassNode parameterType) { + return "()L" + parameterType.redirect().getPackageName().replace('.', '/') + "/" + parameterType.redirect().getNameWithoutPackage() + ";"; + } + + private Handle createBootstrapMethod() { + return new Handle( + Opcodes.H_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;", + false + ); + } + + private Object[] createBootstrapMethodArguments(String abstractMethodDesc, ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode, String syntheticLambdaMethodWithExactTypeDesc) { + return new Object[]{ + Type.getType(abstractMethodDesc), new Handle( Opcodes.H_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;", + lambdaClassNode.getName(), + syntheticLambdaMethodNode.getName(), + syntheticLambdaMethodWithExactTypeDesc, false ), - new Object[]{ - Type.getType(syntheticLambdaMethodDesc), - new Handle( - Opcodes.H_INVOKESTATIC, - lambdaEnclosingClassNode.getName(), - syntheticLambdaMethodNode.getName(), - syntheticLambdaMethodWithExactTypeDesc, - false - ), - Type.getType(syntheticLambdaMethodWithExactTypeDesc) - } + Type.getType(syntheticLambdaMethodWithExactTypeDesc) + }; + } + + private String createMethodDescriptor(MethodNode abstractMethodNode) { + return BytecodeHelper.getMethodDescriptor( + abstractMethodNode.getReturnType().getTypeClass(), + Arrays.stream(abstractMethodNode.getParameters()) + .map(e -> e.getType().getTypeClass()) + .toArray(Class[]::new) ); } @@ -128,10 +139,10 @@ private boolean isFunctionInterface(ClassNode parameterType) { return parameterType.redirect().isInterface() && !parameterType.redirect().getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty(); } - public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { + public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods) { ClassNode lambdaClass = lambdaClassMap.get(expression); if (lambdaClass == null) { - lambdaClass = createLambdaClass(expression, mods, abstractMethodNode); + lambdaClass = createLambdaClass(expression, mods); lambdaClassMap.put(expression, lambdaClass); controller.getAcg().addInnerClass(lambdaClass); lambdaClass.addInterface(ClassHelper.GENERATED_LAMBDA_TYPE); @@ -140,7 +151,7 @@ public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, Meth return lambdaClass; } - protected ClassNode createLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { + protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { ClassNode outerClass = controller.getOutermostClass(); ClassNode classNode = controller.getClassNode(); String name = genLambdaClassName(); @@ -159,13 +170,27 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods, Met answer.setScriptBody(true); } + addSyntheticLambdaMethodNode(expression, answer); + + return answer; + } + + private String genLambdaClassName() { + ClassNode classNode = controller.getClassNode(); + ClassNode outerClass = controller.getOutermostClass(); + MethodNode methodNode = controller.getMethodNode(); + + return classNode.getName() + "$" + + controller.getContext().getNextLambdaInnerName(outerClass, classNode, methodNode); + } + + private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClassNode answer) { Parameter[] parametersWithExactType = createParametersWithExactType(expression); // expression.getParameters(); + ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType(); MethodNode methodNode = - answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, abstractMethodNode.getReturnType(), parametersWithExactType, ClassNode.EMPTY_ARRAY, expression.getCode()); + answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, returnType, parametersWithExactType, ClassNode.EMPTY_ARRAY, expression.getCode()); methodNode.setSourcePosition(expression); - - return answer; } private Parameter[] createParametersWithExactType(LambdaExpression expression) { @@ -174,13 +199,12 @@ private Parameter[] createParametersWithExactType(LambdaExpression expression) { parameters = Parameter.EMPTY_ARRAY; } - ClassNode[] blockParameterTypes = expression.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.CLOSURE_ARGUMENTS); -// Parameter[] parametersWithExactType = new Parameter[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - parameters[i].setType(blockParameterTypes[i]); - parameters[i].setOriginType(blockParameterTypes[i]); + ClassNode inferredType = parameters[i].getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + parameters[i].setType(inferredType); + parameters[i].setOriginType(inferredType); } + return parameters; } @@ -188,13 +212,4 @@ private Parameter[] createParametersWithExactType(LambdaExpression expression) { protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) { return staticTypesClosureWriter.createClosureClass(expression, mods); } - - private String genLambdaClassName() { - ClassNode classNode = controller.getClassNode(); - ClassNode outerClass = controller.getOutermostClass(); - MethodNode methodNode = controller.getMethodNode(); - - return classNode.getName() + "$" - + controller.getContext().getNextLambdaInnerName(outerClass, classNode, methodNode); - } } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index db4aa36c33e..700f9cd1f86 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -39,6 +39,23 @@ class LambdaTest extends GroovyTestCase { ''' } + /* + void testFunction2() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + + @CompileStatic + void p() { + assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 1).collect(Collectors.toList()); + } + + p() + ''' + } + */ + void testBinaryOperator() { if (true) return; From 61474c1ee9b6d9d042abc0f138c234fce219216a Mon Sep 17 00:00:00 2001 From: sunlan Date: Sat, 13 Jan 2018 00:33:44 +0800 Subject: [PATCH 09/47] Add one more test for script --- .../groovy/transform/stc/LambdaTest.groovy | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 700f9cd1f86..ba471fb33aa 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -39,8 +39,7 @@ class LambdaTest extends GroovyTestCase { ''' } - /* - void testFunction2() { + void testFunctionScript() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -48,20 +47,22 @@ class LambdaTest extends GroovyTestCase { @CompileStatic void p() { - assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 1).collect(Collectors.toList()); + assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e + 1).collect(Collectors.toList()); } p() ''' } - */ + /** + * Depends on https://issues.apache.org/jira/browse/GROOVY-8445 + */ void testBinaryOperator() { - if (true) return; + if (true) return // the test can pass only in dynamic mode now, it can not pass static type checking... - /* TODO + /* FIXME TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.util.stream.Stream#reduce(int, groovy.lang.Closure). Please check if the declared type is correct and if the method exists. @ line 13, column 30. assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); @@ -134,10 +135,13 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav ''' } + /** + * Depends on https://issues.apache.org/jira/browse/GROOVY-8445 + */ void testUnaryOperator() { - if (true) return; + if (true) return - /* TODO + /* FIXME TestScript0.groovy: 14: [Static type checking] - Cannot find matching method java.util.List#replaceAll(groovy.lang.Closure). Please check if the declared type is correct and if the method exists. @ line 14, column 17. list.replaceAll(e -> e + 10) From b6ea72dbf2ee4ab63fb1b96569de609679807a34 Mon Sep 17 00:00:00 2001 From: sunlan Date: Sun, 14 Jan 2018 07:47:17 +0800 Subject: [PATCH 10/47] Minor refactoring to prepare supporting local variables --- .../groovy/classgen/asm/LambdaWriter.java | 5 ++ .../asm/sc/StaticTypesLambdaWriter.java | 29 ++++-- .../groovy/runtime/ProxyGeneratorAdapter.java | 2 +- src/test/gls/generics/GenericsTestBase.java | 2 +- .../groovy/transform/stc/LambdaTest.groovy | 88 +++++++++++++++++++ 5 files changed, 115 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java index 3a3968892da..6c0b53dc1d4 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java @@ -18,6 +18,7 @@ */ package org.codehaus.groovy.classgen.asm; +import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.LambdaExpression; public class LambdaWriter extends ClosureWriter { @@ -28,4 +29,8 @@ public LambdaWriter(WriterController wc) { public void writeLambda(LambdaExpression expression) { super.writeClosure(expression); } + + protected Parameter[] getLambdaSharedVariables(LambdaExpression expression) { + return super.getClosureSharedVariables(expression); + } } 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 bdf10d83ad0..e91e7772061 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 @@ -41,6 +41,7 @@ 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; @@ -51,6 +52,8 @@ */ 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"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -87,19 +90,22 @@ public void writeLambda(LambdaExpression expression) { ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); - String syntheticLambdaMethodWithExactTypeDesc = BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode); + controller.getOperandStack().push(parameterType.redirect()); controller.getMethodVisitor().visitInvokeDynamicInsn( abstractMethodNode.getName(), - createAbstractMethodDesc(parameterType), + createAbstractMethodDesc(syntheticLambdaMethodNode, parameterType), createBootstrapMethod(), - createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode, syntheticLambdaMethodWithExactTypeDesc) + createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) ); } - private String createAbstractMethodDesc(ClassNode parameterType) { - return "()L" + parameterType.redirect().getPackageName().replace('.', '/') + "/" + parameterType.redirect().getNameWithoutPackage() + ";"; + private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, ClassNode parameterType) { + Parameter[] lambdaSharedVariables = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); + String methodDescriptor = BytecodeHelper.getMethodDescriptor(parameterType.redirect(), lambdaSharedVariables); + + return methodDescriptor; } private Handle createBootstrapMethod() { @@ -112,17 +118,17 @@ private Handle createBootstrapMethod() { ); } - private Object[] createBootstrapMethodArguments(String abstractMethodDesc, ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode, String syntheticLambdaMethodWithExactTypeDesc) { + private Object[] createBootstrapMethodArguments(String abstractMethodDesc, ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) { return new Object[]{ Type.getType(abstractMethodDesc), new Handle( Opcodes.H_INVOKESTATIC, lambdaClassNode.getName(), syntheticLambdaMethodNode.getName(), - syntheticLambdaMethodWithExactTypeDesc, + BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode), false ), - Type.getType(syntheticLambdaMethodWithExactTypeDesc) + Type.getType(BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode.getReturnType(), syntheticLambdaMethodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE))) }; } @@ -187,9 +193,14 @@ private String genLambdaClassName() { private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClassNode answer) { Parameter[] parametersWithExactType = createParametersWithExactType(expression); // expression.getParameters(); ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType(); + Parameter[] lambdaSharedVariables = getLambdaSharedVariables(expression); + Parameter[] methodParameter = Stream.concat(Arrays.stream(lambdaSharedVariables), Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new); MethodNode methodNode = - answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, returnType, parametersWithExactType, ClassNode.EMPTY_ARRAY, expression.getCode()); + answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, returnType, methodParameter, ClassNode.EMPTY_ARRAY, expression.getCode()); + methodNode.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType); + methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, lambdaSharedVariables); + methodNode.setSourcePosition(expression); } diff --git a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java index 207a7ca46be..0e1f6ccbf0b 100644 --- a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java +++ b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java @@ -142,7 +142,7 @@ public ProxyGeneratorAdapter( final ClassLoader proxyLoader, final boolean emptyBody, final Class delegateClass) { - super(Opcodes.ASM4, new ClassWriter(0)); + super(Opcodes.ASM5, new ClassWriter(0)); this.loader = proxyLoader != null ? createInnerLoader(proxyLoader, interfaces) : findClassLoader(superClass, interfaces); this.visitedMethods = new LinkedHashSet(); this.delegatedClosures = closureMap.isEmpty() ? EMPTY_DELEGATECLOSURE_MAP : new HashMap(); diff --git a/src/test/gls/generics/GenericsTestBase.java b/src/test/gls/generics/GenericsTestBase.java index 801806f7817..dc5fc94942f 100644 --- a/src/test/gls/generics/GenericsTestBase.java +++ b/src/test/gls/generics/GenericsTestBase.java @@ -57,7 +57,7 @@ protected Class createClass(byte[] code, ClassNode classNode) { } private class GenericsTester extends ClassVisitor { public GenericsTester(ClassVisitor cv) { - super(Opcodes.ASM4,cv); + super(Opcodes.ASM5,cv); } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index ba471fb33aa..73ba540ce42 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -194,4 +194,92 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } ''' } + + void testFunctionWithLocalVariables() { + if (true) return + + // FIXME + /* +General error during class generation: ASM reporting processing error for Test1#p with signature void p() in TestScript0.groovy:12. TestScript0.groovy + +groovy.lang.GroovyRuntimeException: ASM reporting processing error for Test1#p with signature void p() in TestScript0.groovy:12. TestScript0.groovy + at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:447) + at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:132) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitMethod(AsmClassGenerator.java:568) + at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1095) + at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:54) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitClass(AsmClassGenerator.java:261) + at org.codehaus.groovy.control.CompilationUnit$18.call(CompilationUnit.java:853) + at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1092) + at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:634) + at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:612) + at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:589) + at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:359) + at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:92) + at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:328) + at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:325) + at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:138) + at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:323) + at groovy.lang.GroovyShell.parseClass(GroovyShell.java:548) + at groovy.lang.GroovyShell.parse(GroovyShell.java:560) + at groovy.lang.GroovyShell.evaluate(GroovyShell.java:444) + at groovy.lang.GroovyShell.evaluate(GroovyShell.java:483) + at groovy.lang.GroovyShell.evaluate(GroovyShell.java:464) + at groovy.test.GroovyAssert.assertScript(GroovyAssert.java:83) + at groovy.util.GroovyTestCase.assertScript(GroovyTestCase.java:203) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:498) + at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210) + at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59) + at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51) + at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157) + at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:169) + at groovy.transform.stc.LambdaTest.testFunctionWithLocalVariables(LambdaTest.groovy:203) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:498) + at junit.framework.TestCase.runTest(TestCase.java:176) + at junit.framework.TestCase.runBare(TestCase.java:141) + at junit.framework.TestResult$1.protect(TestResult.java:122) + at junit.framework.TestResult.runProtected(TestResult.java:142) + at junit.framework.TestResult.run(TestResult.java:125) + at junit.framework.TestCase.run(TestCase.java:129) + at junit.framework.TestSuite.runTest(TestSuite.java:252) + at junit.framework.TestSuite.run(TestSuite.java:247) + at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86) + at org.junit.runner.JUnitCore.run(JUnitCore.java:137) + at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) + at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) + at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) + at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) +Caused by: java.lang.ArrayIndexOutOfBoundsException: -1 + at org.objectweb.asm.Frame.merge(Frame.java:1501) + at org.objectweb.asm.Frame.merge(Frame.java:1478) + at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1497) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:428) + ... 51 more + + */ + + 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() { + String x = "#" + assert ['#1', '#2', '#3'] == Stream.of(1, 2, 3).map(e -> x + e).collect(Collectors.toList()); + } + } + ''' + } } From fb8e3d10b9bcce46ebb474657d67036b14c2bffc Mon Sep 17 00:00:00 2001 From: sunlan Date: Mon, 15 Jan 2018 15:35:13 +0800 Subject: [PATCH 11/47] Add missing `ALOAD` to fix generating bytecode --- .../groovy/classgen/asm/ClosureWriter.java | 2 +- .../asm/sc/StaticTypesLambdaWriter.java | 94 +++++++++++++++++-- .../groovy/transform/stc/LambdaTest.groovy | 88 +++++------------ 3 files changed, 113 insertions(+), 71 deletions(-) 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 d6e215d523a..429fe3b8945 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java @@ -338,7 +338,7 @@ public void visitVariableExpression(VariableExpression expression) { * same method, in this case the constructor. A closure should not * have more than one constructor! */ - private static void removeInitialValues(Parameter[] params) { + protected static void removeInitialValues(Parameter[] params) { for (int i = 0; i < params.length; i++) { if (params[i].hasInitialExpression()) { Parameter p = new Parameter(params[i].getType(), params[i].getName()); 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 e91e7772061..99c0215c008 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 @@ -19,6 +19,8 @@ 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.InnerClassNode; @@ -27,12 +29,16 @@ import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.LambdaExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.classgen.asm.BytecodeHelper; import org.codehaus.groovy.classgen.asm.LambdaWriter; +import org.codehaus.groovy.classgen.asm.OperandStack; import org.codehaus.groovy.classgen.asm.WriterController; import org.codehaus.groovy.classgen.asm.WriterControllerFactory; +import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.transform.stc.StaticTypesMarker; import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -45,6 +51,7 @@ 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.ALOAD; /** * Writer responsible for generating lambda classes in statically compiled mode. @@ -53,7 +60,7 @@ 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 LAMBDA_SHARED_VARIABLES = "__LAMBDA_SHARED_VARIABLES"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -91,9 +98,18 @@ public void writeLambda(LambdaExpression expression) { ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); + MethodVisitor mv = controller.getMethodVisitor(); + OperandStack operandStack = controller.getOperandStack(); - controller.getOperandStack().push(parameterType.redirect()); - controller.getMethodVisitor().visitInvokeDynamicInsn( + Parameter[] lambdaSharedVariableParameters = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); + for (int i = 0; i < lambdaSharedVariableParameters.length; i++) { + mv.visitVarInsn(ALOAD, i); + operandStack.doGroovyCast(lambdaSharedVariableParameters[i].getType().redirect()); +// operandStack.push(lambdaSharedVariableParameters[i].getType().redirect()); + } + + operandStack.push(parameterType.redirect()); + mv.visitInvokeDynamicInsn( abstractMethodNode.getName(), createAbstractMethodDesc(syntheticLambdaMethodNode, parameterType), createBootstrapMethod(), @@ -193,15 +209,40 @@ private String genLambdaClassName() { private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClassNode answer) { Parameter[] parametersWithExactType = createParametersWithExactType(expression); // expression.getParameters(); ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType(); - Parameter[] lambdaSharedVariables = getLambdaSharedVariables(expression); - Parameter[] methodParameter = Stream.concat(Arrays.stream(lambdaSharedVariables), Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new); + Parameter[] localVariableParameters = getLambdaSharedVariables(expression); + removeInitialValues(localVariableParameters); + Parameter[] methodParameter = Stream.concat(Arrays.stream(localVariableParameters), Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new); MethodNode methodNode = - answer.addMethod(DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, returnType, methodParameter, ClassNode.EMPTY_ARRAY, expression.getCode()); + answer.addMethod( + DO_CALL, + Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, + returnType, + methodParameter, + ClassNode.EMPTY_ARRAY, + expression.getCode() + ); methodNode.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType); - methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, lambdaSharedVariables); + methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters); methodNode.setSourcePosition(expression); + + + LocalVariableReplacementVisitor localVariableReplacementVisitor = new LocalVariableReplacementVisitor(methodNode); + + localVariableReplacementVisitor.visitMethod(methodNode); + +// System.out.println(methodNode); + + /* + VariableScope varScope = expression.getVariableScope(); + if (varScope == null) { + throw new RuntimeException( + "Must have a VariableScope by now! for expression: " + expression + " class: " + answer.getName()); + } else { + methodNode.setVariableScope(varScope.copy()); + } + */ } private Parameter[] createParametersWithExactType(LambdaExpression expression) { @@ -223,4 +264,43 @@ private Parameter[] createParametersWithExactType(LambdaExpression expression) { protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) { return staticTypesClosureWriter.createClosureClass(expression, mods); } + + private static final class LocalVariableReplacementVisitor extends ClassCodeVisitorSupport { + private MethodNode methodNode; + + public LocalVariableReplacementVisitor(MethodNode methodNode) { + this.methodNode = methodNode; + } + + @Override + protected SourceUnit getSourceUnit() { + return null; + } + + @Override + public void visitVariableExpression(VariableExpression expression) { + if (expression.isClosureSharedVariable()) { + final String variableName = expression.getName(); +// System.out.println(">>>>>>>>>>>>>>>>>>>>>>\n"); +// System.out.println("1, " + variableName + ":" + expression.isClosureSharedVariable() + "::" + expression.getAccessedVariable()); + 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)."); + } +// System.out.println("2, " + variableName + ":" + parametersWithSameVariableName[0]); + + expression.setAccessedVariable(parametersWithSameVariableName[0]); + expression.setClosureSharedVariable(false); + +// System.out.println("<<<<<<<<<<<<<<<<<<<<<<\n"); + + } + + super.visitVariableExpression(expression); + } + } } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 73ba540ce42..dbd789a24e9 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -199,69 +199,8 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav if (true) return // FIXME - /* -General error during class generation: ASM reporting processing error for Test1#p with signature void p() in TestScript0.groovy:12. TestScript0.groovy - -groovy.lang.GroovyRuntimeException: ASM reporting processing error for Test1#p with signature void p() in TestScript0.groovy:12. TestScript0.groovy - at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:447) - at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:132) - at org.codehaus.groovy.classgen.AsmClassGenerator.visitMethod(AsmClassGenerator.java:568) - at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1095) - at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:54) - at org.codehaus.groovy.classgen.AsmClassGenerator.visitClass(AsmClassGenerator.java:261) - at org.codehaus.groovy.control.CompilationUnit$18.call(CompilationUnit.java:853) - at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1092) - at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:634) - at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:612) - at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:589) - at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:359) - at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:92) - at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:328) - at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:325) - at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:138) - at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:323) - at groovy.lang.GroovyShell.parseClass(GroovyShell.java:548) - at groovy.lang.GroovyShell.parse(GroovyShell.java:560) - at groovy.lang.GroovyShell.evaluate(GroovyShell.java:444) - at groovy.lang.GroovyShell.evaluate(GroovyShell.java:483) - at groovy.lang.GroovyShell.evaluate(GroovyShell.java:464) - at groovy.test.GroovyAssert.assertScript(GroovyAssert.java:83) - at groovy.util.GroovyTestCase.assertScript(GroovyTestCase.java:203) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210) - at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59) - at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51) - at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157) - at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:169) - at groovy.transform.stc.LambdaTest.testFunctionWithLocalVariables(LambdaTest.groovy:203) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at junit.framework.TestCase.runTest(TestCase.java:176) - at junit.framework.TestCase.runBare(TestCase.java:141) - at junit.framework.TestResult$1.protect(TestResult.java:122) - at junit.framework.TestResult.runProtected(TestResult.java:142) - at junit.framework.TestResult.run(TestResult.java:125) - at junit.framework.TestCase.run(TestCase.java:129) - at junit.framework.TestSuite.runTest(TestSuite.java:252) - at junit.framework.TestSuite.run(TestSuite.java:247) - at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86) - at org.junit.runner.JUnitCore.run(JUnitCore.java:137) - at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) - at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) - at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) - at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) -Caused by: java.lang.ArrayIndexOutOfBoundsException: -1 - at org.objectweb.asm.Frame.merge(Frame.java:1501) - at org.objectweb.asm.Frame.merge(Frame.java:1478) - at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1497) - at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:428) - ... 51 more - + /* What we expect is '#1', '#2', '#3' +[groovy.lang.Reference@46d63dbb1, groovy.lang.Reference@46d63dbb2, groovy.lang.Reference@46d63dbb3] */ assertScript ''' @@ -282,4 +221,27 @@ Caused by: java.lang.ArrayIndexOutOfBoundsException: -1 } ''' } + + /* + void testLabs() { + + 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() { + String x = "#" + System.out.println(Stream.of(1, 2, 3).map(e -> x + e).collect(Collectors.toList())); + } + } + ''' + } + */ } From 8616d8967d847026102de999e50deaae1bdfc444 Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 16 Jan 2018 11:14:47 +0800 Subject: [PATCH 12/47] Fix loading local variables for invokedynamic --- .../asm/sc/StaticTypesLambdaWriter.java | 39 ++++++++++++------- .../groovy/transform/stc/LambdaTest.groovy | 22 ++++------- 2 files changed, 33 insertions(+), 28 deletions(-) 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 99c0215c008..cbc6d312237 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 @@ -31,6 +31,8 @@ import org.codehaus.groovy.ast.expr.LambdaExpression; 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; import org.codehaus.groovy.classgen.asm.WriterController; @@ -51,7 +53,7 @@ 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.ALOAD; +import static org.objectweb.asm.Opcodes.ACC_STATIC; /** * Writer responsible for generating lambda classes in statically compiled mode. @@ -95,26 +97,37 @@ public void writeLambda(LambdaExpression expression) { MethodNode abstractMethodNode = abstractMethodNodeList.get(0); String abstractMethodDesc = createMethodDescriptor(abstractMethodNode); - ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC); + ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (controller.getClassNode().isInterface() ? ACC_STATIC : 0)); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); MethodVisitor mv = controller.getMethodVisitor(); - OperandStack operandStack = controller.getOperandStack(); + Parameter[] lambdaSharedVariableParameters = loadSharedVariables(syntheticLambdaMethodNode); - Parameter[] lambdaSharedVariableParameters = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); - for (int i = 0; i < lambdaSharedVariableParameters.length; i++) { - mv.visitVarInsn(ALOAD, i); - operandStack.doGroovyCast(lambdaSharedVariableParameters[i].getType().redirect()); -// operandStack.push(lambdaSharedVariableParameters[i].getType().redirect()); - } - - operandStack.push(parameterType.redirect()); mv.visitInvokeDynamicInsn( abstractMethodNode.getName(), createAbstractMethodDesc(syntheticLambdaMethodNode, parameterType), createBootstrapMethod(), createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) ); + controller.getOperandStack().replace(parameterType.redirect(), lambdaSharedVariableParameters.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); + } + return lambdaSharedVariableParameters; } private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, ClassNode parameterType) { @@ -211,14 +224,14 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType(); Parameter[] localVariableParameters = getLambdaSharedVariables(expression); removeInitialValues(localVariableParameters); - Parameter[] methodParameter = Stream.concat(Arrays.stream(localVariableParameters), Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new); + Parameter[] methodParameters = Stream.concat(Arrays.stream(localVariableParameters), Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new); MethodNode methodNode = answer.addMethod( DO_CALL, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, returnType, - methodParameter, + methodParameters, ClassNode.EMPTY_ARRAY, expression.getCode() ); diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index dbd789a24e9..f12eeda9f33 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -55,7 +55,7 @@ class LambdaTest extends GroovyTestCase { } /** - * Depends on https://issues.apache.org/jira/browse/GROOVY-8445 + * Depends on fixing https://issues.apache.org/jira/browse/GROOVY-8445 */ void testBinaryOperator() { if (true) return @@ -136,7 +136,7 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav } /** - * Depends on https://issues.apache.org/jira/browse/GROOVY-8445 + * Depends on fixing https://issues.apache.org/jira/browse/GROOVY-8445 */ void testUnaryOperator() { if (true) return @@ -196,13 +196,6 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } void testFunctionWithLocalVariables() { - if (true) return - - // FIXME - /* What we expect is '#1', '#2', '#3' -[groovy.lang.Reference@46d63dbb1, groovy.lang.Reference@46d63dbb2, groovy.lang.Reference@46d63dbb3] - */ - assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -222,9 +215,8 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } - /* - void testLabs() { + void testFunctionWithLocalVariables2() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -233,15 +225,15 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav @CompileStatic public class Test1 { public static void main(String[] args) { - p(); + new Test1().p(); } - public static void p() { + public void p() { String x = "#" - System.out.println(Stream.of(1, 2, 3).map(e -> x + e).collect(Collectors.toList())); + Integer y = 23 + assert ['23#1', '23#2', '23#3'] == Stream.of(1, 2, 3).map(e -> '' + y + x + e).collect(Collectors.toList()) } } ''' } - */ } From 0165fb276ef1df048da3cb69b7555710e1ba8801 Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 16 Jan 2018 11:37:20 +0800 Subject: [PATCH 13/47] Refine constructing BSM --- .../asm/sc/StaticTypesLambdaWriter.java | 32 ++++--------------- 1 file changed, 6 insertions(+), 26 deletions(-) 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 cbc6d312237..f3679b3b9ea 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 @@ -97,7 +97,8 @@ public void writeLambda(LambdaExpression expression) { MethodNode abstractMethodNode = abstractMethodNodeList.get(0); String abstractMethodDesc = createMethodDescriptor(abstractMethodNode); - ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (controller.getClassNode().isInterface() ? ACC_STATIC : 0)); + boolean isInterface = controller.getClassNode().isInterface(); + ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0)); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); MethodVisitor mv = controller.getMethodVisitor(); @@ -106,7 +107,7 @@ public void writeLambda(LambdaExpression expression) { mv.visitInvokeDynamicInsn( abstractMethodNode.getName(), createAbstractMethodDesc(syntheticLambdaMethodNode, parameterType), - createBootstrapMethod(), + createBootstrapMethod(isInterface), createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) ); controller.getOperandStack().replace(parameterType.redirect(), lambdaSharedVariableParameters.length); @@ -137,13 +138,13 @@ private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, Cl return methodDescriptor; } - private Handle createBootstrapMethod() { + private Handle createBootstrapMethod(boolean isInterface) { return new Handle( Opcodes.H_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;", - false + isInterface ); } @@ -237,25 +238,9 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas ); methodNode.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType); methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters); - methodNode.setSourcePosition(expression); - - LocalVariableReplacementVisitor localVariableReplacementVisitor = new LocalVariableReplacementVisitor(methodNode); - - localVariableReplacementVisitor.visitMethod(methodNode); - -// System.out.println(methodNode); - - /* - VariableScope varScope = expression.getVariableScope(); - if (varScope == null) { - throw new RuntimeException( - "Must have a VariableScope by now! for expression: " + expression + " class: " + answer.getName()); - } else { - methodNode.setVariableScope(varScope.copy()); - } - */ + new LocalVariableReplacementVisitor(methodNode).visitMethod(methodNode); } private Parameter[] createParametersWithExactType(LambdaExpression expression) { @@ -294,8 +279,6 @@ protected SourceUnit getSourceUnit() { public void visitVariableExpression(VariableExpression expression) { if (expression.isClosureSharedVariable()) { final String variableName = expression.getName(); -// System.out.println(">>>>>>>>>>>>>>>>>>>>>>\n"); -// System.out.println("1, " + variableName + ":" + expression.isClosureSharedVariable() + "::" + expression.getAccessedVariable()); Parameter[] parametersWithSameVariableName = Arrays.stream(methodNode.getParameters()) .filter(e -> variableName.equals(e.getName())) @@ -304,13 +287,10 @@ public void visitVariableExpression(VariableExpression expression) { if (parametersWithSameVariableName.length != 1) { throw new GroovyBugError(parametersWithSameVariableName.length + " parameters with same name " + variableName + " found(Expect only one matched)."); } -// System.out.println("2, " + variableName + ":" + parametersWithSameVariableName[0]); expression.setAccessedVariable(parametersWithSameVariableName[0]); expression.setClosureSharedVariable(false); -// System.out.println("<<<<<<<<<<<<<<<<<<<<<<\n"); - } super.visitVariableExpression(expression); From f70975bb74cd234c1d469ea4510c0da9129e924e Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 16 Jan 2018 12:37:33 +0800 Subject: [PATCH 14/47] Add more tests --- .../groovy/transform/stc/LambdaTest.groovy | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index f12eeda9f33..28708fc019c 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -236,4 +236,29 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } ''' } + + void testFunctionWithLocalVariables3() { + 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) { + p(); + } + + public static void p() { + String x = "x"; + StringBuilder y = new StringBuilder("y"); + assert ['Hello yx1', 'Hello yx2', 'Hello yx3'] == Stream.of(1, 2, 3).map(e -> hello() + y + x + e).collect(Collectors.toList()); + } + + public static String hello() { + return "Hello "; + } + } + ''' + } } From b711a68d90d7b7e1f08f71455d18a227da09c6f2 Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 16 Jan 2018 13:42:00 +0800 Subject: [PATCH 15/47] Add more tests --- .../groovy/transform/stc/LambdaTest.groovy | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 28708fc019c..ad5c9f2d1fc 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -261,4 +261,25 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } ''' } + + void testFunctionWithLocalVariables4() { + 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() { + String x = "x"; + StringBuilder y = new StringBuilder("y"); + assert ['yx1', 'yx2', 'yx3'] == Stream.of(1, 2, 3).map(e -> y + x + e).collect(Collectors.toList()); + } + } + ''' + } } From 6bf63025ea0478f92ae7075990e90f435cf656fc Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 17 Jan 2018 14:45:42 +0800 Subject: [PATCH 16/47] Support invoking instance method in lambda --- .../asm/sc/StaticTypesLambdaWriter.java | 78 ++++++++++++++++--- .../groovy/transform/stc/LambdaTest.groovy | 23 ++++++ 2 files changed, 91 insertions(+), 10 deletions(-) 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 f3679b3b9ea..892927ebaf1 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.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 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 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 void writeLambda(LambdaExpression expression) { 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 void writeLambda(LambdaExpression expression) { 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 @@ private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) { } private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, ClassNode parameterType) { - Parameter[] lambdaSharedVariables = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); - String methodDescriptor = BytecodeHelper.getMethodDescriptor(parameterType.redirect(), lambdaSharedVariables); + List lambdaSharedVariableList = new LinkedList(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 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas removeInitialValues(localVariableParameters); Parameter[] methodParameters = Stream.concat(Arrays.stream(localVariableParameters), Arrays.stream(parametersWithExactType)).toArray(Parameter[]::new); + List methodParameterList = new LinkedList(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 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters); methodNode.setSourcePosition(expression); - new LocalVariableReplacementVisitor(methodNode).visitMethod(methodNode); + new TransformationVisitor(methodNode, thisParameter).visitMethod(methodNode); + } + + private Parameter prependThis(List 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 @@ protected ClassNode createClosureClass(final ClosureExpression expression, final 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 void visitVariableExpression(VariableExpression expression) { 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); + } } } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index ad5c9f2d1fc..79cb71ffbe1 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 "; + } + } + ''' + } } From fceb84f2bcba17984589d661d466655ad8ad762d Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 17 Jan 2018 15:23:36 +0800 Subject: [PATCH 17/47] Add more tests --- .../groovy/transform/stc/LambdaTest.groovy | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 79cb71ffbe1..61021c30cff 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -283,7 +283,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } - void testFunctionWithLocalVariables5() { + void testFunctionWithInstanceMethodCall() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -305,4 +305,50 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } ''' } + + void testFunctionWithInstanceMethodCall2() { + 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 -> this.hello() + e).collect(Collectors.toList()); + } + + public String hello() { + return "Hello "; + } + } + ''' + } + + void testFunctionWithInstanceMethodCall3() { + 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(String name) { + return "Hello $name"; + } + } + ''' + } } From 62c835b4e42071fb167934bb87176389d4cd999f Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 17 Jan 2018 15:26:28 +0800 Subject: [PATCH 18/47] Add more tests --- .../groovy/transform/stc/LambdaTest.groovy | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 61021c30cff..afa61116241 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -237,7 +237,28 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } - void testFunctionWithLocalVariables3() { + void testFunctionWithLocalVariables4() { + 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() { + String x = "x"; + StringBuilder y = new StringBuilder("y"); + assert ['yx1', 'yx2', 'yx3'] == Stream.of(1, 2, 3).map(e -> y + x + e).collect(Collectors.toList()); + } + } + ''' + } + + void testFunctionWithStaticMethodCall() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -262,7 +283,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } - void testFunctionWithLocalVariables4() { + void testFunctionWithStaticMethodCall2() { assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -271,13 +292,17 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav @CompileStatic public class Test4 { public static void main(String[] args) { - new Test4().p(); + p(); } - public void p() { + public static void p() { String x = "x"; StringBuilder y = new StringBuilder("y"); - assert ['yx1', 'yx2', 'yx3'] == Stream.of(1, 2, 3).map(e -> y + x + e).collect(Collectors.toList()); + assert ['Hello yx1', 'Hello yx2', 'Hello yx3'] == Stream.of(1, 2, 3).map(e -> Test4.hello() + y + x + e).collect(Collectors.toList()); + } + + public static String hello() { + return "Hello "; } } ''' From 152b94d95d62d6953ef1c61c73c350824bdbc21a Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 17 Jan 2018 16:33:55 +0800 Subject: [PATCH 19/47] Refine `prependThis` via using `getPlainNodeReference` --- .../groovy/classgen/asm/sc/StaticTypesLambdaWriter.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 892927ebaf1..a88006c7571 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 @@ -264,11 +264,7 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas } private Parameter prependThis(List 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); + ClassNode classNode = controller.getClassNode().getPlainNodeReference(); Parameter thisParameter = new Parameter(classNode, THIS); thisParameter.setOriginType(classNode); From 7210193eba31d837fe4330adb094afd64c714d84 Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 18 Jan 2018 09:34:15 +0800 Subject: [PATCH 20/47] Create an instance for each lambda --- .../asm/sc/StaticTypesLambdaWriter.java | 78 ++++++++++++------- 1 file changed, 52 insertions(+), 26 deletions(-) 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 a88006c7571..0650684ffbc 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 @@ -58,15 +58,19 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.NEW; /** * 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 static final String DO_CALL = "doCall"; + private static final String ORIGINAL_PARAMETERS_WITH_EXACT_TYPE = "__ORIGINAL_PARAMETERS_WITH_EXACT_TYPE"; + private static final String LAMBDA_SHARED_VARIABLES = "__LAMBDA_SHARED_VARIABLES"; + private static final String ENCLOSING_THIS = "__enclosing_this"; + private static final String LAMBDA_THIS = "__lambda_this"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -106,26 +110,43 @@ public void writeLambda(LambdaExpression expression) { ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0)); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); + newAndLoadGroovyLambdaInstance(lambdaClassNode); + loadEnclosingClassInstance(); + Parameter[] lambdaSharedVariableParameters = loadSharedVariables(syntheticLambdaMethodNode); + MethodVisitor mv = controller.getMethodVisitor(); + OperandStack operandStack = controller.getOperandStack(); + + mv.visitInvokeDynamicInsn( + abstractMethodNode.getName(), + createAbstractMethodDesc(syntheticLambdaMethodNode, parameterType, lambdaClassNode), + createBootstrapMethod(isInterface), + createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) + ); + operandStack.replace(parameterType.redirect(), lambdaSharedVariableParameters.length + 1); + } + private void loadEnclosingClassInstance() { + MethodVisitor mv = controller.getMethodVisitor(); OperandStack operandStack = controller.getOperandStack(); if (controller.getMethodNode().isStatic()) { operandStack.pushConstant(ConstantExpression.NULL); } else { mv.visitVarInsn(ALOAD, 0); - operandStack.push(classNode); + operandStack.push(controller.getClassNode()); } + } - Parameter[] lambdaSharedVariableParameters = loadSharedVariables(syntheticLambdaMethodNode); + private void newAndLoadGroovyLambdaInstance(ClassNode lambdaClassNode) { + MethodVisitor mv = controller.getMethodVisitor(); + String lambdaClassInternalName = BytecodeHelper.getClassInternalName(lambdaClassNode); + mv.visitTypeInsn(NEW, lambdaClassInternalName); + mv.visitInsn(DUP); - mv.visitInvokeDynamicInsn( - abstractMethodNode.getName(), - createAbstractMethodDesc(syntheticLambdaMethodNode, parameterType), - createBootstrapMethod(isInterface), - createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) - ); - operandStack.replace(parameterType.redirect(), lambdaSharedVariableParameters.length + 1); + Parameter[] lambdaClassConstructorParameters = Parameter.EMPTY_ARRAY; + mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, "", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaClassConstructorParameters), lambdaClassNode.isInterface()); + controller.getOperandStack().replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length); } private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) { @@ -143,13 +164,15 @@ private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) { BytecodeVariable variable = compileStack.getVariable(parameterName, true); operandStack.loadOrStoreVariable(variable, false); } + return lambdaSharedVariableParameters; } - private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, ClassNode parameterType) { + private String createAbstractMethodDesc(MethodNode syntheticLambdaMethodNode, ClassNode parameterType, ClassNode lambdaClassNode) { List lambdaSharedVariableList = new LinkedList(Arrays.asList(syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES))); - prependThis(lambdaSharedVariableList); + prependEnclosingThis(lambdaSharedVariableList); + prependParameter(lambdaSharedVariableList, LAMBDA_THIS, lambdaClassNode); return BytecodeHelper.getMethodDescriptor(parameterType.redirect(), lambdaSharedVariableList.toArray(Parameter.EMPTY_ARRAY)); } @@ -168,11 +191,11 @@ private Object[] createBootstrapMethodArguments(String abstractMethodDesc, Class return new Object[]{ Type.getType(abstractMethodDesc), new Handle( - Opcodes.H_INVOKESTATIC, + Opcodes.H_INVOKEVIRTUAL, lambdaClassNode.getName(), syntheticLambdaMethodNode.getName(), BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode), - false + lambdaClassNode.isInterface() ), Type.getType(BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode.getReturnType(), syntheticLambdaMethodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE))) }; @@ -245,12 +268,12 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas List methodParameterList = new LinkedList(Arrays.asList(methodParameters)); - Parameter thisParameter = prependThis(methodParameterList); + Parameter thisParameter = prependEnclosingThis(methodParameterList); MethodNode methodNode = answer.addMethod( DO_CALL, - Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, + Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, returnType, methodParameterList.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, @@ -263,16 +286,19 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas new TransformationVisitor(methodNode, thisParameter).visitMethod(methodNode); } - private Parameter prependThis(List methodParameterList) { - ClassNode classNode = controller.getClassNode().getPlainNodeReference(); + private Parameter prependEnclosingThis(List methodParameterList) { + return prependParameter(methodParameterList, ENCLOSING_THIS, controller.getClassNode().getPlainNodeReference()); + } + + private Parameter prependParameter(List methodParameterList, String parameterName, ClassNode parameterType) { + Parameter parameter = new Parameter(parameterType, parameterName); - Parameter thisParameter = new Parameter(classNode, THIS); - thisParameter.setOriginType(classNode); - thisParameter.setClosureSharedVariable(false); + parameter.setOriginType(parameterType); + parameter.setClosureSharedVariable(false); - methodParameterList.add(0, thisParameter); + methodParameterList.add(0, parameter); - return thisParameter; + return parameter; } private Parameter[] createParametersWithExactType(LambdaExpression expression) { From 1296d2f936f3db500cae9ef7655ea9b57e17203d Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 18 Jan 2018 10:46:47 +0800 Subject: [PATCH 21/47] Make Lambda extend Closure --- src/main/groovy/groovy/lang/Lambda.java | 32 ++++++++--------- .../groovy/classgen/asm/ClosureWriter.java | 35 +++++++++++-------- .../asm/sc/StaticTypesLambdaWriter.java | 15 +++++++- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/main/groovy/groovy/lang/Lambda.java b/src/main/groovy/groovy/lang/Lambda.java index 0b09ef09899..82953b0fa75 100644 --- a/src/main/groovy/groovy/lang/Lambda.java +++ b/src/main/groovy/groovy/lang/Lambda.java @@ -19,38 +19,34 @@ package groovy.lang; -import java.io.Serializable; - /** * Represents any lambda object in Groovy. * * @since 3.0.0 */ -public abstract class Lambda extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable, Serializable { +public abstract class Lambda extends Closure { + + public Lambda(Object owner, Object thisObject) { + super(owner, thisObject); + } + /** - * When an object implementing interface Runnable is used - * to create a thread, starting the thread causes the object's - * run method to be called in that separately executing - * thread. - *

- * The general contract of the method run is that it may - * take any action whatsoever. + * Constructor used when the "this" object for the Closure is null. + * This is rarely the case in normal Groovy usage. * - * @see Thread#run() + * @param owner the Closure owner */ - @Override - public void run() { - + public Lambda(Object owner) { + super(owner); } /** - * Computes a result, or throws an exception if unable to do so. + * Invokes the closure without any parameters, returning any value if applicable. * - * @return computed result - * @throws Exception if unable to compute a result + * @return the value if applicable or null if there is no return statement in the closure */ @Override - public V call() throws Exception { + public V call() { return null; } } 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 429fe3b8945..9a4f2005046 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java @@ -248,21 +248,7 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { } // let's make the constructor - BlockStatement block = new BlockStatement(); - // this block does not get a source position, because we don't - // want this synthetic constructor to show up in corbertura reports - VariableExpression outer = new VariableExpression("_outerInstance"); - outer.setSourcePosition(expression); - block.getVariableScope().putReferencedLocalVariable(outer); - VariableExpression thisObject = new VariableExpression("_thisObject"); - thisObject.setSourcePosition(expression); - block.getVariableScope().putReferencedLocalVariable(thisObject); - TupleExpression conArgs = new TupleExpression(outer, thisObject); - block.addStatement( - new ExpressionStatement( - new ConstructorCallExpression( - ClassNode.SUPER, - conArgs))); + BlockStatement block = createBlockStatementForConstructor(expression); // let's assign all the parameter fields from the outer context for (Parameter param : localVariableParams) { @@ -305,6 +291,25 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { return answer; } + protected BlockStatement createBlockStatementForConstructor(ClosureExpression expression) { + BlockStatement block = new BlockStatement(); + // this block does not get a source position, because we don't + // want this synthetic constructor to show up in corbertura reports + VariableExpression outer = new VariableExpression("_outerInstance"); + outer.setSourcePosition(expression); + block.getVariableScope().putReferencedLocalVariable(outer); + VariableExpression thisObject = new VariableExpression("_thisObject"); + thisObject.setSourcePosition(expression); + block.getVariableScope().putReferencedLocalVariable(thisObject); + TupleExpression conArgs = new TupleExpression(outer, thisObject); + block.addStatement( + new ExpressionStatement( + new ConstructorCallExpression( + ClassNode.SUPER, + conArgs))); + return block; + } + private String genClosureClassName() { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); 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 0650684ffbc..f7a6261b6ed 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 @@ -144,7 +144,10 @@ private void newAndLoadGroovyLambdaInstance(ClassNode lambdaClassNode) { mv.visitTypeInsn(NEW, lambdaClassInternalName); mv.visitInsn(DUP); - Parameter[] lambdaClassConstructorParameters = Parameter.EMPTY_ARRAY; + loadEnclosingClassInstance(); + loadEnclosingClassInstance(); + + Parameter[] lambdaClassConstructorParameters = createConstructorParameters(); mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, "", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaClassConstructorParameters), lambdaClassNode.isInterface()); controller.getOperandStack().replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length); } @@ -245,11 +248,21 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { answer.setScriptBody(true); } + Parameter[] constructorParameters = createConstructorParameters(); + answer.addConstructor(ACC_PUBLIC, constructorParameters, ClassNode.EMPTY_ARRAY, super.createBlockStatementForConstructor(expression)); + addSyntheticLambdaMethodNode(expression, answer); return answer; } + private Parameter[] createConstructorParameters() { + Parameter[] params = new Parameter[2]; + params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance"); + params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject"); + return params; + } + private String genLambdaClassName() { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); From 82b08ffa5914b35b8d7494d19860de359f6bd27d Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 18 Jan 2018 10:56:52 +0800 Subject: [PATCH 22/47] Remove temporary methods --- src/main/groovy/groovy/lang/Lambda.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/groovy/groovy/lang/Lambda.java b/src/main/groovy/groovy/lang/Lambda.java index 82953b0fa75..159566c53b1 100644 --- a/src/main/groovy/groovy/lang/Lambda.java +++ b/src/main/groovy/groovy/lang/Lambda.java @@ -31,22 +31,12 @@ public Lambda(Object owner, Object thisObject) { } /** - * Constructor used when the "this" object for the Closure is null. + * Constructor used when the "this" object for the Lambda is null. * This is rarely the case in normal Groovy usage. * - * @param owner the Closure owner + * @param owner the Lambda owner */ public Lambda(Object owner) { super(owner); } - - /** - * Invokes the closure without any parameters, returning any value if applicable. - * - * @return the value if applicable or null if there is no return statement in the closure - */ - @Override - public V call() { - return null; - } } From cd40603da24d8f10e8889518863e39db31ef9de0 Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 18 Jan 2018 11:16:49 +0800 Subject: [PATCH 23/47] Trivial refactoring: Extract constants --- .../groovy/classgen/asm/ClosureWriter.java | 11 ++++--- .../asm/sc/StaticTypesLambdaWriter.java | 7 ++-- .../groovy/transform/stc/LambdaTest.groovy | 33 +++++++++++++++++-- 3 files changed, 42 insertions(+), 9 deletions(-) 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 9a4f2005046..defc7bf5ad0 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java @@ -65,6 +65,9 @@ public class ClosureWriter { + public static final String OUTER_INSTANCE = "_outerInstance"; + public static final String THIS_OBJECT = "_thisObject"; + protected interface UseExistingReference {} private final Map closureClassMap; @@ -279,8 +282,8 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { } Parameter[] params = new Parameter[2 + localVariableParams.length]; - params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance"); - params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject"); + 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); @@ -295,10 +298,10 @@ protected BlockStatement createBlockStatementForConstructor(ClosureExpression ex BlockStatement block = new BlockStatement(); // this block does not get a source position, because we don't // want this synthetic constructor to show up in corbertura reports - VariableExpression outer = new VariableExpression("_outerInstance"); + VariableExpression outer = new VariableExpression(OUTER_INSTANCE); outer.setSourcePosition(expression); block.getVariableScope().putReferencedLocalVariable(outer); - VariableExpression thisObject = new VariableExpression("_thisObject"); + VariableExpression thisObject = new VariableExpression(THIS_OBJECT); thisObject.setSourcePosition(expression); block.getVariableScope().putReferencedLocalVariable(thisObject); TupleExpression conArgs = new TupleExpression(outer, thisObject); 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 f7a6261b6ed..f30fbc23bca 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 @@ -71,6 +71,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter { private static final String LAMBDA_SHARED_VARIABLES = "__LAMBDA_SHARED_VARIABLES"; private static final String ENCLOSING_THIS = "__enclosing_this"; private static final String LAMBDA_THIS = "__lambda_this"; + public static final String INIT = ""; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -148,7 +149,7 @@ private void newAndLoadGroovyLambdaInstance(ClassNode lambdaClassNode) { loadEnclosingClassInstance(); Parameter[] lambdaClassConstructorParameters = createConstructorParameters(); - mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, "", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaClassConstructorParameters), lambdaClassNode.isInterface()); + mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, INIT, BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaClassConstructorParameters), lambdaClassNode.isInterface()); controller.getOperandStack().replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length); } @@ -258,8 +259,8 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { private Parameter[] createConstructorParameters() { Parameter[] params = new Parameter[2]; - params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance"); - params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject"); + params[0] = new Parameter(ClassHelper.OBJECT_TYPE, OUTER_INSTANCE); + params[1] = new Parameter(ClassHelper.OBJECT_TYPE, THIS_OBJECT); return params; } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index afa61116241..31d00605e2b 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -20,6 +20,8 @@ package groovy.transform.stc class LambdaTest extends GroovyTestCase { + private static final boolean SKIP_ERRORS = true; + void testFunction() { assertScript ''' import groovy.transform.CompileStatic @@ -58,7 +60,7 @@ class LambdaTest extends GroovyTestCase { * Depends on fixing https://issues.apache.org/jira/browse/GROOVY-8445 */ void testBinaryOperator() { - if (true) return + if (SKIP_ERRORS) return // the test can pass only in dynamic mode now, it can not pass static type checking... @@ -139,7 +141,7 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav * Depends on fixing https://issues.apache.org/jira/browse/GROOVY-8445 */ void testUnaryOperator() { - if (true) return + if (SKIP_ERRORS) return /* FIXME TestScript0.groovy: 14: [Static type checking] - Cannot find matching method java.util.List#replaceAll(groovy.lang.Closure). Please check if the declared type is correct and if the method exists. @@ -376,4 +378,31 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } ''' } + + void testFunctionCall() { + if (SKIP_ERRORS) return + + /* FIXME + [Static type checking] - Cannot find matching method java.lang.Object#plus(int). Please check if the declared type is correct and if the method exists. + @ line 13, column 35. + assert 2 == (e -> e + 1)(1) + */ + + 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() { + assert 2 == (e -> e + 1)(1) + } + } + ''' + } } From 7e3459347bab0057683663291046b90ee8827840 Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 18 Jan 2018 11:20:45 +0800 Subject: [PATCH 24/47] Exclude Java 9 build temporarily --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9473e53e637..908c7872d93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ sudo: required matrix: include: - - jdk: oraclejdk9 +# - jdk: oraclejdk9 - jdk: oraclejdk8 dist: trusty From 621cd4a4c710fc6da9045e824f904d480705a05b Mon Sep 17 00:00:00 2001 From: sunlan Date: Thu, 18 Jan 2018 18:18:56 +0800 Subject: [PATCH 25/47] Fix lambda in constructor --- .../asm/sc/StaticTypesLambdaWriter.java | 3 ++- .../groovy/transform/stc/LambdaTest.groovy | 23 +++++++++++++++++++ .../parser/antlr4/GroovyParserTest.groovy | 5 +--- 3 files changed, 26 insertions(+), 5 deletions(-) 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 f30fbc23bca..78848be564f 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 @@ -130,8 +130,9 @@ public void writeLambda(LambdaExpression expression) { private void loadEnclosingClassInstance() { MethodVisitor mv = controller.getMethodVisitor(); OperandStack operandStack = controller.getOperandStack(); + CompileStack compileStack = controller.getCompileStack(); - if (controller.getMethodNode().isStatic()) { + if (controller.isStaticMethod() || compileStack.isInSpecialConstructorCall()) { operandStack.pushConstant(ConstantExpression.NULL); } else { mv.visitVarInsn(ALOAD, 0); diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 31d00605e2b..356dffa655f 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -333,6 +333,29 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + void testFunctionInConstructor() { + 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(); + } + + public Test4() { + assert ['Hello Jochen', 'Hello Daniel'] == Stream.of("Jochen", "Daniel").map(e -> hello() + e).collect(Collectors.toList()); + } + + public String hello() { + return "Hello "; + } + } + ''' + } + void testFunctionWithInstanceMethodCall2() { assertScript ''' import groovy.transform.CompileStatic diff --git a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy index cbc3a200342..9b58e976753 100644 --- a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy +++ b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy @@ -141,11 +141,9 @@ class GroovyParserTest extends GroovyTestCase { doTest('core/Closure_10.groovy', [Parameter]) } - /* TODO uncomment void "test groovy core - Lambda"() { doRunAndTestAntlr4('core/Lambda_01x.groovy') } - */ void "test groovy core - MethodReference"() { doRunAndTestAntlr4('core/MethodReference_01x.groovy') @@ -384,8 +382,7 @@ class GroovyParserTest extends GroovyTestCase { doRunAndTestAntlr4('bugs/GROOVY-3898.groovy') doRunAndTestAntlr4('bugs/BUG-GROOVY-8311.groovy') - // TODO uncomment - // doRunAndTestAntlr4('bugs/GROOVY-8228.groovy') + doRunAndTestAntlr4('bugs/GROOVY-8228.groovy') doRunAndTest('bugs/BUG-GROOVY-8426.groovy') } From f14131ec07bf850979daac804d71f4cffb141e8d Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 19 Jan 2018 19:41:44 +0800 Subject: [PATCH 26/47] Support updating non-final local variables in lambda --- .../groovy/classgen/asm/ClosureWriter.java | 78 ++++++++----- .../asm/sc/StaticTypesLambdaWriter.java | 106 +++++++----------- .../groovy/transform/stc/LambdaTest.groovy | 42 +++++++ 3 files changed, 135 insertions(+), 91 deletions(-) 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 defc7bf5ad0..343b714c93a 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.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 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { 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 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) { 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 @@ private String genClosureClassName() { + 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); } /* 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 78848be564f..3e366285d8a 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.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.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 = ""; + public static final String IS_GENERATED_CONSTRUCTOR = "__IS_GENERATED_CONSTRUCTOR"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -111,20 +110,21 @@ public void writeLambda(LambdaExpression expression) { 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 @@ private void loadEnclosingClassInstance() { } } - 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 @@ private void newAndLoadGroovyLambdaInstance(ClassNode lambdaClassNode) { loadEnclosingClassInstance(); loadEnclosingClassInstance(); - Parameter[] lambdaClassConstructorParameters = createConstructorParameters(); + loadSharedVariables(syntheticLambdaMethodNode); + + List 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 lambdaSharedVariableList = new LinkedList(Arrays.asList(syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES))); + private String createAbstractMethodDesc(ClassNode parameterType, ClassNode lambdaClassNode) { + List lambdaSharedVariableList = new LinkedList<>(); prependEnclosingThis(lambdaSharedVariableList); prependParameter(lambdaSharedVariableList, LAMBDA_THIS, lambdaClassNode); @@ -250,19 +256,17 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { 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 @@ private String genLambdaClassName() { + 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 methodParameterList = new LinkedList(Arrays.asList(methodParameters)); - - Parameter thisParameter = prependEnclosingThis(methodParameterList); + List methodParameterList = new LinkedList(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 @@ private void addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClas methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters); methodNode.setSourcePosition(expression); - new TransformationVisitor(methodNode, thisParameter).visitMethod(methodNode); + return methodNode; } private Parameter prependEnclosingThis(List methodParameterList) { @@ -336,12 +338,11 @@ protected ClassNode createClosureClass(final ClosureExpression expression, final 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; } @@ -350,27 +351,6 @@ protected SourceUnit getSourceUnit() { return null; } - @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()) { diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 356dffa655f..136fbe21075 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 + } + } + ''' + } } From ea17e18368a402580c170f6c32ebc32e06d5f1c8 Mon Sep 17 00:00:00 2001 From: sunlan Date: Mon, 22 Jan 2018 07:46:10 +0800 Subject: [PATCH 27/47] Trivial refactoring: add `INFERRED_PARAMETER_TYPE` --- .../groovy/classgen/asm/sc/StaticInvocationWriter.java | 8 ++++---- .../groovy/classgen/asm/sc/StaticTypesLambdaWriter.java | 4 ++-- .../codehaus/groovy/transform/stc/StaticTypesMarker.java | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index 22482e51d8f..b2543375da1 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -75,6 +75,7 @@ import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE; import static org.codehaus.groovy.ast.ClassHelper.getWrapper; import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS; +import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_PARAMETER_TYPE; import static org.objectweb.asm.Opcodes.ACONST_NULL; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.CHECKCAST; @@ -102,7 +103,6 @@ public class StaticInvocationWriter extends InvocationWriter { new Parameter(ClassHelper.OBJECT_TYPE, "args") } ); - public static final String PARAMETER_TYPE = "_parameter_type"; private final AtomicInteger labelCounter = new AtomicInteger(); @@ -436,7 +436,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { // first parameters as usual for (int i = 0; i < para.length - 1; i++) { Expression expression = argumentList.get(i); - expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); + expression.putNodeMetaData(INFERRED_PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); @@ -462,7 +462,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { } else if (argumentListSize == para.length) { for (int i = 0; i < argumentListSize; i++) { Expression expression = argumentList.get(i); - expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); + expression.putNodeMetaData(INFERRED_PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); @@ -494,7 +494,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { } for (int i = 0; i < arguments.length; i++) { Expression expression = arguments[i]; - expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); + expression.putNodeMetaData(INFERRED_PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); 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 3e366285d8a..68ad4add243 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,7 +52,7 @@ import java.util.Map; import java.util.stream.Collectors; -import static org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.PARAMETER_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; import static org.objectweb.asm.Opcodes.ALOAD; @@ -89,7 +89,7 @@ public WriterController makeController(final WriterController normalController) @Override public void writeLambda(LambdaExpression expression) { - ClassNode parameterType = expression.getNodeMetaData(PARAMETER_TYPE); + ClassNode parameterType = expression.getNodeMetaData(INFERRED_PARAMETER_TYPE); List abstractMethodNodeList = parameterType.redirect().getMethods().stream() 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 9785433820b..74113089adc 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java @@ -37,5 +37,6 @@ public enum StaticTypesMarker { PV_FIELDS_MUTATION, // set of private fields that are set from closures or inner classes 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 + 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 } From 411fb6d48b680abcdadcaf9478e1e1e4fc13734a Mon Sep 17 00:00:00 2001 From: sunlan Date: Mon, 22 Jan 2018 21:49:20 +0800 Subject: [PATCH 28/47] Support native lambda on the RHS of variable declaration --- ...esBinaryExpressionMultiTypeDispatcher.java | 14 ++++-- .../asm/sc/StaticTypesLambdaWriter.java | 50 +++++++++++++++++-- .../transform/stc/StaticTypesMarker.java | 3 +- .../groovy/transform/stc/LambdaTest.groovy | 50 +++++++++++++++++++ 4 files changed, 108 insertions(+), 9 deletions(-) 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 116fd16ed35..a5c514dd73a 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.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.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 @@ protected void writePostOrPrefixMethod(int op, String method, Expression express @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 void evaluateEqual(final BinaryExpression expression, final boolean defin 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); 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 68ad4add243..7014a99acaa 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.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 WriterController makeController(final WriterController normalController) @Override public void writeLambda(LambdaExpression expression) { - ClassNode parameterType = expression.getNodeMetaData(INFERRED_PARAMETER_TYPE); + ClassNode inferedType = getInferredType(expression); List 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 void writeLambda(LambdaExpression expression) { 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 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 @@ private Parameter[] createParametersWithExactType(LambdaExpression expression) { 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); } 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 74113089adc..fcde258419e 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 } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 136fbe21075..f9287566e3d 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 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 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()); + } + } + + ''' + } } From f62c784d853ec4fcc596088ad8ed1fdeec807fc5 Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 23 Jan 2018 07:42:20 +0800 Subject: [PATCH 29/47] Trivial refactoring: rename variable names --- .../asm/sc/StaticInvocationWriter.java | 8 ++--- .../asm/sc/StaticTypesLambdaWriter.java | 36 ++++++++----------- .../transform/stc/StaticTypesMarker.java | 2 +- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index b2543375da1..69e0837003e 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -75,7 +75,7 @@ import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE; import static org.codehaus.groovy.ast.ClassHelper.getWrapper; import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS; -import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_PARAMETER_TYPE; +import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE; import static org.objectweb.asm.Opcodes.ACONST_NULL; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.CHECKCAST; @@ -436,7 +436,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { // first parameters as usual for (int i = 0; i < para.length - 1; i++) { Expression expression = argumentList.get(i); - expression.putNodeMetaData(INFERRED_PARAMETER_TYPE, para[i].getType()); + expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); @@ -462,7 +462,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { } else if (argumentListSize == para.length) { for (int i = 0; i < argumentListSize; i++) { Expression expression = argumentList.get(i); - expression.putNodeMetaData(INFERRED_PARAMETER_TYPE, para[i].getType()); + expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); @@ -494,7 +494,7 @@ protected void loadArguments(List argumentList, Parameter[] para) { } for (int i = 0; i < arguments.length; i++) { Expression expression = arguments[i]; - expression.putNodeMetaData(INFERRED_PARAMETER_TYPE, para[i].getType()); + expression.putNodeMetaData(PARAMETER_TYPE, para[i].getType()); expression.visit(acg); if (!isNullConstant(expression)) { operandStack.doGroovyCast(para[i].getType()); 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 7014a99acaa..121c8f6ad59 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 @@ -38,7 +38,6 @@ import org.codehaus.groovy.classgen.asm.OperandStack; import org.codehaus.groovy.classgen.asm.WriterController; import org.codehaus.groovy.classgen.asm.WriterControllerFactory; -import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.transform.stc.StaticTypesMarker; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; @@ -53,7 +52,7 @@ 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.codehaus.groovy.transform.stc.StaticTypesMarker.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; @@ -90,14 +89,14 @@ public WriterController makeController(final WriterController normalController) @Override public void writeLambda(LambdaExpression expression) { - ClassNode inferedType = getInferredType(expression); + ClassNode lambdaType = getLambdaType(expression); List abstractMethodNodeList = - inferedType.redirect().getMethods().stream() + lambdaType.redirect().getMethods().stream() .filter(MethodNode::isAbstract) .collect(Collectors.toList()); - if (!(isFunctionInterface(inferedType) && abstractMethodNodeList.size() == 1)) { + if (!(isFunctionInterface(lambdaType) && abstractMethodNodeList.size() == 1)) { // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; @@ -121,11 +120,11 @@ public void writeLambda(LambdaExpression expression) { mv.visitInvokeDynamicInsn( abstractMethodNode.getName(), - createAbstractMethodDesc(inferedType, lambdaClassNode), + createAbstractMethodDesc(lambdaType, lambdaClassNode), createBootstrapMethod(isInterface), createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) ); - operandStack.replace(inferedType.redirect(), 1); + operandStack.replace(lambdaType.redirect(), 1); if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) { // FIXME declaring variable whose initial value is a lambda, e.g. `Function f = (Integer e) -> 'a' + e` @@ -153,13 +152,13 @@ public void writeLambda(LambdaExpression expression) { } - private ClassNode getInferredType(LambdaExpression expression) { - ClassNode inferedType = expression.getNodeMetaData(INFERRED_PARAMETER_TYPE); + private ClassNode getLambdaType(LambdaExpression expression) { + ClassNode type = expression.getNodeMetaData(PARAMETER_TYPE); - if (null == inferedType) { - inferedType = expression.getNodeMetaData(INFERRED_LAMBDA_TYPE); + if (null == type) { + type = expression.getNodeMetaData(INFERRED_LAMBDA_TYPE); } - return inferedType; + return type; } private void loadEnclosingClassInstance() { @@ -379,16 +378,11 @@ protected ClassNode createClosureClass(final ClosureExpression expression, final } private static final class TransformationVisitor extends CorrectAccessedVariableVisitor { - private Parameter thisParameter; + private Parameter enclosingThisParameter; - public TransformationVisitor(InnerClassNode icn, Parameter thisParameter) { + public TransformationVisitor(InnerClassNode icn, Parameter enclosingThisParameter) { super(icn); - this.thisParameter = thisParameter; - } - - @Override - protected SourceUnit getSourceUnit() { - return null; + this.enclosingThisParameter = enclosingThisParameter; } @Override @@ -399,7 +393,7 @@ public void visitMethodCallExpression(MethodCallExpression call) { if (objectExpression instanceof VariableExpression) { VariableExpression originalObjectExpression = (VariableExpression) objectExpression; if (null == originalObjectExpression.getAccessedVariable()) { - VariableExpression thisVariable = new VariableExpression(thisParameter); + VariableExpression thisVariable = new VariableExpression(enclosingThisParameter); thisVariable.setSourcePosition(originalObjectExpression); call.setObjectExpression(thisVariable); 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 fcde258419e..73eecadb20c 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java @@ -38,6 +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 on an expression + 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 } From 5965c5c07c81266dae31776bfae21f08d7394ebb Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 23 Jan 2018 09:06:32 +0800 Subject: [PATCH 30/47] Try to run tests on Java 9 --- .travis.yml | 2 +- .../groovy/transform/stc/LambdaTest.groovy | 40 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 908c7872d93..9473e53e637 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ sudo: required matrix: include: -# - jdk: oraclejdk9 + - jdk: oraclejdk9 - jdk: oraclejdk8 dist: trusty diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index f9287566e3d..7f28151db1c 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -35,7 +35,7 @@ class LambdaTest extends GroovyTestCase { } public static void p() { - assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 1).collect(Collectors.toList()); + assert [2, 3, 4] == [1, 2, 3].stream().map(e -> e.plus 1).collect(Collectors.toList()); } } ''' @@ -49,7 +49,7 @@ class LambdaTest extends GroovyTestCase { @CompileStatic void p() { - assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e + 1).collect(Collectors.toList()); + assert [2, 3, 4] == [1, 2, 3].stream().map(e -> e + 1).collect(Collectors.toList()); } p() @@ -67,7 +67,7 @@ class LambdaTest extends GroovyTestCase { /* FIXME TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.util.stream.Stream#reduce(int, groovy.lang.Closure). Please check if the declared type is correct and if the method exists. @ line 13, column 30. - assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); + assert 13 == [1, 2, 3].stream().reduce(7, (r, e) -> r + e); ^ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.lang.Object#plus(java.lang.Object). Please check if the declared type is correct and if the method exists. @@ -90,7 +90,7 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav } public static void p() { - assert 13 == Stream.of(1, 2, 3).reduce(7, (r, e) -> r + e); + assert 13 == [1, 2, 3].stream().reduce(7, (r, e) -> r + e); } } ''' @@ -109,7 +109,7 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav } public static void p() { - Stream.of(1, 2, 3).forEach(e -> { System.out.println(e + 1); }); + [1, 2, 3].stream().forEach(e -> { System.out.println(e + 1); }); } } @@ -211,7 +211,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public static void p() { String x = "#" - assert ['#1', '#2', '#3'] == Stream.of(1, 2, 3).map(e -> x + e).collect(Collectors.toList()); + assert ['#1', '#2', '#3'] == [1, 2, 3].stream().map(e -> x + e).collect(Collectors.toList()); } } ''' @@ -233,7 +233,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public 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()) + assert ['23#1', '23#2', '23#3'] == [1, 2, 3].stream().map(e -> '' + y + x + e).collect(Collectors.toList()) } } ''' @@ -254,7 +254,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public void p() { String x = "x"; StringBuilder y = new StringBuilder("y"); - assert ['yx1', 'yx2', 'yx3'] == Stream.of(1, 2, 3).map(e -> y + x + e).collect(Collectors.toList()); + assert ['yx1', 'yx2', 'yx3'] == [1, 2, 3].stream().map(e -> y + x + e).collect(Collectors.toList()); } } ''' @@ -275,7 +275,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public static void p() { String x = "x"; StringBuilder y = new StringBuilder("y"); - assert ['Hello yx1', 'Hello yx2', 'Hello yx3'] == Stream.of(1, 2, 3).map(e -> hello() + y + x + e).collect(Collectors.toList()); + assert ['Hello yx1', 'Hello yx2', 'Hello yx3'] == [1, 2, 3].stream().map(e -> hello() + y + x + e).collect(Collectors.toList()); } public static String hello() { @@ -300,7 +300,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public static void p() { String x = "x"; StringBuilder y = new StringBuilder("y"); - assert ['Hello yx1', 'Hello yx2', 'Hello yx3'] == Stream.of(1, 2, 3).map(e -> Test4.hello() + y + x + e).collect(Collectors.toList()); + assert ['Hello yx1', 'Hello yx2', 'Hello yx3'] == [1, 2, 3].stream().map(e -> Test4.hello() + y + x + e).collect(Collectors.toList()); } public static String hello() { @@ -323,7 +323,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } public void p() { - assert ['Hello Jochen', 'Hello Daniel'] == Stream.of("Jochen", "Daniel").map(e -> hello() + e).collect(Collectors.toList()); + assert ['Hello Jochen', 'Hello Daniel'] == ["Jochen", "Daniel"].stream().map(e -> hello() + e).collect(Collectors.toList()); } public String hello() { @@ -346,7 +346,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } public Test4() { - assert ['Hello Jochen', 'Hello Daniel'] == Stream.of("Jochen", "Daniel").map(e -> hello() + e).collect(Collectors.toList()); + assert ['Hello Jochen', 'Hello Daniel'] == ["Jochen", "Daniel"].stream().map(e -> hello() + e).collect(Collectors.toList()); } public String hello() { @@ -369,7 +369,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } public void p() { - assert ['Hello Jochen', 'Hello Daniel'] == Stream.of("Jochen", "Daniel").map(e -> this.hello() + e).collect(Collectors.toList()); + assert ['Hello Jochen', 'Hello Daniel'] == ["Jochen", "Daniel"].stream().map(e -> this.hello() + e).collect(Collectors.toList()); } public String hello() { @@ -392,7 +392,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } public void p() { - assert ['Hello Jochen', 'Hello Daniel'] == Stream.of("Jochen", "Daniel").map(e -> hello(e)).collect(Collectors.toList()); + assert ['Hello Jochen', 'Hello Daniel'] == ["Jochen", "Daniel"].stream().map(e -> hello(e)).collect(Collectors.toList()); } public String hello(String name) { @@ -443,7 +443,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public static void p() { int i = 1 - assert [2, 4, 7] == Stream.of(1, 2, 3).map(e -> i += e).collect(Collectors.toList()) + assert [2, 4, 7] == [1, 2, 3].stream().map(e -> i += e).collect(Collectors.toList()) assert 7 == i } } @@ -464,7 +464,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public void p() { int i = 1 - assert [2, 4, 7] == Stream.of(1, 2, 3).map(e -> i += e).collect(Collectors.toList()) + assert [2, 4, 7] == [1, 2, 3].stream().map(e -> i += e).collect(Collectors.toList()) assert 7 == i } } @@ -486,7 +486,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav public static void p() { Function f = (Integer e) -> 'a' + e - assert ['a1', 'a2', 'a3'] == Stream.of(1, 2, 3).map(f).collect(Collectors.toList()) + assert ['a1', 'a2', 'a3'] == [1, 2, 3].stream().map(f).collect(Collectors.toList()) } } @@ -509,12 +509,12 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav 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()) + assert ['23#1', '23#2', '23#3'] == [1, 2, 3].stream().map(e -> '' + y + x + e).collect(Collectors.toList()) Function f = (Integer e) -> 'a' + e - assert ['a1', 'a2', 'a3'] == Stream.of(1, 2, 3).map(f).collect(Collectors.toList()) + assert ['a1', 'a2', 'a3'] == [1, 2, 3].stream().map(f).collect(Collectors.toList()) - assert [2, 3, 4] == Stream.of(1, 2, 3).map(e -> e.plus 1).collect(Collectors.toList()); + assert [2, 3, 4] == [1, 2, 3].stream().map(e -> e.plus 1).collect(Collectors.toList()); } } From d53fb4f8a4d07811243f77c36980d153a9a23037 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 26 Jan 2018 16:59:26 +0800 Subject: [PATCH 31/47] Support nested native lambda --- .../asm/sc/StaticTypesLambdaWriter.java | 16 ++-- .../groovy/transform/stc/LambdaTest.groovy | 81 ++++++++++++++++++- 2 files changed, 88 insertions(+), 9 deletions(-) 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 121c8f6ad59..899fd4521fa 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 @@ -107,7 +107,7 @@ public void writeLambda(LambdaExpression expression) { ClassNode classNode = controller.getClassNode(); boolean isInterface = classNode.isInterface(); - ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0)); + ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0), abstractMethodNode); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); newGroovyLambdaInstanceAndLoad(lambdaClassNode, syntheticLambdaMethodNode); @@ -259,10 +259,10 @@ private boolean isFunctionInterface(ClassNode parameterType) { return parameterType.redirect().isInterface() && !parameterType.redirect().getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty(); } - public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods) { + public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode lambdaClass = lambdaClassMap.get(expression); if (lambdaClass == null) { - lambdaClass = createLambdaClass(expression, mods); + lambdaClass = createLambdaClass(expression, mods, abstractMethodNode); lambdaClassMap.put(expression, lambdaClass); controller.getAcg().addInnerClass(lambdaClass); lambdaClass.addInterface(ClassHelper.GENERATED_LAMBDA_TYPE); @@ -271,7 +271,7 @@ public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods) { return lambdaClass; } - protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { + protected ClassNode createLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode outerClass = controller.getOutermostClass(); ClassNode classNode = controller.getClassNode(); String name = genLambdaClassName(); @@ -290,7 +290,7 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods) { answer.setScriptBody(true); } - MethodNode syntheticLambdaMethodNode = addSyntheticLambdaMethodNode(expression, answer); + MethodNode syntheticLambdaMethodNode = addSyntheticLambdaMethodNode(expression, answer, abstractMethodNode); Parameter[] localVariableParameters = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); addFieldsAndGettersForLocalVariables(answer, localVariableParameters); @@ -312,9 +312,9 @@ private String genLambdaClassName() { + controller.getContext().getNextLambdaInnerName(outerClass, classNode, methodNode); } - private MethodNode addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClassNode answer) { + private MethodNode addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClassNode answer, MethodNode abstractMethodNode) { Parameter[] parametersWithExactType = createParametersWithExactType(expression); // expression.getParameters(); - ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType(); +// ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType(); Parameter[] localVariableParameters = getLambdaSharedVariables(expression); removeInitialValues(localVariableParameters); @@ -325,7 +325,7 @@ private MethodNode addSyntheticLambdaMethodNode(LambdaExpression expression, Inn answer.addMethod( DO_CALL, Opcodes.ACC_PUBLIC, - returnType, + abstractMethodNode.getReturnType() /*ClassHelper.OBJECT_TYPE*/ /*returnType*/, methodParameterList.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, expression.getCode() diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 7f28151db1c..212ec11eaa4 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -485,7 +485,7 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } public static void p() { - Function f = (Integer e) -> 'a' + e + Function f = (Integer e) -> 'a' + e // STC can not infer the type of `e`, so we have to specify the type `Integer` by ourselves assert ['a1', 'a2', 'a3'] == [1, 2, 3].stream().map(f).collect(Collectors.toList()) } } @@ -520,4 +520,83 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + + void testFunctionWithNestedLambda() { + 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() { + [1, 2].stream().forEach(e -> { + def list = ['a', 'b'].stream().map(f -> f + e).toList() + if (1 == e) { + assert ['a1', 'b1'] == list + } else if (2 == e) { + assert ['a2', 'b2'] == list + } + }) + + } + } + ''' + } + + void testFunctionWithNestedLambda2() { + 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() { + def list = ['a', 'b'].stream() + .map(e -> { + [1, 2].stream().map(f -> e + f).toList() + }).toList() + + assert ['a1', 'a2'] == list[0] + assert ['b1', 'b2'] == list[1] + } + } + ''' + } + + void testFunctionWithNestedLambda3() { + 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() { + def list = ['a', 'b'].stream() + .map(e -> { + Function x = (Integer f) -> e + f + [1, 2].stream().map(x).toList() + }).toList() + + assert ['a1', 'a2'] == list[0] + assert ['b1', 'b2'] == list[1] + } + } + ''' + } } From f02cf212369ae6987c4baa3020a2362f6971ee0f Mon Sep 17 00:00:00 2001 From: sunlan Date: Mon, 29 Jan 2018 09:44:22 +0800 Subject: [PATCH 32/47] Save lambda object in the wrapper to prepare implementing callable lambda --- src/main/groovy/groovy/lang/Lambda.java | 37 ++++++++++ .../asm/sc/StaticTypesLambdaWriter.java | 72 ++++++++++++++++--- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/main/groovy/groovy/lang/Lambda.java b/src/main/groovy/groovy/lang/Lambda.java index 159566c53b1..6cf2a637ec8 100644 --- a/src/main/groovy/groovy/lang/Lambda.java +++ b/src/main/groovy/groovy/lang/Lambda.java @@ -19,12 +19,19 @@ package groovy.lang; +import org.apache.groovy.internal.util.UncheckedThrow; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.runtime.InvokerInvocationException; + +import static org.codehaus.groovy.classgen.asm.sc.StaticTypesLambdaWriter.SAM_NAME; + /** * Represents any lambda object in Groovy. * * @since 3.0.0 */ public abstract class Lambda extends Closure { + private Object lambdaObject; public Lambda(Object owner, Object thisObject) { super(owner, thisObject); @@ -39,4 +46,34 @@ public Lambda(Object owner, Object thisObject) { public Lambda(Object owner) { super(owner); } + + @Override + public V call(Object... args) { + String methodName; + try { + methodName = (String) this.getClass().getField(SAM_NAME).get(lambdaObject); + } catch (IllegalAccessException e) { + throw new GroovyBugError("Failed to access field " + SAM_NAME + " of " + this.getClass(), e); + } catch (NoSuchFieldException e) { + throw new GroovyBugError("Failed to find field " + SAM_NAME + " in " + this.getClass(), e); + } + + try { + return (V) getMetaClass().invokeMethod(lambdaObject, methodName, args); + } catch (InvokerInvocationException e) { + UncheckedThrow.rethrow(e.getCause()); + return null; // unreachable statement + } catch (Exception e) { + return (V) throwRuntimeException(e); + } + } + + public Object getLambdaObject() { + return lambdaObject; + } + + public void setLambdaObject(Object lambdaObject) { + this.lambdaObject = lambdaObject; + } + } 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 899fd4521fa..008d798e0df 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 @@ -33,6 +33,7 @@ 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; @@ -53,11 +54,14 @@ import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_LAMBDA_TYPE; import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE; +import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.NEW; /** @@ -71,6 +75,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter { private static final String LAMBDA_THIS = "__lambda_this"; public static final String INIT = ""; public static final String IS_GENERATED_CONSTRUCTOR = "__IS_GENERATED_CONSTRUCTOR"; + public static final String LAMBDA_WRAPPER = "__lambda_wrapper"; + public static final String SAM_NAME = "__SAM_NAME"; private StaticTypesClosureWriter staticTypesClosureWriter; private WriterController controller; private WriterControllerFactory factory; @@ -107,10 +113,10 @@ public void writeLambda(LambdaExpression expression) { ClassNode classNode = controller.getClassNode(); boolean isInterface = classNode.isInterface(); - ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0), abstractMethodNode); + ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode); MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); - newGroovyLambdaInstanceAndLoad(lambdaClassNode, syntheticLambdaMethodNode); + BytecodeVariable lambdaWrapperVariable = newGroovyLambdaWrapperAndLoad(lambdaClassNode, syntheticLambdaMethodNode); loadEnclosingClassInstance(); @@ -128,28 +134,64 @@ public void writeLambda(LambdaExpression expression) { if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) { // FIXME declaring variable whose initial value is a lambda, e.g. `Function f = (Integer e) -> 'a' + e` - // Groovy will `POP` twice...(expecting `POP` only once), as a hack, `DUP` to duplicate the element of operand stack: + // Groovy will `POP` multiple times...(expecting `POP` only once), as a hack, `DUP` to duplicate the element of operand stack: /* + L2 + ASTORE 0 + ALOAD 0 + ACONST_NULL 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; + Test1$_p_lambda1.doCall(LTest1;Ljava/lang/Integer;)Ljava/lang/Object;, + (Ljava/lang/Integer;)Ljava/lang/Object; ] - DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION - ASTORE 0 - L2 + DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION + DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION + DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION + DUP ALOAD 0 + SWAP + INVOKEVIRTUAL groovy/lang/Lambda.setLambdaObject (Ljava/lang/Object;)V + LDC Ljava/util/function/Function;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST java/util/function/Function + ASTORE 1 + L3 + ALOAD 1 + POP + POP POP POP */ mv.visitInsn(DUP); + + mv.visitInsn(DUP); + mv.visitInsn(DUP); } + + saveLambdaObjectInWrapper(lambdaType, lambdaWrapperVariable); + } + + private void saveLambdaObjectInWrapper(ClassNode lambdaType, BytecodeVariable lambdaWrapperVariable) { + MethodVisitor mv = controller.getMethodVisitor(); + OperandStack operandStack = controller.getOperandStack(); + + mv.visitInsn(DUP); + operandStack.push(lambdaType.redirect()); + operandStack.loadOrStoreVariable(lambdaWrapperVariable, false); + operandStack.push(ClassHelper.LAMBDA_TYPE); + operandStack.swap(); + + mv.visitMethodInsn( + INVOKEVIRTUAL, "groovy/lang/Lambda", "setLambdaObject", "(Ljava/lang/Object;)V", false); + + operandStack.remove(1); } private ClassNode getLambdaType(LambdaExpression expression) { @@ -174,7 +216,7 @@ private void loadEnclosingClassInstance() { } } - private void newGroovyLambdaInstanceAndLoad(ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) { + private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) { MethodVisitor mv = controller.getMethodVisitor(); String lambdaClassInternalName = BytecodeHelper.getClassInternalName(lambdaClassNode); mv.visitTypeInsn(NEW, lambdaClassInternalName); @@ -197,7 +239,15 @@ private void newGroovyLambdaInstanceAndLoad(ClassNode lambdaClassNode, MethodNod 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); + OperandStack operandStack = controller.getOperandStack(); + operandStack.replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length); + + BytecodeVariable variable = controller.getCompileStack().defineVariable(new VariableExpression(LAMBDA_WRAPPER, ClassHelper.LAMBDA_TYPE), false); + operandStack.storeVar(variable); + + operandStack.loadOrStoreVariable(variable, false); + + return variable; } private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) { @@ -290,6 +340,8 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods, Met answer.setScriptBody(true); } + answer.addField(SAM_NAME, ACC_PUBLIC | ACC_STATIC | ACC_FINAL, ClassHelper.STRING_TYPE, new ConstantExpression(abstractMethodNode.getName())); + MethodNode syntheticLambdaMethodNode = addSyntheticLambdaMethodNode(expression, answer, abstractMethodNode); Parameter[] localVariableParameters = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES); From 0b8beb93c60c2ea05bf6b1b4490cebffd2056e1a Mon Sep 17 00:00:00 2001 From: sunlan Date: Mon, 29 Jan 2018 12:51:21 +0800 Subject: [PATCH 33/47] Fix types in the operand stack --- .../asm/sc/StaticTypesLambdaWriter.java | 35 +++++++++---------- .../stc/StaticTypeCheckingVisitor.java | 5 +++ .../groovy/transform/stc/LambdaTest.groovy | 10 ++---- 3 files changed, 24 insertions(+), 26 deletions(-) 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 008d798e0df..7f44c90864e 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 @@ -113,10 +113,10 @@ public void writeLambda(LambdaExpression expression) { ClassNode classNode = controller.getClassNode(); boolean isInterface = classNode.isInterface(); - ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode); - MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0); + ClassNode lambdaWrapperClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode); + MethodNode syntheticLambdaMethodNode = lambdaWrapperClassNode.getMethods(DO_CALL).get(0); - BytecodeVariable lambdaWrapperVariable = newGroovyLambdaWrapperAndLoad(lambdaClassNode, syntheticLambdaMethodNode); + BytecodeVariable lambdaWrapperVariable = newGroovyLambdaWrapperAndLoad(lambdaWrapperClassNode, syntheticLambdaMethodNode); loadEnclosingClassInstance(); @@ -126,11 +126,11 @@ public void writeLambda(LambdaExpression expression) { mv.visitInvokeDynamicInsn( abstractMethodNode.getName(), - createAbstractMethodDesc(lambdaType, lambdaClassNode), + createAbstractMethodDesc(lambdaType, lambdaWrapperClassNode), createBootstrapMethod(isInterface), - createBootstrapMethodArguments(abstractMethodDesc, lambdaClassNode, syntheticLambdaMethodNode) + createBootstrapMethodArguments(abstractMethodDesc, lambdaWrapperClassNode, syntheticLambdaMethodNode) ); - operandStack.replace(lambdaType.redirect(), 1); + operandStack.replace(lambdaType.redirect(), 2); if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) { // FIXME declaring variable whose initial value is a lambda, e.g. `Function f = (Integer e) -> 'a' + e` @@ -175,23 +175,20 @@ public void writeLambda(LambdaExpression expression) { } - saveLambdaObjectInWrapper(lambdaType, lambdaWrapperVariable); + saveLambdaObjectInWrapper(lambdaWrapperVariable); + operandStack.replace(lambdaType.redirect(), 2); } - private void saveLambdaObjectInWrapper(ClassNode lambdaType, BytecodeVariable lambdaWrapperVariable) { + private void saveLambdaObjectInWrapper(BytecodeVariable lambdaWrapperVariable) { MethodVisitor mv = controller.getMethodVisitor(); OperandStack operandStack = controller.getOperandStack(); mv.visitInsn(DUP); - operandStack.push(lambdaType.redirect()); operandStack.loadOrStoreVariable(lambdaWrapperVariable, false); - operandStack.push(ClassHelper.LAMBDA_TYPE); operandStack.swap(); mv.visitMethodInsn( INVOKEVIRTUAL, "groovy/lang/Lambda", "setLambdaObject", "(Ljava/lang/Object;)V", false); - - operandStack.remove(1); } private ClassNode getLambdaType(LambdaExpression expression) { @@ -216,10 +213,10 @@ private void loadEnclosingClassInstance() { } } - private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) { + private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaWrapperClassNode, MethodNode syntheticLambdaMethodNode) { MethodVisitor mv = controller.getMethodVisitor(); - String lambdaClassInternalName = BytecodeHelper.getClassInternalName(lambdaClassNode); - mv.visitTypeInsn(NEW, lambdaClassInternalName); + String lambdaWrapperClassInternalName = BytecodeHelper.getClassInternalName(lambdaWrapperClassNode); + mv.visitTypeInsn(NEW, lambdaWrapperClassInternalName); mv.visitInsn(DUP); loadEnclosingClassInstance(); @@ -228,7 +225,7 @@ private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaClassNode loadSharedVariables(syntheticLambdaMethodNode); List constructorNodeList = - lambdaClassNode.getDeclaredConstructors().stream() + lambdaWrapperClassNode.getDeclaredConstructors().stream() .filter(e -> Boolean.TRUE.equals(e.getNodeMetaData(IS_GENERATED_CONSTRUCTOR))) .collect(Collectors.toList()); @@ -237,10 +234,10 @@ private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaClassNode } ConstructorNode constructorNode = constructorNodeList.get(0); - Parameter[] lambdaClassConstructorParameters = constructorNode.getParameters(); - mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, INIT, BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaClassConstructorParameters), lambdaClassNode.isInterface()); + Parameter[] lambdaWrapperClassConstructorParameters = constructorNode.getParameters(); + mv.visitMethodInsn(INVOKESPECIAL, lambdaWrapperClassInternalName, INIT, BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaWrapperClassConstructorParameters), lambdaWrapperClassNode.isInterface()); OperandStack operandStack = controller.getOperandStack(); - operandStack.replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length); + operandStack.replace(ClassHelper.LAMBDA_TYPE, lambdaWrapperClassConstructorParameters.length); BytecodeVariable variable = controller.getCompileStack().defineVariable(new VariableExpression(LAMBDA_WRAPPER, ClassHelper.LAMBDA_TYPE), false); operandStack.storeVar(variable); diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index a37228477ab..4688e3b1b77 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -133,6 +133,7 @@ import static org.codehaus.groovy.ast.ClassHelper.GSTRING_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Iterator_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.LAMBDA_TYPE; import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE; import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE; @@ -4098,6 +4099,10 @@ protected List findMethod( if (receiver.isInterface()) { collectAllInterfaceMethodsByName(receiver, name, methods); methods.addAll(OBJECT_TYPE.getMethods(name)); + + if (!receiver.getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty()) { + methods.addAll(LAMBDA_TYPE.getDeclaredMethods("call")); + } } // TODO: investigate the trait exclusion a bit further, needed otherwise // CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 212ec11eaa4..1a0b1ab6afd 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -405,16 +405,11 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav void testFunctionCall() { if (SKIP_ERRORS) return - /* FIXME - [Static type checking] - Cannot find matching method java.lang.Object#plus(int). Please check if the declared type is correct and if the method exists. - @ line 13, column 35. - assert 2 == (e -> e + 1)(1) - */ - assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors import java.util.stream.Stream + import java.util.function.Function @CompileStatic public class Test1 { @@ -423,7 +418,8 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } public static void p() { - assert 2 == (e -> e + 1)(1) + Function f = (Integer e) -> (Integer) (e + 1) // Casting is required... [Static type checking] - Incompatible generic argument types. Cannot assign java.util.function.Function to: java.util.function.Function + assert 2 == f(1) } } ''' From c24c0b7e6a67dcdf277207d4261cfa6f2b55031f Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 10:12:36 +0800 Subject: [PATCH 34/47] Support callable native lambda --- .../org/codehaus/groovy/ast/ClassHelper.java | 4 ++ .../groovy/classgen/asm/InvocationWriter.java | 29 ++++++++ .../asm/sc/StaticTypesLambdaWriter.java | 6 +- .../stc/StaticTypeCheckingVisitor.java | 10 ++- .../groovy/transform/stc/LambdaTest.groovy | 69 ++++++++++++++++++- 5 files changed, 108 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 9fc8643b4cd..ed984cea2bd 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -396,6 +396,10 @@ public static boolean isSAMType(ClassNode type) { return findSAM(type) != null; } + public static boolean isFunctionInterface(ClassNode type) { + return type.isInterface() && !type.getAnnotations(FunctionalInterface_Type).isEmpty() && isSAMType(type); + } + /** * Returns the single abstract method of a class node, if it is a SAM type, or null otherwise. * diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index 10a82ed2bde..0d8cc1b64b0 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -479,11 +479,25 @@ protected String getMethodName(Expression message) { return methodName; } + private boolean isFunctionInterfaceCall(MethodCallExpression call) { + ClassNode type = call.getObjectExpression().getType(); + + if (ClassHelper.isFunctionInterface(type)) { + return true; + } + + return false; + } + public void writeInvokeMethod(MethodCallExpression call) { if (isClosureCall(call)) { // let's invoke the closure method invokeClosure(call.getArguments(), call.getMethodAsString()); } else { + if (isFunctionInterfaceCall(call)) { + call = transformToRealMethodCall(call); + } + boolean isSuperMethodCall = usesSuper(call); MethodCallerMultiAdapter adapter = invokeMethod; if (isSuperMethodCall && call.isSafe()) { @@ -498,6 +512,21 @@ public void writeInvokeMethod(MethodCallExpression call) { } } + private MethodCallExpression transformToRealMethodCall(MethodCallExpression call) { + ClassNode type = call.getObjectExpression().getType(); + final MethodNode methodNode = ClassHelper.findSAM(type); + + call = (MethodCallExpression) call.transformExpression(expression -> { + if (!(expression instanceof ConstantExpression)) { + return expression; + } + + return new ConstantExpression(methodNode.getName()); + }); + call.setMethodTarget(methodNode); + return call; + } + private boolean isClosureCall(MethodCallExpression call) { // are we a local variable? // it should not be an explicitly "this" qualified method call 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 7f44c90864e..2d877ec46d1 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 @@ -102,7 +102,7 @@ public void writeLambda(LambdaExpression expression) { .filter(MethodNode::isAbstract) .collect(Collectors.toList()); - if (!(isFunctionInterface(lambdaType) && abstractMethodNodeList.size() == 1)) { + if (!(ClassHelper.isFunctionInterface(lambdaType.redirect()) && abstractMethodNodeList.size() == 1)) { // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; @@ -302,10 +302,6 @@ private String createMethodDescriptor(MethodNode abstractMethodNode) { ); } - private boolean isFunctionInterface(ClassNode parameterType) { - return parameterType.redirect().isInterface() && !parameterType.redirect().getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty(); - } - public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) { ClassNode lambdaClass = lambdaClassMap.get(expression); if (lambdaClass == null) { diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 4688e3b1b77..d2e9bb2f945 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -133,7 +133,6 @@ import static org.codehaus.groovy.ast.ClassHelper.GSTRING_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Iterator_TYPE; -import static org.codehaus.groovy.ast.ClassHelper.LAMBDA_TYPE; import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE; import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE; @@ -4100,8 +4099,13 @@ protected List findMethod( collectAllInterfaceMethodsByName(receiver, name, methods); methods.addAll(OBJECT_TYPE.getMethods(name)); - if (!receiver.getAnnotations(ClassHelper.FunctionalInterface_Type).isEmpty()) { - methods.addAll(LAMBDA_TYPE.getDeclaredMethods("call")); + if (ClassHelper.isFunctionInterface(receiver)) { + MethodNode sam = ClassHelper.findSAM(receiver); + MethodNode callMethodNode = new MethodNode("call", sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); + callMethodNode.setDeclaringClass(sam.getDeclaringClass()); + callMethodNode.setSourcePosition(sam); + + methods.addAll(Collections.singletonList(callMethodNode)); } } // TODO: investigate the trait exclusion a bit further, needed otherwise diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 1a0b1ab6afd..560ab7760af 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -403,8 +403,6 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav } void testFunctionCall() { - if (SKIP_ERRORS) return - assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -425,6 +423,73 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + void testFunctionCall2() { + 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) { + new Test1().p(); + } + + public void p() { + Function f = (Integer e) -> (Integer) (e + 1) // Casting is required... [Static type checking] - Incompatible generic argument types. Cannot assign java.util.function.Function to: java.util.function.Function + assert 2 == f(1) + } + } + ''' + } + + void testConsumerCall() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + import java.util.function.Consumer + + @CompileStatic + public class Test1 { + public static void main(String[] args) { + p(); + } + + public static void p() { + int r = 1 + Consumer c = (Integer e) -> { r += e } + c(2) + assert 3 == r + } + } + ''' + } + + void testConsumerCall2() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + import java.util.function.Consumer + + @CompileStatic + public class Test1 { + public static void main(String[] args) { + new Test1().p(); + } + + public void p() { + int r = 1 + Consumer c = (Integer e) -> { r += e } + c(2) + assert 3 == r + } + } + ''' + } + void testFunctionWithUpdatingLocalVariable() { assertScript ''' import groovy.transform.CompileStatic From a4de62c89b7c9d7b269771e44061144af21c5b2c Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 11:02:45 +0800 Subject: [PATCH 35/47] Check the method name of callable lambda --- .../codehaus/groovy/classgen/asm/InvocationWriter.java | 4 ++++ .../groovy/classgen/asm/sc/StaticTypesLambdaWriter.java | 9 ++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index 0d8cc1b64b0..b9e1011e8c3 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -480,6 +480,10 @@ protected String getMethodName(Expression message) { } private boolean isFunctionInterfaceCall(MethodCallExpression call) { + if (!"call".equals(call.getMethodAsString())) { + return false; + } + ClassNode type = call.getObjectExpression().getType(); if (ClassHelper.isFunctionInterface(type)) { 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 2d877ec46d1..96a07d8fc72 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 @@ -97,18 +97,13 @@ public WriterController makeController(final WriterController normalController) public void writeLambda(LambdaExpression expression) { ClassNode lambdaType = getLambdaType(expression); - List abstractMethodNodeList = - lambdaType.redirect().getMethods().stream() - .filter(MethodNode::isAbstract) - .collect(Collectors.toList()); - - if (!(ClassHelper.isFunctionInterface(lambdaType.redirect()) && abstractMethodNodeList.size() == 1)) { + if (!ClassHelper.isFunctionInterface(lambdaType.redirect())) { // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; } - MethodNode abstractMethodNode = abstractMethodNodeList.get(0); + MethodNode abstractMethodNode = ClassHelper.findSAM(lambdaType.redirect()); String abstractMethodDesc = createMethodDescriptor(abstractMethodNode); ClassNode classNode = controller.getClassNode(); From 2823724cdf44eb3b35b771c7ca8ef3646b9a1693 Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 13:04:10 +0800 Subject: [PATCH 36/47] Disambiguate methods by name --- .../groovy/classgen/asm/InvocationWriter.java | 12 ++++++++-- .../stc/StaticTypeCheckingVisitor.java | 24 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index b9e1011e8c3..bfb9f7d4bf5 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -484,9 +484,17 @@ private boolean isFunctionInterfaceCall(MethodCallExpression call) { return false; } - ClassNode type = call.getObjectExpression().getType(); + Expression objectExpression = call.getObjectExpression(); + + if (null == objectExpression) { + return false; + } + + if (AsmClassGenerator.isThisExpression(objectExpression)) { + return false; + } - if (ClassHelper.isFunctionInterface(type)) { + if (ClassHelper.isFunctionInterface(objectExpression.getType())) { return true; } diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index d2e9bb2f945..2e4dc2e566c 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -3164,6 +3164,8 @@ public void visitMethodCallExpression(MethodCallExpression call) { addCategoryMethodCallError(call); } mn = disambiguateMethods(mn, chosenReceiver!=null?chosenReceiver.getType():null, args, call); + + if (mn.size() == 1) { MethodNode directMethodCallCandidate = mn.get(0); if (call.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) == null && @@ -3958,7 +3960,7 @@ protected MethodNode findMethodOrFail( return null; } - private List disambiguateMethods(List methods, ClassNode receiver, ClassNode[] argTypes, final Expression expr) { + private List disambiguateMethods(List methods, ClassNode receiver, ClassNode[] argTypes, final Expression call) { if (methods.size()>1 && receiver!=null && argTypes!=null) { List filteredWithGenerics = new LinkedList(); for (MethodNode methodNode : methods) { @@ -3969,8 +3971,26 @@ private List disambiguateMethods(List methods, ClassNode if (filteredWithGenerics.size()==1) { return filteredWithGenerics; } - methods = extension.handleAmbiguousMethods(methods, expr); + methods = extension.handleAmbiguousMethods(methods, call); } + + if (methods.size() > 1) { + if (call instanceof MethodCall) { + List methodNodeList = new LinkedList<>(); + + String methodName = ((MethodCall) call).getMethodAsString(); + + for (MethodNode methodNode : methods) { + if (!methodNode.getName().equals(methodName)) { + continue; + } + methodNodeList.add(methodNode); + } + + methods = methodNodeList; + } + } + return methods; } From 552e01039aaad874a1409f4029df132817c7b0bb Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 16:35:37 +0800 Subject: [PATCH 37/47] Remove `Lambda` class --- src/main/groovy/groovy/lang/Lambda.java | 79 ------------------- .../org/codehaus/groovy/ast/ClassHelper.java | 7 +- .../asm/sc/StaticTypesLambdaWriter.java | 45 +++++------ 3 files changed, 22 insertions(+), 109 deletions(-) delete mode 100644 src/main/groovy/groovy/lang/Lambda.java diff --git a/src/main/groovy/groovy/lang/Lambda.java b/src/main/groovy/groovy/lang/Lambda.java deleted file mode 100644 index 6cf2a637ec8..00000000000 --- a/src/main/groovy/groovy/lang/Lambda.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package groovy.lang; - -import org.apache.groovy.internal.util.UncheckedThrow; -import org.codehaus.groovy.GroovyBugError; -import org.codehaus.groovy.runtime.InvokerInvocationException; - -import static org.codehaus.groovy.classgen.asm.sc.StaticTypesLambdaWriter.SAM_NAME; - -/** - * Represents any lambda object in Groovy. - * - * @since 3.0.0 - */ -public abstract class Lambda extends Closure { - private Object lambdaObject; - - public Lambda(Object owner, Object thisObject) { - super(owner, thisObject); - } - - /** - * Constructor used when the "this" object for the Lambda is null. - * This is rarely the case in normal Groovy usage. - * - * @param owner the Lambda owner - */ - public Lambda(Object owner) { - super(owner); - } - - @Override - public V call(Object... args) { - String methodName; - try { - methodName = (String) this.getClass().getField(SAM_NAME).get(lambdaObject); - } catch (IllegalAccessException e) { - throw new GroovyBugError("Failed to access field " + SAM_NAME + " of " + this.getClass(), e); - } catch (NoSuchFieldException e) { - throw new GroovyBugError("Failed to find field " + SAM_NAME + " in " + this.getClass(), e); - } - - try { - return (V) getMetaClass().invokeMethod(lambdaObject, methodName, args); - } catch (InvokerInvocationException e) { - UncheckedThrow.rethrow(e.getCause()); - return null; // unreachable statement - } catch (Exception e) { - return (V) throwRuntimeException(e); - } - } - - public Object getLambdaObject() { - return lambdaObject; - } - - public void setLambdaObject(Object lambdaObject) { - this.lambdaObject = lambdaObject; - } - -} diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index ed984cea2bd..5f5fc3f9194 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -24,7 +24,6 @@ import groovy.lang.GroovyInterceptable; import groovy.lang.GroovyObject; import groovy.lang.GroovyObjectSupport; -import groovy.lang.Lambda; import groovy.lang.MetaClass; import groovy.lang.Range; import groovy.lang.Reference; @@ -60,7 +59,7 @@ public class ClassHelper { private static final Class[] classes = new Class[]{ Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE, - Closure.class, Lambda.class, GString.class, List.class, Map.class, Range.class, + Closure.class, GString.class, List.class, Map.class, Range.class, Pattern.class, Script.class, String.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Double.class, Float.class, BigDecimal.class, BigInteger.class, @@ -76,7 +75,7 @@ public class ClassHelper { public static final ClassNode DYNAMIC_TYPE = makeCached(Object.class), OBJECT_TYPE = DYNAMIC_TYPE, VOID_TYPE = makeCached(Void.TYPE), - CLOSURE_TYPE = makeCached(Closure.class), LAMBDA_TYPE = makeCached(Lambda.class), + CLOSURE_TYPE = makeCached(Closure.class), GSTRING_TYPE = makeCached(GString.class), LIST_TYPE = makeWithoutCaching(List.class), MAP_TYPE = makeWithoutCaching(Map.class), RANGE_TYPE = makeCached(Range.class), PATTERN_TYPE = makeCached(Pattern.class), STRING_TYPE = makeCached(String.class), @@ -116,7 +115,7 @@ public class ClassHelper { OBJECT_TYPE, boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE, int_TYPE, long_TYPE, double_TYPE, float_TYPE, - VOID_TYPE, CLOSURE_TYPE, LAMBDA_TYPE, GSTRING_TYPE, + VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE, LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE, SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE, Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE, 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 96a07d8fc72..35d22692ddc 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 @@ -61,7 +61,6 @@ import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.NEW; /** @@ -111,7 +110,7 @@ public void writeLambda(LambdaExpression expression) { ClassNode lambdaWrapperClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode); MethodNode syntheticLambdaMethodNode = lambdaWrapperClassNode.getMethods(DO_CALL).get(0); - BytecodeVariable lambdaWrapperVariable = newGroovyLambdaWrapperAndLoad(lambdaWrapperClassNode, syntheticLambdaMethodNode); + newGroovyLambdaWrapperAndLoad(lambdaWrapperClassNode, syntheticLambdaMethodNode); loadEnclosingClassInstance(); @@ -145,8 +144,6 @@ public void writeLambda(LambdaExpression expression) { (Ljava/lang/Integer;)Ljava/lang/Object; ] DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION - DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION - DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION DUP ALOAD 0 SWAP @@ -159,31 +156,27 @@ public void writeLambda(LambdaExpression expression) { ALOAD 1 POP POP - POP - POP */ mv.visitInsn(DUP); - mv.visitInsn(DUP); - mv.visitInsn(DUP); + /* +org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: +General error during class generation: size==0 + +java.lang.ArrayIndexOutOfBoundsException: size==0 + at org.codehaus.groovy.classgen.asm.OperandStack.getTopOperand(OperandStack.java:693) + at org.codehaus.groovy.classgen.asm.BinaryExpressionHelper.evaluateEqual(BinaryExpressionHelper.java:397) + at org.codehaus.groovy.classgen.asm.sc.StaticTypesBinaryExpressionMultiTypeDispatcher.evaluateEqual(StaticTypesBinaryExpressionMultiTypeDispatcher.java:179) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitDeclarationExpression(AsmClassGenerator.java:694) + at org.codehaus.groovy.ast.expr.DeclarationExpression.visit(DeclarationExpression.java:89) + at org.codehaus.groovy.classgen.asm.StatementWriter.writeExpressionStatement(StatementWriter.java:633) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitExpressionStatement(AsmClassGenerator.java:681) + at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42) + */ +// operandStack.pop(); } - - saveLambdaObjectInWrapper(lambdaWrapperVariable); - operandStack.replace(lambdaType.redirect(), 2); - } - - private void saveLambdaObjectInWrapper(BytecodeVariable lambdaWrapperVariable) { - MethodVisitor mv = controller.getMethodVisitor(); - OperandStack operandStack = controller.getOperandStack(); - - mv.visitInsn(DUP); - operandStack.loadOrStoreVariable(lambdaWrapperVariable, false); - operandStack.swap(); - - mv.visitMethodInsn( - INVOKEVIRTUAL, "groovy/lang/Lambda", "setLambdaObject", "(Ljava/lang/Object;)V", false); } private ClassNode getLambdaType(LambdaExpression expression) { @@ -232,9 +225,9 @@ private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaWrapperCl Parameter[] lambdaWrapperClassConstructorParameters = constructorNode.getParameters(); mv.visitMethodInsn(INVOKESPECIAL, lambdaWrapperClassInternalName, INIT, BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaWrapperClassConstructorParameters), lambdaWrapperClassNode.isInterface()); OperandStack operandStack = controller.getOperandStack(); - operandStack.replace(ClassHelper.LAMBDA_TYPE, lambdaWrapperClassConstructorParameters.length); + operandStack.replace(ClassHelper.CLOSURE_TYPE, lambdaWrapperClassConstructorParameters.length); - BytecodeVariable variable = controller.getCompileStack().defineVariable(new VariableExpression(LAMBDA_WRAPPER, ClassHelper.LAMBDA_TYPE), false); + BytecodeVariable variable = controller.getCompileStack().defineVariable(new VariableExpression(LAMBDA_WRAPPER, ClassHelper.CLOSURE_TYPE), false); operandStack.storeVar(variable); operandStack.loadOrStoreVariable(variable, false); @@ -315,7 +308,7 @@ protected ClassNode createLambdaClass(LambdaExpression expression, int mods, Met String name = genLambdaClassName(); boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass(); - InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.LAMBDA_TYPE.getPlainNodeReference()); + InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.CLOSURE_TYPE.getPlainNodeReference()); answer.setEnclosingMethod(controller.getMethodNode()); answer.setSynthetic(true); answer.setUsingGenerics(outerClass.isUsingGenerics()); From dbcac60f64f921cd5e8ee02c4af126c9a6d8b883 Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 16:38:43 +0800 Subject: [PATCH 38/47] Remove unnecessary check of `FunctionalInterface` --- src/main/java/org/codehaus/groovy/ast/ClassHelper.java | 4 ---- .../org/codehaus/groovy/classgen/asm/InvocationWriter.java | 2 +- .../groovy/classgen/asm/sc/StaticTypesLambdaWriter.java | 2 +- .../groovy/transform/stc/StaticTypeCheckingVisitor.java | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 5f5fc3f9194..93658d734ad 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -395,10 +395,6 @@ public static boolean isSAMType(ClassNode type) { return findSAM(type) != null; } - public static boolean isFunctionInterface(ClassNode type) { - return type.isInterface() && !type.getAnnotations(FunctionalInterface_Type).isEmpty() && isSAMType(type); - } - /** * Returns the single abstract method of a class node, if it is a SAM type, or null otherwise. * diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index bfb9f7d4bf5..bc56b5a668a 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -494,7 +494,7 @@ private boolean isFunctionInterfaceCall(MethodCallExpression call) { return false; } - if (ClassHelper.isFunctionInterface(objectExpression.getType())) { + if (ClassHelper.isSAMType(objectExpression.getType())) { return true; } 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 35d22692ddc..a1a797c595d 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 @@ -96,7 +96,7 @@ public WriterController makeController(final WriterController normalController) public void writeLambda(LambdaExpression expression) { ClassNode lambdaType = getLambdaType(expression); - if (!ClassHelper.isFunctionInterface(lambdaType.redirect())) { + if (!ClassHelper.isSAMType(lambdaType.redirect())) { // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 2e4dc2e566c..dcb3ab68c16 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -4119,7 +4119,7 @@ protected List findMethod( collectAllInterfaceMethodsByName(receiver, name, methods); methods.addAll(OBJECT_TYPE.getMethods(name)); - if (ClassHelper.isFunctionInterface(receiver)) { + if (ClassHelper.isSAMType(receiver)) { MethodNode sam = ClassHelper.findSAM(receiver); MethodNode callMethodNode = new MethodNode("call", sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); callMethodNode.setDeclaringClass(sam.getDeclaringClass()); From 37dc8454315615d8f02609d50c0a5ce935e36cab Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 17:17:10 +0800 Subject: [PATCH 39/47] Make `CorrectAccessedVariableVisitor` extends `CodeVisitorSupport` --- .../groovy/classgen/asm/ClosureWriter.java | 10 +---- .../asm/sc/StaticTypesLambdaWriter.java | 17 ++++++- .../groovy/transform/stc/LambdaTest.groovy | 44 +++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) 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 343b714c93a..7c445992425 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java @@ -19,9 +19,9 @@ package org.codehaus.groovy.classgen.asm; 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.CodeVisitorSupport; import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.InnerClassNode; @@ -44,7 +44,6 @@ 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; @@ -332,7 +331,7 @@ private String genClosureClassName() { + controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode); } - protected static class CorrectAccessedVariableVisitor extends ClassCodeVisitorSupport { + protected static class CorrectAccessedVariableVisitor extends CodeVisitorSupport { private InnerClassNode icn; public CorrectAccessedVariableVisitor(InnerClassNode icn) { @@ -350,11 +349,6 @@ public void visitVariableExpression(VariableExpression expression) { expression.setAccessedVariable(fn); } } - - @Override - protected SourceUnit getSourceUnit() { - return null; - } } private static void correctAccessedVariable(final InnerClassNode closureClass, ClosureExpression ce) { 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 a1a797c595d..60f8f7dd227 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,6 +20,7 @@ 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; @@ -39,6 +40,7 @@ import org.codehaus.groovy.classgen.asm.OperandStack; import org.codehaus.groovy.classgen.asm.WriterController; import org.codehaus.groovy.classgen.asm.WriterControllerFactory; +import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.transform.stc.StaticTypesMarker; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; @@ -410,14 +412,20 @@ protected ClassNode createClosureClass(final ClosureExpression expression, final return staticTypesClosureWriter.createClosureClass(expression, mods); } - private static final class TransformationVisitor extends CorrectAccessedVariableVisitor { + private static final class TransformationVisitor extends ClassCodeVisitorSupport { + private CorrectAccessedVariableVisitor correctAccessedVariableVisitor; private Parameter enclosingThisParameter; public TransformationVisitor(InnerClassNode icn, Parameter enclosingThisParameter) { - super(icn); + correctAccessedVariableVisitor = new CorrectAccessedVariableVisitor(icn); this.enclosingThisParameter = enclosingThisParameter; } + @Override + public void visitVariableExpression(VariableExpression expression) { + correctAccessedVariableVisitor.visitVariableExpression(expression); + } + @Override public void visitMethodCallExpression(MethodCallExpression call) { if (!call.getMethodTarget().isStatic()) { @@ -437,5 +445,10 @@ public void visitMethodCallExpression(MethodCallExpression call) { super.visitMethodCallExpression(call); } + + @Override + protected SourceUnit getSourceUnit() { + return null; + } } } diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 560ab7760af..9bf97742368 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -444,6 +444,27 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + void testFunctionCall3() { + 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 f = (Integer e) -> (Integer) (e + 1) // Casting is required... [Static type checking] - Incompatible generic argument types. Cannot assign java.util.function.Function to: java.util.function.Function + assert 2 == f.apply(1) + } + } + ''' + } + void testConsumerCall() { assertScript ''' import groovy.transform.CompileStatic @@ -490,6 +511,29 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + void testConsumerCall3() { + assertScript ''' + import groovy.transform.CompileStatic + import java.util.stream.Collectors + import java.util.stream.Stream + import java.util.function.Consumer + + @CompileStatic + public class Test1 { + public static void main(String[] args) { + p(); + } + + public static void p() { + int r = 1 + Consumer c = (Integer e) -> { r += e } + c.accept(2) + assert 3 == r + } + } + ''' + } + void testFunctionWithUpdatingLocalVariable() { assertScript ''' import groovy.transform.CompileStatic From 9f51d3b3efa0c891b3bfba6ac23e684597caf6d3 Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 17:47:02 +0800 Subject: [PATCH 40/47] Refine STC of `call` method --- .../transform/stc/StaticTypeCheckingVisitor.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index dcb3ab68c16..e9ccffdbc04 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -281,13 +281,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { public static final MethodNode CLOSURE_CALL_ONE_ARG; public static final MethodNode CLOSURE_CALL_VARGS; + public static final String CALL = "call"; + static { // Cache closure call methods - CLOSURE_CALL_NO_ARG = CLOSURE_TYPE.getDeclaredMethod("call", Parameter.EMPTY_ARRAY); - CLOSURE_CALL_ONE_ARG = CLOSURE_TYPE.getDeclaredMethod("call", new Parameter[]{ + CLOSURE_CALL_NO_ARG = CLOSURE_TYPE.getDeclaredMethod(CALL, Parameter.EMPTY_ARRAY); + CLOSURE_CALL_ONE_ARG = CLOSURE_TYPE.getDeclaredMethod(CALL, new Parameter[]{ new Parameter(OBJECT_TYPE, "arg") }); - CLOSURE_CALL_VARGS = CLOSURE_TYPE.getDeclaredMethod("call", new Parameter[]{ + CLOSURE_CALL_VARGS = CLOSURE_TYPE.getDeclaredMethod(CALL, new Parameter[]{ new Parameter(OBJECT_TYPE.makeArray(), "args") }); } @@ -3404,7 +3406,7 @@ protected void storeTargetMethod(final Expression call, final MethodNode directM } protected boolean isClosureCall(final String name, final Expression objectExpression, final Expression arguments) { - if (objectExpression instanceof ClosureExpression && ("call".equals(name)||"doCall".equals(name))) return true; + if (objectExpression instanceof ClosureExpression && (CALL.equals(name)||"doCall".equals(name))) return true; if (objectExpression == VariableExpression.THIS_EXPRESSION) { FieldNode fieldNode = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name); if (fieldNode != null) { @@ -3414,7 +3416,7 @@ protected boolean isClosureCall(final String name, final Expression objectExpres } } } else { - if (!"call".equals(name) && !"doCall".equals(name)) return false; + if (!CALL.equals(name) && !"doCall".equals(name)) return false; } return (getType(objectExpression).equals(CLOSURE_TYPE)); } @@ -4119,9 +4121,9 @@ protected List findMethod( collectAllInterfaceMethodsByName(receiver, name, methods); methods.addAll(OBJECT_TYPE.getMethods(name)); - if (ClassHelper.isSAMType(receiver)) { + if (CALL.equals(name) && ClassHelper.isSAMType(receiver)) { MethodNode sam = ClassHelper.findSAM(receiver); - MethodNode callMethodNode = new MethodNode("call", sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); + MethodNode callMethodNode = new MethodNode(CALL, sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); callMethodNode.setDeclaringClass(sam.getDeclaringClass()); callMethodNode.setSourcePosition(sam); From 91448dd1e9ded947543eb6f415ba2b3cb727e774 Mon Sep 17 00:00:00 2001 From: sunlan Date: Wed, 31 Jan 2018 18:28:53 +0800 Subject: [PATCH 41/47] Remove the unnecessary method `getClassVisitor` --- .../java/org/codehaus/groovy/classgen/AsmClassGenerator.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java index 7721260d4bb..f658c72f0f7 100644 --- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java +++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java @@ -2181,7 +2181,4 @@ public boolean addInnerClass(ClassNode innerClass) { return innerClasses.add(innerClass); } - public ClassVisitor getClassVisitor() { - return cv; - } } From 13eed82a7873052e763e8ecda33d417441ff2e8d Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 2 Feb 2018 11:07:23 +0800 Subject: [PATCH 42/47] Refine the check of functional interface, which must be an interface --- .../org/codehaus/groovy/ast/ClassHelper.java | 6 +++ .../groovy/classgen/asm/InvocationWriter.java | 2 +- .../asm/sc/StaticTypesLambdaWriter.java | 2 +- .../stc/StaticTypeCheckingVisitor.java | 2 +- .../groovy/transform/stc/LambdaTest.groovy | 50 +++++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 93658d734ad..7ce72a32c2e 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -395,6 +395,12 @@ public static boolean isSAMType(ClassNode type) { return findSAM(type) != null; } + public static boolean isFunctionalInterface(ClassNode type) { + // Functional interface must be an interface at first, or the following exception will occur: + // java.lang.invoke.LambdaConversionException: Functional interface SamCallable is not an interface + return type.isInterface() && isSAMType(type); + } + /** * Returns the single abstract method of a class node, if it is a SAM type, or null otherwise. * diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index bc56b5a668a..6ab3fd942bc 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -494,7 +494,7 @@ private boolean isFunctionInterfaceCall(MethodCallExpression call) { return false; } - if (ClassHelper.isSAMType(objectExpression.getType())) { + if (ClassHelper.isFunctionalInterface(objectExpression.getType())) { return true; } 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 60f8f7dd227..ab9d0f3a5fb 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 @@ -98,7 +98,7 @@ public WriterController makeController(final WriterController normalController) public void writeLambda(LambdaExpression expression) { ClassNode lambdaType = getLambdaType(expression); - if (!ClassHelper.isSAMType(lambdaType.redirect())) { + if (!ClassHelper.isFunctionalInterface(lambdaType.redirect())) { // if the parameter type is not real FunctionInterface, generate the default bytecode, which is actually a closure super.writeLambda(expression); return; diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index e9ccffdbc04..c3f01481535 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -4121,7 +4121,7 @@ protected List findMethod( collectAllInterfaceMethodsByName(receiver, name, methods); methods.addAll(OBJECT_TYPE.getMethods(name)); - if (CALL.equals(name) && ClassHelper.isSAMType(receiver)) { + if (CALL.equals(name) && ClassHelper.isFunctionalInterface(receiver)) { MethodNode sam = ClassHelper.findSAM(receiver); MethodNode callMethodNode = new MethodNode(CALL, sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); callMethodNode.setDeclaringClass(sam.getDeclaringClass()); diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index 9bf97742368..dc47754ca96 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -534,6 +534,56 @@ TestScript0.groovy: 14: [Static type checking] - Cannot find matching method jav ''' } + void testSamCall() { + 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() { + SamCallable c = (int e) -> e + assert 1 == c(1) + } + } + + @CompileStatic + interface SamCallable { + int call(int p); + } + ''' + } + + void testSamCall2() { + 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() { + SamCallable c = (int e) -> e // This is actually a closure(not a native lambda), because "Functional interface SamCallable is not an interface" + assert 1 == c(1) + } + } + + @CompileStatic + abstract class SamCallable { + abstract int call(int p); + } + ''' + } + void testFunctionWithUpdatingLocalVariable() { assertScript ''' import groovy.transform.CompileStatic From 9a21c9eb16d53b071a18d1eb3806c51afe332c2b Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 2 Feb 2018 17:44:20 +0800 Subject: [PATCH 43/47] GROOVY-8445: Static analysis does not work properly when coercing Closure to FunctionInterface --- .../org/codehaus/groovy/ast/ClassHelper.java | 9 ++++++++- .../groovy/ast/tools/GenericsUtils.java | 7 +++++++ .../stc/StaticTypeCheckingSupport.java | 8 ++++++++ .../transform/stc/MethodCallsSTCTest.groovy | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java index 7ce72a32c2e..57537fa46d4 100644 --- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java +++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java @@ -410,7 +410,14 @@ public static boolean isFunctionalInterface(ClassNode type) { public static MethodNode findSAM(ClassNode type) { if (!Modifier.isAbstract(type.getModifiers())) return null; if (type.isInterface()) { - List methods = type.getMethods(); + List methods; + if (type.isInterface()) { + // e.g. BinaryOperator extends BiFunction, BinaryOperator contains no abstract method, but it is really a SAM + methods = type.redirect().getAllDeclaredMethods(); + } else { + methods = type.getMethods(); + } + MethodNode found = null; for (MethodNode mi : methods) { // ignore methods, that are not abstract and from Object diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java index 653f3270cca..a7cf7c045d1 100644 --- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -444,6 +444,13 @@ public static void extractSuperClassGenerics(ClassNode type, ClassNode target, M // have first to find matching super class or interface Map genSpec = createGenericsSpec(type); ClassNode superClass = ClassHelper.getNextSuperClass(type,target); + + if (superClass == null) { + if (ClassHelper.isPrimitiveType(type)) { + superClass = ClassHelper.getNextSuperClass(ClassHelper.getWrapper(type), target); + } + } + if (superClass!=null){ ClassNode corrected = GenericsUtils.correctToGenericsSpecRecurse(genSpec, superClass); extractSuperClassGenerics(corrected, target, spec); diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index f83e32c97c7..126cc419705 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -20,6 +20,7 @@ package org.codehaus.groovy.transform.stc; import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.MethodNode; @@ -1768,6 +1769,13 @@ static void extractGenericsConnections(Map connections, Cl // have first to find matching super class or interface Map genSpec = GenericsUtils.createGenericsSpec(type); ClassNode superClass = getNextSuperClass(type,target); + + if (superClass == null) { + if (ClassHelper.isPrimitiveType(type)) { + superClass = ClassHelper.getNextSuperClass(ClassHelper.getWrapper(type), target); + } + } + if (superClass!=null){ ClassNode corrected; if (missesGenericsTypes(type)) { diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy index 74c20955fe7..4afc665066f 100644 --- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy +++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy @@ -1238,6 +1238,26 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-8445 + void testGroovy8445() { + 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() { + assert 13 == Stream.of(1, 2, 3).reduce(7, {Integer r, Integer e -> r + e}); + } + } + ''' + } + static class MyMethodCallTestClass { static int mul(int... args) { args.toList().inject(1) { x,y -> x*y } } From 68df3e2b0be065e9823db33a7d6f9f9c4f29a55f Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 2 Feb 2018 18:15:08 +0800 Subject: [PATCH 44/47] Add more tests blocked by GROOVY-8445 --- .../groovy/transform/stc/LambdaTest.groovy | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index dc47754ca96..a24e3c1691d 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -56,28 +56,7 @@ class LambdaTest extends GroovyTestCase { ''' } - /** - * Depends on fixing https://issues.apache.org/jira/browse/GROOVY-8445 - */ void testBinaryOperator() { - if (SKIP_ERRORS) return - - // the test can pass only in dynamic mode now, it can not pass static type checking... - - /* FIXME -TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.util.stream.Stream#reduce(int, groovy.lang.Closure). Please check if the declared type is correct and if the method exists. - @ line 13, column 30. - assert 13 == [1, 2, 3].stream().reduce(7, (r, e) -> r + e); - ^ - -TestScript0.groovy: 13: [Static type checking] - Cannot find matching method java.lang.Object#plus(java.lang.Object). Please check if the declared type is correct and if the method exists. - @ line 13, column 69. - (1, 2, 3).reduce(7, (r, e) -> r + e); - ^ - -2 errors - */ - assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors @@ -90,7 +69,7 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav } public static void p() { - assert 13 == [1, 2, 3].stream().reduce(7, (r, e) -> r + e); + assert 13 == [1, 2, 3].stream().reduce(7, (Integer r, Integer e) -> r + e); } } ''' @@ -137,26 +116,7 @@ TestScript0.groovy: 13: [Static type checking] - Cannot find matching method jav ''' } - /** - * Depends on fixing https://issues.apache.org/jira/browse/GROOVY-8445 - */ void testUnaryOperator() { - if (SKIP_ERRORS) return - - /* FIXME -TestScript0.groovy: 14: [Static type checking] - Cannot find matching method java.util.List#replaceAll(groovy.lang.Closure). Please check if the declared type is correct and if the method exists. - @ line 14, column 17. - list.replaceAll(e -> e + 10) - ^ - -TestScript0.groovy: 14: [Static type checking] - Cannot find matching method java.lang.Object#plus(int). Please check if the declared type is correct and if the method exists. - @ line 14, column 38. - list.replaceAll(e -> e + 10) - ^ - -2 errors - */ - assertScript ''' import groovy.transform.CompileStatic import java.util.stream.Collectors From 4a06c9fc3792820be2f94be9f2c4c3a2f153df96 Mon Sep 17 00:00:00 2001 From: sunlan Date: Fri, 2 Feb 2018 18:30:38 +0800 Subject: [PATCH 45/47] Workaround for IncompatibleClassChangeError(only occurred in Java9) java.lang.IncompatibleClassChangeError: Method java.util.stream.Stream.of([Ljava/lang/Object;)Ljava/util/stream/Stream; must be InterfaceMethodref constant (cherry picked from commit 2bd2407) --- src/test/groovy/transform/stc/MethodCallsSTCTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy index 4afc665066f..24373791e5e 100644 --- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy +++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy @@ -1252,7 +1252,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase { } public static void p() { - assert 13 == Stream.of(1, 2, 3).reduce(7, {Integer r, Integer e -> r + e}); + assert 13 == [1, 2, 3].stream().reduce(7, {Integer r, Integer e -> r + e}); } } ''' From dffe45b547d6f324a47ae10c5420fb5c670ea907 Mon Sep 17 00:00:00 2001 From: Daniel Sun Date: Sat, 3 Feb 2018 20:17:28 +0800 Subject: [PATCH 46/47] Trivial refactoring: remove duplicated code (cherry picked from commit 04e3099) --- .../groovy/ast/tools/GenericsUtils.java | 44 +++++++++++-------- .../stc/StaticTypeCheckingSupport.java | 39 ++++++++-------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java index a7cf7c045d1..642f90cd73a 100644 --- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -38,7 +38,6 @@ import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.syntax.ParserException; import org.codehaus.groovy.syntax.Reduction; -import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport; import java.io.StringReader; import java.util.ArrayList; @@ -48,6 +47,9 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getCorrectedClassNode; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf; + /** * Utility methods to deal with generic types. * @@ -56,6 +58,7 @@ */ public class GenericsUtils { public static final GenericsType[] EMPTY_GENERICS_ARRAY = new GenericsType[0]; + public static final String JAVA_LANG_OBJECT = "java.lang.Object"; /** * Given a parameterized type and a generic type information, aligns actual type parameters. For example, if a @@ -213,7 +216,7 @@ public static ClassNode parameterizeType(final ClassNode hint, final ClassNode t } return target; } - if (!target.equals(hint) && StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(target, hint)) { + if (!target.equals(hint) && implementsInterfaceOrIsSubclassOf(target, hint)) { ClassNode nextSuperClass = ClassHelper.getNextSuperClass(target, hint); if (!hint.equals(nextSuperClass)) { Map genericsSpec = createGenericsSpec(hint); @@ -425,40 +428,45 @@ public static Map addMethodGenerics(MethodNode current, Map spec) { + public static void extractSuperClassGenerics(ClassNode type, ClassNode target, Map spec) { // TODO: this method is very similar to StaticTypesCheckingSupport#extractGenericsConnections, // but operates on ClassNodes instead of GenericsType - if (target==null || type==target) return; + if (target == null || type == target) return; if (type.isArray() && target.isArray()) { extractSuperClassGenerics(type.getComponentType(), target.getComponentType(), spec); - } else if (type.isArray() && target.getName().equals("java.lang.Object")) { + } else if (type.isArray() && JAVA_LANG_OBJECT.equals(target.getName())) { // Object is superclass of arrays but no generics involved - } else if (target.isGenericsPlaceHolder() || type.equals(target) || !StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(type, target)) { + } else if (target.isGenericsPlaceHolder() || type.equals(target) || !implementsInterfaceOrIsSubclassOf(type, target)) { // structural match route if (target.isGenericsPlaceHolder()) { - spec.put(target.getGenericsTypes()[0].getName(),type); + spec.put(target.getGenericsTypes()[0].getName(), type); } else { extractSuperClassGenerics(type.getGenericsTypes(), target.getGenericsTypes(), spec); } } else { // have first to find matching super class or interface - Map genSpec = createGenericsSpec(type); - ClassNode superClass = ClassHelper.getNextSuperClass(type,target); + ClassNode superClass = getSuperClass(type, target); - if (superClass == null) { - if (ClassHelper.isPrimitiveType(type)) { - superClass = ClassHelper.getNextSuperClass(ClassHelper.getWrapper(type), target); - } - } - - if (superClass!=null){ - ClassNode corrected = GenericsUtils.correctToGenericsSpecRecurse(genSpec, superClass); + if (superClass != null) { + ClassNode corrected = getCorrectedClassNode(type, superClass, false); extractSuperClassGenerics(corrected, target, spec); } else { // if we reach here, we have an unhandled case - throw new GroovyBugError("The type "+type+" seems not to normally extend "+target+". Sorry, I cannot handle this."); + throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this."); + } + } + } + + public static ClassNode getSuperClass(ClassNode type, ClassNode target) { + ClassNode superClass = ClassHelper.getNextSuperClass(type, target); + + if (superClass == null) { + if (ClassHelper.isPrimitiveType(type)) { + superClass = ClassHelper.getNextSuperClass(ClassHelper.getWrapper(type), target); } } + + return superClass; } private static void extractSuperClassGenerics(GenericsType[] usage, GenericsType[] declaration, Map spec) { diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index 126cc419705..5f0e983126e 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -20,7 +20,6 @@ package org.codehaus.groovy.transform.stc; import org.codehaus.groovy.GroovyBugError; -import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.MethodNode; @@ -96,7 +95,6 @@ import static org.codehaus.groovy.ast.ClassHelper.char_TYPE; import static org.codehaus.groovy.ast.ClassHelper.double_TYPE; import static org.codehaus.groovy.ast.ClassHelper.float_TYPE; -import static org.codehaus.groovy.ast.ClassHelper.getNextSuperClass; import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper; import static org.codehaus.groovy.ast.ClassHelper.getWrapper; import static org.codehaus.groovy.ast.ClassHelper.int_TYPE; @@ -108,6 +106,7 @@ import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching; import static org.codehaus.groovy.ast.ClassHelper.short_TYPE; import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE; +import static org.codehaus.groovy.ast.tools.GenericsUtils.getSuperClass; import static org.codehaus.groovy.syntax.Types.ASSIGN; import static org.codehaus.groovy.syntax.Types.BITWISE_AND; import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL; @@ -1754,43 +1753,41 @@ private static boolean equalIncludingGenerics(ClassNode orig, ClassNode copy) { * Should the target not have any generics this method does nothing. */ static void extractGenericsConnections(Map connections, ClassNode type, ClassNode target) { - if (target==null || type==target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return; - if (type == null || type==UNKNOWN_PARAMETER_TYPE) return; + if (target == null || type == target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return; + if (type == null || type == UNKNOWN_PARAMETER_TYPE) return; if (type.isArray() && target.isArray()) { extractGenericsConnections(connections, type.getComponentType(), target.getComponentType()); } else if (target.isGenericsPlaceHolder() || type.equals(target) || !implementsInterfaceOrIsSubclassOf(type, target)) { // structural match route if (target.isGenericsPlaceHolder()) { - connections.put(target.getGenericsTypes()[0].getName(),new GenericsType(type)); + connections.put(target.getGenericsTypes()[0].getName(), new GenericsType(type)); } else { extractGenericsConnections(connections, type.getGenericsTypes(), target.getGenericsTypes()); } } else { // have first to find matching super class or interface - Map genSpec = GenericsUtils.createGenericsSpec(type); - ClassNode superClass = getNextSuperClass(type,target); + ClassNode superClass = getSuperClass(type, target); - if (superClass == null) { - if (ClassHelper.isPrimitiveType(type)) { - superClass = ClassHelper.getNextSuperClass(ClassHelper.getWrapper(type), target); - } - } - - if (superClass!=null){ - ClassNode corrected; - if (missesGenericsTypes(type)) { - corrected = superClass.getPlainNodeReference(); - } else { - corrected = GenericsUtils.correctToGenericsSpecRecurse(genSpec, superClass); - } + if (superClass != null) { + ClassNode corrected = getCorrectedClassNode(type, superClass, true); extractGenericsConnections(connections, corrected, target); } else { // if we reach here, we have an unhandled case - throw new GroovyBugError("The type "+type+" seems not to normally extend "+target+". Sorry, I cannot handle this."); + throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this."); } } } + public static ClassNode getCorrectedClassNode(ClassNode type, ClassNode superClass, boolean handlingGenerics) { + ClassNode corrected; + if (handlingGenerics && missesGenericsTypes(type)) { + corrected = superClass.getPlainNodeReference(); + } else { + corrected = GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(type), superClass); + } + return corrected; + } + private static void extractGenericsConnections(Map connections, GenericsType[] usage, GenericsType[] declaration) { // if declaration does not provide generics, there is no connection to make if (usage==null || declaration==null || declaration.length==0) return; From 005aa6c0594ccacc6d0322f6633d74ee54ad18eb Mon Sep 17 00:00:00 2001 From: sunlan Date: Tue, 6 Feb 2018 08:32:22 +0800 Subject: [PATCH 47/47] Update the comment on the workaround --- .../asm/sc/StaticTypesLambdaWriter.java | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) 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 ab9d0f3a5fb..ab45f9ce5cd 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 @@ -130,53 +130,42 @@ public void writeLambda(LambdaExpression expression) { if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) { // FIXME declaring variable whose initial value is a lambda, e.g. `Function f = (Integer e) -> 'a' + e` - // Groovy will `POP` multiple times...(expecting `POP` only once), as a hack, `DUP` to duplicate the element of operand stack: + // Groovy will `POP` automatically, use `DUP` to duplicate the element of operand stack: /* - L2 - ASTORE 0 - ALOAD 0 - ACONST_NULL 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/Object;, - (Ljava/lang/Integer;)Ljava/lang/Object; + 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 - DUP + DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION + ASTORE 0 + L2 ALOAD 0 - SWAP - INVOKEVIRTUAL groovy/lang/Lambda.setLambdaObject (Ljava/lang/Object;)V - LDC Ljava/util/function/Function;.class - INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; - CHECKCAST java/util/function/Function - ASTORE 1 - L3 - ALOAD 1 - POP + POP <-------------- Since operand stack is not empty, the `POP`s are issued by `controller.getOperandStack().popDownTo(mark);` in the method `org.codehaus.groovy.classgen.asm.StatementWriter.writeExpressionStatement`, but when we try to `operandStack.pop();` instead of `mv.visitInsn(DUP);`, we will get AIOOBE... POP */ mv.visitInsn(DUP); /* -org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: -General error during class generation: size==0 - -java.lang.ArrayIndexOutOfBoundsException: size==0 - at org.codehaus.groovy.classgen.asm.OperandStack.getTopOperand(OperandStack.java:693) - at org.codehaus.groovy.classgen.asm.BinaryExpressionHelper.evaluateEqual(BinaryExpressionHelper.java:397) - at org.codehaus.groovy.classgen.asm.sc.StaticTypesBinaryExpressionMultiTypeDispatcher.evaluateEqual(StaticTypesBinaryExpressionMultiTypeDispatcher.java:179) - at org.codehaus.groovy.classgen.AsmClassGenerator.visitDeclarationExpression(AsmClassGenerator.java:694) - at org.codehaus.groovy.ast.expr.DeclarationExpression.visit(DeclarationExpression.java:89) - at org.codehaus.groovy.classgen.asm.StatementWriter.writeExpressionStatement(StatementWriter.java:633) - at org.codehaus.groovy.classgen.AsmClassGenerator.visitExpressionStatement(AsmClassGenerator.java:681) - at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42) - */ -// operandStack.pop(); + org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: + General error during class generation: size==0 + + java.lang.ArrayIndexOutOfBoundsException: size==0 + at org.codehaus.groovy.classgen.asm.OperandStack.getTopOperand(OperandStack.java:693) + at org.codehaus.groovy.classgen.asm.BinaryExpressionHelper.evaluateEqual(BinaryExpressionHelper.java:397) + at org.codehaus.groovy.classgen.asm.sc.StaticTypesBinaryExpressionMultiTypeDispatcher.evaluateEqual(StaticTypesBinaryExpressionMultiTypeDispatcher.java:179) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitDeclarationExpression(AsmClassGenerator.java:694) + at org.codehaus.groovy.ast.expr.DeclarationExpression.visit(DeclarationExpression.java:89) + at org.codehaus.groovy.classgen.asm.StatementWriter.writeExpressionStatement(StatementWriter.java:633) + at org.codehaus.groovy.classgen.AsmClassGenerator.visitExpressionStatement(AsmClassGenerator.java:681) + at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42) + */ + // operandStack.pop(); } }