Package dk.brics.jwig.analysis.jaive

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

package dk.brics.jwig.analysis.jaive;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import soot.Body;
import soot.Local;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.ValueBox;
import soot.jimple.ClassConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.internal.JAssignStmt;
import soot.jimple.internal.JIdentityStmt;
import soot.toolkits.scalar.SimpleLocalDefs;
import dk.brics.automaton.Automaton;
import dk.brics.jwig.WebApp;
import dk.brics.jwig.WebSite;
import dk.brics.jwig.analysis.JwigResolver;
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.LambdaTransition;
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.WebMethodState;
import dk.brics.jwig.analysis.graph.WebMethodTransition;
import dk.brics.jwig.analysis.jaive.feedback.DirectFilterInvocation;
import dk.brics.jwig.analysis.jaive.feedback.Feedbacks;
import dk.brics.jwig.analysis.jaive.feedback.MayHitMoreUnexistingWebMethods;
import dk.brics.jwig.analysis.jaive.feedback.NoWebMethodHit;
import dk.brics.jwig.analysis.jaive.feedback.NonConstantWebAppArgumentUsed;
import dk.brics.jwig.analysis.jaive.feedback.UnregisteredWebAppReference;
import dk.brics.jwig.analysis.jaive.feedback.UnresolvedWebAppGivenToMakeURL;
import dk.brics.jwig.analysis.jaive.feedback.UnusedFilter;

/**
* Links makeURL invocations to WebMethods (and the run-methods of
* submithandlers).
*/
public class InterfaceInvocationLinker {
    Logger log = Logger.getLogger(InterfaceInvocationLinker.class);
    private final JwigResolver resolver;

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

    /**
     * Links makeURL invocations to WebMethods (and the run-methods of
     * submithandlers). This is done by altering the call graph of the
     * {@link WebSite}.
     *
     * Once done, the {@link StateMachine} represents a call graph containing
     * non-java calls too.
     *
     * @param interfacee
     *            as the interface of the {@link WebSite} to analyze
     * @param stateMachine
     *            as the (java) call graph
     * @param resolver
     * @return
     */
    public void link(Interface interfacee, StateMachine stateMachine) {
        log.info("Linking makeURL invocations to the WebMethod interfaces");
        // TODO make light weight String constant analysis for makeURL with
        // fallback to StringAnalysis, in the same way as the analysis for
        // XML.plug("foo",o)
        MyStringAnalysis analysis = new MyStringAnalysis(stateMachine,
                getMakeURLInvokingClasses(stateMachine));
        for (InvokeExpr expr : stateMachine.getMakeURLExpressions()) {
            linkMakeURL(stateMachine, expr, analysis, interfacee);
        }
        linkFilterGroups(interfacee, stateMachine);

        log.info("Done linking makeURL invocations to the Registered Methods");
        log.info("Checking for ambiguous priorities");
        PredecessorResolver predecessorResolver = new PredecessorResolver(
                stateMachine);
        // TODO move to checking phase
        for (WebMethodState state : stateMachine.getInitialStates())
            checkPriorities(state);
        // TODO move to checking phase
        for (FilterState state : stateMachine.getFilterStates())
            checkFilterInvocation(state, predecessorResolver);

        log.info("Done checking for ambiguous priorities");
    }

    private Set<SootClass> getMakeURLInvokingClasses(StateMachine stateMachine) {
        Set<SootClass> classes = new HashSet<SootClass>();
        for (MethodStatementContainer container : stateMachine
                .getMakeURLLocations()) {
            SootClass c = container.getMethod().getDeclaringClass();
            classes.add(c);
        }
        return classes;
    }

    private void linkFilterGroups(Interface interfacee,
            StateMachine stateMachine) {
        final Collection<WebMethodState> initialStates = stateMachine
                .getInitialStates();
        for (WebMethodState initialState : initialStates) {
            FilterGroup filterGroup = interfacee.getFilterGroup(resolver
                    .getJavaMethod(initialState.getMethod()));

            // get WebMethod state
            Method method = filterGroup.getWebMethod();
            SootMethod webMethod = resolver.getSootMethod(method);
            State webMethodState = stateMachine.getState(webMethod);
            webMethodState.setDefaultPriority(filterGroup.isDefaultPriority());
            webMethodState.setPriority(filterGroup.getPriority());

            for (Filter filter : filterGroup.getFilters()) {
                // get the Filter state
                final SootMethod filterMethod = resolver.getSootMethod(filter
                        .getMethod());
                State filterState = stateMachine.getState(filterMethod);
                filterState.setPriority(filter.getPriority());
                filterState.setDefaultPriority(filter.isDefaultPriority());

                // link the WebMethod of the FilterGroup to the Filter
                FilterTransition filterTransition = new FilterTransition();
                webMethodState.addSuccessor(filterTransition);
                filterTransition.setTarget(filterState);
            }
        }
    }

    private void checkFilterInvocation(FilterState state,
            PredecessorResolver predecessorResolver) {
        final Set<Transition> predecessors = predecessorResolver
                .getPredecessors(state);
        if (predecessors.isEmpty())
            Feedbacks.add(new UnusedFilter(state.getMethod()));
    }

    /**
     * Checks the {@link FilterTransition}s from a {@link WebMethodState} for
     * ambiguities. If two transitions have the same priority, they are said to
     * have ambiguous priority.
     *
     * @param state
     *            as the {@link WebMethodState} to check.
     */
    private void checkPriorities(WebMethodState state) {
        Map<Integer, State> priorityMap = new HashMap<Integer, State>();
        for (Transition transition : state.getTransitions()) {
            if (transition instanceof FilterTransition) {
                final State target = transition.getTarget();
                if (!target.isDefaultPriority()) {
                    final Integer priority = target.getPriority();
                    if (priorityMap.containsKey(priority)) {
                        Feedbacks.add(new AmbiguousPriority(state, target,
                                priorityMap.get(priority)));
                    } else {
                        priorityMap.put(priority, target);
                    }
                }
            }
        }
    }

    /**
     * Adds edges from the enclosing method of a makeURL invocation to the
     * target webmethods (plural) of the makeURL. Thus the StateMachine is
     * modified.
     *
     * @param interfacee
     */
    private void linkMakeURL(StateMachine stateMachine, InvokeExpr expr,
            MyStringAnalysis analysis, Interface interfacee) {

        SootMethod makeURLMethod = expr.getMethod();

        Automaton possibleValues = analysis
                .getPossibleNameValuesOfMakeURLInvocation(expr);

        MethodStatementContainer container = stateMachine
                .getMakeURLLocation(expr);
        SootMethod enclosingMethod = container.getMethod();
        Stmt statement = container.getStatement();

        Set<SootClass> targetedWebApps = getTargetedWebApps(stateMachine, expr,
                makeURLMethod, enclosingMethod, statement, interfacee);

        // makeURL called in an unknown context
        if (targetedWebApps.isEmpty()) {
            Feedbacks.add(new UnresolvedWebAppGivenToMakeURL(enclosingMethod,
                    statement));
        }

        final State callerState = stateMachine.getState(enclosingMethod);
        for (SootClass containingClass : targetedWebApps) {
            Set<Method> webMethods = getTargetedWebMethods(possibleValues,
                    containingClass, enclosingMethod, statement, interfacee);
            for (Method webMethod : webMethods) {
                // create state
                SootMethod method = resolver.getSootMethod(webMethod);
                State webMethodState = stateMachine.getState(method);

                // link
                Transition transition = new WebMethodTransition(expr);
                callerState.addSuccessor(transition);
                transition.setTarget(webMethodState);
            }
        }
    }

    private Set<SootClass> getTargetedWebApps(StateMachine stateMachine,
            InvokeExpr expr, SootMethod makeURLMethod,
            SootMethod enclosingMethod, Stmt statement, Interface interfacee) {
        int classParamPosition = resolver
                .findClassPositionInParameterList(makeURLMethod);
        Set<SootClass> targetedWebApps = new HashSet<SootClass>();
        if (classParamPosition != -1) {
            // class parameter used:
            ValueBox classParameterArg = expr.getArgBox(classParamPosition);
            Value classParameter = classParameterArg.getValue();
            if (classParameter instanceof ClassConstant) {
                // constant class used, add it to the set of containing classes.
                ClassConstant classConstant = (ClassConstant) classParameter;
                targetedWebApps.add(Scene.v().getSootClass(
                        classConstant.getValue().replace('/', '.')));
            } else {
                // not constant: extract the types by hand
                Set<Type> reachingTypes = getReachingWebAppClassDescriptors(
                        classParameter, statement,
                        enclosingMethod.retrieveActiveBody());
                for (Type type : reachingTypes) {
                    // the makeURL signature guarantees that the type is a
                    // WebApp
                    targetedWebApps.add(((RefType) type).getSootClass());
                }
            }
        } else {
            // class parameter not used, find the context:
            targetedWebApps.addAll(findPredecessorWebApp(stateMachine,
                    resolver, enclosingMethod));
        }

        // check if all makeURLs point to registered WebApps
        Collection<SootClass> unregisteredWebApps = new LinkedList<SootClass>();
        final Set<SootClass> webApps = new HashSet<SootClass>(
                getSootClasses(interfacee.getWebApps()));

        for (SootClass targetedWebApp : targetedWebApps) {
            if (!webApps.contains(targetedWebApp)) {
                Feedbacks.add(new UnregisteredWebAppReference(targetedWebApp,
                        enclosingMethod, statement));
                unregisteredWebApps.add(targetedWebApp);
            }
        }
        targetedWebApps.removeAll(unregisteredWebApps);
        return targetedWebApps;
    }

    public Set<Type> getReachingWebAppClassDescriptors(Value value, Stmt st,
            Body body) {
        SimpleLocalDefs simpleLocalDefs = resolver.getSimpleLocalDefs(body);
        return getReachingWebAppClassDescriptors(value, st, simpleLocalDefs,
                body.getMethod());
    }

    private Set<Type> getReachingWebAppClassDescriptors(Value value, Stmt st,
            SimpleLocalDefs simpleLocalDefs, SootMethod enclosingMethod) {
        Set<Type> types = new HashSet<Type>();
        if (value instanceof Local) // recursion case
            types.addAll(getReachingWebAppClassDescriptors((Local) value, st,
                    simpleLocalDefs, enclosingMethod));
        else if (value instanceof ClassConstant) {
            // constant class used
            ClassConstant classConstant = (ClassConstant) value;
            final RefType type = Scene.v()
                    .getSootClass(classConstant.getValue().replace('/', '.'))
                    .getType();
            types.add(type);
        } else {
            // unknown: warn
            Feedbacks
                    .add(new NonConstantWebAppArgumentUsed(enclosingMethod, st));
        }
        return types;
    }

    private Set<Type> getReachingWebAppClassDescriptors(Local local, Stmt st,
            SimpleLocalDefs simpleLocalDefs, SootMethod enclosingMethod) {
        Set<Type> types = new HashSet<Type>();
        List<Unit> defsOfAt = simpleLocalDefs.getDefsOfAt(local, st);
        for (Unit unit : defsOfAt) {
            Value value;
            if (unit instanceof JAssignStmt) {
                JAssignStmt assign = (JAssignStmt) unit;
                value = assign.getRightOp();
            } else if (unit instanceof JIdentityStmt) {
                JIdentityStmt identity = (JIdentityStmt) unit;
                value = identity.getRightOp();
            } else {
                throw new RuntimeException(
                        "getConcreteReachingTypes: unknown type "
                                + unit.getClass() + " at \"" + unit.toString());
            }
            types.addAll(getReachingWebAppClassDescriptors(value, (Stmt) unit,
                    simpleLocalDefs, enclosingMethod));
        }
        return types;
    }

    private List<SootClass> getSootClasses(Set<Class<? extends WebApp>> set) {
        List<SootClass> soots = new ArrayList<SootClass>();
        for (Class<?> classs : set) {
            soots.add(resolver.getSootClass(classs));
        }
        return soots;
    }

    // TODO move this to some class?
    public static Set<SootClass> findPredecessorWebApp(
            StateMachine stateMachine, JwigResolver resolver,
            SootMethod enclosingMethod) {
        Set<SootClass> targetedWebApps = new HashSet<SootClass>();
        PredecessorResolver pred = new PredecessorResolver(stateMachine);
        HashSet<State> seen = new HashSet<State>();
        LinkedList<State> workList = new LinkedList<State>();
        workList.add(stateMachine.getState(enclosingMethod));
        while (!workList.isEmpty()) {
            // move _backwards_ from the current state, gathering any
            // webapps on the way - these can be context
            State state1 = workList.removeFirst();
            SootMethod callingMethod = state1.getMethod();
            SootClass declaringClass = callingMethod.getDeclaringClass();
            if (resolver.isWebApp(declaringClass)) {
                targetedWebApps.add(declaringClass);
            } else {
                for (Transition predecessor : pred.getPredecessors(state1)) {
                    if (predecessor instanceof HandlerTransition
                            || predecessor instanceof LambdaTransition
                            || predecessor instanceof FilterTransition) {
                        State target = predecessor.getTarget();
                        if (!seen.contains(target)) {
                            seen.add(target);
                            workList.add(target);
                        }
                    }
                }
            }
        }
        return targetedWebApps;
    }

    /**
     * Finds all WebMethods which matches a makeURL call by containing webapp
     * and name. Will report errors if some webmethod names doesn't exist.
     *
     *
     * @param possibleWebMethodNamesAutomaton
     *            the automaton representing all the webmethod names the makeURL
     *            expression can generate
     * @param targetedWebApp
     *            the webapp the makeURL expression will hit - supposed to
     *            contain the webmethods
     * @param makeURLEnclosingMethod
     *            (for error reporting) the method enclosing the makeURL
     *            expression
     * @param makeURLstatement
     *            the statement containing the makeURLExpression (for error
     *            reporting)
     * @return all the webmethods with names which matches
     */
    private Set<Method> getTargetedWebMethods(
            Automaton possibleWebMethodNamesAutomaton,
            SootClass targetedWebApp, SootMethod makeURLEnclosingMethod,
            Stmt makeURLstatement, Interface interfacee) {

        @SuppressWarnings("unchecked")
        Class<? extends WebApp> webAppClass = (Class<? extends WebApp>) resolver
                .getJavaClass(targetedWebApp);

        final Set<Method> webMethodsByName = interfacee.getWebMethodsByName(
                webAppClass, possibleWebMethodNamesAutomaton);

        // errror checking
        if (webMethodsByName.isEmpty()) {
            // no webmethods hits is an error
            Feedbacks.add(new NoWebMethodHit(makeURLEnclosingMethod,
                    makeURLstatement, targetedWebApp,
                    possibleWebMethodNamesAutomaton));
        } else {
            // check if the makeURL can hit more names than we have available: a
            // possible error
            Automaton possibleMethodNames = findMatcher(webMethodsByName);
            Automaton slack = possibleWebMethodNamesAutomaton
                    .minus(possibleMethodNames);
            final boolean hasSlack = !slack.isEmpty();
            if (hasSlack) {
                Feedbacks.add(new MayHitMoreUnexistingWebMethods(
                        makeURLEnclosingMethod, makeURLstatement, slack));
            }
        }
        HashSet<Method> webMethods = new HashSet<Method>();
        for (Method method : webMethodsByName) {
            if (resolver.isFilter(method)) {
                Feedbacks.add(new DirectFilterInvocation(
                        makeURLEnclosingMethod, makeURLstatement, resolver
                                .getSootMethod(method)));
            } else {
                webMethods.add(method);
            }
        }
        return webMethods;
    }

    /**
     * Constructs an {@link Automaton} with a language of all the names of the
     * webmethods
     *
     * @return the constructed {@link Automaton}
     */
    private Automaton findMatcher(Collection<Method> methods) {
        Automaton a = Automaton.makeEmpty();
        for (Method m : methods) {
            String name = m.getName();
            a = a.union(Automaton.makeString(name));
        }
        a.determinize();
        a.minimize();
        return a;
    }

}
TOP

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

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.