/**
*
*/
package soottocfg.cfg.method;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import org.jgrapht.Graphs;
import com.google.common.base.Preconditions;
import soottocfg.cfg.LiveVars;
import soottocfg.cfg.Node;
import soottocfg.cfg.statement.Statement;
import soottocfg.cfg.variable.Variable;
import soottocfg.util.SetOperations;
/**
* @author schaef
*
*/
public class CfgBlock implements Node, Serializable {
/**
*
*/
private static final long serialVersionUID = 8807957025110526199L;
protected final String label;
protected List<Statement> statements;
protected final Method method;
public CfgBlock(Method m) {
this.label = "Block" + m.vertexSet().size();
this.statements = new LinkedList<Statement>();
this.method = m;
this.method.addVertex(this);
}
/**
* Try not to use this one, only if you clone blocks.
* @param m
* @param label
*/
public CfgBlock(Method m, String label) {
this.label = label;
this.statements = new LinkedList<Statement>();
this.method = m;
this.method.addVertex(this);
}
public Method getMethod() {
return method;
}
public String getLabel() {
return this.label;
}
/**
* Adds a {@link Statement} to the end of the block.
* @param s Statement to be added.
*/
public void addStatement(Statement s) {
this.statements.add(s);
}
/**
* Adds a {@link Statement} at a given {@code position} to the block.
* Does not check if {@code position} is a valid position within the
* block.
* @param s
* @param position
* @exception IndexOutOfBoundsException If position is not a valid index in the body.
*/
public void addStatement(int position, Statement s) {
Preconditions.checkNotNull(s);
this.statements.add(position, s);
}
/**
* Get the {@link Statement} list of this block.
* @return The {@link Statement} list
*/
public List<Statement> getStatements() {
return this.statements;
}
public void removeStatement(Statement toRemove) {
this.statements.remove(toRemove);
}
public void removeStatements(Collection<Statement> toRemove) {
this.statements.removeAll(toRemove);
}
public void swapStatements(int i, int j) {
Collections.swap(this.statements, i, j);
}
/**
* Replaces the statements inside the block by the statements in
* {@param statements}.
* @param statements Collection of {@link Statement}s to be added to the block.
*/
public void setStatements(Collection<Statement> statements) {
this.statements = new LinkedList<Statement>(statements);
}
@Override
public String toString() {
Preconditions.checkArgument(this.method.containsVertex(this),
String.format(
"Block %s in not %s.",
this.label, this.method.getMethodName()));
StringBuilder sb = new StringBuilder();
sb.append(this.label);
sb.append(":\n");
for (Statement s : this.statements) {
sb.append("(ln ");
sb.append(s.getJavaSourceLine());
sb.append(")\t");
sb.append(s.toString());
sb.append("\n");
}
if (this.method.outDegreeOf(this) != 0) {
sb.append("\tgoto:\n");
for (CfgEdge edge : this.method.outgoingEdgesOf(this)) {
sb.append("\t ");
if (edge.getLabel().isPresent()) {
sb.append("if ");
sb.append(edge.getLabel().get());
sb.append(": ");
}
sb.append(method.getEdgeTarget(edge).getLabel());
sb.append("\n");
}
} else {
sb.append("\treturn\n");
}
return sb.toString();
}
@Override
public Set<Variable> getUseVariables() {
Set<Variable> used = new HashSet<Variable>();
for (Statement s : statements) {
used.addAll(s.getUseVariables());
}
// The variables in the conditional belong to this block.
//Think of it as a if (cond) goto L1 else goto L2;
//as the last stmt in the block.
for (CfgEdge edge : this.method.outgoingEdgesOf(this)) {
if (edge.getLabel().isPresent()) {
used.addAll(edge.getLabel().get().getUseVariables());
}
}
//TODO: Martin also added the incoming edges.
for (CfgEdge edge : this.method.incomingEdgesOf(this)) {
if (edge.getLabel().isPresent()) {
used.addAll(edge.getLabel().get().getUseVariables());
}
}
return used;
}
@Override
public Set<Variable> getDefVariables() {
Set<Variable> used = new HashSet<Variable>();
for (Statement s : statements) {
used.addAll(s.getDefVariables());
}
return used;
}
public boolean isExit() {
return this.method.outDegreeOf(this) == 0;
}
// Calculates the live-in variables for each statement
public LiveVars<Statement> computeLiveVariables(LiveVars<CfgBlock> vars) {
// Reserve the necessary size in the hashmap
Map<Statement, Set<Variable>> in = new HashMap<Statement, Set<Variable>>(getStatements().size());
Map<Statement, Set<Variable>> out = new HashMap<Statement, Set<Variable>>(getStatements().size());
// Start by initializing in to empty.
for (Statement s : getStatements()) {
in.put(s, new HashSet<Variable>());
}
// Start with the variables that are live out of the block are also live
// out of the last statement
Set<Variable> currentLiveOut = vars.liveOut.get(this);
// Go through the statements in reverse order
for (ListIterator<Statement> li = getStatements().listIterator(getStatements().size()); li.hasPrevious();) {
Statement stmt = li.previous();
out.put(stmt, currentLiveOut);
Set<Variable> liveIn = SetOperations.union(stmt.getUseVariables(),
SetOperations.minus(currentLiveOut, stmt.getDefVariables()));
in.put(stmt, liveIn);
currentLiveOut = liveIn;
}
// The live in of the 0th statement should be the same as the live in of
// the whole block
// assert (currentLiveOut.equals(vars.liveIn.get(this)));
//TODO: Martin says this assertion is not true anymore because the conditional
//also has to be live. Because in reality, there is an assume from the incoming
//edge at the beginning of the block. Otherwise the dead code elimination would
//eliminate the statement that assigns stuff to the conditional.
return new LiveVars<Statement>(in, out);
}
public Set<Variable> computeLiveOut(Map<CfgBlock, Set<Variable>> in) {
Set<Variable> out = new HashSet<Variable>();
// Exit blocks have all globals and all out params live at exit
if (isExit()) {
//TODO
// out.addAll(getMethod().getModifiedGlobals());
if (!getMethod().getOutParams().isEmpty()) {
out.addAll(getMethod().getOutParams());
}
} else {
for (CfgBlock suc : Graphs.successorListOf(method, this)) {
out.addAll(in.get(suc));
}
//TODO: is this correct? Add all variable used in the
//edges to live out.
for (CfgEdge edge : method.outgoingEdgesOf(this)) {
if (edge.getLabel().isPresent()) {
out.addAll(edge.getLabel().get().getUseVariables());
}
}
}
return out;
}
/**
* Returns a new block with a deep copy of the statements of this
* block.
* The new block has a different label from this block.
* @return A deep copy of this block where all statements have been
* copied as well.
*/
public CfgBlock deepCopy() {
CfgBlock copy = new CfgBlock(method);
List<Statement> stmtCopy = new LinkedList<Statement>();
for (Statement s : statements) {
stmtCopy.add(s.deepCopy());
}
copy.setStatements(stmtCopy);
return copy;
}
}