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

pascal.taie.analysis.pta.PointerAnalysisResultImpl Maven / Gradle / Ivy

The newest version!
/*
 * Tai-e: A Static Analysis Framework for Java
 *
 * Copyright (C) 2022 Tian Tan 
 * Copyright (C) 2022 Yue Li 
 *
 * This file is part of Tai-e.
 *
 * Tai-e 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.
 *
 * Tai-e 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 Tai-e. If not, see .
 */

package pascal.taie.analysis.pta;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pascal.taie.analysis.graph.callgraph.CallGraph;
import pascal.taie.analysis.graph.callgraph.DefaultCallGraph;
import pascal.taie.analysis.graph.callgraph.Edge;
import pascal.taie.analysis.graph.flowgraph.ObjectFlowGraph;
import pascal.taie.analysis.pta.core.cs.element.ArrayIndex;
import pascal.taie.analysis.pta.core.cs.element.CSCallSite;
import pascal.taie.analysis.pta.core.cs.element.CSManager;
import pascal.taie.analysis.pta.core.cs.element.CSMethod;
import pascal.taie.analysis.pta.core.cs.element.CSObj;
import pascal.taie.analysis.pta.core.cs.element.CSVar;
import pascal.taie.analysis.pta.core.cs.element.InstanceField;
import pascal.taie.analysis.pta.core.cs.element.Pointer;
import pascal.taie.analysis.pta.core.cs.element.StaticField;
import pascal.taie.analysis.pta.core.heap.Obj;
import pascal.taie.analysis.pta.core.solver.PointerFlowGraph;
import pascal.taie.analysis.pta.core.solver.PropagateTypes;
import pascal.taie.ir.exp.ArrayAccess;
import pascal.taie.ir.exp.InstanceFieldAccess;
import pascal.taie.ir.exp.StaticFieldAccess;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.Invoke;
import pascal.taie.language.classes.JField;
import pascal.taie.language.classes.JMethod;
import pascal.taie.language.type.ArrayType;
import pascal.taie.util.AbstractResultHolder;
import pascal.taie.util.Canonicalizer;
import pascal.taie.util.Indexer;
import pascal.taie.util.collection.HybridBitSet;
import pascal.taie.util.collection.Maps;
import pascal.taie.util.collection.Pair;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

public class PointerAnalysisResultImpl extends AbstractResultHolder
        implements PointerAnalysisResult {

    private static final Logger logger = LogManager.getLogger(PointerAnalysisResultImpl.class);

    private final PropagateTypes propTypes;

    private final CSManager csManager;

    /**
     * Points-to set of local variables.
     */
    private final Map> varPointsTo = Maps.newConcurrentMap(4096);

    /**
     * Points-to sets of instance field expressions, e.g., v.f.
     */
    private final Map, Set> ifieldPointsTo = Maps.newConcurrentMap(1024);

    /**
     * Points-to set of static field expressions, e.g., T.f.
     */
    private final Map> sfieldPointsTo = Maps.newConcurrentMap(512);

    /**
     * Points-to set of array expressions, e.g., a[i].
     */
    private final Map> arrayPointsTo = Maps.newConcurrentMap(1024);

    /**
     * Set of all (reachable) objects in the program.
     */
    private final Set objects;

    /**
     * Canonicalizes (context-insensitive) points-to set.
     */
    private final Canonicalizer> canonicalizer = new Canonicalizer<>();

    /**
     * Context-sensitive call graph.
     */
    private final CallGraph csCallGraph;

    /**
     * Obj indexer.
     */
    private final Indexer objIndexer;

    /**
     * Call graph (context projected out).
     */
    private CallGraph callGraph;

    private final PointerFlowGraph pfg;

    /**
     * Object flow graph (context projected out).
     */
    private ObjectFlowGraph ofg;

    public PointerAnalysisResultImpl(
            PropagateTypes propTypes, CSManager csManager,
            Indexer objIndexer, CallGraph csCallGraph,
            PointerFlowGraph pfg) {
        this.propTypes = propTypes;
        this.csManager = csManager;
        this.objIndexer = objIndexer;
        this.csCallGraph = csCallGraph;
        this.pfg = pfg;
        this.objects = removeContexts(getCSObjects().stream());
    }

    @Override
    public Collection getCSVars() {
        return csManager.getCSVars();
    }

    @Override
    public Collection getVars() {
        return csManager.getVars();
    }

    @Override
    public Collection getInstanceFields() {
        return csManager.getInstanceFields();
    }

    @Override
    public Collection getArrayIndexes() {
        return csManager.getArrayIndexes();
    }

    @Override
    public Collection getStaticFields() {
        return csManager.getStaticFields();
    }

    @Override
    public Collection getCSObjects() {
        return csManager.getObjects();
    }

    @Override
    public Collection getObjects() {
        return objects;
    }

    @Override
    public Indexer getObjectIndexer() {
        return objIndexer;
    }

    @Override
    public Set getPointsToSet(Var var) {
        if (!propTypes.isAllowed(var)) {
            return Set.of();
        }
        return varPointsTo.computeIfAbsent(var, v ->
                removeContexts(csManager.getCSVarsOf(var)
                        .stream()
                        .flatMap(Pointer::objects)));
    }

    @Override
    public Set getPointsToSet(InstanceFieldAccess access) {
        if (!propTypes.isAllowed(access)) {
            return Set.of();
        }
        Var base = access.getBase();
        JField field = access.getFieldRef().resolveNullable();
        return field != null ? getPointsToSet(base, field) : Set.of();
    }

    @Override
    public Set getPointsToSet(Var base, JField field) {
        if (!propTypes.isAllowed(field.getType())) {
            return Set.of();
        }
        if (field.isStatic()) {
            logger.warn("{} is not an instance field", field);
            return Set.of();
        }
        // TODO - properly handle non-exist base.field
        return ifieldPointsTo.computeIfAbsent(new Pair<>(base, field), p ->
                removeContexts(csManager.getCSVarsOf(base)
                        .stream()
                        .flatMap(Pointer::objects)
                        .map(o -> csManager.getInstanceField(o, field))
                        .flatMap(InstanceField::objects)));
    }

    @Override
    public Set getPointsToSet(Obj base, JField field) {
        if (!propTypes.isAllowed(field.getType())) {
            return Set.of();
        }
        if (field.isStatic()) {
            logger.warn("{} is not an instance field", field);
            return Set.of();
        }
        // TODO - properly handle non-exist base.field
        return removeContexts(csManager.getCSObjsOf(base)
                .stream()
                .map(o -> csManager.getInstanceField(o, field))
                .flatMap(InstanceField::objects));
    }

    @Override
    public Set getPointsToSet(StaticFieldAccess access) {
        if (!propTypes.isAllowed(access)) {
            return Set.of();
        }
        JField field = access.getFieldRef().resolveNullable();
        return field != null ? getPointsToSet(field) : Set.of();
    }

    @Override
    public Set getPointsToSet(JField field) {
        if (!propTypes.isAllowed(field.getType())) {
            return Set.of();
        }
        if (!field.isStatic()) {
            logger.warn("{} is not a static field", field);
            return Set.of();
        }
        return sfieldPointsTo.computeIfAbsent(field, f ->
                removeContexts(csManager.getStaticField(field).objects()));
    }

    @Override
    public Set getPointsToSet(ArrayAccess access) {
        if (!propTypes.isAllowed(access)) {
            return Set.of();
        }
        return getPointsToSet(access.getBase(), access.getIndex());
    }

    @Override
    public Set getPointsToSet(Var base, Var index) {
        if (base.getType() instanceof ArrayType baseType) {
            if (!propTypes.isAllowed(baseType.elementType())) {
                return Set.of();
            }
        } else {
            logger.warn("{} is not an array", base);
            return Set.of();
        }
        return arrayPointsTo.computeIfAbsent(base, b ->
                removeContexts(csManager.getCSVarsOf(b)
                        .stream()
                        .flatMap(Pointer::objects)
                        .map(csManager::getArrayIndex)
                        .flatMap(ArrayIndex::objects)));
    }

    @Override
    public Set getPointsToSet(Obj array) {
        if (array.getType() instanceof ArrayType baseType) {
            if (!propTypes.isAllowed(baseType.elementType())) {
                return Set.of();
            }
        } else {
            logger.warn("{} is not an array", array);
            return Set.of();
        }
        return removeContexts(csManager.getCSObjsOf(array)
                .stream()
                .map(csManager::getArrayIndex)
                .flatMap(ArrayIndex::objects));
    }

    @Override
    public boolean mayAlias(Var v1, Var v2) {
        Set s1 = getPointsToSet(v1);
        Set s2 = getPointsToSet(v2);
        return !Collections.disjoint(s1, s2);
    }

    @Override
    public boolean mayAlias(InstanceFieldAccess if1, InstanceFieldAccess if2) {
        return Objects.equals(
                if1.getFieldRef().resolveNullable(),
                if2.getFieldRef().resolveNullable())
                && mayAlias(if1.getBase(), if2.getBase());
    }

    @Override
    public boolean mayAlias(ArrayAccess a1, ArrayAccess a2) {
        return mayAlias(a1.getBase(), a2.getBase());
    }

    /**
     * Removes contexts of a context-sensitive points-to set and
     * returns a new resulting set.
     */
    private Set removeContexts(Stream objects) {
        Set set = new HybridBitSet<>(objIndexer, true);
        objects.map(CSObj::getObject).forEach(set::add);
        return canonicalizer.get(Collections.unmodifiableSet(set));
    }

    @Override
    public CallGraph getCSCallGraph() {
        return csCallGraph;
    }

    @Override
    public CallGraph getCallGraph() {
        if (callGraph == null) {
            callGraph = removeContexts(csCallGraph);
        }
        return callGraph;
    }

    /**
     * Removes contexts in a context-sensitive call graph and
     * returns a new resulting call graph.
     */
    private static CallGraph removeContexts(
            CallGraph csCallGraph) {
        DefaultCallGraph callGraph = new DefaultCallGraph();
        csCallGraph.entryMethods()
                .map(CSMethod::getMethod)
                .forEach(callGraph::addEntryMethod);
        csCallGraph.reachableMethods()
                .map(CSMethod::getMethod)
                .forEach(callGraph::addReachableMethod);
        csCallGraph.edges()
                .map(CIEdge::new)
                .forEach(callGraph::addEdge);
        return callGraph;
    }

    /**
     * Represents context-insensitive call edges.
     */
    private static class CIEdge extends Edge {

        private static final Canonicalizer canonicalizer = new Canonicalizer<>();

        private final String info;

        /**
         * Removes contexts and keeps info of given context-sensitive edge.
         */
        private CIEdge(Edge edge) {
            super(edge.getKind(),
                    edge.getCallSite().getCallSite(),
                    edge.getCallee().getMethod());
            this.info = canonicalizer.get(edge.getInfo());
        }

        @Override
        public String getInfo() {
            return info;
        }
    }

    public ObjectFlowGraph getObjectFlowGraph() {
        if (ofg == null) {
            ofg = new ObjectFlowGraph(pfg, getCallGraph());
        }
        return ofg;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy