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:47:54 UTC

[groovy] branch master updated (f72f95a -> 3d9ad2d)

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

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


    from f72f95a  workaround for gradle plugin bug
     new 106478d  GROOVY-9320: Support serializable lambda expression
     new 3d9ad2d  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 master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 106478d77ee62f851bb5acc2f06c1385f02d4580
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 f762710..b01bb77 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -954,4 +954,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 master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 3d9ad2d6e6af371550a8284df1906353cfbeecc9
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 b01bb77..2cc88d3 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -1061,6 +1061,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