/** * */ 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; } }