soot.toolkits.graph.BlockGraph Maven / Gradle / Ivy
package soot.toolkits.graph;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 1999 Patrice Pominville, Raja Vallee-Rai
* %%
* This program 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 2.1 of the
* License, or (at your option) any later version.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import soot.Body;
import soot.Trap;
import soot.Unit;
import soot.jimple.NopStmt;
import soot.util.Chain;
/**
*
* Represents the control flow graph of a {@link Body} at the basic block level. Each node of the graph is a {@link Block}
* while the edges represent the flow of control from one basic block to the next.
*
*
*
* This is an abstract base class for different variants of {@link BlockGraph}, where the variants differ in how they analyze
* the control flow between individual units (represented by passing different variants of {@link UnitGraph} to the
* BlockGraph
constructor) and in how they identify block leaders (represented by overriding
* BlockGraph
's definition of {@link computeLeaders()}.
*/
public abstract class BlockGraph implements DirectedGraph {
protected Body mBody;
protected Chain mUnits;
protected List mBlocks;
protected List mHeads = new ArrayList();
protected List mTails = new ArrayList();
/**
* Create a BlockGraph
representing at the basic block level the control flow specified, at the
* Unit
level, by a given {@link UnitGraph}.
*
* @param unitGraph
* A representation of the control flow at the level of individual {@link Unit}s.
*/
protected BlockGraph(UnitGraph unitGraph) {
mBody = unitGraph.getBody();
mUnits = mBody.getUnits();
Set leaders = computeLeaders(unitGraph);
buildBlocks(leaders, unitGraph);
}
/**
*
* Utility method for computing the basic block leaders for a {@link Body}, given its {@link UnitGraph} (i.e., the
* instructions which begin new basic blocks).
*
*
*
* This implementation designates as basic block leaders :
*
*
*
* - Any
Unit
which has zero predecessors (e.g. the Unit
following a return or unconditional
* branch) or more than one predecessor (e.g. a merge point).
*
* Unit
s which are the target of any branch (even if they have no other predecessors and the branch has no
* other successors, which is possible for the targets of unconditional branches or degenerate conditional branches which
* both branch and fall through to the same Unit
).
*
* - All successors of any
Unit
which has more than one successor (this includes the successors of
* Unit
s which may throw an exception that gets caught within the Body
, as well the successors of
* conditional branches).
*
* - The first
Unit
in any Trap
handler. (Strictly speaking, if unitGraph
were a
* ExceptionalUnitGraph
that included only a single unexceptional predecessor for some handler—because
* no trapped unit could possibly throw the exception that the handler catches, while the code preceding the handler fell
* through to the handler's code—then you could merge the handler into the predecessor's basic block; but such
* situations occur only in carefully contrived bytecode.)
*
*
*
*
* @param unitGraph
* is the Unit
-level CFG which is to be split into basic blocks.
*
* @return the {@link Set} of {@link Unit}s in unitGraph
which are block leaders.
*/
protected Set computeLeaders(UnitGraph unitGraph) {
Body body = unitGraph.getBody();
if (body != mBody) {
throw new RuntimeException("BlockGraph.computeLeaders() called with a UnitGraph that doesn't match its mBody.");
}
Set leaders = new HashSet();
// Trap handlers start new basic blocks, no matter how many
// predecessors they have.
Chain traps = body.getTraps();
for (Iterator trapIt = traps.iterator(); trapIt.hasNext();) {
Trap trap = trapIt.next();
leaders.add(trap.getHandlerUnit());
}
for (Iterator unitIt = body.getUnits().iterator(); unitIt.hasNext();) {
Unit u = unitIt.next();
List predecessors = unitGraph.getPredsOf(u);
int predCount = predecessors.size();
List successors = unitGraph.getSuccsOf(u);
int succCount = successors.size();
if (predCount != 1) { // If predCount == 1 but the predecessor
leaders.add(u); // is a branch, u will get added by that
} // branch's successor test.
if ((succCount > 1) || (u.branches())) {
for (Iterator it = successors.iterator(); it.hasNext();) {
leaders.add((Unit) it.next()); // The cast is an
} // assertion check.
}
}
return leaders;
}
/**
*
* A utility method that does most of the work of constructing basic blocks, once the set of block leaders has been
* determined, and which designates the heads and tails of the graph.
*
*
*
* BlockGraph
provides an implementation of buildBlocks()
which splits the {@link Unit}s in
* unitGraph
so that each Unit
in the passed set of block leaders is the first unit in a block.
* It defines as heads the blocks which begin with Unit
s which are heads in unitGraph
, and
* defines as tails the blocks which end with Unit
s which are tails in unitGraph
. Subclasses
* might override this behavior.
*
* @param leaders
* Contains Unit
s which are to be block leaders.
*
* @param unitGraph
* Provides information about the predecessors and successors of each Unit
in the Body
,
* for determining the predecessors and successors of each created {@link Block}.
*
* @return a {@link Map} from {@link Unit}s which begin or end a block to the block which contains them.
*/
protected Map buildBlocks(Set leaders, UnitGraph unitGraph) {
List blockList = new ArrayList(leaders.size());
Map unitToBlock = new HashMap(); // Maps head
// and tail
// units to
// their blocks, for building
// predecessor and successor lists.
Unit blockHead = null;
int blockLength = 0;
Iterator unitIt = mUnits.iterator();
if (unitIt.hasNext()) {
blockHead = unitIt.next();
if (!leaders.contains(blockHead)) {
throw new RuntimeException("BlockGraph: first unit not a leader!");
}
blockLength++;
}
Unit blockTail = blockHead;
int indexInMethod = 0;
while (unitIt.hasNext()) {
Unit u = unitIt.next();
if (leaders.contains(u)) {
addBlock(blockHead, blockTail, indexInMethod, blockLength, blockList, unitToBlock);
indexInMethod++;
blockHead = u;
blockLength = 0;
}
blockTail = u;
blockLength++;
}
if (blockLength > 0) {
// Add final block.
addBlock(blockHead, blockTail, indexInMethod, blockLength, blockList, unitToBlock);
}
// The underlying UnitGraph defines heads and tails.
for (Iterator it = unitGraph.getHeads().iterator(); it.hasNext();) {
Unit headUnit = (Unit) it.next();
Block headBlock = unitToBlock.get(headUnit);
if (headBlock.getHead() == headUnit) {
mHeads.add(headBlock);
} else {
throw new RuntimeException("BlockGraph(): head Unit is not the first unit in the corresponding Block!");
}
}
for (Iterator it = unitGraph.getTails().iterator(); it.hasNext();) {
Unit tailUnit = (Unit) it.next();
Block tailBlock = unitToBlock.get(tailUnit);
if (tailBlock.getTail() == tailUnit) {
mTails.add(tailBlock);
} else {
throw new RuntimeException("BlockGraph(): tail Unit is not the last unit in the corresponding Block!");
}
}
for (Iterator blockIt = blockList.iterator(); blockIt.hasNext();) {
Block block = blockIt.next();
List predUnits = unitGraph.getPredsOf(block.getHead());
List predBlocks = new ArrayList(predUnits.size());
for (Iterator predIt = predUnits.iterator(); predIt.hasNext();) {
Unit predUnit = predIt.next();
Block predBlock = unitToBlock.get(predUnit);
if (predBlock == null) {
throw new RuntimeException("BlockGraph(): block head mapped to null block!");
}
predBlocks.add(predBlock);
}
if (predBlocks.size() == 0) {
block.setPreds(Collections.emptyList());
// If the UnreachableCodeEliminator is not eliminating
// unreachable handlers, then they will have no
// predecessors, yet not be heads.
/*
* if (! mHeads.contains(block)) { throw new RuntimeException("Block with no predecessors is not a head!" );
*
* // Note that a block can be a head even if it has // predecessors: a handler that might catch an exception //
* thrown by the first Unit in the method. }
*/
} else {
block.setPreds(Collections.unmodifiableList(predBlocks));
if (block.getHead() == mUnits.getFirst()) {
mHeads.add(block); // Make the first block a head
// even if the Body is one huge loop.
}
}
List succUnits = unitGraph.getSuccsOf(block.getTail());
List succBlocks = new ArrayList(succUnits.size());
for (Iterator succIt = succUnits.iterator(); succIt.hasNext();) {
Unit succUnit = succIt.next();
Block succBlock = unitToBlock.get(succUnit);
if (succBlock == null) {
throw new RuntimeException("BlockGraph(): block tail mapped to null block!");
}
succBlocks.add(succBlock);
}
if (succBlocks.size() == 0) {
block.setSuccs(Collections.emptyList());
if (!mTails.contains(block)) {
// if this block is totally empty and unreachable, we remove it
if (block.getPreds().isEmpty() && block.getHead() == block.getTail() && block.getHead() instanceof NopStmt) {
blockIt.remove();
} else {
throw new RuntimeException("Block with no successors is not a tail!: " + block.toString());
// Note that a block can be a tail even if it has
// successors: a return that throws a caught exception.
}
}
} else {
block.setSuccs(Collections.unmodifiableList(succBlocks));
}
}
mBlocks = Collections.unmodifiableList(blockList);
mHeads = Collections.unmodifiableList(mHeads);
if (mTails.size() == 0) {
mTails = Collections.emptyList();
} else {
mTails = Collections.unmodifiableList(mTails);
}
return unitToBlock;
}
/**
* A utility method which creates a new block and adds information about it to data structures used to build the graph.
*
* @param head
* The first unit in the block.
* @param tail
* The last unit in the block.
* @param index
* The index of this block this {@link Body}.
* @param length
* The number of units in this block.
* @param blockList
* The list of blocks for this method. addBlock()
will add the newly created block to this list.
* @param unitToBlock
* A map from units to blocks. addBlock()
will add mappings from head
and
* tail
to the new block
*/
private void addBlock(Unit head, Unit tail, int index, int length, List blockList, Map unitToBlock) {
Block block = new Block(head, tail, mBody, index, length, this);
blockList.add(block);
unitToBlock.put(tail, block);
unitToBlock.put(head, block);
}
/**
* Returns the {@link Body} this {@link BlockGraph} is derived from.
*
* @return The {@link Body} this {@link BlockGraph} is derived from.
*/
public Body getBody() {
return mBody;
}
/**
* Returns a list of the Blocks composing this graph.
*
* @return A list of the blocks composing this graph in the same order as they partition underlying Body instance's unit
* chain.
* @see Block
*/
public List getBlocks() {
return mBlocks;
}
public String toString() {
Iterator it = mBlocks.iterator();
StringBuffer buf = new StringBuffer();
while (it.hasNext()) {
Block someBlock = it.next();
buf.append(someBlock.toString() + '\n');
}
return buf.toString();
}
/* DirectedGraph implementation */
public List getHeads() {
return mHeads;
}
public List getTails() {
return mTails;
}
public List getPredsOf(Block b) {
return b.getPreds();
}
public List getSuccsOf(Block b) {
return b.getSuccs();
}
public int size() {
return mBlocks.size();
}
public Iterator iterator() {
return mBlocks.iterator();
}
}