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
}