/* Soot - a J*va Optimization Framework
* Copyright (C) 2003 Jerome Miecznikowski
* Copyright (C) 2006 Nomair A. Naeem
*
* This library 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 library 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* CHANGE LOG:
* 26-January-2006: Fixed Bug in Dava. Could not detect empty infinte loops.
* 5-April -2006: Fixed bug in Fix_MultiEntryPoint read comment dated 5 th April 2005
*/
package soot.dava.toolkits.base.finders;
import soot.*;
import java.util.*;
import soot.dava.*;
import soot.util.*;
import soot.jimple.*;
import soot.grimp.internal.*;
import soot.toolkits.graph.*;
import soot.jimple.internal.*;
import soot.dava.internal.asg.*;
import soot.dava.internal.SET.*;
import soot.dava.internal.javaRep.*;
import soot.dava.toolkits.base.misc.*;
public class CycleFinder implements FactFinder
{
public CycleFinder( Singletons.Global g ) {}
public static CycleFinder v() { return G.v().soot_dava_toolkits_base_finders_CycleFinder(); }
public void find( DavaBody body, AugmentedStmtGraph asg, SETNode SET) throws RetriggerAnalysisException
{
Dava.v().log("CycleFinder::find()");
AugmentedStmtGraph wasg = (AugmentedStmtGraph) asg.clone();
List<List> component_list = build_component_list( wasg);
// loop through all nestings
while (component_list.isEmpty() == false) {
IterableSet node_list = new IterableSet();
// loop through all the strongly connected components
Iterator<List> cit = component_list.iterator();
while (cit.hasNext()) {
node_list.clear();
node_list.addAll( cit.next());
//node_list contains all the nodes belonging to this SCC
IterableSet entry_points = get_EntryPoint( node_list);
//if more than one entry points found
if (entry_points.size() > 1) {
LinkedList<AugmentedStmt> asgEntryPoints = new LinkedList<AugmentedStmt>();
Iterator it = entry_points.iterator();
while (it.hasNext())
asgEntryPoints.addLast( asg.get_AugStmt( ((AugmentedStmt) it.next()).get_Stmt()));
IterableSet asgScc = new IterableSet();
it = node_list.iterator();
while (it.hasNext())
asgScc.addLast( asg.get_AugStmt( ((AugmentedStmt) it.next()).get_Stmt()));
fix_MultiEntryPoint( body, asg, asgEntryPoints, asgScc);
throw new RetriggerAnalysisException();
}
//gets to this code only if each SCC has one entry point?
AugmentedStmt entry_point = (AugmentedStmt) entry_points.getFirst();
AugmentedStmt
characterizing_stmt = find_CharacterizingStmt( entry_point, node_list, wasg),
succ_stmt = null;
if (characterizing_stmt != null) {
Iterator sit = characterizing_stmt.bsuccs.iterator();
while (sit.hasNext()) {
succ_stmt = (AugmentedStmt) sit.next();
if (node_list.contains( succ_stmt) == false)
break;
}
}
wasg.calculate_Reachability( succ_stmt, new HashSet(), entry_point);
IterableSet cycle_body = get_CycleBody( entry_point, succ_stmt, asg, wasg);
SETCycleNode newNode = null;
if (characterizing_stmt != null) {
Iterator enlit = body.get_ExceptionFacts().iterator();
checkExceptionLoop:
while (enlit.hasNext()) {
ExceptionNode en = (ExceptionNode) enlit.next();
IterableSet tryBody = en.get_TryBody();
if (tryBody.contains( asg.get_AugStmt( characterizing_stmt.get_Stmt()))) {
Iterator cbit = cycle_body.iterator();
while (cbit.hasNext()) {
AugmentedStmt cbas = (AugmentedStmt) cbit.next();
if (tryBody.contains( cbas) == false) {
characterizing_stmt = null;
break checkExceptionLoop;
}
}
}
}
}
/*
if (tryBody.contains( asg.get_AugStmt( characterizing_stmt.get_Stmt()))) {
if (checkExceptionNodes.contains( en) == false)
checkExceptionNodes.add( en);
Iterator cbit = cycle_body.snapshotIterator();
while (cbit.hasNext()) {
AugmentedStmt cbas = (AugmentedStmt) cbit.next();
if (tryBody.contains( cbas) == false)
cycle_body.remove( cbas);
}
}
}
enlit = checkExceptionNodes.iterator();
exceptionNestingLoop:
while (enlit.hasNext()) {
ExceptionNode en = (ExceptionNode) enlit.next();
Iterator cbit = cycle_body.iterator();
while (cbit.hasNext()) {
AugmentedStmt cbas = (AugmentedStmt) cbit.next();
if (en.get_TryBody().contains( cbas) == false) {
characterizing_stmt = null;
break exceptionNestingLoop;
}
}
}
}
*/
// unconditional loop
if (characterizing_stmt == null) {
wasg.remove_AugmentedStmt( entry_point);
newNode = new SETUnconditionalWhileNode( cycle_body);
}
else {
body.consume_Condition( asg.get_AugStmt( characterizing_stmt.get_Stmt()));
wasg.remove_AugmentedStmt( characterizing_stmt);
IfStmt condition = (IfStmt) characterizing_stmt.get_Stmt();
if ( cycle_body.contains( asg.get_AugStmt( condition.getTarget())) == false)
condition.setCondition( ConditionFlipper.flip((ConditionExpr) condition.getCondition()));
if (characterizing_stmt == entry_point)
newNode = new SETWhileNode( asg.get_AugStmt( characterizing_stmt.get_Stmt()), cycle_body);
else
newNode = new SETDoWhileNode( asg.get_AugStmt( characterizing_stmt.get_Stmt()), asg.get_AugStmt( entry_point.get_Stmt()), cycle_body);
}
if (newNode != null)
SET.nest( newNode);
}
component_list = build_component_list( wasg);
}
}
/*
* Nomair A. Naeem Entry point to a SCC ARE those stmts whose predecessor
* does not belong to the SCC
*/
private IterableSet get_EntryPoint( IterableSet nodeList){
IterableSet entryPoints = new IterableSet();
Iterator it = nodeList.iterator();
while (it.hasNext()) {
AugmentedStmt as = (AugmentedStmt) it.next();
Iterator pit = as.cpreds.iterator();
while (pit.hasNext()) {
Object po = pit.next();
if (nodeList.contains( po) == false) {
entryPoints.add( as);
break;
}
}
}
return entryPoints;
}
private List<List> build_component_list( AugmentedStmtGraph asg){
List<List> c_list = new LinkedList<List>();
StronglyConnectedComponentsFast scc = new StronglyConnectedComponentsFast( asg);
//makes sure that all scc's with only one statement in them are removed
/*
26th Jan 2006 Nomair A. Naeem
This could be potentially bad since self loops will also get removed
Adding code to check for self loop (a stmt is a self loop if its pred and succ
contain the stmt itself
*/
Iterator<List> scomit = scc.getComponents().iterator();
while (scomit.hasNext()) {
List wcomp = scomit.next();
if (wcomp.size() > 1)
c_list.add( wcomp);
else if (wcomp.size()==1){
//this is a scc of one augmented stmt
//We should add those which are self loops
AugmentedStmt as = (AugmentedStmt)wcomp.get(0);
if(as.cpreds.contains(as) && (as.csuccs.contains(as))){
//"as" has a predecssor and successor which is as i.e. it is a self loop
List<AugmentedStmt> currentComponent = null;
currentComponent = new StationaryArrayList();
currentComponent.add(as);
//System.out.println("Special add of"+as);
c_list.add(currentComponent);
}
}
}
return c_list;
}
private AugmentedStmt find_CharacterizingStmt( AugmentedStmt entry_point, IterableSet sc_component, AugmentedStmtGraph asg)
{
/*
* Check whether we are a while loop.
*/
if (entry_point.get_Stmt() instanceof IfStmt) {
// see if there's a successor who's not in the strict loop set
Iterator sit = entry_point.bsuccs.iterator();
while (sit.hasNext())
if (sc_component.contains( sit.next()) == false)
return entry_point;
}
/*
* We're not a while loop. Get the candidates for condition on a do-while loop.
*/
IterableSet candidates = new IterableSet();
HashMap candSuccMap = new HashMap();
HashSet blockers = new HashSet();
// Get the set of all candidates.
Iterator pit = entry_point.bpreds.iterator();
while (pit.hasNext()) {
AugmentedStmt pas = (AugmentedStmt) pit.next();
if ((pas.get_Stmt() instanceof GotoStmt) && (pas.bpreds.size() == 1))
pas = (AugmentedStmt) pas.bpreds.get(0);
if ((sc_component.contains( pas)) && (pas.get_Stmt() instanceof IfStmt)) {
Iterator spasit = pas.bsuccs.iterator();
while (spasit.hasNext()) {
AugmentedStmt spas = (AugmentedStmt) spasit.next();
if (sc_component.contains( spas) == false) {
candidates.add( pas);
candSuccMap.put( pas, spas);
blockers.add( spas);
break;
}
}
}
}
/*
* If there was no candidate, we are an unconditional loop.
*/
if (candidates.isEmpty())
return null;
/*
* Get the best candidate for the do-while condition.
*/
if (candidates.size() == 1)
return (AugmentedStmt) candidates.getFirst();
// Take the candidate(s) whose successor has maximal reachability from all candidates.
asg.calculate_Reachability( candidates, blockers, entry_point);
IterableSet max_Reach_Set = null;
int reachSize = 0;
Iterator candit = candidates.iterator();
while (candit.hasNext()) {
AugmentedStmt as = (AugmentedStmt) candit.next();
int current_reach_size = ((AugmentedStmt) candSuccMap.get( as)).get_Reachers().intersection( candidates).size();
if (current_reach_size > reachSize) {
max_Reach_Set = new IterableSet();
reachSize = current_reach_size;
}
if (current_reach_size == reachSize)
max_Reach_Set.add( as);
}
candidates = max_Reach_Set;
if (candidates.size() == 1)
return (AugmentedStmt) candidates.getFirst();
// Find a single source shortest path from the entry point to any of the remaining candidates.
HashSet<Object> touchSet = new HashSet<Object>();
LinkedList<Object> worklist = new LinkedList<Object>();
worklist.addLast( entry_point);
touchSet.add( entry_point);
while (worklist.isEmpty() == false) {
Iterator sit = ((AugmentedStmt) worklist.removeFirst()).csuccs.iterator();
while (sit.hasNext()) {
Object so = sit.next();
if (candidates.contains( so))
return (AugmentedStmt) so;
if ((sc_component.contains( so)) && (touchSet.contains( so) == false)) {
worklist.addLast( so);
touchSet.add( so);
}
}
}
throw new RuntimeException( "Somehow didn't find a condition for a do-while loop!");
}
private IterableSet get_CycleBody( AugmentedStmt entry_point, AugmentedStmt boundary_stmt, AugmentedStmtGraph asg, AugmentedStmtGraph wasg)
{
IterableSet cycle_body = new IterableSet();
LinkedList<AugmentedStmt> worklist = new LinkedList<AugmentedStmt>();
AugmentedStmt asg_ep = asg.get_AugStmt( entry_point.get_Stmt());
worklist.add( entry_point);
cycle_body.add( asg_ep);
while (worklist.isEmpty() == false) {
AugmentedStmt as = worklist.removeFirst();
Iterator sit = as.csuccs.iterator();
while (sit.hasNext()) {
AugmentedStmt wsas = (AugmentedStmt) sit.next();
AugmentedStmt sas = asg.get_AugStmt( wsas.get_Stmt());
if (cycle_body.contains( sas))
continue;
/*
if (sas.get_Dominators().contains( asg_ep) == false) {
G.v().out.println( wsas + " not dominated by " + asg_ep);
G.v().out.println( "doms");
Iterator dit = sas.get_Dominators().iterator();
while (dit.hasNext())
G.v().out.println( " " + dit.next());
G.v().out.println("preds");
dit = sas.cpreds.iterator();
while (dit.hasNext())
G.v().out.println( " " + dit.next());
}
*/
if ((cycle_body.contains( sas) == false) && (sas.get_Dominators().contains( asg_ep))) {
if ((boundary_stmt != null) &&
((wsas.get_Reachers().contains( boundary_stmt)) || (wsas == boundary_stmt)))
continue;
// G.v().out.println( sas);
worklist.add( wsas);
cycle_body.add( sas);
}
}
}
return cycle_body;
}
private void fix_MultiEntryPoint( DavaBody body, AugmentedStmtGraph asg, LinkedList<AugmentedStmt> entry_points, IterableSet scc)
{
AugmentedStmt naturalEntryPoint = get_NaturalEntryPoint( entry_points, scc);
Local controlLocal = body.get_ControlLocal();
Unit defaultTarget = naturalEntryPoint.get_Stmt();
LinkedList targets = new LinkedList();
/*
* Nomair A Naeem, Micheal Batchelder
* 5 th April 2005
* shouldnt send empty targets list to constructor of GTableSwitch since
* then it just creates an empty array to hold the targets..
* we intend to fill these in later using the setTarget method
*
* hence the hack is to just send in null fully aware that they are going to be changed
* to the target we want within the following while loop
*/
for(int i=0;i<entry_points.size();i++)
targets.add(null);
/* 5th April End code change */
TableSwitchStmt tss = new GTableSwitchStmt( controlLocal, 0, entry_points.size() - 2, targets, defaultTarget);
AugmentedStmt dispatchStmt = new AugmentedStmt( tss);
IterableSet
predecessorSet = new IterableSet(),
indirectionStmtSet = new IterableSet(),
directionStmtSet = new IterableSet();
int count = 0;
Iterator<AugmentedStmt> epit = entry_points.iterator();
while (epit.hasNext()) {
AugmentedStmt entryPoint = epit.next();
GotoStmt gotoStmt = new JGotoStmt( entryPoint.get_Stmt());
AugmentedStmt indirectionStmt = new AugmentedStmt( gotoStmt);
indirectionStmtSet.add( indirectionStmt);
tss.setTarget( count++, gotoStmt);
dispatchStmt.add_BSucc( indirectionStmt);
indirectionStmt.add_BPred( dispatchStmt);
indirectionStmt.add_BSucc( entryPoint);
entryPoint.add_BPred( indirectionStmt);
asg.add_AugmentedStmt( indirectionStmt);
LinkedList toRemove = new LinkedList();
Iterator pit = entryPoint.cpreds.iterator();
while (pit.hasNext()) {
AugmentedStmt pas = (AugmentedStmt) pit.next();
if ((pas == indirectionStmt) || ((entryPoint != naturalEntryPoint) && (scc.contains( pas))))
continue;
if (scc.contains( pas) == false)
predecessorSet.add( pas);
AssignStmt asnStmt = new GAssignStmt( controlLocal, DIntConstant.v( count, null));
AugmentedStmt directionStmt = new AugmentedStmt( asnStmt);
directionStmtSet.add( directionStmt);
patch_Stmt( pas.get_Stmt(), entryPoint.get_Stmt(), asnStmt);
// Mark the original predecessor to be removed.
toRemove.addLast( pas);
pas.csuccs.remove( entryPoint);
pas.csuccs.add( directionStmt);
if (pas.bsuccs.contains( entryPoint)) {
pas.bsuccs.remove( entryPoint);
pas.bsuccs.add( directionStmt);
}
directionStmt.cpreds.add( pas);
if (pas.bsuccs.contains( directionStmt))
directionStmt.bpreds.add( pas);
directionStmt.add_BSucc( dispatchStmt);
dispatchStmt.add_BPred( directionStmt);
asg.add_AugmentedStmt( directionStmt);
}
Iterator trit = toRemove.iterator();
while (trit.hasNext()) {
AugmentedStmt ras = (AugmentedStmt) trit.next();
entryPoint.cpreds.remove( ras);
if (entryPoint.bpreds.contains( ras))
entryPoint.bpreds.remove( ras);
}
}
asg.add_AugmentedStmt( dispatchStmt);
Iterator efit = body.get_ExceptionFacts().iterator();
exceptionFactLoop:
while (efit.hasNext()) {
ExceptionNode en = (ExceptionNode) efit.next();
IterableSet tryBody = en.get_TryBody();
epit = entry_points.iterator();
while (epit.hasNext())
if (tryBody.contains( epit.next()) == false)
continue exceptionFactLoop;
en.add_TryStmts( indirectionStmtSet);
en.add_TryStmt( dispatchStmt);
Iterator pit = predecessorSet.iterator();
while (pit.hasNext())
if (tryBody.contains( pit.next()) == false)
continue exceptionFactLoop;
en.add_TryStmts( directionStmtSet);
}
}
private AugmentedStmt get_NaturalEntryPoint( LinkedList<AugmentedStmt> entry_points, IterableSet scc)
{
AugmentedStmt best_candidate = null;
int minScore = 0;
Iterator<AugmentedStmt> epit = entry_points.iterator();
while (epit.hasNext()) {
AugmentedStmt entryPoint = epit.next();
HashSet<AugmentedStmt>
touchSet = new HashSet<AugmentedStmt>(),
backTargets = new HashSet<AugmentedStmt>();
touchSet.add( entryPoint);
DFS( entryPoint, touchSet, backTargets, scc);
if ((best_candidate == null) || (backTargets.size() < minScore)) {
minScore = touchSet.size();
best_candidate = entryPoint;
}
}
return best_candidate;
}
private void DFS( AugmentedStmt as, HashSet<AugmentedStmt> touchSet, HashSet<AugmentedStmt> backTargets, IterableSet scc)
{
Iterator sit = as.csuccs.iterator();
while (sit.hasNext()) {
AugmentedStmt sas = (AugmentedStmt) sit.next();
if (scc.contains( sas) == false)
continue;
if (touchSet.contains( sas)) {
if (backTargets.contains( sas) == false)
backTargets.add( sas);
}
else {
touchSet.add( sas);
DFS( sas, touchSet, backTargets, scc);
touchSet.remove( sas);
}
}
}
private void patch_Stmt( Stmt src, Stmt oldDst, Stmt newDst)
{
if (src instanceof GotoStmt) {
((GotoStmt) src).setTarget( newDst);
return;
}
if (src instanceof IfStmt) {
IfStmt ifs = (IfStmt) src;
if (ifs.getTarget() == oldDst)
ifs.setTarget( newDst);
return;
}
if (src instanceof TableSwitchStmt) {
TableSwitchStmt tss = (TableSwitchStmt) src;
if (tss.getDefaultTarget() == oldDst) {
tss.setDefaultTarget( newDst);
return;
}
for (int i = tss.getLowIndex(); i <= tss.getHighIndex(); i++)
if (tss.getTarget( i) == oldDst) {
tss.setTarget( i, newDst);
return;
}
}
if (src instanceof LookupSwitchStmt) {
LookupSwitchStmt lss = (LookupSwitchStmt) src;
if (lss.getDefaultTarget() == oldDst) {
lss.setDefaultTarget( newDst);
return;
}
for (int i = 0; i < lss.getTargetCount(); i++)
if (lss.getTarget( i) == oldDst) {
lss.setTarget( i, newDst);
return;
}
}
}
}