All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.walkmod.javalang.compiler.actions.LoadMethodDeclarationsAction Maven / Gradle / Ivy

There is a newer version: 2.3.10
Show newest version
/*
 * Copyright (C) 2015 Raquel Pau and Albert Coroleu.
 * 
 * Walkmod is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 * 
 * Walkmod is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with Walkmod. If
 * not, see .
 */
package org.walkmod.javalang.compiler.actions;

import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.walkmod.javalang.ast.MethodSymbolData;
import org.walkmod.javalang.ast.Node;
import org.walkmod.javalang.ast.SymbolDataAware;
import org.walkmod.javalang.ast.TypeParameter;
import org.walkmod.javalang.ast.body.AnnotationDeclaration;
import org.walkmod.javalang.ast.body.BodyDeclaration;
import org.walkmod.javalang.ast.body.ClassOrInterfaceDeclaration;
import org.walkmod.javalang.ast.body.ConstructorDeclaration;
import org.walkmod.javalang.ast.body.EnumConstantDeclaration;
import org.walkmod.javalang.ast.body.EnumDeclaration;
import org.walkmod.javalang.ast.body.MethodDeclaration;
import org.walkmod.javalang.ast.body.Parameter;
import org.walkmod.javalang.ast.body.TypeDeclaration;
import org.walkmod.javalang.ast.expr.ObjectCreationExpr;
import org.walkmod.javalang.ast.type.ClassOrInterfaceType;
import org.walkmod.javalang.ast.type.Type;
import org.walkmod.javalang.compiler.providers.SymbolActionProvider;
import org.walkmod.javalang.compiler.reflection.MethodInspector;
import org.walkmod.javalang.compiler.symbols.ASTSymbolTypeResolver;
import org.walkmod.javalang.compiler.symbols.MethodSymbol;
import org.walkmod.javalang.compiler.symbols.ReferenceType;
import org.walkmod.javalang.compiler.symbols.Scope;
import org.walkmod.javalang.compiler.symbols.Symbol;
import org.walkmod.javalang.compiler.symbols.SymbolAction;
import org.walkmod.javalang.compiler.symbols.SymbolTable;
import org.walkmod.javalang.compiler.symbols.SymbolType;
import org.walkmod.javalang.compiler.types.TypeVisitorAdapter;
import org.walkmod.javalang.compiler.types.TypesLoaderVisitor;
import org.walkmod.javalang.visitors.GenericVisitorAdapter;
import org.walkmod.javalang.visitors.VoidVisitorAdapter;

public class LoadMethodDeclarationsAction extends SymbolAction {

    private SymbolActionProvider actionProvider;
    private TypeVisitorAdapter expressionTypeAnalyzer;

    public LoadMethodDeclarationsAction(SymbolActionProvider actionProvider,
            TypeVisitorAdapter expressionTypeAnalyzer) {

        this.actionProvider = actionProvider;
        this.expressionTypeAnalyzer = expressionTypeAnalyzer;
    }

    private void pushMethod(SymbolType st, SymbolTable table, MethodDeclaration md) throws Exception {
        Scope sc = new Scope();
        table.pushScope(sc);
        LoadTypeParamsAction action = new LoadTypeParamsAction();
        action.load(table, md.getTypeParameters(), null);
        Type type = md.getType();
        SymbolType resolvedType = ASTSymbolTypeResolver.getInstance().valueOf(type);
        if (resolvedType == null) {
            resolvedType = new SymbolType(Object.class);
        } else {
            resolvedType.setClazz(TypesLoaderVisitor.getClassLoader().loadClass(resolvedType));
        }
        type.setSymbolData(resolvedType);

        List params = md.getParameters();
        List tps = md.getTypeParameters();
        SymbolType[] args = null;
        boolean hasDynamicArgs = false;
        if (params != null) {
            args = new SymbolType[params.size()];
            for (int i = 0; i < args.length; i++) {
                Parameter currentParam = params.get(i);

                args[i] = ASTSymbolTypeResolver.getInstance().valueOf(currentParam.getType(), tps);
                int arrayCount = currentParam.getId().getArrayCount();
                if (arrayCount > 0) {
                    args[i].setArrayCount(args[i].getArrayCount() + arrayCount);
                }
                params.get(i).getType().setSymbolData(args[i]);
                if (i == args.length - 1) {
                    hasDynamicArgs = params.get(i).isVarArgs();
                    if (hasDynamicArgs) {
                        args[i] = args[i].clone();
                        args[i].setArrayCount(args[i].getArrayCount() + 1);
                    }
                }

            }
        }

        List actions = null;

        if (actionProvider != null) {
            actions = actionProvider.getActions(md);
        }

        MethodSymbol method = new MethodSymbol(md.getName(), resolvedType, md, st, args, false, hasDynamicArgs,
                (Method) null, actions);
        sc.setRootSymbol(method);
        method.setInnerScope(sc);

        md.accept(expressionTypeAnalyzer, null);
        table.popScope(true);
        MethodSymbolData msd = md.getSymbolData();
        if (msd == null) {
            throw new RuntimeException("Ops! The following method can't be solved: " + md.toString());
        }
        method.setReferencedMethod(msd.getMethod());
        table.pushSymbol(method, true);
    }

    private void pushConstructor(SymbolType st, SymbolTable table, ConstructorDeclaration md) throws Exception {
        Type type = new ClassOrInterfaceType(md.getName());
        SymbolType resolvedType = ASTSymbolTypeResolver.getInstance().valueOf(type);
        type.setSymbolData(resolvedType);

        List params = md.getParameters();
        List tps = md.getTypeParameters();

        SymbolType[] args = null;
        boolean hasDynamicArgs = false;
        if (params != null) {
            args = new SymbolType[params.size()];
            for (int i = 0; i < args.length; i++) {

                args[i] = ASTSymbolTypeResolver.getInstance().valueOf(params.get(i).getType(), tps);

                params.get(i).getType().setSymbolData(args[i]);
                if (i == args.length - 1) {
                    hasDynamicArgs = params.get(i).isVarArgs();
                }
            }
        }
        List actions = null;
        if (actionProvider != null) {
            actions = actionProvider.getActions(md);
        }
        MethodSymbol method = new MethodSymbol(md.getName(), resolvedType, md, st, args, false, hasDynamicArgs,
                (java.lang.reflect.Constructor) null, actions);

        Scope scope = new Scope(method);
        method.setInnerScope(scope);
        table.pushScope(scope);
        md.accept(expressionTypeAnalyzer, null);
        table.popScope(true);

        method.setReferencedConstructor(md.getSymbolData().getConstructor());

        table.pushSymbol(method);
    }

    @Override
    public void doPush(Symbol symbol, SymbolTable table) throws Exception {
        if (symbol.getName().equals("this")) {
            Node node = symbol.getLocation();
            MethodsPopulator populator = new MethodsPopulator(table);
            node.accept(populator, table.getScopes().peek());
        }

    }

    private class MethodsPopulator extends VoidVisitorAdapter {

        private SymbolTable table;

        public MethodsPopulator(SymbolTable table) {
            this.table = table;
        }

        @Override
        public void visit(ObjectCreationExpr o, Scope scope) {
            if (!scope.hasMethodsLoaded()) {
                table.pushScope(scope);
                List types = new LinkedList();
                types.add(o.getType());
                loadExtendsOrImplements(types);
                loadMethods(o.getAnonymousClassBody(), scope);

                table.popScope(true);
            }
        }

        @Override
        public void visit(EnumConstantDeclaration o, Scope scope) {
            if (!scope.hasMethodsLoaded()) {
                table.pushScope(scope);
                loadMethods(o.getClassBody(), scope);
                table.popScope(true);
            }
        }

        @Override
        public void visit(ClassOrInterfaceDeclaration n, Scope scope) {
            if (!scope.hasMethodsLoaded()) {
                table.pushScope(scope);
                loadExtendsOrImplements(n.getExtends());
                loadMethods(n.getMembers(), scope);
                if (!n.isInterface()) {
                    loadExtendsOrImplements(n.getImplements());
                }
                table.popScope(true);
            }
        }

        private void loadMethods(List members, Scope scope) {
            if (!scope.hasMethodsLoaded() && members != null) {
                try {
                    for (BodyDeclaration member : members) {
                        if (member instanceof ConstructorDeclaration) {
                            ConstructorDeclaration cd = (ConstructorDeclaration) member;
                            pushConstructor((SymbolType) ((SymbolDataAware) member.getParentNode()).getSymbolData(),
                                    table, cd);
                        }
                        if (member instanceof MethodDeclaration) {
                            MethodDeclaration md = (MethodDeclaration) member;
                            pushMethod((SymbolType) ((SymbolDataAware) member.getParentNode()).getSymbolData(),
                                    table, md);

                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException("Error loading methods in a given scope", e);
                }
                scope.setHasMethodsLoaded(true);
            }
        }

        @Override
        public void visit(EnumDeclaration n, Scope scope) {
            table.pushScope(scope);
            loadMethods(n.getMembers(), scope);
            table.popScope(true);
        }

        @Override
        public void visit(AnnotationDeclaration n, Scope scope) {
            table.pushScope(scope);
            loadMethods(n.getMembers(), scope);
            table.popScope(true);
        }

        class NameBuilder extends GenericVisitorAdapter {
            public String visit(ClassOrInterfaceType n, Object arg) {
                String result = "";

                ClassOrInterfaceType scope = n.getScope();
                if (scope != null) {
                    result = scope.accept(this, arg) + ".";
                }

                return result + n.getName();
            }
        }

        private NameBuilder nameBuilder = new NameBuilder();

        private void loadMethods(Collection methods, List params, SymbolType st) {
            for (Method method : methods) {
                Map parameterTypes = null;
                try {
                    java.lang.reflect.Type[] genericParameterTypes = method.getGenericParameterTypes();
                    SymbolType[] methodArgs = new SymbolType[genericParameterTypes.length];
                    if (params != null) {
                        parameterTypes = new HashMap();

                        TypeVariable[] typeParams = method.getDeclaringClass().getTypeParameters();
                        for (int i = 0; i < params.size() && i < typeParams.length; i++) {
                            SymbolType.valueOf(typeParams[i], params.get(i), parameterTypes, null);
                        }
                    }

                    for (int i = 0; i < genericParameterTypes.length; i++) {
                        methodArgs[i] = SymbolType.valueOf(genericParameterTypes[i], parameterTypes);

                    }

                    SymbolType returnType = SymbolType.valueOf(method, parameterTypes);
                    MethodSymbol methodSymbol = new MethodSymbol(method.getName(), returnType, null, st, methodArgs,
                            false, method.isVarArgs(), method, null);

                    table.pushSymbol(methodSymbol);

                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }

        public void loadExtendsOrImplements(List extendsList) {
            if (extendsList != null) {
                for (ClassOrInterfaceType type : extendsList) {
                    String name = type.accept(nameBuilder, null);
                    Symbol s = table.findSymbol(name, ReferenceType.TYPE);
                    if (s == null) {
                        SymbolType st = ASTSymbolTypeResolver.getInstance().valueOf(type);
                        if (st == null || st.getClazz() == null) {
                            throw new RuntimeException("Error resolving  " + name);
                        }
                        name = st.getClazz().getCanonicalName();
                        s = table.findSymbol(name, ReferenceType.TYPE);
                    }

                    if (s != null) {
                        Object location = s.getLocation();
                        boolean isInterfaceImplementation = false;
                        if (location != null && location instanceof TypeDeclaration) {

                            if (s.getType().getClazz().isInterface()) {
                                Node parent = type.getParentNode();
                                if (parent != null) {
                                    if (parent instanceof ClassOrInterfaceDeclaration) {
                                        ClassOrInterfaceDeclaration decl = (ClassOrInterfaceDeclaration) parent;

                                        isInterfaceImplementation = !decl.isInterface();
                                    }
                                }
                            }
                            if (!isInterfaceImplementation) {
                                TypeDeclaration superClass = ((TypeDeclaration) location);
                                superClass.accept(this, s.getInnerScope());
                            } else {
                                // we need to load in the same scope the
                                // "default" methods
                                SymbolType typeArg = ASTSymbolTypeResolver.getInstance().valueOf(type);

                                Node parent = type.getParentNode();
                                if (parent instanceof SymbolDataAware) {
                                    SymbolDataAware ctxt = (SymbolDataAware) parent;
                                    Set methods = MethodInspector.getInhertitedDefaultMethods(
                                            typeArg.getClazz(), ctxt.getSymbolData().getClazz());
                                    if (!methods.isEmpty()) {
                                        Scope aux = s.getInnerScope();

                                        TypeDeclaration superClass = ((TypeDeclaration) location);

                                        superClass.accept(this, aux);

                                        Iterator it = methods.iterator();

                                        while (it.hasNext()) {
                                            Method current = it.next();
                                            Symbol sType = table.findSymbol(
                                                    current.getDeclaringClass().getCanonicalName(), ReferenceType.TYPE);
                                            if (sType != null) {
                                                aux = sType.getInnerScope();
                                                if (aux != null) {
                                                    Class[] argClasses = current.getParameterTypes();
                                                    SymbolType[] args = new SymbolType[argClasses.length];
                                                    for (int i = 0; i < args.length; i++) {
                                                        args[i] = new SymbolType(argClasses[i]);
                                                    }
                                                    Symbol sym = aux.findSymbol(current.getName(), null, args, null,
                                                            ReferenceType.METHOD);

                                                    if (sym != null) {
                                                        sym.getType().setMethod(current);
                                                        table.pushSymbol(sym);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                        } else {
                            Class clazz = s.getType().getClazz();
                            Set methods = null;

                            if (!isInterfaceImplementation) {
                                methods = MethodInspector.getInheritedMethods(clazz);
                                Symbol parent = table.findSymbol("super", ReferenceType.VARIABLE);
                                if (parent == null) {
                                    parent = table.pushSymbol("super", ReferenceType.TYPE, new SymbolType(clazz), null);
                                }

                                Scope parentScope = parent.getInnerScope();
                                if (parentScope == null) {
                                    parentScope = new Scope(parent);
                                    parent.setInnerScope(parentScope);
                                }

                                SymbolType typeArg = ASTSymbolTypeResolver.getInstance().valueOf(type);

                                List params = typeArg.getParameterizedTypes();

                                table.pushScope(parentScope);
                                loadMethods(methods, params, s.getType());
                                table.popScope(true);
                            } else {
                                SymbolType typeArg = ASTSymbolTypeResolver.getInstance().valueOf(type);
                                List params = typeArg.getParameterizedTypes();
                                methods = MethodInspector.getInhertitedDefaultMethods(typeArg.getClazz(), clazz);
                                loadMethods(methods, params, s.getType());
                            }

                        }
                    }
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy