org.evosuite.graphs.cdg.DominatorTree Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite 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.0 of the License, or
* (at your option) any later version.
*
* EvoSuite 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 Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
package org.evosuite.graphs.cdg;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.evosuite.graphs.EvoSuiteGraph;
import org.evosuite.graphs.cfg.ControlFlowGraph;
import org.jgrapht.graph.DefaultEdge;
/**
*
* Given a CFG this class computes the immediateDominators and the
* dominatingFrontiers for each CFG vertex
*
* The current algorithm to determine the immediateDominators runs in time
* O(e*log n) where e is the number of control flow edges and n the number of
* CFG vertices and is taken from:
*
* "A Fast Algorithm for Finding Dominators in a Flowgraph" THOMAS LENGAUER and
* ROBERT ENDRE TARJAN 1979, Stanford University
*
* DOI: 10.1145/357062.357071
* http://portal.acm.org/citation.cfm?doid=357062.357071
*
*
* The algorithm for computing the dominatingFrontiers when given the
* immediateDominators is taken from
*
* "Efficiently Computing Static Single Assignment Form and the Control
* Dependence Graph" RON CYTRON, JEANNE FERRANTE, BARRY K. ROSEN, and MARK N.
* WEGMAN IBM Research Division and F. KENNETH ZADECK Brown University 1991
*
* @author Andre Mis
*/
public class DominatorTree extends EvoSuiteGraph, DefaultEdge> {
private static Logger logger = LoggerFactory.getLogger(DominatorTree.class);
private int nodeCount = 0;
private final ControlFlowGraph cfg;
private final Map> dominatorNodesMap = new LinkedHashMap>();
private final Map> dominatorIDMap = new LinkedHashMap>();
private final Map> dominatingFrontiers = new LinkedHashMap>();
/**
* Will start the computation of all immediateDominators for the given CFG
* which can later be retrieved via getImmediateDominator()
*
* @param cfg a {@link org.evosuite.graphs.cfg.ControlFlowGraph} object.
*/
public DominatorTree(ControlFlowGraph cfg) {
super(DefaultEdge.class);
logger.debug("Computing DominatorTree for " + cfg.getName());
this.cfg = cfg;
createDominatorNodes();
V root = cfg.determineEntryPoint(); // TODO change to getEntryPoint()
logger.debug("determined root: " + root);
DominatorNode rootNode = getDominatorNodeFor(root);
depthFirstAnalyze(rootNode);
computeSemiDominators();
computeImmediateDominators(rootNode);
createDominatorTree();
computeDominatorFrontiers(rootNode);
// toDot();
}
private void createDominatorTree() {
// add dominator nodes
addVertices(dominatorIDMap.values());
logger.debug("DTNodes: " + vertexCount());
// build up tree by adding for each node v an edge from v.iDom to v
for (DominatorNode v : vertexSet()) {
if (v.isRootNode())
continue;
if (addEdge(v.immediateDominator, v) == null)
throw new IllegalStateException(
"internal error while building dominator tree edges");
logger.debug("added DTEdge from " + v.immediateDominator.n + " to " + v.n);
}
logger.debug("DTEdges: " + edgeCount());
// sanity check
if (isEmpty())
throw new IllegalStateException("expect dominator trees to not be empty");
// check tree is connected
if (!isConnected())
throw new IllegalStateException("dominator tree expected to be connected");
// TODO more sanity checks - no one likes to be insane ;)
}
private void computeDominatorFrontiers(DominatorNode currentNode) {
// TODO check assumption: exitPoints in original CFG are exitPoints in resulting DominatorTree
for (DominatorNode child : getChildren(currentNode))
computeDominatorFrontiers(child);
logger.debug("computing dominatingFrontier for: " + currentNode.toString());
Set dominatingFrontier = dominatingFrontiers.get(currentNode);
if (dominatingFrontier == null)
dominatingFrontier = new HashSet();
// "local"
for (V child : cfg.getChildren(currentNode.node)) {
DominatorNode y = getDominatorNodeFor(child);
if (y.immediateDominator.n != currentNode.n) {
logger.debug(" LOCAL adding to DFs: " + y.node);
dominatingFrontier.add(y.node);
}
}
// "up"
for (DominatorNode z : getChildren(currentNode))
for (V y : dominatingFrontiers.get(z.node))
if (getDominatorNodeFor(y).immediateDominator.n != currentNode.n) {
logger.debug(" UP adding to DFs: " + y);
dominatingFrontier.add(y);
}
dominatingFrontiers.put(currentNode.node, dominatingFrontier);
}
/**
* Given a node of this objects CFG this method returns it's previously
* computed immediateDominator
*
* The immediateDominator iDom of a node v has the following properties:
*
* 1) iDom dominates v
*
* 2) every other dominator of v dominates iDom
*
* A node w dominates v or is a dominator of v if and only if every path
* from the CFG's entryPoint to v contains w
*
* @param v
* A node within this objects CFG for wich the immediateDominator
* is to be returned
* @return a V object.
*/
public V getImmediateDominator(V v) {
if (v == null)
throw new IllegalArgumentException("null given");
DominatorNode domNode = dominatorNodesMap.get(v);
if (domNode == null)
throw new IllegalStateException("unknown vertice given");
if (domNode.immediateDominator == null) {
// sanity check: this is only allowed to happen if v is root of CFG
if (domNode.n != 1)
throw new IllegalStateException(
"expect known node without iDom to be root of CFG");
return null;
}
return domNode.immediateDominator.node;
}
/**
* Getter for the field dominatingFrontiers
.
*
* @param v a V object.
* @return a {@link java.util.Set} object.
*/
public Set getDominatingFrontiers(V v) {
if (v == null)
throw new IllegalStateException("null given");
return dominatingFrontiers.get(v);
}
// computation
private void createDominatorNodes() {
for (V v : cfg.vertexSet())
dominatorNodesMap.put(v, new DominatorNode(v));
}
private void depthFirstAnalyze(DominatorNode currentNode) {
// step 1
initialize(currentNode);
for (V w : cfg.getChildren(currentNode.node)) {
DominatorNode wNode = getDominatorNodeFor(w);
if (wNode.semiDominator == null) {
wNode.parent = currentNode;
depthFirstAnalyze(wNode);
}
}
}
private void initialize(DominatorNode currentNode) {
nodeCount++;
currentNode.n = nodeCount;
currentNode.semiDominator = currentNode;
logger.debug("created " + currentNode.toString() + " for "
+ currentNode.node.toString());
dominatorIDMap.put(nodeCount, currentNode);
}
private void computeSemiDominators() {
for (int i = nodeCount; i >= 2; i--) {
DominatorNode w = getDominatorNodeById(i);
// step 2
for (V current : cfg.getParents(w.node)) {
DominatorNode v = getDominatorNodeFor(current);
DominatorNode u = v.eval();
if (u.semiDominator.n < w.semiDominator.n)
w.semiDominator = u.semiDominator;
}
w.semiDominator.bucket.add(w);
w.link(w.parent);
// step 3
while (!w.parent.bucket.isEmpty()) {
DominatorNode v = w.parent.getFromBucket();
if (!w.parent.bucket.remove(v))
throw new IllegalStateException("internal error");
DominatorNode u = v.eval();
v.immediateDominator = (u.semiDominator.n < v.semiDominator.n ? u
: w.parent);
}
}
}
private void computeImmediateDominators(DominatorNode rootNode) {
// step 4
for (int i = 2; i <= nodeCount; i++) {
DominatorNode w = getDominatorNodeById(i);
if (w.immediateDominator != w.semiDominator)
w.immediateDominator = w.immediateDominator.immediateDominator;
// logger.debug("iDom for node "+i+" was: "+w.immediateDominator.n);
}
rootNode.immediateDominator = null;
}
private DominatorNode getDominatorNodeById(int id) {
DominatorNode r = dominatorIDMap.get(id);
if (r == null)
throw new IllegalArgumentException("id unknown to this tree");
return r;
}
private DominatorNode getDominatorNodeFor(V v) {
DominatorNode r = dominatorNodesMap.get(v);
if (r == null)
throw new IllegalStateException(
"expect dominatorNodesMap to contain domNodes for all Vs");
return r;
}
/** {@inheritDoc} */
@Override
public String getName() {
return "DominatorTree" + graphId;
}
}