pascal.taie.analysis.sideeffect.TopologicalSolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tai-e Show documentation
Show all versions of tai-e Show documentation
An easy-to-learn/use static analysis framework for Java
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.sideeffect;
import pascal.taie.analysis.graph.callgraph.CallGraph;
import pascal.taie.analysis.pta.PointerAnalysisResult;
import pascal.taie.analysis.pta.core.heap.Obj;
import pascal.taie.ir.exp.FieldAccess;
import pascal.taie.ir.exp.InstanceFieldAccess;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.Invoke;
import pascal.taie.ir.stmt.Stmt;
import pascal.taie.ir.stmt.StoreArray;
import pascal.taie.ir.stmt.StoreField;
import pascal.taie.language.classes.JMethod;
import pascal.taie.util.Indexer;
import pascal.taie.util.collection.CollectionUtils;
import pascal.taie.util.collection.IndexerBitSet;
import pascal.taie.util.collection.Maps;
import pascal.taie.util.collection.Sets;
import pascal.taie.util.graph.MergedNode;
import pascal.taie.util.graph.MergedSCCGraph;
import pascal.taie.util.graph.TopologicalSorter;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Computes modification information based on pointer analysis
* and topological sorting of call graph.
*/
class TopologicalSolver {
private final boolean onlyApp;
TopologicalSolver(boolean onlyApp) {
this.onlyApp = onlyApp;
}
SideEffect solve(PointerAnalysisResult pta) {
CallGraph callGraph = pta.getCallGraph();
// 1. compute the objects directly modified by each method and stmt
Map> methodDirectMods = Maps.newMap();
Map> stmtDirectMods = Maps.newMap();
computeDirectMods(pta, callGraph, stmtDirectMods, methodDirectMods);
// 2. compute the objects directly modified by
// the methods of each SCC in the call graph
var mg = new MergedSCCGraph<>(callGraph);
Map> sccDirectMods = computeSCCDirectMods(
mg.getNodes(), methodDirectMods);
// 3. fully compute the objects modified by each method
Indexer indexer = pta.getObjectIndexer();
Map> methodMods = computeMethodMods(
mg, callGraph, sccDirectMods, indexer);
return new SideEffect(methodMods, stmtDirectMods, callGraph);
}
private void computeDirectMods(
PointerAnalysisResult pta,
CallGraph, JMethod> callGraph,
Map> stmtDirectMods,
Map> methodDirectMods) {
callGraph.forEach(method -> {
Set mMods = Sets.newHybridSet();
method.getIR().forEach(stmt -> {
Set sMods = Set.of();
if (stmt instanceof StoreField storeField) {
FieldAccess fieldAccess = storeField.getFieldAccess();
if (fieldAccess instanceof InstanceFieldAccess instAccess) {
Var base = instAccess.getBase();
sMods = pta.getPointsToSet(base);
}
} else if (stmt instanceof StoreArray storeArray) {
Var base = storeArray.getArrayAccess().getBase();
sMods = pta.getPointsToSet(base);
}
if (!sMods.isEmpty()) {
sMods = sMods.stream()
.filter(this::isRelevant)
.collect(Collectors.toUnmodifiableSet());
}
if (!sMods.isEmpty()) {
mMods.addAll(sMods);
stmtDirectMods.put(stmt, sMods);
}
});
if (!mMods.isEmpty()) {
methodDirectMods.put(method, mMods);
}
});
}
private boolean isRelevant(Obj obj) {
if (onlyApp && obj.getContainerMethod().isPresent()) {
return obj.getContainerMethod().get().isApplication();
}
return false;
}
private static Map> computeSCCDirectMods(
Set> sccs,
Map> methodDirectMods) {
Map> sccDirectMods = Maps.newMap();
sccs.forEach(scc -> {
Set mods = Sets.newHybridSet();
scc.getNodes().forEach(m ->
mods.addAll(methodDirectMods.getOrDefault(m, Set.of())));
scc.getNodes().forEach(m -> sccDirectMods.put(m, mods));
});
return sccDirectMods;
}
private static Map> computeMethodMods(
MergedSCCGraph mg,
CallGraph, JMethod> callGraph,
Map> sccDirectMods,
Indexer indexer) {
Map> methodMods = Maps.newMap();
// to accelerate side-effect analysis, we propagate modified objects
// of methods (methodMods) based on topological sorting of call graph,
// so that each method only needs to be processed once
var sorter = new TopologicalSorter<>(mg, true);
sorter.get().forEach(scc -> {
Set mods = new IndexerBitSet<>(indexer, true);
// add SCC direct mods
Set sccNodes = Sets.newSet(scc.getNodes());
JMethod rep = CollectionUtils.getOne(sccNodes);
mods.addAll(sccDirectMods.get(rep));
// add callees' mods
sccNodes.forEach(m -> callGraph.getCalleesOfM(m)
.stream()
// avoid redundantly adding SCC direct mods
.filter(callee -> !sccNodes.contains(callee))
.forEach(callee -> mods.addAll(
methodMods.getOrDefault(callee, Set.of()))));
if (!mods.isEmpty()) {
sccNodes.forEach(m -> methodMods.put(m, mods));
}
});
return methodMods;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy