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/01/29 01:44:28 UTC
groovy git commit: Save lambda object in the wrapper to prepare
implementing callable lambda
Repository: groovy
Updated Branches:
refs/heads/native-lambda d53fb4f8a -> f02cf2123
Save lambda object in the wrapper to prepare implementing callable lambda
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/f02cf212
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/f02cf212
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/f02cf212
Branch: refs/heads/native-lambda
Commit: f02cf212369ae6987c4baa3020a2362f6971ee0f
Parents: d53fb4f
Author: sunlan <su...@apache.org>
Authored: Mon Jan 29 09:44:22 2018 +0800
Committer: sunlan <su...@apache.org>
Committed: Mon Jan 29 09:44:22 2018 +0800
----------------------------------------------------------------------
src/main/groovy/groovy/lang/Lambda.java | 37 ++++++++++
.../asm/sc/StaticTypesLambdaWriter.java | 72 +++++++++++++++++---
2 files changed, 99 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/f02cf212/src/main/groovy/groovy/lang/Lambda.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Lambda.java b/src/main/groovy/groovy/lang/Lambda.java
index 159566c..6cf2a63 100644
--- a/src/main/groovy/groovy/lang/Lambda.java
+++ b/src/main/groovy/groovy/lang/Lambda.java
@@ -19,12 +19,19 @@
package groovy.lang;
+import org.apache.groovy.internal.util.UncheckedThrow;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+
+import static org.codehaus.groovy.classgen.asm.sc.StaticTypesLambdaWriter.SAM_NAME;
+
/**
* Represents any lambda object in Groovy.
*
* @since 3.0.0
*/
public abstract class Lambda<V> extends Closure<V> {
+ private Object lambdaObject;
public Lambda(Object owner, Object thisObject) {
super(owner, thisObject);
@@ -39,4 +46,34 @@ public abstract class Lambda<V> extends Closure<V> {
public Lambda(Object owner) {
super(owner);
}
+
+ @Override
+ public V call(Object... args) {
+ String methodName;
+ try {
+ methodName = (String) this.getClass().getField(SAM_NAME).get(lambdaObject);
+ } catch (IllegalAccessException e) {
+ throw new GroovyBugError("Failed to access field " + SAM_NAME + " of " + this.getClass(), e);
+ } catch (NoSuchFieldException e) {
+ throw new GroovyBugError("Failed to find field " + SAM_NAME + " in " + this.getClass(), e);
+ }
+
+ try {
+ return (V) getMetaClass().invokeMethod(lambdaObject, methodName, args);
+ } catch (InvokerInvocationException e) {
+ UncheckedThrow.rethrow(e.getCause());
+ return null; // unreachable statement
+ } catch (Exception e) {
+ return (V) throwRuntimeException(e);
+ }
+ }
+
+ public Object getLambdaObject() {
+ return lambdaObject;
+ }
+
+ public void setLambdaObject(Object lambdaObject) {
+ this.lambdaObject = lambdaObject;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/f02cf212/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
index 899fd45..008d798 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
@@ -33,6 +33,7 @@ import org.codehaus.groovy.ast.expr.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;
@@ -53,11 +54,14 @@ 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.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.NEW;
/**
@@ -71,6 +75,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
private static final String LAMBDA_THIS = "__lambda_this";
public static final String INIT = "<init>";
public static final String IS_GENERATED_CONSTRUCTOR = "__IS_GENERATED_CONSTRUCTOR";
+ public static final String LAMBDA_WRAPPER = "__lambda_wrapper";
+ public static final String SAM_NAME = "__SAM_NAME";
private StaticTypesClosureWriter staticTypesClosureWriter;
private WriterController controller;
private WriterControllerFactory factory;
@@ -107,10 +113,10 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
ClassNode classNode = controller.getClassNode();
boolean isInterface = classNode.isInterface();
- ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0), abstractMethodNode);
+ ClassNode lambdaClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode);
MethodNode syntheticLambdaMethodNode = lambdaClassNode.getMethods(DO_CALL).get(0);
- newGroovyLambdaInstanceAndLoad(lambdaClassNode, syntheticLambdaMethodNode);
+ BytecodeVariable lambdaWrapperVariable = newGroovyLambdaWrapperAndLoad(lambdaClassNode, syntheticLambdaMethodNode);
loadEnclosingClassInstance();
@@ -128,28 +134,64 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
if (null != expression.getNodeMetaData(INFERRED_LAMBDA_TYPE)) {
// FIXME declaring variable whose initial value is a lambda, e.g. `Function<Integer, String> f = (Integer e) -> 'a' + e`
- // Groovy will `POP` twice...(expecting `POP` only once), as a hack, `DUP` to duplicate the element of operand stack:
+ // Groovy will `POP` multiple times...(expecting `POP` only once), as a hack, `DUP` to duplicate the element of operand stack:
/*
+ L2
+ ASTORE 0
+ ALOAD 0
+ ACONST_NULL
INVOKEDYNAMIC apply(LTest1$_p_lambda1;LTest1;)Ljava/util/function/Function; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)Ljava/lang/Object;,
// handle kind 0x5 : INVOKEVIRTUAL
- Test1$_p_lambda1.doCall(LTest1;Ljava/lang/Integer;)Ljava/lang/String;,
- (Ljava/lang/Integer;)Ljava/lang/String;
+ Test1$_p_lambda1.doCall(LTest1;Ljava/lang/Integer;)Ljava/lang/Object;,
+ (Ljava/lang/Integer;)Ljava/lang/Object;
]
- DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION
- ASTORE 0
- L2
+ DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION
+ DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION
+ DUP <-------------- FIXME ADDED ON PURPOSE, WE SHOULD REMOVE IT AFTER FIND BETTER SOLUTION
+ DUP
ALOAD 0
+ SWAP
+ INVOKEVIRTUAL groovy/lang/Lambda.setLambdaObject (Ljava/lang/Object;)V
+ LDC Ljava/util/function/Function;.class
+ INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+ CHECKCAST java/util/function/Function
+ ASTORE 1
+ L3
+ ALOAD 1
+ POP
+ POP
POP
POP
*/
mv.visitInsn(DUP);
+
+ mv.visitInsn(DUP);
+ mv.visitInsn(DUP);
}
+
+ saveLambdaObjectInWrapper(lambdaType, lambdaWrapperVariable);
+ }
+
+ private void saveLambdaObjectInWrapper(ClassNode lambdaType, BytecodeVariable lambdaWrapperVariable) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ OperandStack operandStack = controller.getOperandStack();
+
+ mv.visitInsn(DUP);
+ operandStack.push(lambdaType.redirect());
+ operandStack.loadOrStoreVariable(lambdaWrapperVariable, false);
+ operandStack.push(ClassHelper.LAMBDA_TYPE);
+ operandStack.swap();
+
+ mv.visitMethodInsn(
+ INVOKEVIRTUAL, "groovy/lang/Lambda", "setLambdaObject", "(Ljava/lang/Object;)V", false);
+
+ operandStack.remove(1);
}
private ClassNode getLambdaType(LambdaExpression expression) {
@@ -174,7 +216,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
}
}
- private void newGroovyLambdaInstanceAndLoad(ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) {
+ private BytecodeVariable newGroovyLambdaWrapperAndLoad(ClassNode lambdaClassNode, MethodNode syntheticLambdaMethodNode) {
MethodVisitor mv = controller.getMethodVisitor();
String lambdaClassInternalName = BytecodeHelper.getClassInternalName(lambdaClassNode);
mv.visitTypeInsn(NEW, lambdaClassInternalName);
@@ -197,7 +239,15 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
ConstructorNode constructorNode = constructorNodeList.get(0);
Parameter[] lambdaClassConstructorParameters = constructorNode.getParameters();
mv.visitMethodInsn(INVOKESPECIAL, lambdaClassInternalName, INIT, BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, lambdaClassConstructorParameters), lambdaClassNode.isInterface());
- controller.getOperandStack().replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length);
+ OperandStack operandStack = controller.getOperandStack();
+ operandStack.replace(ClassHelper.LAMBDA_TYPE, lambdaClassConstructorParameters.length);
+
+ BytecodeVariable variable = controller.getCompileStack().defineVariable(new VariableExpression(LAMBDA_WRAPPER, ClassHelper.LAMBDA_TYPE), false);
+ operandStack.storeVar(variable);
+
+ operandStack.loadOrStoreVariable(variable, false);
+
+ return variable;
}
private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) {
@@ -290,6 +340,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
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);