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 <[email protected]> Authored: Mon Jan 29 09:44:22 2018 +0800 Committer: sunlan <[email protected]> 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);
