You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/12/04 21:48:35 UTC

[groovy] branch GROOVY_3_0_X updated (5e65f52 -> ba6e33e)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a change to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 5e65f52  workaround for gradle plugin bug
     new 8e3ae68  GROOVY-9320: Support serializable lambda expression
     new ba6e33e  GROOVY-9320: Add more tests (closes #1110)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/codehaus/groovy/ast/ClassHelper.java  |   4 +
 .../codehaus/groovy/ast/expr/LambdaExpression.java |   9 +
 .../asm/sc/AbstractFunctionalInterfaceWriter.java  |  33 +-
 .../classgen/asm/sc/StaticTypesLambdaWriter.java   | 133 +++++-
 ...StaticTypesMethodReferenceExpressionWriter.java |   4 +-
 src/test/groovy/transform/stc/LambdaTest.groovy    | 490 +++++++++++++++++++++
 6 files changed, 652 insertions(+), 21 deletions(-)


[groovy] 01/02: GROOVY-9320: Support serializable lambda expression

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 8e3ae6823b2e8d2a2adae18e5b61f1bb6eab55b9
Author: Daniel.Sun <re...@hotmail.com>
AuthorDate: Mon Dec 2 14:18:24 2019 +0800

    GROOVY-9320: Support serializable lambda expression
---
 .../java/org/codehaus/groovy/ast/ClassHelper.java  |   4 +
 .../codehaus/groovy/ast/expr/LambdaExpression.java |   9 +
 .../asm/sc/AbstractFunctionalInterfaceWriter.java  |  33 +-
 .../classgen/asm/sc/StaticTypesLambdaWriter.java   | 133 +++++++-
 ...StaticTypesMethodReferenceExpressionWriter.java |   4 +-
 src/test/groovy/transform/stc/LambdaTest.groovy    | 334 +++++++++++++++++++++
 6 files changed, 496 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
index b5f7fac..bbca52a 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
@@ -57,8 +57,10 @@ import org.codehaus.groovy.util.ReferenceBundle;
 import org.codehaus.groovy.vmplugin.VMPluginFactory;
 import org.objectweb.asm.Opcodes;
 
+import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
+import java.lang.invoke.SerializedLambda;
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Modifier;
 import java.math.BigDecimal;
@@ -100,6 +102,7 @@ public class ClassHelper {
             DYNAMIC_TYPE = makeCached(Object.class),
             OBJECT_TYPE = DYNAMIC_TYPE,
             CLOSURE_TYPE = makeCached(Closure.class),
+            SERIALIZEDLAMBDA_TYPE = makeCached(SerializedLambda .class),
             GSTRING_TYPE = makeCached(GString.class),
             RANGE_TYPE = makeCached(Range.class),
             PATTERN_TYPE = makeCached(Pattern.class),
@@ -134,6 +137,7 @@ public class ClassHelper {
             Annotation_TYPE = makeCached(Annotation.class),
             ELEMENT_TYPE_TYPE = makeCached(ElementType.class),
             AUTOCLOSEABLE_TYPE = makeCached(AutoCloseable.class),
+            SERIALIZABLE_TYPE = makeCached(Serializable.class),
 
             // uncached constants
             MAP_TYPE = makeWithoutCaching(Map.class),
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 080e450..64cb989 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/LambdaExpression.java
@@ -36,6 +36,7 @@ import org.codehaus.groovy.ast.stmt.Statement;
  * </pre>
  */
 public class LambdaExpression extends ClosureExpression {
+    private boolean serializable;
     public LambdaExpression(Parameter[] parameters, Statement code) {
         super(parameters, code);
     }
@@ -53,4 +54,12 @@ public class LambdaExpression extends ClosureExpression {
             return "() -> { ... }";
         }
     }
+
+    public boolean isSerializable() {
+        return serializable;
+    }
+
+    public void setSerializable(boolean serializable) {
+        this.serializable = serializable;
+    }
 }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
index fe03ad7..e425cc0 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
@@ -29,6 +29,7 @@ import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 
 import java.util.Arrays;
+import java.util.LinkedList;
 import java.util.List;
 
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
@@ -60,7 +61,17 @@ public interface AbstractFunctionalInterfaceWriter {
         );
     }
 
-    default Handle createBootstrapMethod(boolean isInterface) {
+    default Handle createBootstrapMethod(boolean isInterface, boolean serializable) {
+        if (serializable) {
+            return new Handle(
+                    Opcodes.H_INVOKESTATIC,
+                    "java/lang/invoke/LambdaMetafactory",
+                    "altMetafactory",
+                    "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;",
+                    isInterface
+            );
+        }
+
         return new Handle(
                 Opcodes.H_INVOKESTATIC,
                 "java/lang/invoke/LambdaMetafactory",
@@ -70,20 +81,28 @@ public interface AbstractFunctionalInterfaceWriter {
         );
     }
 
-    default Object[] createBootstrapMethodArguments(String abstractMethodDesc, int insn, ClassNode methodOwnerClassNode, MethodNode methodNode) {
+    default Object[] createBootstrapMethodArguments(String abstractMethodDesc, int insn, ClassNode methodOwnerClassNode, MethodNode methodNode, boolean serializable) {
         Parameter[] parameters = methodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE);
+        List<Object> argumentList = new LinkedList<>();
 
-        return new Object[]{
-                Type.getType(abstractMethodDesc),
+        argumentList.add(Type.getType(abstractMethodDesc));
+        argumentList.add(
                 new Handle(
                         insn,
                         BytecodeHelper.getClassInternalName(methodOwnerClassNode.getName()),
                         methodNode.getName(),
                         BytecodeHelper.getMethodDescriptor(methodNode),
                         methodOwnerClassNode.isInterface()
-                ),
-                Type.getType(BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(), parameters))
-        };
+                )
+        );
+        argumentList.add(Type.getType(BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(), parameters)));
+
+        if (serializable) {
+            argumentList.add(5);
+            argumentList.add(0);
+        }
+
+        return argumentList.toArray();
     }
 
     default ClassNode convertParameterType(ClassNode parameterType, ClassNode inferredType) {
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 521bc2d..5005e33 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
@@ -27,12 +27,18 @@ 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.builder.AstStringCompiler;
+import org.codehaus.groovy.ast.expr.ClassExpression;
 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.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.BytecodeInstruction;
+import org.codehaus.groovy.classgen.BytecodeSequence;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
 import org.codehaus.groovy.classgen.asm.CompileStack;
 import org.codehaus.groovy.classgen.asm.LambdaWriter;
@@ -43,7 +49,6 @@ import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 
 import java.util.Arrays;
 import java.util.HashMap;
@@ -52,13 +57,24 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import static org.codehaus.groovy.ast.ClassHelper.SERIALIZABLE_TYPE;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
 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.CHECKCAST;
 import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 import static org.objectweb.asm.Opcodes.NEW;
 
 /**
@@ -99,18 +115,29 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
             return;
         }
 
+        boolean implementsSerializable = functionalInterfaceType.implementsInterface(SERIALIZABLE_TYPE);
+        expression.setSerializable(expression.isSerializable() || implementsSerializable);
+
         MethodNode abstractMethodNode = ClassHelper.findSAM(redirect);
         String abstractMethodDesc = createMethodDescriptor(abstractMethodNode);
 
         ClassNode classNode = controller.getClassNode();
+
         boolean isInterface = classNode.isInterface();
         ClassNode lambdaWrapperClassNode = getOrAddLambdaClass(expression, ACC_PUBLIC | ACC_FINAL | (isInterface ? ACC_STATIC : 0) | ACC_SYNTHETIC, abstractMethodNode);
         MethodNode syntheticLambdaMethodNode = lambdaWrapperClassNode.getMethods(DO_CALL).get(0);
 
-        newGroovyLambdaWrapperAndLoad(lambdaWrapperClassNode, syntheticLambdaMethodNode);
+        boolean canDeserialize = classNode.hasMethod(createDeserializeLambdaMethodName(lambdaWrapperClassNode), createDeserializeLambdaMethodParams());
 
-        loadEnclosingClassInstance();
+        if (!canDeserialize) {
+            if (expression.isSerializable()) {
+                addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaWrapperClassNode);
+                addDeserializeLambdaMethod();
+            }
 
+            newGroovyLambdaWrapperAndLoad(lambdaWrapperClassNode, expression);
+            loadEnclosingClassInstance();
+        }
 
         MethodVisitor mv = controller.getMethodVisitor();
         OperandStack operandStack = controller.getOperandStack();
@@ -118,12 +145,21 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         mv.visitInvokeDynamicInsn(
                 abstractMethodNode.getName(),
                 createAbstractMethodDesc(functionalInterfaceType, lambdaWrapperClassNode),
-                createBootstrapMethod(isInterface),
-                createBootstrapMethodArguments(abstractMethodDesc, Opcodes.H_INVOKEVIRTUAL, lambdaWrapperClassNode, syntheticLambdaMethodNode)
+                createBootstrapMethod(isInterface, expression.isSerializable()),
+                createBootstrapMethodArguments(abstractMethodDesc, H_INVOKEVIRTUAL, lambdaWrapperClassNode, syntheticLambdaMethodNode, expression.isSerializable())
         );
+
+        if (expression.isSerializable()) {
+            mv.visitTypeInsn(CHECKCAST, "java/io/Serializable");
+        }
+
         operandStack.replace(redirect, 2);
     }
 
+    private Parameter[] createDeserializeLambdaMethodParams() {
+        return new Parameter[]{new Parameter(ClassHelper.SERIALIZEDLAMBDA_TYPE, SERIALIZED_LAMBDA_PARAM_NAME)};
+    }
+
     private void loadEnclosingClassInstance() {
         MethodVisitor mv = controller.getMethodVisitor();
         OperandStack operandStack = controller.getOperandStack();
@@ -137,7 +173,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         }
     }
 
-    private void newGroovyLambdaWrapperAndLoad(ClassNode lambdaWrapperClassNode, MethodNode syntheticLambdaMethodNode) {
+    private void newGroovyLambdaWrapperAndLoad(ClassNode lambdaWrapperClassNode, LambdaExpression expression) {
         MethodVisitor mv = controller.getMethodVisitor();
         String lambdaWrapperClassInternalName = BytecodeHelper.getClassInternalName(lambdaWrapperClassNode);
         mv.visitTypeInsn(NEW, lambdaWrapperClassInternalName);
@@ -146,7 +182,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         loadEnclosingClassInstance();
         controller.getOperandStack().dup();
 
-        loadSharedVariables(syntheticLambdaMethodNode);
+        loadSharedVariables(expression);
 
         List<ConstructorNode> constructorNodeList =
                 lambdaWrapperClassNode.getDeclaredConstructors().stream()
@@ -164,8 +200,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         operandStack.replace(ClassHelper.CLOSURE_TYPE, lambdaWrapperClassConstructorParameters.length);
     }
 
-    private Parameter[] loadSharedVariables(MethodNode syntheticLambdaMethodNode) {
-        Parameter[] lambdaSharedVariableParameters = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
+    private Parameter[] loadSharedVariables(LambdaExpression expression) {
+        Parameter[] lambdaSharedVariableParameters = expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
         for (Parameter parameter : lambdaSharedVariableParameters) {
             String parameterName = parameter.getName();
             loadReference(parameterName, controller);
@@ -211,6 +247,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         answer.setUsingGenerics(outerClass.isUsingGenerics());
         answer.setSourcePosition(expression);
 
+        addSerialVersionUIDField(answer);
+
         if (staticMethodOrInStaticClass) {
             answer.setStaticClass(true);
         }
@@ -219,7 +257,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         }
 
         MethodNode syntheticLambdaMethodNode = addSyntheticLambdaMethodNode(expression, answer, abstractMethodNode);
-        Parameter[] localVariableParameters = syntheticLambdaMethodNode.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
+        Parameter[] localVariableParameters = expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
 
         addFieldsAndGettersForLocalVariables(answer, localVariableParameters);
         ConstructorNode constructorNode = addConstructor(expression, localVariableParameters, answer, createBlockStatementForConstructor(expression, outerClass, classNode));
@@ -231,6 +269,10 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         return answer;
     }
 
+    private void addSerialVersionUIDField(InnerClassNode answer) {
+        answer.addFieldFirst("serialVersionUID", ACC_PRIVATE | ACC_STATIC | ACC_FINAL, ClassHelper.long_TYPE, new ConstantExpression(-1L, true));
+    }
+
     private String genLambdaClassName() {
         ClassNode classNode = controller.getClassNode();
         ClassNode outerClass = controller.getOutermostClass();
@@ -252,14 +294,14 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         MethodNode methodNode =
                 answer.addMethod(
                         DO_CALL,
-                        Opcodes.ACC_PUBLIC,
+                        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);
+        expression.putNodeMetaData(LAMBDA_SHARED_VARIABLES, localVariableParameters);
         methodNode.setSourcePosition(expression);
 
         return methodNode;
@@ -292,6 +334,73 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         return parameters;
     }
 
+    private static final String SERIALIZED_LAMBDA_PARAM_NAME = "serializedLambda";
+    private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
+    private void addDeserializeLambdaMethod() {
+        ClassNode classNode = controller.getClassNode();
+        Parameter[] parameters = createDeserializeLambdaMethodParams();
+        if (classNode.hasMethod(DESERIALIZE_LAMBDA_METHOD_NAME, parameters)) {
+            return;
+        }
+        Statement code = block(
+                declS(localVarX("enclosingClass", ClassHelper.DYNAMIC_TYPE), new ClassExpression(classNode)),
+                ((BlockStatement) new AstStringCompiler().compile(
+                        "return enclosingClass" +
+                                ".getDeclaredMethod(\"\\$deserializeLambda_${serializedLambda.getImplClass().replace('/', '$')}\\$\", serializedLambda.getClass())" +
+                                ".invoke(null, serializedLambda)"
+                ).get(0)).getStatements().get(0)
+        );
+
+        classNode.addSyntheticMethod(
+                DESERIALIZE_LAMBDA_METHOD_NAME,
+                ACC_PRIVATE | ACC_STATIC,
+                ClassHelper.OBJECT_TYPE,
+                parameters,
+                ClassNode.EMPTY_ARRAY,
+                code);
+    }
+
+    private void addDeserializeLambdaMethodForEachLambdaExpression(LambdaExpression lambdaExpression, ClassNode lambdaWrapperClassNode) {
+        ClassNode classNode = controller.getClassNode();
+        Statement code = block(
+                new BytecodeSequence(new BytecodeInstruction() {
+                    @Override
+                    public void visit(MethodVisitor mv) {
+                        callGetCapturedArg(mv, ICONST_0, lambdaWrapperClassNode);
+                        callGetCapturedArg(mv, ICONST_1, classNode);
+                    }
+
+                    private void callGetCapturedArg(MethodVisitor mv, int capturedArgIndex, ClassNode resultType) {
+                        OperandStack operandStack = controller.getOperandStack();
+
+                        mv.visitVarInsn(ALOAD, 0);
+                        mv.visitInsn(capturedArgIndex);
+                        mv.visitMethodInsn(
+                                INVOKEVIRTUAL,
+                                "java/lang/invoke/SerializedLambda",
+                                "getCapturedArg",
+                                "(I)Ljava/lang/Object;",
+                                false);
+                        mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(resultType));
+                        operandStack.push(resultType);
+                    }
+                }),
+                returnS(lambdaExpression)
+        );
+
+        classNode.addSyntheticMethod(
+                createDeserializeLambdaMethodName(lambdaWrapperClassNode),
+                ACC_PUBLIC | ACC_STATIC,
+                ClassHelper.OBJECT_TYPE,
+                createDeserializeLambdaMethodParams(),
+                ClassNode.EMPTY_ARRAY,
+                code);
+    }
+
+    private String createDeserializeLambdaMethodName(ClassNode lambdaWrapperClassNode) {
+        return "$deserializeLambda_" + lambdaWrapperClassNode.getName().replace('.', '$') + "$";
+    }
+
     @Override
     protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) {
         return staticTypesClosureWriter.createClosureClass(expression, mods);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index a8f8c29..c12a556 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -153,12 +153,12 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         mv.visitInvokeDynamicInsn(
                 abstractMethodNode.getName(),
                 createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
-                createBootstrapMethod(isInterface),
+                createBootstrapMethod(isInterface, false),
                 createBootstrapMethodArguments(
                         abstractMethodDesc,
                         methodRefMethod.isStatic() || isConstructorReference ? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL,
                         isConstructorReference ? controller.getClassNode() : typeOrTargetRefType,
-                        methodRefMethod)
+                        methodRefMethod, false)
         );
 
         if (isClassExpr) {
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 98d7074..0e72390 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -908,4 +908,338 @@ class LambdaTest extends GroovyTestCase {
             p()
         '''
     }
+
+    void testSerialize() {
+        assertScript '''
+        import java.util.function.Function
+        
+        interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            def p() {
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+        }
+
+        assert new Test1().p().length > 0
+        '''
+    }
+
+    void testSerializeFailed() {
+        shouldFail(NotSerializableException, '''
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            def p() {
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        Function<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+        }
+
+        new Test1().p()
+        ''')
+    }
+
+    void testDeserialize() {
+        assertScript '''
+        package tests.lambda
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(new Test1().p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
+
+    void testDeserialize2() {
+        assertScript '''
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            static byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(Test1.p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
+    void testDeserializeNestedLambda() {
+        assertScript '''
+        import java.util.function.Function
+        
+        interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            def p() {
+                    def out1 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f1 = (Integer e) -> 'a' + e
+                    out1.withObjectOutputStream {
+                        it.writeObject(f1)
+                    }
+                    def result1 = out1.toByteArray()
+                    
+                    def out2 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f2 = (Integer e) -> 'b' + e
+                    out2.withObjectOutputStream {
+                        it.writeObject(f2)
+                    }
+                    def result2 = out2.toByteArray()
+                    
+                    // nested lambda expression
+                    def out3 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f3 = (Integer e) -> {
+                        SerializableFunction<Integer, String> nf = ((Integer ne) -> 'n' + ne)
+                        'c' + nf(e)
+                    }
+                    out3.withObjectOutputStream {
+                        it.writeObject(f3)
+                    }
+                    def result3 = out3.toByteArray()
+                    
+                    return [result1, result2, result3]
+            }
+        }
+        
+        def (byte[] serializedLambdaBytes1, byte[] serializedLambdaBytes2, byte[] serializedLambdaBytes3) = new Test1().p()
+        new ByteArrayInputStream(serializedLambdaBytes1).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'a1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes2).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'b1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes3).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'cn1' == f.apply(1)
+        }
+        '''
+    }
+
+    void testDeserializeNestedLambda2() {
+        assertScript '''
+        import java.util.function.Function
+        
+        interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            def p() {
+                    def out1 = new ByteArrayOutputStream()
+                    out1.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    def result1 = out1.toByteArray()
+                    
+                    def out2 = new ByteArrayOutputStream()
+                    out2.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'b' + e)
+                        it.writeObject(f)
+                    }
+                    def result2 = out2.toByteArray()
+                    
+                    // nested lambda expression
+                    def out3 = new ByteArrayOutputStream()
+                    out3.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> {
+                            SerializableFunction<Integer, String> nf = ((Integer ne) -> 'n' + ne)
+                            'c' + nf(e)
+                        })
+                        it.writeObject(f)
+                    }
+                    def result3 = out3.toByteArray()
+                    
+                    return [result1, result2, result3]
+            }
+        }
+        
+        def (byte[] serializedLambdaBytes1, byte[] serializedLambdaBytes2, byte[] serializedLambdaBytes3) = new Test1().p()
+        new ByteArrayInputStream(serializedLambdaBytes1).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'a1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes2).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'b1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes3).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'cn1' == f.apply(1)
+        }
+        '''
+    }
+
+    void testDeserializeNestedLambda3() {
+        assertScript '''
+        import java.util.function.Function
+        
+        interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            static p() {
+                    def out1 = new ByteArrayOutputStream()
+                    out1.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    def result1 = out1.toByteArray()
+                    
+                    def out2 = new ByteArrayOutputStream()
+                    out2.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'b' + e)
+                        it.writeObject(f)
+                    }
+                    def result2 = out2.toByteArray()
+                    
+                    // nested lambda expression
+                    def out3 = new ByteArrayOutputStream()
+                    out3.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> {
+                            SerializableFunction<Integer, String> nf = ((Integer ne) -> 'n' + ne)
+                            'c' + nf(e)
+                        })
+                        it.writeObject(f)
+                    }
+                    def result3 = out3.toByteArray()
+                    
+                    return [result1, result2, result3]
+            }
+        }
+        
+        def (byte[] serializedLambdaBytes1, byte[] serializedLambdaBytes2, byte[] serializedLambdaBytes3) = Test1.p()
+        new ByteArrayInputStream(serializedLambdaBytes1).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'a1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes2).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'b1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes3).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'cn1' == f.apply(1)
+        }
+        '''
+    }
+
+    void testDeserializeNestedLambda4() {
+        assertScript '''
+        import java.util.function.Function
+        
+        interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            static p() {
+                    def out1 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f1 = (Integer e) -> 'a' + e
+                    out1.withObjectOutputStream {
+                        it.writeObject(f1)
+                    }
+                    def result1 = out1.toByteArray()
+                    
+                    def out2 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f2 = (Integer e) -> 'b' + e
+                    out2.withObjectOutputStream {
+                        it.writeObject(f2)
+                    }
+                    def result2 = out2.toByteArray()
+                    
+                    // nested lambda expression
+                    def out3 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f3 = (Integer e) -> {
+                        SerializableFunction<Integer, String> nf = ((Integer ne) -> 'n' + ne)
+                        'c' + nf(e)
+                    }
+                    out3.withObjectOutputStream {
+                        it.writeObject(f3)
+                    }
+                    def result3 = out3.toByteArray()
+                    
+                    return [result1, result2, result3]
+            }
+        }
+        
+        def (byte[] serializedLambdaBytes1, byte[] serializedLambdaBytes2, byte[] serializedLambdaBytes3) = Test1.p()
+        new ByteArrayInputStream(serializedLambdaBytes1).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'a1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes2).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'b1' == f.apply(1)
+        }
+        
+        new ByteArrayInputStream(serializedLambdaBytes3).withObjectInputStream(Test1.class.classLoader) {
+            SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+            assert 'cn1' == f.apply(1)
+        }
+        '''
+    }
 }


[groovy] 02/02: GROOVY-9320: Add more tests (closes #1110)

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit ba6e33e2cf0a3f4e68b14e55b88c6f4f1b72f703
Author: Daniel.Sun <re...@hotmail.com>
AuthorDate: Mon Dec 2 18:14:51 2019 +0800

    GROOVY-9320: Add more tests (closes #1110)
---
 src/test/groovy/transform/stc/LambdaTest.groovy | 156 ++++++++++++++++++++++++
 1 file changed, 156 insertions(+)

diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 0e72390..27cfd37 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -1015,6 +1015,162 @@ class LambdaTest extends GroovyTestCase {
         '''
     }
 
+    void testDeserialize3() {
+        assertScript '''
+        package tests.lambda
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        String c = 'a'
+                        SerializableFunction<Integer, String> f = (Integer e) -> c + e
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(new Test1().p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
+    void testDeserialize4() {
+        assertScript '''
+        package tests.lambda
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    String c = 'a'
+                    SerializableFunction<Integer, String> f = (Integer e) -> c + e
+                    out.withObjectOutputStream {
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(new Test1().p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
+    void testDeserialize5() {
+        assertScript '''
+        package tests.lambda
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            static byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    String c = 'a'
+                    SerializableFunction<Integer, String> f = (Integer e) -> c + e
+                    out.withObjectOutputStream {
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(Test1.p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
+    void testDeserialize6() {
+        assertScript '''
+        package tests.lambda
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            private String c = 'a'
+            
+            byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f = (Integer e) -> c + e
+                    out.withObjectOutputStream {
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(new Test1().p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
+    void testDeserialize7() {
+        assertScript '''
+        package tests.lambda
+        import java.util.function.Function
+        
+        @groovy.transform.CompileStatic
+        class Test1 implements Serializable {
+            private static final long serialVersionUID = -1L;
+            private static final String c = 'a'
+            static byte[] p() {
+                    def out = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f = (Integer e) -> c + e
+                    out.withObjectOutputStream {
+                        it.writeObject(f)
+                    }
+                    
+                    return out.toByteArray()
+            }
+            
+            static void main(String[] args) {
+                new ByteArrayInputStream(Test1.p()).withObjectInputStream(Test1.class.classLoader) {
+                    SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
+                    assert 'a1' == f.apply(1)
+                }
+            }
+            
+            interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
+        }
+        '''
+    }
+
     void testDeserializeNestedLambda() {
         assertScript '''
         import java.util.function.Function