You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2018/02/18 05:02:39 UTC

[2/3] groovy git commit: GROOVY-8466: Support native lambda in static compilation mode(closes #654)

GROOVY-8466: Support native lambda in static compilation mode(closes #654)

(cherry picked from commit 302be69)


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

Branch: refs/heads/GROOVY_2_6_X
Commit: af33df82f5feaff93a49ab7f9c17c061d4c0c25e
Parents: bb9809d
Author: danielsun1106 <re...@hotmail.com>
Authored: Sun Feb 18 02:32:14 2018 +0800
Committer: danielsun1106 <re...@hotmail.com>
Committed: Sun Feb 18 12:18:00 2018 +0800

----------------------------------------------------------------------
 .../org/codehaus/groovy/ast/ClassHelper.java    |  17 +-
 .../codehaus/groovy/ast/GroovyCodeVisitor.java  |   5 +
 .../java/org/codehaus/groovy/ast/Parameter.java |   4 +
 .../groovy/ast/expr/LambdaExpression.java       |   5 +
 .../groovy/classgen/AsmClassGenerator.java      |   7 +
 .../groovy/classgen/GeneratorContext.java       |  14 +-
 .../groovy/classgen/asm/ClosureWriter.java      | 120 ++--
 .../classgen/asm/DelegatingController.java      |   7 +-
 .../groovy/classgen/asm/InvocationWriter.java   |  41 ++
 .../groovy/classgen/asm/LambdaWriter.java       |  36 +
 .../groovy/classgen/asm/WriterController.java   |   6 +
 .../classgen/asm/sc/StaticInvocationWriter.java |   4 +
 ...ypesBinaryExpressionMultiTypeDispatcher.java |  14 +-
 .../asm/sc/StaticTypesLambdaWriter.java         | 443 ++++++++++++
 .../asm/sc/StaticTypesWriterController.java     |  11 +
 .../groovy/runtime/GeneratedLambda.java         |  28 +
 .../stc/StaticTypeCheckingVisitor.java          |  45 +-
 .../groovy/transform/stc/StaticTypesMarker.java |   4 +-
 src/test/groovy/transform/stc/LambdaTest.groovy | 717 +++++++++++++++++++
 19 files changed, 1467 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
index 3626242..d8ce0d7 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
@@ -30,6 +30,7 @@ import groovy.lang.Reference;
 import groovy.lang.Script;
 import org.codehaus.groovy.classgen.asm.util.TypeDescriptionUtil;
 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;
@@ -64,7 +65,7 @@ public class ClassHelper {
             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[]{
@@ -74,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),
             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),
@@ -100,9 +102,12 @@ 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),
+            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);
@@ -118,7 +123,7 @@ public class ClassHelper {
             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
     };
 
@@ -383,6 +388,12 @@ public class ClassHelper {
         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.
      *

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java b/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java
index 12787c0..db793cc 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.DeclarationExpression;
 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);

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/ast/Parameter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java
index 1b22128..b2ddb09 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 class Parameter extends AnnotatedNode implements Variable {
     public void setModifiers(int modifiers) {
         this.modifiers = modifiers;
     }
+
+    public Expression getDefaultValue() {
+        return defaultValue;
+    }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java
----------------------------------------------------------------------
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 f4f1001..ab30513 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 class LambdaExpression extends ClosureExpression {
         super(parameters, code);
     }
 
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitLambdaExpression(this);
+    }
+
     @Override
     public String getText() {
         String paramText = AstToTextHelper.getParametersText(this.getParameters());

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 8123e83..bfb70f7 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.EmptyExpression;
 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 class AsmClassGenerator extends ClassGenerator {
         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
      */
@@ -2228,4 +2234,5 @@ public class AsmClassGenerator extends ClassGenerator {
         mn.getUnit().addGeneratedInnerClass((InnerClassNode)innerClass);
         return innerClasses.add(innerClass);
     }
+
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java
index 8b8a510..dd318bd 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 class GeneratorContext {
     }
 
     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 class GeneratorContext {
             if (enclosingClass.isDerivedFrom(ClassHelper.CLOSURE_TYPE)) {
                 methodName = "";
             } else {
-                methodName = "_"+encodeAsValidClassName(methodName);
+                methodName = "_" + encodeAsValidClassName(methodName);
             }
         }
-        return methodName + "_closure" + closureClassIdx++;
+
+        return methodName + "_" + classifier + closureClassIdx++;
     }
 
 

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index af305ba..7c44599 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -19,7 +19,6 @@
 package org.codehaus.groovy.classgen.asm;
 
 import org.codehaus.groovy.GroovyBugError;
-import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.CodeVisitorSupport;
@@ -65,6 +64,9 @@ import static org.objectweb.asm.Opcodes.NEW;
 
 public class ClosureWriter {
 
+    public static final String OUTER_INSTANCE = "_outerInstance";
+    public static final String THIS_OBJECT = "_thisObject";
+
     protected interface UseExistingReference {}
 
     private final Map<Expression,ClassNode> closureClassMap;
@@ -184,9 +186,8 @@ public class ClosureWriter {
     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 = genClosureClassName();
         boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
 
         Parameter[] parameters = expression.getParameters();
@@ -249,23 +250,31 @@ public class ClosureWriter {
         }
 
         // 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
+        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();
@@ -292,35 +301,58 @@ public class ClosureWriter {
                         new ReturnStatement(fieldExp));
             }
         }
+    }
 
-        Parameter[] params = new Parameter[2 + localVariableParams.length];
-        params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance");
-        params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject");
-        System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
+    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(OUTER_INSTANCE);
+        outer.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(outer);
+        VariableExpression thisObject = new VariableExpression(THIS_OBJECT);
+        thisObject.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(thisObject);
+        TupleExpression conArgs = new TupleExpression(outer, thisObject);
+        block.addStatement(
+                new ExpressionStatement(
+                        new ConstructorCallExpression(
+                                ClassNode.SUPER,
+                                conArgs)));
+        return block;
+    }
 
-        ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block);
-        sn.setSourcePosition(expression);
-        
-        correctAccessedVariable(answer,expression);
-        
-        return answer;
+    private String genClosureClassName() {
+        ClassNode classNode = controller.getClassNode();
+        ClassNode outerClass = controller.getOutermostClass();
+        MethodNode methodNode = controller.getMethodNode();
+
+        return classNode.getName() + "$"
+                + controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode);
+    }
+
+    protected static class CorrectAccessedVariableVisitor extends CodeVisitorSupport {
+        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);
+            }
+        }
     }
 
     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);
     }
 
     /*
@@ -330,7 +362,7 @@ public class ClosureWriter {
      * 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());

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
----------------------------------------------------------------------
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 22acbaa..3553a82 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 class DelegatingController extends WriterController {
     @Override
     public ClosureWriter getClosureWriter() {
         return delegationController.getClosureWriter();
-    } 
+    }
+
+    @Override
+    public LambdaWriter getLambdaWriter() {
+        return delegationController.getLambdaWriter();
+    }
     
     @Override
     public CompileStack getCompileStack() {

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
----------------------------------------------------------------------
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 10a82ed..6ab3fd9 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,37 @@ public class InvocationWriter {
         return methodName;
     }
 
+    private boolean isFunctionInterfaceCall(MethodCallExpression call) {
+        if (!"call".equals(call.getMethodAsString())) {
+            return false;
+        }
+
+        Expression objectExpression = call.getObjectExpression();
+
+        if (null == objectExpression) {
+            return false;
+        }
+
+        if (AsmClassGenerator.isThisExpression(objectExpression)) {
+            return false;
+        }
+
+        if (ClassHelper.isFunctionalInterface(objectExpression.getType())) {
+            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 +524,21 @@ public class InvocationWriter {
         }
     }
 
+    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

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java
----------------------------------------------------------------------
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 0000000..6c0b53d
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/LambdaWriter.java
@@ -0,0 +1,36 @@
+/*
+ *  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.Parameter;
+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);
+    }
+
+    protected Parameter[] getLambdaSharedVariables(LambdaExpression expression) {
+        return super.getClosureSharedVariables(expression);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java
----------------------------------------------------------------------
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 952814c..2e4cd29 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 class WriterController {
         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();
@@ -196,6 +198,10 @@ public class WriterController {
         return closureWriter;
     }
 
+    public LambdaWriter getLambdaWriter() {
+        return lambdaWriter;
+    }
+
     public ClassVisitor getCv() {
         return cv;
     }

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
----------------------------------------------------------------------
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 88b6d99..7705f91 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.CLOSURE_TYPE;
 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.PARAMETER_TYPE;
 import static org.objectweb.asm.Opcodes.ACONST_NULL;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.CHECKCAST;
@@ -438,6 +439,7 @@ public class StaticInvocationWriter extends InvocationWriter {
             // 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());
@@ -463,6 +465,7 @@ public class StaticInvocationWriter extends InvocationWriter {
         } 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());
@@ -494,6 +497,7 @@ public class StaticInvocationWriter extends InvocationWriter {
             }
             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());

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
index 9088d8b..c327ba1 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -33,6 +33,7 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
 import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.LambdaExpression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.PropertyExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
@@ -71,6 +72,8 @@ import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
 import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_ADD_METHOD;
 import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CLASSNODE;
 import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CONSTRUCTOR;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_LAMBDA_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
 
 /**
  * A specialized version of the multi type binary expression dispatcher which is aware of static compilation.
@@ -134,8 +137,8 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
 
     @Override
     public void evaluateEqual(final BinaryExpression expression, final boolean defineVariable) {
+        Expression leftExpression = expression.getLeftExpression();
         if (!defineVariable) {
-            Expression leftExpression = expression.getLeftExpression();
             if (leftExpression instanceof PropertyExpression) {
                 PropertyExpression pexp = (PropertyExpression) leftExpression;
                 if (makeSetProperty(
@@ -147,10 +150,15 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
                         pexp.isImplicitThis(),
                         pexp instanceof AttributeExpression)) return;
             }
+        } else {
+            Expression rightExpression = expression.getRightExpression();
+            if (rightExpression instanceof LambdaExpression) {
+                rightExpression.putNodeMetaData(INFERRED_LAMBDA_TYPE, leftExpression.getNodeMetaData(INFERRED_TYPE));
+            }
         }
         // GROOVY-5620: Spread safe/Null safe operator on LHS is not supported
-        if (expression.getLeftExpression() instanceof PropertyExpression
-                && ((PropertyExpression) expression.getLeftExpression()).isSpreadSafe()
+        if (leftExpression instanceof PropertyExpression
+                && ((PropertyExpression) leftExpression).isSpreadSafe()
                 && StaticTypeCheckingSupport.isAssignment(expression.getOperation().getType())) {
             // rewrite it so that it can be statically compiled
             transformSpreadOnLHS(expression);

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
new file mode 100644
index 0000000..ab45f9c
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
@@ -0,0 +1,443 @@
+/*
+ *  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.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;
+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;
+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;
+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;
+
+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;
+
+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.NEW;
+
+/**
+ * Writer responsible for generating lambda classes in statically compiled mode.
+ */
+public class StaticTypesLambdaWriter extends LambdaWriter {
+    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";
+    public static final String INIT = "<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;
+    private final Map<Expression,ClassNode> 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
+    public void writeLambda(LambdaExpression expression) {
+        ClassNode lambdaType = getLambdaType(expression);
+
+        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;
+        }
+
+        MethodNode abstractMethodNode = ClassHelper.findSAM(lambdaType.redirect());
+        String abstractMethodDesc = createMethodDescriptor(abstractMethodNode);
+
+        ClassNode classNode = controller.getClassNode();
+        boolean isInterface = classNode.isInterface();
+        ClassNode lambdaWrapperClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode);
+        MethodNode syntheticLambdaMethodNode = lambdaWrapperClassNode.getMethods(DO_CALL).get(0);
+
+        newGroovyLambdaWrapperAndLoad(lambdaWrapperClassNode, syntheticLambdaMethodNode);
+
+        loadEnclosingClassInstance();
+
+
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+
+        mv.visitInvokeDynamicInsn(
+                abstractMethodNode.getName(),
+                createAbstractMethodDesc(lambdaType, lambdaWrapperClassNode),
+                createBootstrapMethod(isInterface),
+                createBootstrapMethodArguments(abstractMethodDesc, lambdaWrapperClassNode, syntheticLambdaMethodNode)
+        );
+        operandStack.replace(lambdaType.redirect(), 2);
+
+        if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) {
+            // FIXME declaring variable whose initial value is a lambda, e.g. `Function<Integer, String> f = (Integer e) -> 'a' + e`
+            //       Groovy will `POP` automatically, use `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           <-------------- 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();
+        }
+
+    }
+
+    private ClassNode getLambdaType(LambdaExpression expression) {
+        ClassNode type = expression.getNodeMetaData(PARAMETER_TYPE);
+
+        if (null == type) {
+            type = expression.getNodeMetaData(INFERRED_LAMBDA_TYPE);
+        }
+        return type;
+    }
+
+    private void loadEnclosingClassInstance() {
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+        CompileStack compileStack = controller.getCompileStack();
+
+        if (controller.isStaticMethod() || compileStack.isInSpecialConstructorCall()) {
+            operandStack.pushConstant(ConstantExpression.NULL);
+        } else {
+            mv.visitVarInsn(ALOAD, 0);
+            operandStack.push(controller.getClassNode());
+        }
+    }
+
+    private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaWrapperClassNode, MethodNode syntheticLambdaMethodNode) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        String lambdaWrapperClassInternalName = BytecodeHelper.getClassInternalName(lambdaWrapperClassNode);
+        mv.visitTypeInsn(NEW, lambdaWrapperClassInternalName);
+        mv.visitInsn(DUP);
+
+        loadEnclosingClassInstance();
+        loadEnclosingClassInstance();
+
+        loadSharedVariables(syntheticLambdaMethodNode);
+
+        List<ConstructorNode> constructorNodeList =
+                lambdaWrapperClassNode.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[] lambdaWrapperClassConstructorParameters = constructorNode.getParameters();
+        mv.visitMethodInsn(INVOKESPECIAL, lambdaWrapperClassInternalName, INIT, BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaWrapperClassConstructorParameters), lambdaWrapperClassNode.isInterface());
+        OperandStack operandStack = controller.getOperandStack();
+        operandStack.replace(ClassHelper.CLOSURE_TYPE, lambdaWrapperClassConstructorParameters.length);
+
+        BytecodeVariable variable = controller.getCompileStack().defineVariable(new VariableExpression(LAMBDA_WRAPPER, ClassHelper.CLOSURE_TYPE), false);
+        operandStack.storeVar(variable);
+
+        operandStack.loadOrStoreVariable(variable, false);
+
+        return variable;
+    }
+
+    private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) {
+        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);
+            }
+        }
+
+        return lambdaSharedVariableParameters;
+    }
+
+    private String createAbstractMethodDesc(ClassNode parameterType, ClassNode lambdaClassNode) {
+        List<Parameter> lambdaSharedVariableList = new LinkedList<>();
+
+        prependEnclosingThis(lambdaSharedVariableList);
+        prependParameter(lambdaSharedVariableList, LAMBDA_THIS, lambdaClassNode);
+
+        return BytecodeHelper.getMethodDescriptor(parameterType.redirect(), lambdaSharedVariableList.toArray(Parameter.EMPTY_ARRAY));
+    }
+
+    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;",
+                isInterface
+        );
+    }
+
+    private Object[] createBootstrapMethodArguments(String abstractMethodDesc, ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) {
+        return new Object[]{
+                Type.getType(abstractMethodDesc),
+                new Handle(
+                        Opcodes.H_INVOKEVIRTUAL,
+                        lambdaClassNode.getName(),
+                        syntheticLambdaMethodNode.getName(),
+                        BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode),
+                        lambdaClassNode.isInterface()
+                ),
+                Type.getType(BytecodeHelper.getMethodDescriptor(syntheticLambdaMethodNode.getReturnType(), syntheticLambdaMethodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE)))
+        };
+    }
+
+    private String createMethodDescriptor(MethodNode abstractMethodNode) {
+        return BytecodeHelper.getMethodDescriptor(
+                abstractMethodNode.getReturnType().getTypeClass(),
+                Arrays.stream(abstractMethodNode.getParameters())
+                        .map(e -> e.getType().getTypeClass())
+                        .toArray(Class[]::new)
+        );
+    }
+
+    public ClassNode getOrAddLambdaClass(LambdaExpression expression, int mods, MethodNode abstractMethodNode) {
+        ClassNode lambdaClass = lambdaClassMap.get(expression);
+        if (lambdaClass == null) {
+            lambdaClass = createLambdaClass(expression, mods, abstractMethodNode);
+            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, MethodNode abstractMethodNode) {
+        ClassNode outerClass = controller.getOutermostClass();
+        ClassNode classNode = controller.getClassNode();
+        String name = genLambdaClassName();
+        boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
+
+        InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.CLOSURE_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);
+        }
+
+        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);
+
+        addFieldsAndGettersForLocalVariables(answer, localVariableParameters);
+        ConstructorNode constructorNode = addConstructor(expression, localVariableParameters, answer, createBlockStatementForConstructor(expression));
+        constructorNode.putNodeMetaData(IS_GENERATED_CONSTRUCTOR, Boolean.TRUE);
+
+        Parameter enclosingThisParameter = syntheticLambdaMethodNode.getParameters()[0];
+        new TransformationVisitor(answer, enclosingThisParameter).visitMethod(syntheticLambdaMethodNode);
+
+        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 MethodNode addSyntheticLambdaMethodNode(LambdaExpression expression, InnerClassNode answer, MethodNode abstractMethodNode) {
+        Parameter[] parametersWithExactType = createParametersWithExactType(expression); // expression.getParameters();
+//        ClassNode returnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //abstractMethodNode.getReturnType();
+        Parameter[] localVariableParameters = getLambdaSharedVariables(expression);
+        removeInitialValues(localVariableParameters);
+
+        List<Parameter> methodParameterList = new LinkedList<Parameter>(Arrays.asList(parametersWithExactType));
+        prependEnclosingThis(methodParameterList);
+
+        MethodNode methodNode =
+                answer.addMethod(
+                        DO_CALL,
+                        Opcodes.ACC_PUBLIC,
+                        abstractMethodNode.getReturnType() /*ClassHelper.OBJECT_TYPE*/ /*returnType*/,
+                        methodParameterList.toArray(Parameter.EMPTY_ARRAY),
+                        ClassNode.EMPTY_ARRAY,
+                        expression.getCode()
+                );
+        methodNode.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
+        methodNode.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters);
+        methodNode.setSourcePosition(expression);
+
+        return methodNode;
+    }
+
+    private Parameter prependEnclosingThis(List<Parameter> methodParameterList) {
+        return prependParameter(methodParameterList, ENCLOSING_THIS, controller.getClassNode().getPlainNodeReference());
+    }
+
+    private Parameter prependParameter(List<Parameter> methodParameterList, String parameterName, ClassNode parameterType) {
+        Parameter parameter = new Parameter(parameterType, parameterName);
+
+        parameter.setOriginType(parameterType);
+        parameter.setClosureSharedVariable(false);
+
+        methodParameterList.add(0, parameter);
+
+        return parameter;
+    }
+
+    private Parameter[] createParametersWithExactType(LambdaExpression expression) {
+        Parameter[] parameters = expression.getParameters();
+        if (parameters == null) {
+            parameters = Parameter.EMPTY_ARRAY;
+        }
+
+        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);
+        }
+
+        return parameters;
+    }
+
+    @Override
+    protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) {
+        return staticTypesClosureWriter.createClosureClass(expression, mods);
+    }
+
+    private static final class TransformationVisitor extends ClassCodeVisitorSupport {
+        private CorrectAccessedVariableVisitor correctAccessedVariableVisitor;
+        private Parameter enclosingThisParameter;
+
+        public TransformationVisitor(InnerClassNode icn, Parameter enclosingThisParameter) {
+            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()) {
+                Expression objectExpression = call.getObjectExpression();
+
+                if (objectExpression instanceof VariableExpression) {
+                    VariableExpression originalObjectExpression = (VariableExpression) objectExpression;
+                    if (null == originalObjectExpression.getAccessedVariable()) {
+                        VariableExpression thisVariable = new VariableExpression(enclosingThisParameter);
+                        thisVariable.setSourcePosition(originalObjectExpression);
+
+                        call.setObjectExpression(thisVariable);
+                        call.setImplicitThis(false);
+                    }
+                }
+            }
+
+            super.visitMethodCallExpression(call);
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
----------------------------------------------------------------------
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 47ef3d4..15943e5 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.CallSiteWriter;
 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 class StaticTypesWriterController extends DelegatingController {
         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 class StaticTypesWriterController extends DelegatingController {
         }
         return super.getClosureWriter();
     }
+
+    @Override
+    public LambdaWriter getLambdaWriter() {
+        if (isInStaticallyCheckedMethod) {
+            return lambdaWriter;
+        }
+        return super.getLambdaWriter();
+    }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/runtime/GeneratedLambda.java
----------------------------------------------------------------------
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 0000000..0a74b99
--- /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 { }

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
----------------------------------------------------------------------
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 710c80f..28a7ca4 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")
         });
     }
@@ -3164,6 +3166,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         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 &&
@@ -3402,7 +3406,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     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) {
@@ -3412,7 +3416,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
             }
         } 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));
     }
@@ -3958,7 +3962,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return null;
     }
 
-    private List<MethodNode> disambiguateMethods(List<MethodNode> methods, ClassNode receiver, ClassNode[] argTypes, final Expression expr) {
+    private List<MethodNode> disambiguateMethods(List<MethodNode> methods, ClassNode receiver, ClassNode[] argTypes, final Expression call) {
         if (methods.size()>1 && receiver!=null && argTypes!=null) {
             List<MethodNode> filteredWithGenerics = new LinkedList<MethodNode>();
             for (MethodNode methodNode : methods) {
@@ -3969,8 +3973,26 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             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<MethodNode> 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;
     }
 
@@ -4098,6 +4120,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (receiver.isInterface()) {
                 collectAllInterfaceMethodsByName(receiver, name, methods);
                 methods.addAll(OBJECT_TYPE.getMethods(name));
+
+                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());
+                    callMethodNode.setSourcePosition(sam);
+
+                    methods.addAll(Collections.singletonList(callMethodNode));
+                }
             }
             // TODO: investigate the trait exclusion a bit further, needed otherwise
             // CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying

http://git-wip-us.apache.org/repos/asf/groovy/blob/af33df82/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
index 9785433..73eecad 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,7 @@ 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
+    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
 }