/** * */ package bixie.transformation; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import util.Log; import boogie.controlflow.BasicBlock; import boogie.controlflow.CfgProcedure; import boogie.controlflow.CfgVariable; import boogie.controlflow.expression.CfgExpression; import boogie.controlflow.expression.CfgIdentifierExpression; import boogie.controlflow.statement.CfgAssertStatement; import boogie.controlflow.statement.CfgAssumeStatement; import boogie.controlflow.statement.CfgCallStatement; import boogie.controlflow.statement.CfgHavocStatement; import boogie.controlflow.statement.CfgStatement; /** * @author schaef * */ public class CallUnwinding { public CallUnwinding () { } public void unwindCalls(CfgProcedure p) { Log.debug("eliminateCalls for: " + p.getProcedureName()); BasicBlock root = p.getRootNode(); LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>(); LinkedList<BasicBlock> done = new LinkedList<BasicBlock>(); todo.add(root); while (!todo.isEmpty()) { BasicBlock current = todo.pop(); done.add(current); /* * For each BasicBlock, iterate over the statements. If a statement * is a call, collect all variables in the modifies clause and in * the LHS of the call statement and replace the call by a Havoc for * all these variables. */ LinkedList<CfgStatement> statements = current.getStatements(); //shallow copy for iteration ... needed because we're modifying "statements" LinkedList<CfgStatement> iterlist = new LinkedList<CfgStatement>(statements); for (CfgStatement stmt : iterlist) { if (stmt instanceof CfgCallStatement) { CfgCallStatement call = (CfgCallStatement)stmt; int offset = 0; LinkedList<CfgStatement> callcontract = new LinkedList<CfgStatement>(); //insert the assert statements that are enforced by the "requires" clauses //of the callee. HashMap<CfgVariable, CfgExpression> substitutes = new HashMap<CfgVariable, CfgExpression>(); for (int j=offset; j<call.getCallee().getInParams().length; j++) { substitutes.put(call.getCallee().getInParams()[j], call.getArguments()[j]); } for (CfgExpression xp : call.getCallee().getRequires()) { callcontract.add(new CfgAssertStatement(call.getLocation(), xp.substitute(substitutes))); } //create the havoc statement for the modifies clause. HashSet<CfgVariable> modifies = new HashSet<CfgVariable>(); modifies.addAll(call.getCallee().getModifies()); for (CfgIdentifierExpression lhs : call.getLeftHandSide()) { modifies.add(lhs.getVariable()); } CfgHavocStatement havoc = new CfgHavocStatement( call.getLocation(), modifies.toArray(new CfgVariable[modifies.size()])); // Log.error(" call: "+ call.toString()); // Log.error(" becomes: "+ havoc.toString()); havoc.setReplacedStatement(call); callcontract.add(havoc); //insert the assume statements that are guaranteed by the "ensures" clauses //of the callee. substitutes = new HashMap<CfgVariable, CfgExpression>(); for (int j=offset; j<call.getCallee().getOutParams().length; j++) { substitutes.put(call.getCallee().getOutParams()[j], call.getLeftHandSide()[j]); } for (CfgExpression xp : call.getCallee().getEnsures()) { if (statements.indexOf(stmt)<statements.size()-1) { statements.add(new CfgAssumeStatement(call.getLocation(), xp.substitute(substitutes))); } else { callcontract.add(new CfgAssumeStatement(call.getLocation(), xp.substitute(substitutes))); } } //now merge callcontract back into statements at and replace the original call stmt. statements.addAll(statements.indexOf(stmt), callcontract); //and now remove the old call statement. statements.remove(stmt); } else { //TODO } } current.setStatements(statements); for (BasicBlock next : current.getSuccessors()) { if (!todo.contains(next) && !done.contains(next)) { todo.add(next); } } } } }