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 2020/09/28 02:19:08 UTC

[groovy] 01/02: reuse object expression knowledge

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

commit 6a38568b2af9a10c96295061e2ea2774c5553b67
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Sep 27 20:46:06 2020 -0500

    reuse object expression knowledge
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 135 +++++++++------------
 1 file changed, 59 insertions(+), 76 deletions(-)

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 2dd8b3e..0cdb70b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2404,11 +2404,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
             }
 
-            int candidateCnt = candidates.size();
-            if (0 == candidateCnt) {
+            if (candidates.isEmpty()) {
                 candidates = extension.handleMissingMethod(
                     getType(expression.getExpression()), nameText, null, null, null);
-            } else if (candidateCnt > 1) {
+            } else if (candidates.size() > 1) {
                 candidates = extension.handleAmbiguousMethods(candidates, expression);
             }
 
@@ -3291,60 +3290,67 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         objectExpression.visit(this);
         call.getMethod().visit(this);
 
-        // if the call expression is a spread operator call, then we must make sure that
-        // the call is made on a collection type
+        ClassNode receiver = getType(objectExpression);
+        // if it's a spread operator call, then make sure receiver is array or collection
         if (call.isSpreadSafe()) {
-            // TODO: check if this should not be change to iterator based call logic
-            ClassNode expressionType = getType(objectExpression);
-            if (!implementsInterfaceOrIsSubclassOf(expressionType, Collection_TYPE) && !expressionType.isArray()) {
+            if (!receiver.isArray() && !implementsInterfaceOrIsSubclassOf(receiver, Collection_TYPE)) {
                 addStaticTypeError("Spread operator can only be used on collection types", objectExpression);
-                return;
             } else {
                 // type check call as if it was made on component type
-                ClassNode componentType = inferComponentType(expressionType, int_TYPE);
+                ClassNode componentType = inferComponentType(receiver, int_TYPE);
                 MethodCallExpression subcall = callX(castX(componentType, EmptyExpression.INSTANCE), name, call.getArguments());
-                subcall.setLineNumber(call.getLineNumber());
-                subcall.setColumnNumber(call.getColumnNumber());
+                subcall.setLineNumber(call.getLineNumber()); subcall.setColumnNumber(call.getColumnNumber());
                 subcall.setImplicitThis(call.isImplicitThis());
                 visitMethodCallExpression(subcall);
                 // the inferred type here should be a list of what the subcall returns
                 ClassNode subcallReturnType = getType(subcall);
-                ClassNode listNode = LIST_TYPE.getPlainNodeReference();
-                listNode.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(subcallReturnType))});
-                storeType(call, listNode);
-                // store target method
+                ClassNode listType = LIST_TYPE.getPlainNodeReference();
+                listType.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(subcallReturnType))});
+                storeType(call, listType);
                 storeTargetMethod(call, subcall.getNodeMetaData(DIRECT_METHOD_CALL_TARGET));
-                typeCheckingContext.popEnclosingMethodCall();
-                return;
             }
+            typeCheckingContext.popEnclosingMethodCall();
+            return;
         }
 
         Expression callArguments = call.getArguments();
         ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments);
 
         checkForbiddenSpreadArgument(argumentList);
-
-        // for arguments, we need to visit closures *after* the method has been chosen
-
-        ClassNode receiver = getType(objectExpression);
+        // visit closures *after* the method has been chosen
         visitMethodCallArguments(receiver, argumentList, false, null);
 
-        ClassNode[] args = getArgumentTypes(argumentList);
-        boolean isCallOnClosure = isClosureCall(name, objectExpression, callArguments);
+        boolean isThisObjectExpression = isThisExpression(objectExpression);
+        boolean isCallOnClosure = false;
+        FieldNode fieldNode = null;
+        switch (name) {
+            case "call":
+            case "doCall":
+                if (!isThisObjectExpression) {
+                    isCallOnClosure = receiver.equals(CLOSURE_TYPE);
+                    break;
+                }
+            default:
+                if (isThisObjectExpression) {
+                    ClassNode enclosingType = typeCheckingContext.getEnclosingClassNode();
+                    fieldNode = enclosingType.getDeclaredField(name);
+                    if (fieldNode != null && getType(fieldNode).equals(CLOSURE_TYPE)
+                            && !enclosingType.hasPossibleMethod(name, callArguments)) {
+                        isCallOnClosure = true;
+                    }
+                }
+        }
 
         try {
+            ClassNode[] args = getArgumentTypes(argumentList);
             boolean callArgsVisited = false;
             if (isCallOnClosure) {
-                // this is a closure.call() call
-                if (isThisExpression(objectExpression)) {
-                    // isClosureCall() check verified earlier that a field exists
-                    FieldNode field = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name);
-                    GenericsType[] genericsTypes = field.getType().getGenericsTypes();
+                if (fieldNode != null) {
+                    GenericsType[] genericsTypes = getType(fieldNode).getGenericsTypes();
                     if (genericsTypes != null) {
                         ClassNode closureReturnType = genericsTypes[0].getType();
-                        Object data = field.getNodeMetaData(CLOSURE_ARGUMENTS);
-                        if (data != null) {
-                            Parameter[] parameters = (Parameter[]) data;
+                        Parameter[] parameters = fieldNode.getNodeMetaData(CLOSURE_ARGUMENTS);
+                        if (parameters != null) {
                             typeCheckClosureCall(callArguments, args, parameters);
                         }
                         storeType(call, closureReturnType);
@@ -3352,19 +3358,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 } else if (objectExpression instanceof VariableExpression) {
                     Variable variable = findTargetVariable((VariableExpression) objectExpression);
                     if (variable instanceof ASTNode) {
-                        Object data = ((ASTNode) variable).getNodeMetaData(CLOSURE_ARGUMENTS);
-                        if (data != null) {
-                            Parameter[] parameters = (Parameter[]) data;
+                        Parameter[] parameters = ((ASTNode) variable).getNodeMetaData(CLOSURE_ARGUMENTS);
+                        if (parameters != null) {
                             typeCheckClosureCall(callArguments, args, parameters);
                         }
                         ClassNode type = getType(((ASTNode) variable));
-                        if (type != null && type.equals(CLOSURE_TYPE)) {
+                        if (CLOSURE_TYPE.equals(type)) {
                             GenericsType[] genericsTypes = type.getGenericsTypes();
                             type = OBJECT_TYPE;
-                            if (genericsTypes != null) {
-                                if (!genericsTypes[0].isPlaceholder()) {
-                                    type = genericsTypes[0].getType();
-                                }
+                            if (genericsTypes != null && !genericsTypes[0].isPlaceholder()) {
+                                type = genericsTypes[0].getType();
                             }
                         }
                         if (type != null) {
@@ -3375,24 +3378,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     // we can get actual parameters directly
                     Parameter[] parameters = ((ClosureExpression) objectExpression).getParameters();
                     typeCheckClosureCall(callArguments, args, parameters);
-                    ClassNode data = getInferredReturnType(objectExpression);
-                    if (data != null) {
-                        storeType(call, data);
+                    ClassNode type = getInferredReturnType(objectExpression);
+                    if (type != null) {
+                        storeType(call, type);
                     }
                 }
 
-                int nbOfArgs;
+                int nArgs = 0;
                 if (callArguments instanceof ArgumentListExpression) {
-                    ArgumentListExpression list = (ArgumentListExpression) callArguments;
-                    nbOfArgs = list.getExpressions().size();
-                } else {
-                    // todo : other cases
-                    nbOfArgs = 0;
+                    nArgs = ((ArgumentListExpression) callArguments).getExpressions().size();
                 }
-                storeTargetMethod(call,
-                        nbOfArgs == 0 ? CLOSURE_CALL_NO_ARG :
-                                nbOfArgs == 1 ? CLOSURE_CALL_ONE_ARG :
-                                        CLOSURE_CALL_VARGS);
+                storeTargetMethod(call, nArgs == 0 ? CLOSURE_CALL_NO_ARG : nArgs == 1 ? CLOSURE_CALL_ONE_ARG : CLOSURE_CALL_VARGS);
             } else {
                 // method call receivers are :
                 //   - possible "with" receivers
@@ -3413,7 +3409,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     // if we are not in a static context but the current receiver is a static class, we must
                     // ensure that all methods are either static or declared by the current receiver or a superclass
                     if (!mn.isEmpty()
-                            && (call.isImplicitThis() || isThisExpression(objectExpression))
+                            && (isThisObjectExpression || call.isImplicitThis())
                             && (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0)) {
                         // we create separate method lists just to be able to print out
                         // a nice error message to the user
@@ -3422,8 +3418,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         List<MethodNode> accessibleMethods = new LinkedList<>();
                         List<MethodNode> inaccessibleMethods = new LinkedList<>();
                         for (final MethodNode node : mn) {
-                            if (node.isStatic()
-                                    || (!typeCheckingContext.isInStaticContext && implementsInterfaceOrIsSubclassOf(receiverType, node.getDeclaringClass()))) {
+                            if (node.isStatic() || (!typeCheckingContext.isInStaticContext
+                                    && implementsInterfaceOrIsSubclassOf(receiverType, node.getDeclaringClass()))) {
                                 accessibleMethods.add(node);
                             } else {
                                 inaccessibleMethods.add(node);
@@ -3443,7 +3439,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         break;
                     }
                 }
-                if (mn.isEmpty() && call.isImplicitThis() && isThisExpression(objectExpression) && typeCheckingContext.getEnclosingClosure() != null) {
+                if (mn.isEmpty() && isThisObjectExpression && call.isImplicitThis() && typeCheckingContext.getEnclosingClosure() != null) {
                     mn = CLOSURE_TYPE.getDeclaredMethods(name);
                     if (!mn.isEmpty()) {
                         chosenReceiver = Receiver.make(CLOSURE_TYPE);
@@ -3463,14 +3459,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
                     if (mn.size() == 1) {
                         MethodNode directMethodCallCandidate = mn.get(0);
+                        ClassNode declaringClass = directMethodCallCandidate.getDeclaringClass();
                         if (call.getNodeMetaData(DYNAMIC_RESOLUTION) == null
-                                && !directMethodCallCandidate.isStatic() && objectExpression instanceof ClassExpression
-                                && !"java.lang.Class".equals(directMethodCallCandidate.getDeclaringClass().getName())) {
-                            ClassNode owner = directMethodCallCandidate.getDeclaringClass();
-                            addStaticTypeError("Non static method " + owner.getName() + "#" + directMethodCallCandidate.getName() + " cannot be called from static context", call);
+                                && objectExpression instanceof ClassExpression
+                                && !directMethodCallCandidate.isStatic()
+                                && !declaringClass.equals(CLASS_Type)) {
+                            addStaticTypeError("Non static method " + declaringClass.getName() + "#" + directMethodCallCandidate.getName() + " cannot be called from static context", call);
                         }
                         if (chosenReceiver == null) {
-                            chosenReceiver = Receiver.make(directMethodCallCandidate.getDeclaringClass());
+                            chosenReceiver = Receiver.make(declaringClass);
                         }
 
                         ClassNode returnType = getType(directMethodCallCandidate);
@@ -3831,20 +3828,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    protected boolean isClosureCall(final String name, final Expression objectExpression, final Expression arguments) {
-        if (objectExpression instanceof ClosureExpression && ("call".equals(name) || "doCall".equals(name))) return true;
-        if (isThisExpression(objectExpression)) {
-            FieldNode fieldNode = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name);
-            if (fieldNode != null && CLOSURE_TYPE.equals(fieldNode.getType())
-                    && !typeCheckingContext.getEnclosingClassNode().hasPossibleMethod(name, arguments)) {
-                return true;
-            }
-        } else if (!"call".equals(name) && !"doCall".equals(name)) {
-            return false;
-        }
-        return getType(objectExpression).equals(CLOSURE_TYPE);
-    }
-
     protected void typeCheckClosureCall(final Expression callArguments, final ClassNode[] args, final Parameter[] parameters) {
         if (allParametersAndArgumentsMatch(parameters, args) < 0 &&
                 lastArgMatchesVarg(parameters, args) < 0) {