Package dk.brics.jwig.analysis.jaive

Source Code of dk.brics.jwig.analysis.jaive.InterfaceInvocationChecker

package dk.brics.jwig.analysis.jaive;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

import soot.RefType;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.jimple.DefinitionStmt;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.internal.JArrayRef;
import soot.jimple.internal.JAssignStmt;
import soot.jimple.internal.JNewArrayExpr;
import soot.toolkits.graph.CompleteUnitGraph;
import dk.brics.jwig.RequiredParameter;
import dk.brics.jwig.URLPattern;
import dk.brics.jwig.WebApp;
import dk.brics.jwig.WebContext;
import dk.brics.jwig.analysis.JwigResolver;
import dk.brics.jwig.analysis.Parameter;
import dk.brics.jwig.analysis.Parameters;
import dk.brics.jwig.analysis.VarArgInfo;
import dk.brics.jwig.analysis.graph.FilterState;
import dk.brics.jwig.analysis.graph.FilterTransition;
import dk.brics.jwig.analysis.graph.HandlerTransition;
import dk.brics.jwig.analysis.graph.PredecessorResolver;
import dk.brics.jwig.analysis.graph.State;
import dk.brics.jwig.analysis.graph.StateMachine;
import dk.brics.jwig.analysis.graph.StateMachine.MethodStatementContainer;
import dk.brics.jwig.analysis.graph.Transition;
import dk.brics.jwig.analysis.graph.WebMethodTransition;
import dk.brics.jwig.analysis.jaive.feedback.ArityMismatch;
import dk.brics.jwig.analysis.jaive.feedback.CouldNotFindCommonSuperType;
import dk.brics.jwig.analysis.jaive.feedback.CouldNotInferRecordTypeForVarArgs;
import dk.brics.jwig.analysis.jaive.feedback.Feedbacks;
import dk.brics.jwig.analysis.jaive.feedback.FilterParameterTypeMismatch;
import dk.brics.jwig.analysis.jaive.feedback.MakeURLCallDoesNotTypeCheck;
import dk.brics.jwig.analysis.jaive.feedback.MissingFilterParameter;
import dk.brics.jwig.analysis.jaive.feedback.PossiblyMissingFilterParameter;
import dk.brics.jwig.analysis.jaive.feedback.UnanalyzedVarArgs;
import dk.brics.jwig.analysis.jaive.feedback.WebAppParamMismatch;

/**
* Type checks invocations of webmethods, filters and the run-methods of
* AbstractHandlers.
*/
public class InterfaceInvocationChecker {

    private final JwigResolver resolver;

    private static final Logger log = Logger
            .getLogger(InterfaceInvocationChecker.class);

    public InterfaceInvocationChecker() {
        this.resolver = JwigResolver.get();
    }

    public void check(StateMachine machine) {
        log.info("Type checking invocations of makeURL.");
        typeCheckInvocations(machine);
        log.info("Done checking invocations of makeURL.");
        log.info("Checking filter invocations.");
        PredecessorResolver predecessorResolver = new PredecessorResolver(
                machine);
        for (FilterState filter : machine.getFilterStates()) {
            checkFilterParameters(predecessorResolver, filter);
        }
        log.info("Done checking filter invocations.");
        // new FormChecker(machine).checkForms();
    }

    /**
     * Checks if the arguments passed to a filter is sufficiently present.
     *
     * @see #checkFilterParameters(PredecessorResolver, FilterState)
     *
     * @param webMethod
     *            as the invoking webmethod
     * @param webMethodParameters
     *            as the parameters of the invoking webmethod
     * @param p
     *            as the parameter of the filter to check.
     */
    private void checkFilterParameterRequired(SootMethod webMethod,
            Parameters webMethodParameters, Parameter p) {
        if (p.isRequired()) {
            Parameter fromParameter = webMethodParameters.getParameter(p
                    .getName());
            if (fromParameter == null)
                Feedbacks.add(new MissingFilterParameter(p, webMethod));
            else {
                if (!fromParameter.isRequired())
                    Feedbacks.add(new PossiblyMissingFilterParameter(p,
                            fromParameter.getHost()));
            }
        }
    }

    /**
     * Checks the invocation of filters with respect to the WebMethods which can
     * hit this filter. The parameters of the WebMethod is name matched and
     * type-checked against the filterparameters.
     *
     * If the annotaion {@link RequiredParameter} is present at a filter
     * parameter, the WebMethod is supposed to use this annotation too for the
     * corresponding parameter. If the annotation is missing at the WebMethod,
     * but the parameter is present - a warning is issued. But if the parameter
     * is missing, it is an error.
     *
     * @param predecessorResolver
     *            as the {@link PredecessorResolver} to use to find the
     *            WebMethods invoking this filter.
     * @param filter
     *            as the filter to check.
     */
    private void checkFilterParameters(PredecessorResolver predecessorResolver,
            FilterState filter) {
        Parameters filterParameters = resolver
                .getParameters(filter.getMethod());
        Set<Transition> predecessors = predecessorResolver
                .getPredecessors(filter);
        for (Transition transition : predecessors) {
            State origin = transition.getOrigin();
            final SootMethod webMethod = origin.getMethod();
            final Parameters webMethodParameters = resolver
                    .getParameters(webMethod);
            checkFilterParameters(webMethod, webMethodParameters,
                    filterParameters);
        }
    }

    /**
     * Checks the filter parameters.
     *
     * @see #checkFilterParameters(PredecessorResolver, FilterState)
     *
     * @param webMethod
     *            as the invoking webmethod
     * @param webMethodParameters
     *            as the parameters of the invoking webmethod
     * @param filterParameters
     *            as the parameters of the invoked filter.
     */
    private void checkFilterParameters(SootMethod webMethod,
            Parameters webMethodParameters, Parameters filterParameters) {
        for (Parameter filterParameter : filterParameters.getParameters()) {
            checkFilterParameterRequired(webMethod, webMethodParameters,
                    filterParameter);
            Parameter fromParameter = webMethodParameters
                    .getParameter(filterParameter.getName());
            // type check the matching parameters:
            if (fromParameter != null) {
                final Type toType = filterParameter.getType();
                if (!resolver.isParameterType(toType)
                        && !resolver.isAssignable(fromParameter.getType(),
                                toType))
                    Feedbacks.add(new FilterParameterTypeMismatch(
                            fromParameter, filterParameter));
            }
        }
    }

    /**
     * Checks if a non-mapped {@link WebContext#makeURL(String, Object...)}
     * invocation between two {@link WebApp}s is legal with respect to the
     * webapp params of the two involved webapps.
     *
     * The invocation is illegal if the two webapps doesn't have the same webapp
     * params. In that case the mapped version of makeURL should be used.
     *
     * @param contextClass
     *            as the WebApp context the invocation is invoked in
     * @param transition
     *            as the transition representing the invocation.
     * @param enclosingMethod
     *            as the method containing the invocation
     * @param enclosingStatement
     *            as the statement containing the invocation
     */
    private void checkWebAppParams(SootClass contextClass,
            WebMethodTransition transition, SootMethod enclosingMethod,
            Stmt enclosingStatement) {
        final SootClass targetClass = transition.getTarget().getMethod()
                .getDeclaringClass();
        InvokeExpr expr = transition.getExpr();

        // ignore transitions to the same class
        if (targetClass.equals(contextClass))
            return;

        Set<String> originParams = getWebAppParams(contextClass);
        Set<String> targetParams = getWebAppParams(targetClass);
        if (!originParams.equals(targetParams)
                && !resolver.isMappedMakeURL(expr.getMethod())) {
            // find the mismatch:

            HashSet<String> originCopy = new HashSet<String>(originParams);
            HashSet<String> targetCopy = new HashSet<String>(targetParams);
            originCopy.removeAll(targetParams);
            targetCopy.removeAll(originParams);
            if (!originCopy.isEmpty() || !targetCopy.isEmpty()) {
                Feedbacks.add(new WebAppParamMismatch(enclosingMethod,
                        enclosingStatement, targetClass, originParams,
                        targetParams, contextClass));
            }
        }
    }

    /**
     * Utility method for fetching as information about the varArg parameter
     * given to makeURL.
     *
     * If no information can be found (i.e. not instantiated like: 'new X[3]'),
     * null is returned.
     *
     * @param jAssignStmt
     *            as the assignement to the varArgs variable
     * @param makeURLEnclosingMethod
     *            the method enclosing the makeURL expression (for error
     *            reporting)
     * @param makeURLStatement
     *            the statement containing the makeURLExpression (for error
     *            reporting)
     * @return the {@link VarArgInfo} or null
     */
    private VarArgInfo getVarArgInfo(DefinitionStmt jAssignStmt) {
        Value right = jAssignStmt.getRightOp();
        if (right instanceof JNewArrayExpr) {
            // "= new X[3]; // where X is the basetype"
            JNewArrayExpr jNewArrayExpr = (JNewArrayExpr) right;
            Value size = jNewArrayExpr.getSize();
            if (size instanceof IntConstant) {
                IntConstant constant = (IntConstant) size;
                Type[] varArgTypes = new Type[constant.value];
                Type varArgBase = jNewArrayExpr.getBaseType();
                VarArgInfo varArgInfo = new VarArgInfo(constant.value,
                        varArgTypes, varArgBase);
                return varArgInfo;
            }
        }
        return null;
    }

    /**
     * Finds all webapp params of a class (most probably a WebApp) by analyzing
     * the {@link URLPattern} (if any).
     *
     * @param classs
     *            as the class to analyze
     * @return the set of webapp parameter names for this class.
     */
    private Set<String> getWebAppParams(SootClass classs) {
        Class<?> webapp = resolver.getJavaClass(classs);
        URLPattern pattern = webapp.getAnnotation(URLPattern.class);
        if (pattern != null)
            return new HashSet<String>(new MyPatternMatcher(pattern.value(),
                    false).getParameters());
        return new HashSet<String>();
    }

    /**
     * Type checks a single invocation: either a WebMethod or a RunMethod.
     */
    private void typeCheckInvocations(StateMachine machine) {
        Set<State> states = machine.getStateFlattener().getAllStates();
        for (State state : states) {
            for (Transition t : state.getTransitions()) {
                if (t instanceof HandlerTransition) {
                    // TODO move enough information into the HandlerTransition
                    // to do the analysis here?
                } else if (t instanceof WebMethodTransition) {
                    final WebMethodTransition webMethodTransition = (WebMethodTransition) t;
                    final InvokeExpr expr = webMethodTransition.getExpr();
                    MethodStatementContainer container = machine
                            .getMakeURLLocation(expr);
                    SootMethod enclosingMethod = container.getMethod();
                    Stmt enclosingStatement = container.getStatement();

                    typeCheckWebMethodInvocation(webMethodTransition,
                            enclosingMethod, enclosingStatement);

                    final Set<SootClass> possibleContexts = InterfaceInvocationLinker
                            .findPredecessorWebApp(machine, resolver,
                                    enclosingMethod);
                    for (SootClass context : possibleContexts)
                        checkWebAppParams(context, webMethodTransition,
                                enclosingMethod, enclosingStatement);
                } else if (t instanceof FilterTransition) {
                    // TODO move the filter checking here (with cache to keep
                    // performance)
                }
            }
        }
    }

    /**
     * Type checks a single WebMethod invocation.
     *
     * This is done by comparing the varArgs given to makeURL with the
     * parameters of the webmethod. Does not support overloading. If the user
     * constructed the varargs array himself, the analysis could suffer greatly.
     *
     * Will report errors if something doesn't type check.
     *
     * @param transition
     *            as the {@link WebMethodTransition} representing the
     *            invocation.
     * @param enclosingMethod
     *            as the method enclosing the invocation
     * @param enclosingStatement
     *            as the statement enclosing the invocation
     */
    private void typeCheckWebMethodInvocation(WebMethodTransition transition,
            SootMethod enclosingMethod, Stmt enclosingStatement) {
        InvokeExpr expr = transition.getExpr();
        SootMethod webMethod = transition.getTarget().getMethod();
        Type[] varArgTypes = null;
        Type varArgBase = null;
        CompleteUnitGraph graph = new CompleteUnitGraph(
                enclosingMethod.retrieveActiveBody());

        Value varArgs = expr.getArg(expr.getArgs().size() - 1);
        int varArgSize = -1;

        // go through all assignment to determine the varArg types given
        // to makeURL. It is assumed that the varargs array is generated by
        // soot.
        boolean skipAnalysis = false;
        for (Unit unit : graph) {
            assert unit instanceof Stmt;
            Stmt statement = (Stmt) unit;
            if (statement instanceof JAssignStmt) {
                JAssignStmt jAssignStmt = (JAssignStmt) statement;
                if (jAssignStmt.getLeftOp().equals(varArgs)) {
                    // assigning to the varArgs variable
                    log.debug("Found varargs instantiation");

                    VarArgInfo varArgInfo = getVarArgInfo(jAssignStmt);
                    if (varArgInfo != null) {
                        varArgSize = varArgInfo.getSize();
                        varArgTypes = varArgInfo.getTypes();
                        varArgBase = varArgInfo.getBaseType();
                    } else {
                        Feedbacks.add(new UnanalyzedVarArgs(enclosingMethod,
                                enclosingStatement));
                        // we cant do anything, but to skip this analysis
                        skipAnalysis = true;
                        break;
                    }
                } else if (!skipAnalysis
                        && jAssignStmt.getLeftOp() instanceof JArrayRef) {
                    JArrayRef jArrayRef = (JArrayRef) jAssignStmt.getLeftOp();
                    if (jArrayRef.getBase().equals(varArgs)) {
                        // assigning to the varArg array values. Compute
                        // the type at the assigned position. This will always
                        // happen after the varargs instantiation!

                        if (jArrayRef.getIndex() instanceof IntConstant) {
                            IntConstant intConstant = (IntConstant) jArrayRef
                                    .getIndex();
                            int index = intConstant.value;
                            Type oldType = varArgTypes[index];
                            Type type = jAssignStmt.getRightOp().getType();
                            if (oldType instanceof RefType
                                    && type instanceof RefType) {
                                RefType refType = (RefType) oldType;
                                RefType refType2 = (RefType) type;
                                type = resolver.getLeastCommonSupertypeOf(
                                        refType, refType2);
                            } else if (oldType == null || oldType.equals(type)) {
                                // do nothing, new type is good enough
                            } else {
                                Feedbacks.add(new CouldNotFindCommonSuperType(
                                        oldType, type, enclosingMethod,
                                        enclosingStatement));
                            }
                            varArgTypes[index] = type;
                        } else {
                            // unknown index: reset the varArgTypes

                            // TODO complete reset is overkill, least
                            // common
                            // super type is sufficient, but still
                            // unlikely
                            // to make any sense.
                            Feedbacks
                                    .add(new CouldNotInferRecordTypeForVarArgs(
                                            enclosingMethod,
                                            enclosingStatement, varArgBase));
                            final int length = varArgTypes.length;
                            for (int i = 0; i < length; i++) {
                                varArgTypes[i] = varArgBase;
                            }
                        }
                    }
                }
            }
        }
        if (!skipAnalysis) {
            int count = webMethod.getParameterCount();
            if (count == varArgSize) {
                // the number of varArgs match the method parameter count
                @SuppressWarnings("unchecked")
                List<Type> types = webMethod.getParameterTypes();
                if (!resolver.typeCheckArguments(varArgTypes, types)) {
                    Feedbacks.add(new MakeURLCallDoesNotTypeCheck(varArgTypes,
                            types, webMethod, enclosingMethod,
                            enclosingStatement));
                }
            } else {
                Feedbacks.add(new ArityMismatch(webMethod, count, varArgSize,
                        enclosingMethod, enclosingStatement));
            }
        }

    }
}
TOP

Related Classes of dk.brics.jwig.analysis.jaive.InterfaceInvocationChecker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.