Search in sources :

Example 86 with MethodNode

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

the class StaticTypeCheckingVisitor method addArrayMethods.

/**
     * add various getAt and setAt methods for primitive arrays
     * @param receiver the receiver class
     * @param name  the name of the method
     * @param args the argument classes
     */
private static void addArrayMethods(List<MethodNode> methods, ClassNode receiver, String name, ClassNode[] args) {
    if (args.length != 1)
        return;
    if (!receiver.isArray())
        return;
    if (!isIntCategory(getUnwrapper(args[0])))
        return;
    if ("getAt".equals(name)) {
        MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, receiver.getComponentType(), new Parameter[] { new Parameter(args[0], "arg") }, null, null);
        node.setDeclaringClass(receiver.redirect());
        methods.add(node);
    } else if ("setAt".equals(name)) {
        MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, VOID_TYPE, new Parameter[] { new Parameter(args[0], "arg") }, null, null);
        node.setDeclaringClass(receiver.redirect());
        methods.add(node);
    }
}
Also used : MethodNode(org.codehaus.groovy.ast.MethodNode) Parameter(org.codehaus.groovy.ast.Parameter)

Example 87 with MethodNode

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

the class StaticTypeCheckingVisitor method allowStaticAccessToMember.

/**
     * This method is used to filter search results in which null means "no match",
     * to filter out illegal access to instance members from a static context.
     *
     * Return null if the given member is not static, but we want to access in
     * a static way (staticOnly=true). If we want to access in a non-static way
     * we always return the member, since then access to static members and
     * non-static members is allowed.
     */
@SuppressWarnings("unchecked")
private <T> T allowStaticAccessToMember(T member, boolean staticOnly) {
    if (member == null)
        return null;
    if (!staticOnly)
        return member;
    boolean isStatic;
    if (member instanceof Variable) {
        Variable v = (Variable) member;
        isStatic = Modifier.isStatic(v.getModifiers());
    } else if (member instanceof List) {
        List<MethodNode> list = (List<MethodNode>) member;
        if (list.size() == 1) {
            return (T) Collections.singletonList(allowStaticAccessToMember(list.get(0), staticOnly));
        }
        return (T) Collections.emptyList();
    } else {
        MethodNode mn = (MethodNode) member;
        isStatic = mn.isStatic();
    }
    if (staticOnly && !isStatic)
        return null;
    return member;
}
Also used : Variable(org.codehaus.groovy.ast.Variable) DynamicVariable(org.codehaus.groovy.ast.DynamicVariable) MethodNode(org.codehaus.groovy.ast.MethodNode) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList)

Example 88 with MethodNode

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

the class StaticTypeCheckingVisitor method visitBinaryExpression.

@Override
public void visitBinaryExpression(BinaryExpression expression) {
    int op = expression.getOperation().getType();
    if (op == COMPARE_IDENTICAL || op == COMPARE_NOT_IDENTICAL) {
        // we'll report those as errors later
        return;
    }
    BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
    typeCheckingContext.pushEnclosingBinaryExpression(expression);
    try {
        final Expression leftExpression = expression.getLeftExpression();
        final Expression rightExpression = expression.getRightExpression();
        leftExpression.visit(this);
        SetterInfo setterInfo = removeSetterInfo(leftExpression);
        if (setterInfo != null) {
            if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
                return;
            }
        } else {
            rightExpression.visit(this);
        }
        ClassNode lType = getType(leftExpression);
        ClassNode rType = getType(rightExpression);
        if (isNullConstant(rightExpression)) {
            if (!isPrimitiveType(lType))
                // primitive types should be ignored as they will result in another failure
                rType = UNKNOWN_PARAMETER_TYPE;
        }
        BinaryExpression reversedBinaryExpression = binX(rightExpression, expression.getOperation(), leftExpression);
        ClassNode resultType = op == KEYWORD_IN ? getResultType(rType, op, lType, reversedBinaryExpression) : getResultType(lType, op, rType, expression);
        if (op == KEYWORD_IN) {
            // in case of the "in" operator, the receiver and the arguments are reversed
            // so we use the reversedExpression and get the target method from it
            storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
        } else if (op == LEFT_SQUARE_BRACKET && leftExpression instanceof VariableExpression && leftExpression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) == null) {
            storeType(leftExpression, lType);
        }
        if (resultType == null) {
            resultType = lType;
        }
        // if left expression is a closure shared variable, a second pass should be done
        if (leftExpression instanceof VariableExpression) {
            VariableExpression leftVar = (VariableExpression) leftExpression;
            if (leftVar.isClosureSharedVariable()) {
                // if left expression is a closure shared variable, we should check it twice
                // see GROOVY-5874
                typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<Void>(expression));
            }
        }
        if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
            // unchecked assignment
            // examples:
            // List<A> list = new LinkedList()
            // List<A> list = []
            // Iterable<A> list = new LinkedList()
            // in that case, the inferred type of the binary expression is the type of the RHS
            // "completed" with generics type information available in the LHS
            ClassNode completedType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
            resultType = completedType;
        }
        if (isArrayOp(op) && enclosingBinaryExpression != null && enclosingBinaryExpression.getLeftExpression() == expression && isAssignment(enclosingBinaryExpression.getOperation().getType()) && !lType.isArray()) {
            // left hand side of an assignment : map['foo'] = ...
            Expression enclosingBE_rightExpr = enclosingBinaryExpression.getRightExpression();
            if (!(enclosingBE_rightExpr instanceof ClosureExpression)) {
                enclosingBE_rightExpr.visit(this);
            }
            ClassNode[] arguments = { rType, getType(enclosingBE_rightExpr) };
            List<MethodNode> nodes = findMethod(lType.redirect(), "putAt", arguments);
            if (nodes.size() == 1) {
                typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingBE_rightExpr);
            } else if (nodes.isEmpty()) {
                addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
            }
        }
        boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression;
        if (!isEmptyDeclaration && isAssignment(op)) {
            if (rightExpression instanceof ConstructorCallExpression) {
                inferDiamondType((ConstructorCallExpression) rightExpression, lType);
            }
            ClassNode originType = getOriginalDeclarationType(leftExpression);
            typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
            // and we must update the result type
            if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) {
                resultType = originType;
            } else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) {
                // for example, LHS is List<ConcreteClass> and RHS is List<T> where T is a placeholder
                resultType = lType;
            }
            // make sure we keep primitive types
            if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
                resultType = originType;
            }
            // if we are in an if/else branch, keep track of assignment
            if (typeCheckingContext.ifElseForWhileAssignmentTracker != null && leftExpression instanceof VariableExpression && !isNullConstant(rightExpression)) {
                Variable accessedVariable = ((VariableExpression) leftExpression).getAccessedVariable();
                if (accessedVariable instanceof VariableExpression) {
                    VariableExpression var = (VariableExpression) accessedVariable;
                    List<ClassNode> types = typeCheckingContext.ifElseForWhileAssignmentTracker.get(var);
                    if (types == null) {
                        types = new LinkedList<ClassNode>();
                        ClassNode type = var.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
                        types.add(type);
                        typeCheckingContext.ifElseForWhileAssignmentTracker.put(var, types);
                    }
                    types.add(resultType);
                }
            }
            storeType(leftExpression, resultType);
            // if right expression is a ClosureExpression, store parameter type information
            if (leftExpression instanceof VariableExpression) {
                if (rightExpression instanceof ClosureExpression) {
                    Parameter[] parameters = ((ClosureExpression) rightExpression).getParameters();
                    leftExpression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, parameters);
                } else if (rightExpression instanceof VariableExpression && ((VariableExpression) rightExpression).getAccessedVariable() instanceof Expression && ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS) != null) {
                    Variable targetVariable = findTargetVariable((VariableExpression) leftExpression);
                    if (targetVariable instanceof ASTNode) {
                        ((ASTNode) targetVariable).putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS));
                    }
                }
            }
        } else if (op == KEYWORD_INSTANCEOF) {
            pushInstanceOfTypeInfo(leftExpression, rightExpression);
        }
        if (!isEmptyDeclaration) {
            storeType(expression, resultType);
        }
    } finally {
        typeCheckingContext.popEnclosingBinaryExpression();
    }
}
Also used : LowestUpperBoundClassNode(org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode) ClassNode(org.codehaus.groovy.ast.ClassNode) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) Variable(org.codehaus.groovy.ast.Variable) DynamicVariable(org.codehaus.groovy.ast.DynamicVariable) ClosureSignatureHint(groovy.transform.stc.ClosureSignatureHint) MethodNode(org.codehaus.groovy.ast.MethodNode) ASTNode(org.codehaus.groovy.ast.ASTNode) Parameter(org.codehaus.groovy.ast.Parameter)

Example 89 with MethodNode

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

the class TraitASTTransformation method createHelperClass.

private ClassNode createHelperClass(final ClassNode cNode) {
    ClassNode helper = new InnerClassNode(cNode, Traits.helperClassName(cNode), ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, ClassNode.EMPTY_ARRAY, null);
    cNode.setModifiers(ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT);
    checkInnerClasses(cNode);
    MethodNode initializer = createInitMethod(false, cNode, helper);
    MethodNode staticInitializer = createInitMethod(true, cNode, helper);
    // apply the verifier to have the property nodes generated
    generatePropertyMethods(cNode);
    // prepare fields
    List<FieldNode> fields = new ArrayList<FieldNode>();
    Set<String> fieldNames = new HashSet<String>();
    for (FieldNode field : cNode.getFields()) {
        if (!"metaClass".equals(field.getName()) && (!field.isSynthetic() || field.getName().indexOf('$') < 0)) {
            fields.add(field);
            fieldNames.add(field.getName());
        }
    }
    ClassNode fieldHelper = null;
    if (!fields.isEmpty()) {
        fieldHelper = new InnerClassNode(cNode, Traits.fieldHelperClassName(cNode), ACC_STATIC | ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT, ClassHelper.OBJECT_TYPE);
    }
    // add methods
    List<MethodNode> methods = new ArrayList<MethodNode>(cNode.getMethods());
    List<MethodNode> nonPublicAPIMethods = new LinkedList<MethodNode>();
    for (final MethodNode methodNode : methods) {
        boolean declared = methodNode.getDeclaringClass() == cNode;
        if (declared) {
            if (!methodNode.isSynthetic() && (methodNode.isProtected() || methodNode.getModifiers() == 0)) {
                unit.addError(new SyntaxException("Cannot have protected/package private method in a trait (" + cNode.getName() + "#" + methodNode.getTypeDescriptor() + ")", methodNode.getLineNumber(), methodNode.getColumnNumber()));
                return null;
            }
            helper.addMethod(processMethod(cNode, helper, methodNode, fieldHelper, fieldNames));
            if (methodNode.isPrivate() || methodNode.isStatic()) {
                nonPublicAPIMethods.add(methodNode);
            }
        }
    }
    // remove methods which should not appear in the trait interface
    for (MethodNode privateMethod : nonPublicAPIMethods) {
        cNode.removeMethod(privateMethod);
    }
    // add fields
    for (FieldNode field : fields) {
        processField(field, initializer, staticInitializer, fieldHelper, helper, cNode, fieldNames);
    }
    // clear properties to avoid generation of methods
    cNode.getProperties().clear();
    // copy annotations
    copyClassAnnotations(cNode, helper);
    // reuse the full list of fields
    fields = new ArrayList<FieldNode>(cNode.getFields());
    for (FieldNode field : fields) {
        cNode.removeField(field.getName());
    }
    // visit AST xforms
    registerASTTranformations(helper);
    unit.getAST().addClass(helper);
    if (fieldHelper != null) {
        unit.getAST().addClass(fieldHelper);
    }
    // resolve scope (for closures)
    resolveScope(helper);
    if (fieldHelper != null) {
        resolveScope(fieldHelper);
    }
    return helper;
}
Also used : InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) ClassNode(org.codehaus.groovy.ast.ClassNode) 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) SyntaxException(org.codehaus.groovy.syntax.SyntaxException) HashSet(java.util.HashSet)

Example 90 with MethodNode

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

the class StaticTypeCheckingVisitor method visitClass.

@Override
public void visitClass(final ClassNode node) {
    if (shouldSkipClassNode(node))
        return;
    if (extension.beforeVisitClass(node)) {
        extension.afterVisitClass(node);
        return;
    }
    Object type = node.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
    if (type != null) {
        // transformation has already been run on this class node
        // so we'll use a silent collector in order not to duplicate errors
        typeCheckingContext.pushErrorCollector();
    }
    typeCheckingContext.pushEnclosingClassNode(node);
    Set<MethodNode> oldVisitedMethod = typeCheckingContext.alreadyVisitedMethods;
    typeCheckingContext.alreadyVisitedMethods = new LinkedHashSet<MethodNode>();
    super.visitClass(node);
    Iterator<InnerClassNode> innerClasses = node.getInnerClasses();
    while (innerClasses.hasNext()) {
        InnerClassNode innerClassNode = innerClasses.next();
        visitClass(innerClassNode);
    }
    typeCheckingContext.alreadyVisitedMethods = oldVisitedMethod;
    node.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, node);
    // works in a two pass sequence and we don't want to skip the second pass
    for (MethodNode methodNode : node.getMethods()) {
        methodNode.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE);
    }
    for (ConstructorNode constructorNode : node.getDeclaredConstructors()) {
        constructorNode.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE);
    }
    extension.afterVisitClass(node);
}
Also used : MethodNode(org.codehaus.groovy.ast.MethodNode) ConstructorNode(org.codehaus.groovy.ast.ConstructorNode) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode)

Aggregations

MethodNode (org.codehaus.groovy.ast.MethodNode)294 ClassNode (org.codehaus.groovy.ast.ClassNode)193 InnerClassNode (org.codehaus.groovy.ast.InnerClassNode)94 Parameter (org.codehaus.groovy.ast.Parameter)79 BlockStatement (org.codehaus.groovy.ast.stmt.BlockStatement)65 FieldNode (org.codehaus.groovy.ast.FieldNode)57 LinkedList (java.util.LinkedList)50 VariableExpression (org.codehaus.groovy.ast.expr.VariableExpression)50 Expression (org.codehaus.groovy.ast.expr.Expression)49 LowestUpperBoundClassNode (org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode)47 MethodCallExpression (org.codehaus.groovy.ast.expr.MethodCallExpression)43 AnnotationNode (org.codehaus.groovy.ast.AnnotationNode)39 ArrayList (java.util.ArrayList)36 ExpressionStatement (org.codehaus.groovy.ast.stmt.ExpressionStatement)36 Statement (org.codehaus.groovy.ast.stmt.Statement)34 ConstantExpression (org.codehaus.groovy.ast.expr.ConstantExpression)33 ArgumentListExpression (org.codehaus.groovy.ast.expr.ArgumentListExpression)30 ClassExpression (org.codehaus.groovy.ast.expr.ClassExpression)29 BinaryExpression (org.codehaus.groovy.ast.expr.BinaryExpression)27 ReturnStatement (org.codehaus.groovy.ast.stmt.ReturnStatement)27