You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/12/14 15:32:23 UTC
[groovy] branch master updated: STC: consistent parameter (aka target) type for functional expression
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 241c694d30 STC: consistent parameter (aka target) type for functional expression
241c694d30 is described below
commit 241c694d3091bde4ab206cf9133281e79acd143b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Dec 14 09:15:25 2022 -0600
STC: consistent parameter (aka target) type for functional expression
---
.../asm/sc/AbstractFunctionalInterfaceWriter.java | 56 +++++++----------
.../classgen/asm/sc/StaticInvocationWriter.java | 13 ++--
...icTypesBinaryExpressionMultiTypeDispatcher.java | 7 +--
.../classgen/asm/sc/StaticTypesLambdaWriter.java | 29 ++++-----
...StaticTypesMethodReferenceExpressionWriter.java | 15 ++---
.../transform/stc/StaticTypeCheckingVisitor.java | 13 +++-
.../groovy/transform/stc/StaticTypesMarker.java | 63 ++++++++++++-------
src/test/groovy/transform/stc/LambdaTest.groovy | 70 ++++++++--------------
.../transform/stc/MethodReferenceTest.groovy | 8 +--
9 files changed, 138 insertions(+), 136 deletions(-)
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 a6a5ba9f24..8e2d6d448e 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
@@ -18,12 +18,9 @@
*/
package org.codehaus.groovy.classgen.asm.sc;
-import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.syntax.RuntimeParserException;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
@@ -34,8 +31,10 @@ import java.util.List;
import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
-import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_FUNCTIONAL_INTERFACE_TYPE;
-import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.isDynamicTyped;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getClassInternalName;
+import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getMethodDescriptor;
/**
* Represents functional interface writer which contains some common methods to complete generating bytecode
@@ -43,21 +42,12 @@ import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE
*/
public interface AbstractFunctionalInterfaceWriter {
- default ClassNode getFunctionalInterfaceType(final Expression expression) {
- ClassNode type = expression.getNodeMetaData(PARAMETER_TYPE);
- if (type == null) {
- type = expression.getNodeMetaData(INFERRED_FUNCTIONAL_INTERFACE_TYPE);
- }
- return type;
- }
+ default String createMethodDescriptor(final MethodNode method) {
+ Class<?> returnType = method.getReturnType().getTypeClass();
+ Class<?>[] parameterTypes = Arrays.stream(method.getParameters())
+ .map(p -> p.getType().getTypeClass()).toArray(Class[]::new);
- default String createMethodDescriptor(final MethodNode abstractMethodNode) {
- return BytecodeHelper.getMethodDescriptor(
- abstractMethodNode.getReturnType().getTypeClass(),
- Arrays.stream(abstractMethodNode.getParameters())
- .map(e -> e.getType().getTypeClass())
- .toArray(Class[]::new)
- );
+ return getMethodDescriptor(returnType, parameterTypes);
}
default Handle createBootstrapMethod(final boolean isInterface, final boolean serializable) {
@@ -87,13 +77,13 @@ public interface AbstractFunctionalInterfaceWriter {
arguments[1] = new Handle(
insn, // H_INVOKESTATIC or H_INVOKEVIRTUAL or H_INVOKEINTERFACE (GROOVY-9853)
- BytecodeHelper.getClassInternalName(methodOwner.getName()),
+ getClassInternalName(methodOwner.getName()),
methodNode.getName(),
- BytecodeHelper.getMethodDescriptor(methodNode),
+ getMethodDescriptor(methodNode),
methodOwner.isInterface());
arguments[2] = Type.getType(
- BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(), parameters));
+ getMethodDescriptor(methodNode.getReturnType(), parameters));
return arguments;
}
@@ -104,10 +94,10 @@ public interface AbstractFunctionalInterfaceWriter {
}
ClassNode type;
- boolean isParameterTypePrimitive = ClassHelper.isPrimitiveType(parameterType);
- boolean isInferredTypePrimitive = ClassHelper.isPrimitiveType(inferredType);
+ boolean isParameterTypePrimitive = isPrimitiveType(parameterType);
+ boolean isInferredTypePrimitive = isPrimitiveType(inferredType);
if (!isParameterTypePrimitive && isInferredTypePrimitive) {
- if (ClassHelper.isDynamicTyped(parameterType) && ClassHelper.isPrimitiveType(targetType) // (1)
+ if (isDynamicTyped(parameterType) && isPrimitiveType(targetType) // (1)
|| !parameterType.equals(getUnwrapper(parameterType)) && !inferredType.equals(getWrapper(inferredType)) // (2)
) {
// GROOVY-9790: bootstrap method initialization exception raised when lambda parameter type is wrong
@@ -134,20 +124,20 @@ public interface AbstractFunctionalInterfaceWriter {
*/
@Deprecated
default ClassNode convertParameterType(final ClassNode parameterType, final ClassNode inferredType) {
- if (!ClassHelper.getWrapper(inferredType.redirect()).isDerivedFrom(ClassHelper.getWrapper(parameterType.redirect()))) {
+ if (!getWrapper(inferredType.redirect()).isDerivedFrom(getWrapper(parameterType.redirect()))) {
throw new RuntimeParserException("The inferred type[" + inferredType.redirect() + "] is not compatible with the parameter type[" + parameterType.redirect() + "]", parameterType);
} else {
- boolean isParameterTypePrimitive = ClassHelper.isPrimitiveType(parameterType);
- boolean isInferredTypePrimitive = ClassHelper.isPrimitiveType(inferredType);
+ boolean isParameterTypePrimitive = isPrimitiveType(parameterType);
+ boolean isInferredTypePrimitive = isPrimitiveType(inferredType);
ClassNode type;
if (!isParameterTypePrimitive && isInferredTypePrimitive) {
- if (parameterType != ClassHelper.getUnwrapper(parameterType) && inferredType != ClassHelper.getWrapper(inferredType)) {
+ if (parameterType != getUnwrapper(parameterType) && inferredType != getWrapper(inferredType)) {
type = inferredType;
} else {
- type = ClassHelper.getWrapper(inferredType);
+ type = getWrapper(inferredType);
}
} else if (isParameterTypePrimitive && !isInferredTypePrimitive) {
- type = ClassHelper.getUnwrapper(inferredType);
+ type = getUnwrapper(inferredType);
} else {
type = inferredType;
}
@@ -155,11 +145,11 @@ public interface AbstractFunctionalInterfaceWriter {
}
}
- default Parameter prependParameter(final List<Parameter> methodParameterList, final String parameterName, final ClassNode parameterType) {
+ default Parameter prependParameter(final List<Parameter> parameterList, final String parameterName, final ClassNode parameterType) {
Parameter parameter = new Parameter(parameterType, parameterName);
parameter.setClosureSharedVariable(false);
parameter.setOriginType(parameterType);
- methodParameterList.add(0, parameter);
+ parameterList.add(0, parameter);
return parameter;
}
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index cd02b265c6..545b23180e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -35,7 +35,9 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.LambdaExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.SpreadExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
@@ -515,10 +517,13 @@ public class StaticInvocationWriter extends InvocationWriter {
return ClassHelper.getWrapper(argumentType).isDerivedFrom(ClassHelper.getWrapper(parameterType));
}
- private void visitArgument(final Expression argumentExpr, final ClassNode parameterType) {
- argumentExpr.putNodeMetaData(StaticTypesMarker.PARAMETER_TYPE, parameterType);
- argumentExpr.visit(controller.getAcg());
- if (!isNullConstant(argumentExpr)) {
+ private void visitArgument(final Expression argument, final ClassNode parameterType) {
+ if (argument instanceof LambdaExpression || argument instanceof MethodReferenceExpression) {
+ // ensure target type available to the argument expression's bytecode generator
+ argument.getNodeMetaData(StaticTypesMarker.PARAMETER_TYPE, x -> parameterType);
+ }
+ argument.visit(controller.getAcg());
+ if (!isNullConstant(argument)) {
controller.getOperandStack().doGroovyCast(parameterType);
}
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
index fd002236da..f742067051 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -78,8 +78,8 @@ import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIS
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isAssignment;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.inferLoopElementType;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET;
-import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_FUNCTIONAL_INTERFACE_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.DADD;
import static org.objectweb.asm.Opcodes.DCONST_1;
@@ -201,9 +201,8 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
}
} else {
Expression rightExpression = expression.getRightExpression();
- if (rightExpression instanceof LambdaExpression || rightExpression instanceof MethodReferenceExpression) {
- rightExpression.putNodeMetaData(INFERRED_FUNCTIONAL_INTERFACE_TYPE, leftExpression.getNodeMetaData(INFERRED_TYPE));
- }
+ if (rightExpression instanceof LambdaExpression || rightExpression instanceof MethodReferenceExpression)
+ rightExpression.getNodeMetaData(PARAMETER_TYPE, x -> leftExpression.getNodeMetaData(INFERRED_TYPE));
}
// GROOVY-5620: spread-safe operator on LHS is not supported
if (leftExpression instanceof PropertyExpression
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 9b9fb1d638..5d5d7f0e4c 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
@@ -19,6 +19,7 @@
package org.codehaus.groovy.classgen.asm.sc;
import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.CodeVisitorSupport;
import org.codehaus.groovy.ast.ConstructorNode;
@@ -41,7 +42,6 @@ import org.codehaus.groovy.classgen.asm.OperandStack;
import org.codehaus.groovy.classgen.asm.WriterController;
import org.codehaus.groovy.classgen.asm.WriterControllerFactory;
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
-import org.codehaus.groovy.transform.stc.StaticTypesMarker;
import org.objectweb.asm.MethodVisitor;
import java.util.HashMap;
@@ -56,7 +56,6 @@ import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.SERIALIZABLE_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.SERIALIZEDLAMBDA_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.findSAM;
import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
@@ -65,6 +64,8 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
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.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_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_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -86,8 +87,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
private static final String IS_GENERATED_CONSTRUCTOR = "__IS_GENERATED_CONSTRUCTOR";
private static final String LAMBDA_SHARED_VARIABLES = "__LAMBDA_SHARED_VARIABLES";
- private final StaticTypesClosureWriter staticTypesClosureWriter;
private final Map<Expression, ClassNode> lambdaClassNodes = new HashMap<>();
+ private final StaticTypesClosureWriter staticTypesClosureWriter;
public StaticTypesLambdaWriter(final WriterController controller) {
super(controller);
@@ -96,19 +97,16 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
@Override
public void writeLambda(final LambdaExpression expression) {
- ClassNode functionalInterface = getFunctionalInterfaceType(expression);
- if (functionalInterface == null || !functionalInterface.isInterface()) {
+ // functional interface target is required for native lambda generation
+ ClassNode functionalType = expression.getNodeMetaData(PARAMETER_TYPE);
+ MethodNode abstractMethod = ClassHelper.findSAM(functionalType);
+ if (abstractMethod == null || !functionalType.isInterface()) {
+ // generate bytecode for closure
super.writeLambda(expression);
return;
}
- MethodNode abstractMethod = findSAM(functionalInterface.redirect());
- if (abstractMethod == null) {
- super.writeLambda(expression);
- return;
- }
-
- if (!expression.isSerializable() && functionalInterface.implementsInterface(SERIALIZABLE_TYPE)) {
+ if (!expression.isSerializable() && functionalType.implementsInterface(SERIALIZABLE_TYPE)) {
expression.setSerializable(true);
}
@@ -130,7 +128,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
MethodVisitor mv = controller.getMethodVisitor();
mv.visitInvokeDynamicInsn(
abstractMethod.getName(),
- createAbstractMethodDesc(functionalInterface.redirect(), lambdaClass),
+ createAbstractMethodDesc(functionalType.redirect(), lambdaClass),
createBootstrapMethod(enclosingClass.isInterface(), expression.isSerializable()),
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable())
);
@@ -138,8 +136,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
mv.visitTypeInsn(CHECKCAST, "java/io/Serializable");
}
- OperandStack operandStack = controller.getOperandStack();
- operandStack.replace(functionalInterface.redirect(), 1);
+ controller.getOperandStack().replace(functionalType.redirect(), 1);
}
private static Parameter[] createDeserializeLambdaMethodParams() {
@@ -295,7 +292,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
for (int i = 0, n = parameters.length; i < n; i += 1) {
Parameter targetParameter = targetParameters[i];
Parameter parameter = parameters[i];
- ClassNode inferredType = parameter.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+ ClassNode inferredType = parameter.getNodeMetaData(INFERRED_TYPE);
if (inferredType != null) {
ClassNode type = convertParameterType(targetParameter.getType(), parameter.getType(), inferredType);
parameter.setOriginType(type);
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 c1804e74c1..d318fbcc57 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
@@ -74,9 +74,10 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
@Override
public void writeMethodReferenceExpression(final MethodReferenceExpression methodReferenceExpression) {
- ClassNode functionalInterfaceType = getFunctionalInterfaceType(methodReferenceExpression);
- MethodNode abstractMethod = ClassHelper.findSAM(functionalInterfaceType);
- if (abstractMethod == null || !functionalInterfaceType.isInterface()) {
+ // functional interface target is required for native method reference generation
+ ClassNode functionalType = methodReferenceExpression.getNodeMetaData(StaticTypesMarker.PARAMETER_TYPE);
+ MethodNode abstractMethod = ClassHelper.findSAM(functionalType);
+ if (abstractMethod == null || !functionalType.isInterface()) {
// generate the default bytecode -- most likely a method closure
super.writeMethodReferenceExpression(methodReferenceExpression);
return;
@@ -104,7 +105,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
}
validate(methodReferenceExpression, typeOrTargetRefType, methodRefName, methodRefMethod, parametersWithExactType,
- resolveClassNodeGenerics(extractPlaceholders(functionalInterfaceType), null, abstractMethod.getReturnType()));
+ resolveClassNodeGenerics(extractPlaceholders(functionalType), null, abstractMethod.getReturnType()));
if (isExtensionMethod(methodRefMethod)) {
ExtensionMethodNode extensionMethodNode = (ExtensionMethodNode) methodRefMethod;
@@ -162,7 +163,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
}
String methodName = abstractMethod.getName();
- String methodDesc = BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(),
+ String methodDesc = BytecodeHelper.getMethodDescriptor(functionalType.redirect(),
isClassExpression ? Parameter.EMPTY_ARRAY : new Parameter[]{new Parameter(typeOrTargetRefType, "__METHODREF_EXPR_INSTANCE")});
Handle bootstrapMethod = createBootstrapMethod(classNode.isInterface(), false);
@@ -177,9 +178,9 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, methodDesc, bootstrapMethod, bootstrapArgs);
if (isClassExpression) {
- controller.getOperandStack().push(functionalInterfaceType);
+ controller.getOperandStack().push(functionalType);
} else {
- controller.getOperandStack().replace(functionalInterfaceType, 1);
+ controller.getOperandStack().replace(functionalType, 1);
}
}
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 2e38ae20bd..c833106456 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -320,6 +320,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLU
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.IMPLICIT_RECEIVER;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_RETURN_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_FIELDS_ACCESS;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_FIELDS_MUTATION;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_METHODS_ACCESS;
@@ -984,6 +985,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
LambdaExpression lambda = constructLambdaExpressionForMethodReference(target, (MethodReferenceExpression) source);
inferParameterAndReturnTypesOfClosureOnRHS(target, lambda);
+ source.putNodeMetaData(PARAMETER_TYPE, lambda.getNodeMetaData(PARAMETER_TYPE));
source.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambda.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
}
}
@@ -1016,6 +1018,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
addStaticTypeError("Wrong number of parameters for method target " + toMethodParametersString(findSAM(lhsType).getName(), samParameterTypes), rhsExpression);
}
+ rhsExpression.putNodeMetaData(PARAMETER_TYPE, lhsType);
storeInferredReturnType(rhsExpression, typeInfo.getV2());
}
@@ -2816,9 +2819,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
for (int i = 0; i < nExpressions; i += 1) {
Expression expression = expressions.get(i);
if (visitClosures == (expression instanceof ClosureExpression || expression instanceof MethodPointerExpression)) {
+ ClassNode targetType = null;
if (visitClosures && nthParameter != -1) { // GROOVY-10636: vargs call
Parameter target = parameters[Math.min(i, nthParameter)];
- ClassNode targetType = target.getType();
+ targetType = target.getType();
if (targetType.isArray() && i >= nthParameter)
targetType = targetType.getComponentType();
@@ -2846,6 +2850,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
expression.visit(this);
expression.removeNodeMetaData(DELEGATION_METADATA);
+
+ MethodNode sam = findSAM(targetType);
+ if (sam != null) {
+ Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, selectedMethod, arguments);
+ targetType = applyGenericsContext(context, targetType);
+ expression.putNodeMetaData(PARAMETER_TYPE, targetType);
+ }
}
if (i == 0 && parameters.length > 0 && expression instanceof MapExpression) {
checkNamedParamsAnnotation(parameters[0], (MapExpression) expression);
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
index 56077e007b..fa66212a7f 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
@@ -19,28 +19,47 @@
package org.codehaus.groovy.transform.stc;
/**
- * This enumeration is used by the AST transformations which rely on static type checking, either
- * to store or to retrieve information from AST node metadata. The values of this enumeration are
- * used as metadata keys.
+ * This enumeration is used by the AST transformations which rely on static type
+ * checking, either to store or to retrieve information from AST node metadata.
+ * The values of this enumeration are used as metadata keys.
*/
public enum StaticTypesMarker {
- INFERRED_TYPE, // used to store type information on class nodes
- DECLARATION_INFERRED_TYPE, // in flow analysis, represents the type of the declaration node lhs
- INFERRED_RETURN_TYPE, // used to store inferred return type for methods and closures
- CLOSURE_ARGUMENTS, // used to store closure argument types on a variable expression
- READONLY_PROPERTY, // used to tell that a property expression refers to a readonly property
- INITIAL_EXPRESSION, // used to store the default expression for a parameter
- DIRECT_METHOD_CALL_TARGET, // used to store the MethodNode a MethodCallExpression should target
- DELEGATION_METADATA, // used to store the delegation strategy and delegate type of a closure when declared with @DelegatesTo
- IMPLICIT_RECEIVER, // if the receiver is implicit but not "this", store the name of the receiver (delegate or owner)
- PV_FIELDS_ACCESS, // set of private fields that are accessed from closures or inner classes
- PV_FIELDS_MUTATION, // set of private fields that are set from closures or inner classes
- PV_METHODS_ACCESS, // set of private methods that are accessed from closures or inner classes
- DYNAMIC_RESOLUTION, // call recognized by a type checking extension as a dynamic method call
- SUPER_MOP_METHOD_REQUIRED, // used to store the list of MOP methods that still have to be generated
- PARAMETER_TYPE, // used to store the parameter type information of method invocation on an expression
- INFERRED_FUNCTIONAL_INTERFACE_TYPE, // used to store the function interface type information on an expression
- CONSTRUCTED_LAMBDA_EXPRESSION, // used to store the constructed lambda expression for method reference and constructor reference
- SWITCH_CONDITION_EXPRESSION_TYPE, // used to store the condition expression type of the switch-case statement
- TYPE // used to store the result of `org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.getType`
+ /** used to store type information on class nodes */
+ INFERRED_TYPE,
+ /** in flow analysis, represents the type of the declaration node LHS */
+ DECLARATION_INFERRED_TYPE,
+ /** used to store inferred return type for methods and closures */
+ INFERRED_RETURN_TYPE,
+ /** used to store closure argument types on a variable expression */
+ CLOSURE_ARGUMENTS,
+ /** used to tell that a property expression refers to a readonly property */
+ READONLY_PROPERTY,
+ /** used to store the default expression for a parameter */
+ INITIAL_EXPRESSION,
+ /** used to store the MethodNode a MethodCallExpression should target */
+ DIRECT_METHOD_CALL_TARGET,
+ /** used to store the delegation strategy and delegate type of a closure derived from {@link groovy.lang.DelegatesTo DelegatesTo} metadata */
+ DELEGATION_METADATA,
+ /** if the receiver is implicit but not "this", store the name of the receiver (delegate or owner) */
+ IMPLICIT_RECEIVER,
+ /** set of private fields that are accessed from closures or inner classes */
+ PV_FIELDS_ACCESS,
+ /** set of private fields that are set from closures or inner classes */
+ PV_FIELDS_MUTATION,
+ /** set of private methods that are accessed from closures or inner classes */
+ PV_METHODS_ACCESS,
+ /** call recognized by a type checking extension as a dynamic method call */
+ DYNAMIC_RESOLUTION,
+ /** used to store the list of MOP methods that still have to be generated */
+ SUPER_MOP_METHOD_REQUIRED,
+ /** used to store the parameter type information of method invocation on an expression */
+ PARAMETER_TYPE,
+ /** used to store the function interface type information on an expression */
+ INFERRED_FUNCTIONAL_INTERFACE_TYPE,
+ /** used to store the constructed lambda expression for method reference and constructor reference */
+ CONSTRUCTED_LAMBDA_EXPRESSION,
+ /** used to store the condition expression type of the switch-case statement */
+ SWITCH_CONDITION_EXPRESSION_TYPE,
+ /** used to store the result of {@link StaticTypeCheckingVisitor#getType} */
+ TYPE
}
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index c117fbac24..3380975445 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -717,60 +717,48 @@ final class LambdaTest {
@Test
void testFunctionalInterface1() {
assertScript shell, '''
- class Test1 {
- static main(args) {
- p()
- }
-
- static void p() {
- SamCallable c = (int e) -> e
- assert 1 == c(1)
- }
+ interface SamCallable {
+ int call(int i)
}
- interface SamCallable {
- int call(int p)
+ void p() {
+ SamCallable c = (int x) -> x
+ assert c(1) == 1
}
+
+ p()
'''
}
@Test
void testFunctionalInterface2() {
assertScript shell, '''
- class Test1 {
- static main(args) {
- p()
- }
-
- static void p() {
- SamCallable c = e -> e
- assert 1 == c(1)
- }
+ interface SamCallable {
+ int call(int i)
}
- interface SamCallable {
- int call(int p)
+ void p() {
+ SamCallable c = x -> x
+ assert c(1) == 1
}
+
+ p()
'''
}
@Test
void testFunctionalInterface3() {
assertScript shell, '''
- class Test1 {
- static main(args) {
- p()
- }
-
- static void p() {
- SamCallable c = (int e) -> e // This is actually a closure(not a native lambda), because "Functional interface SamCallable is not an interface"
- assert 1 == c(1)
- }
+ abstract class SamCallable {
+ abstract int call(int i)
}
- abstract class SamCallable {
- abstract int call(int p)
+ void p() {
+ SamCallable c = (int x) -> x // this is a closure, not a native lambda
+ assert c(1) == 1
}
+
+ p()
'''
}
@@ -1179,11 +1167,7 @@ final class LambdaTest {
class C implements Serializable {
private static final long serialVersionUID = -1L
String s = 'a'
- SerializableFunction<Integer, String> f
-
- {
- f = (Integer i) -> s + i
- }
+ transient SerializableFunction<Integer, String> f = (Integer i) -> s + i
byte[] test() {
def out = new ByteArrayOutputStream()
@@ -1213,11 +1197,7 @@ final class LambdaTest {
class C {
String s = 'a'
- SerializableFunction<Integer, String> f
-
- {
- f = (Integer i) -> s + i
- }
+ SerializableFunction<Integer, String> f = (Integer i) -> s + i
byte[] test() {
def out = new ByteArrayOutputStream()
@@ -1357,7 +1337,7 @@ final class LambdaTest {
package tests.lambda
class C implements Serializable {
- private static final long serialVersionUID = -1L;
+ private static final long serialVersionUID = -1L
private String s = 'a'
byte[] test() {
@@ -1420,7 +1400,7 @@ final class LambdaTest {
package tests.lambda
class C implements Serializable {
- private static final long serialVersionUID = -1L;
+ private static final long serialVersionUID = -1L
private String getS() { 'a' }
byte[] test() {
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 79dcbf2bcd..29cc338a05 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -741,7 +741,7 @@ final class MethodReferenceTest {
assertScript shell, '''
@CompileStatic
void test() {
- def f = Math::abs // No explicit type defined, so it is actually a method closure. We can make it smarter in a later version.
+ def f = Math::abs // No explicit type defined, so it is actually a method closure
def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
assert [1, 2, 3] == result
}
@@ -845,7 +845,7 @@ final class MethodReferenceTest {
@CompileStatic
void test() {
def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
- assert result.every(e -> e instanceof Thread)
+ assert result.every { it instanceof Thread }
}
test()
@@ -871,10 +871,10 @@ final class MethodReferenceTest {
@CompileStatic
void test() {
def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
- assert result.every(e -> e instanceof Thread)
+ assert result.every { it instanceof Thread }
result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
- assert result.every(e -> e instanceof Thread)
+ assert result.every { it instanceof Thread }
}
test()