Search in sources :

Example 71 with PropertyNode

use of org.codehaus.groovy.ast.PropertyNode in project groovy by apache.

the class StaticTypeCheckingVisitor method existsProperty.

/**
     * Checks whether a property exists on the receiver, or on any of the possible receiver classes (found in the
     * temporary type information table)
     *
     * @param pexp             a property expression
     * @param readMode         if true, look for property read, else for property set
     * @param visitor          if not null, when the property node is found, visit it with the provided visitor
     * @return true if the property is defined in any of the possible receiver classes
     */
protected boolean existsProperty(final PropertyExpression pexp, final boolean readMode, final ClassCodeVisitorSupport visitor) {
    super.visitPropertyExpression(pexp);
    String propertyName = pexp.getPropertyAsString();
    if (propertyName == null)
        return false;
    Expression objectExpression = pexp.getObjectExpression();
    final ClassNode objectExpressionType = getType(objectExpression);
    boolean staticOnlyAccess = isClassClassNodeWrappingConcreteType(objectExpressionType);
    if ("this".equals(propertyName) && staticOnlyAccess) {
        // Outer.this for any level of nesting
        ClassNode outerNode = objectExpressionType.getGenericsTypes()[0].getType();
        List<ClassNode> candidates = typeCheckingContext.getEnclosingClassNodes();
        ClassNode found = null;
        for (ClassNode current : candidates) {
            if (!current.isStaticClass() && current instanceof InnerClassNode && outerNode.equals(current.getOuterClass())) {
                found = current;
                break;
            }
        }
        if (found != null) {
            storeType(pexp, outerNode);
            return true;
        }
    }
    if (objectExpressionType.isArray() && "length".equals(pexp.getPropertyAsString())) {
        storeType(pexp, int_TYPE);
        if (visitor != null) {
            PropertyNode node = new PropertyNode("length", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, int_TYPE, objectExpressionType, null, null, null);
            visitor.visitProperty(node);
        }
        return true;
    }
    boolean foundGetterOrSetter = false;
    List<Receiver<String>> receivers = new LinkedList<Receiver<String>>();
    List<Receiver<String>> owners = makeOwnerList(objectExpression);
    addReceivers(receivers, owners, pexp.isImplicitThis());
    String capName = MetaClassHelper.capitalize(propertyName);
    boolean isAttributeExpression = pexp instanceof AttributeExpression;
    HashSet<ClassNode> handledNodes = new HashSet<ClassNode>();
    for (Receiver<String> receiver : receivers) {
        ClassNode testClass = receiver.getType();
        LinkedList<ClassNode> queue = new LinkedList<ClassNode>();
        queue.add(testClass);
        if (isPrimitiveType(testClass)) {
            queue.add(getWrapper(testClass));
        }
        while (!queue.isEmpty()) {
            ClassNode current = queue.removeFirst();
            if (handledNodes.contains(current))
                continue;
            handledNodes.add(current);
            Set<ClassNode> allInterfaces = current.getAllInterfaces();
            for (ClassNode intf : allInterfaces) {
                //TODO: apply right generics here!
                queue.add(GenericsUtils.parameterizeType(current, intf));
            }
            // in case of a lookup on Class we look for instance methods on Class
            // as well, since in case of a static property access we have the class
            // itself in the list of receivers already;
            boolean staticOnly;
            if (isClassClassNodeWrappingConcreteType(current)) {
                staticOnly = false;
            } else {
                staticOnly = staticOnlyAccess;
            }
            FieldNode field = current.getDeclaredField(propertyName);
            field = allowStaticAccessToMember(field, staticOnly);
            if (storeField(field, isAttributeExpression, pexp, current, visitor, receiver.getData(), !readMode))
                return true;
            boolean isThisExpression = objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression() && objectExpressionType.equals(current);
            if (storeField(field, isThisExpression, pexp, receiver.getType(), visitor, receiver.getData(), !readMode))
                return true;
            MethodNode getter = findGetter(current, "get" + capName, pexp.isImplicitThis());
            getter = allowStaticAccessToMember(getter, staticOnly);
            if (getter == null)
                getter = findGetter(current, "is" + capName, pexp.isImplicitThis());
            getter = allowStaticAccessToMember(getter, staticOnly);
            final String setterName = "set" + capName;
            List<MethodNode> setters = findSetters(current, setterName, false);
            setters = allowStaticAccessToMember(setters, staticOnly);
            // need to visit even if we only look for a setters for compatibility
            if (visitor != null && getter != null)
                visitor.visitMethod(getter);
            PropertyNode propertyNode = current.getProperty(propertyName);
            propertyNode = allowStaticAccessToMember(propertyNode, staticOnly);
            //prefer explicit getter or setter over property if receiver is not 'this'
            boolean checkGetterOrSetter = !isThisExpression || propertyNode == null;
            if (readMode && checkGetterOrSetter) {
                if (getter != null) {
                    ClassNode cn = inferReturnTypeGenerics(current, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
                    storeInferredTypeForPropertyExpression(pexp, cn);
                    pexp.removeNodeMetaData(StaticTypesMarker.READONLY_PROPERTY);
                    String delegationData = receiver.getData();
                    if (delegationData != null)
                        pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
                    return true;
                }
            } else if (!readMode && checkGetterOrSetter) {
                if (!setters.isEmpty()) {
                    if (visitor != null) {
                        if (field != null) {
                            visitor.visitField(field);
                        } else {
                            for (MethodNode setter : setters) {
                                ClassNode setterType = setter.getParameters()[0].getOriginType();
                                FieldNode virtual = new FieldNode(propertyName, 0, setterType, current, EmptyExpression.INSTANCE);
                                visitor.visitField(virtual);
                            }
                        }
                    }
                    //TODO: apply generics on parameter[0]?
                    //                                storeType(pexp, setter.getParameters()[0].getType());
                    SetterInfo info = new SetterInfo(current, setterName, setters);
                    BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
                    if (enclosingBinaryExpression != null) {
                        putSetterInfo(enclosingBinaryExpression.getLeftExpression(), info);
                    }
                    String delegationData = receiver.getData();
                    if (delegationData != null) {
                        pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
                    }
                    return true;
                } else if (getter != null && propertyNode == null) {
                    pexp.putNodeMetaData(StaticTypesMarker.READONLY_PROPERTY, true);
                }
            }
            foundGetterOrSetter = foundGetterOrSetter || !setters.isEmpty() || getter != null;
            if (storeProperty(propertyNode, pexp, current, visitor, receiver.getData()))
                return true;
            if (storeField(field, true, pexp, current, visitor, receiver.getData(), !readMode))
                return true;
            // we stop now, otherwise we must check the parent class
            if (/*!isAttributeExpression && */
            current.getSuperClass() != null) {
                queue.add(current.getUnresolvedSuperClass());
            }
        }
        // GROOVY-5568, the property may be defined by DGM
        List<ClassNode> dgmReceivers = new ArrayList<ClassNode>(2);
        dgmReceivers.add(testClass);
        if (isPrimitiveType(testClass))
            dgmReceivers.add(getWrapper(testClass));
        for (ClassNode dgmReceiver : dgmReceivers) {
            List<MethodNode> methods = findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
            for (MethodNode m : findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
                if (Boolean_TYPE.equals(getWrapper(m.getReturnType())))
                    methods.add(m);
            }
            if (!methods.isEmpty()) {
                List<MethodNode> methodNodes = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
                if (methodNodes.size() == 1) {
                    MethodNode getter = methodNodes.get(0);
                    if (visitor != null) {
                        visitor.visitMethod(getter);
                    }
                    ClassNode cn = inferReturnTypeGenerics(dgmReceiver, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
                    storeInferredTypeForPropertyExpression(pexp, cn);
                    return true;
                }
            }
        }
    }
    for (Receiver<String> receiver : receivers) {
        ClassNode testClass = receiver.getType();
        ClassNode propertyType = getTypeForMapPropertyExpression(testClass, objectExpressionType, pexp);
        if (propertyType == null)
            propertyType = getTypeForListPropertyExpression(testClass, objectExpressionType, pexp);
        if (propertyType == null)
            propertyType = getTypeForSpreadExpression(testClass, objectExpressionType, pexp);
        if (propertyType == null)
            continue;
        if (visitor != null) {
            // todo : type inference on maps and lists, if possible
            PropertyNode node = new PropertyNode(propertyName, Opcodes.ACC_PUBLIC, propertyType, receiver.getType(), null, null, null);
            node.setDeclaringClass(receiver.getType());
            visitor.visitProperty(node);
        }
        storeType(pexp, propertyType);
        String delegationData = receiver.getData();
        if (delegationData != null)
            pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
        return true;
    }
    return foundGetterOrSetter;
}
Also used : LowestUpperBoundClassNode(org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode) ClassNode(org.codehaus.groovy.ast.ClassNode) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) FieldNode(org.codehaus.groovy.ast.FieldNode) ArrayList(java.util.ArrayList) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) LinkedList(java.util.LinkedList) MethodNode(org.codehaus.groovy.ast.MethodNode) PropertyNode(org.codehaus.groovy.ast.PropertyNode) LinkedHashSet(java.util.LinkedHashSet) HashSet(java.util.HashSet)

Aggregations

PropertyNode (org.codehaus.groovy.ast.PropertyNode)71 ClassNode (org.codehaus.groovy.ast.ClassNode)36 FieldNode (org.codehaus.groovy.ast.FieldNode)30 ArrayList (java.util.ArrayList)25 MethodNode (org.codehaus.groovy.ast.MethodNode)19 BlockStatement (org.codehaus.groovy.ast.stmt.BlockStatement)16 VariableExpression (org.codehaus.groovy.ast.expr.VariableExpression)12 Parameter (org.codehaus.groovy.ast.Parameter)11 InnerClassNode (org.codehaus.groovy.ast.InnerClassNode)10 Expression (org.codehaus.groovy.ast.expr.Expression)10 LowestUpperBoundClassNode (org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode)8 AnnotationNode (org.codehaus.groovy.ast.AnnotationNode)7 ConstructorNode (org.codehaus.groovy.ast.ConstructorNode)6 DynamicVariable (org.codehaus.groovy.ast.DynamicVariable)6 CastExpression (org.codehaus.groovy.ast.expr.CastExpression)6 ConstantExpression (org.codehaus.groovy.ast.expr.ConstantExpression)6 MethodCallExpression (org.codehaus.groovy.ast.expr.MethodCallExpression)6 SyntaxErrorMessage (org.codehaus.groovy.control.messages.SyntaxErrorMessage)6 SyntaxException (org.codehaus.groovy.syntax.SyntaxException)6 HashSet (java.util.HashSet)5