/* Soot - a J*va Optimization Framework
* Copyright (C) 2003 Jerome Miecznikowski
*
* 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.
*/
package soot.dava.toolkits.base.finders;
import soot.*;
import java.util.*;
import soot.dava.*;
import soot.util.*;
import soot.jimple.*;
import soot.dava.internal.asg.*;
import soot.dava.internal.SET.*;
public class SwitchFinder implements FactFinder
{
public SwitchFinder( Singletons.Global g ) {}
public static SwitchFinder v() { return G.v().soot_dava_toolkits_base_finders_SwitchFinder(); }
private IterableSet junkBody;
private HashSet targetSet;
private LinkedList targetList, snTargetList, tSuccList;
private HashMap index2target, tSucc2indexSet, tSucc2target, tSucc2Body;
public void find( DavaBody davaBody, AugmentedStmtGraph asg, SETNode SET) throws RetriggerAnalysisException
{
Dava.v().log( "SwitchFinder::find()");
final String defaultStr = "default";
Iterator asgit = asg.iterator();
while (asgit.hasNext()) {
AugmentedStmt as = (AugmentedStmt) asgit.next();
Stmt s = as.get_Stmt();
if (((s instanceof TableSwitchStmt) == false) && ((s instanceof LookupSwitchStmt) == false))
continue;
Value key = null;
junkBody = new IterableSet();
targetSet = new HashSet();
targetList = new LinkedList();
snTargetList = new LinkedList();
tSuccList = new LinkedList();
index2target = new HashMap();
tSucc2indexSet = new HashMap();
tSucc2target = new HashMap();
tSucc2Body = new HashMap();
if (s instanceof TableSwitchStmt) {
TableSwitchStmt tss = (TableSwitchStmt) s;
int target_count = tss.getHighIndex() - tss.getLowIndex() + 1;
for (int i=0; i<target_count; i++)
build_Bindings( as, new Integer(i + tss.getLowIndex()), asg.get_AugStmt( (Stmt) tss.getTarget( i)));
build_Bindings( as, defaultStr, asg.get_AugStmt( (Stmt) tss.getDefaultTarget()));
key = tss.getKey();
}
else if (s instanceof LookupSwitchStmt) {
LookupSwitchStmt lss = (LookupSwitchStmt) s;
int target_count = lss.getTargetCount();
for (int i=0; i<target_count; i++)
build_Bindings( as, new Integer( lss.getLookupValue( i)), asg.get_AugStmt( (Stmt) lss.getTarget( i)));
build_Bindings( as, defaultStr, asg.get_AugStmt( (Stmt) lss.getDefaultTarget()));
key = lss.getKey();
}
Iterator tsit = tSuccList.iterator();
while (tsit.hasNext()) {
AugmentedStmt tSucc = (AugmentedStmt) tsit.next();
AugmentedStmt target = (AugmentedStmt) tSucc2target.get( tSucc);
snTargetList.addLast( new SwitchNode( target, (TreeSet<Object>) tSucc2indexSet.get( tSucc), (IterableSet) tSucc2Body.get( tSucc)));
}
TreeSet
targetHeads = new TreeSet(),
killBodies = new TreeSet();
// Get the set of head cases and clear those bodies that should not be included in the switch. Clear as mud, huh? :-)
{
asg.calculate_Reachability( targetList, targetSet, as);
SwitchNodeGraph sng = new SwitchNodeGraph( snTargetList);
killBodies.addAll( snTargetList);
snTargetList = new LinkedList();
LinkedList worklist = new LinkedList();
worklist.addAll( sng.getHeads());
while (worklist.isEmpty() == false) {
SwitchNode sn = (SwitchNode) worklist.removeFirst();
snTargetList.addLast( sn);
killBodies.remove( sn);
SwitchNode champ = null;
Iterator sit = sn.get_Succs().iterator();
while (sit.hasNext()) {
SwitchNode ssn = (SwitchNode) sit.next();
if ((champ == null) || (champ.get_Score() < ssn.get_Score()))
champ = ssn;
}
if ((champ != null) && (champ.get_Score() > 0))
worklist.addLast( champ);
}
Iterator kit = killBodies.iterator();
while (kit.hasNext()) {
SwitchNode sn = (SwitchNode) kit.next();
IterableSet snBody = sn.get_Body();
snBody.clear();
snBody.add( sn.get_AugStmt());
}
sng = new SwitchNodeGraph( snTargetList);
targetHeads.addAll( sng.getHeads());
}
LinkedList<SwitchNode> switchNodeList = new LinkedList<SwitchNode>();
// Now, merge the targetHeads list and the killBodies list, keeping bundles of case fall throughs from the node graph.
{
while ((targetHeads.isEmpty() == false) || (killBodies.isEmpty() == false)) {
if ((targetHeads.isEmpty()) ||
((targetHeads.isEmpty() == false) && (killBodies.isEmpty() == false) && (((SwitchNode) targetHeads.first()).compareTo( killBodies.first()) > 0))) {
SwitchNode nextNode = (SwitchNode) killBodies.first();
killBodies.remove( nextNode);
switchNodeList.addLast( nextNode);
}
else {
SwitchNode nextNode = (SwitchNode) targetHeads.first();
targetHeads.remove( nextNode);
while (true) {
switchNodeList.addLast( nextNode);
if (nextNode.get_Succs().isEmpty())
break;
nextNode = (SwitchNode) nextNode.get_Succs().get(0);
}
}
}
}
IterableSet body = new IterableSet();
body.add( as);
Iterator<SwitchNode> snlit = switchNodeList.iterator();
while (snlit.hasNext()) {
SwitchNode sn = snlit.next();
body.addAll( sn.get_Body());
if (sn.get_IndexSet().contains( defaultStr)) {
sn.get_IndexSet().clear();
sn.get_IndexSet().add( defaultStr);
}
}
body.addAll( junkBody);
Iterator enlit = davaBody.get_ExceptionFacts().iterator();
while (enlit.hasNext()) {
ExceptionNode en = (ExceptionNode) enlit.next();
IterableSet tryBody = en.get_TryBody();
if (tryBody.contains( as)) {
Iterator fbit = body.snapshotIterator();
while (fbit.hasNext()) {
AugmentedStmt fbas = (AugmentedStmt) fbit.next();
if (tryBody.contains( fbas) == false) {
body.remove( fbas);
snlit = switchNodeList.iterator();
while (snlit.hasNext()) {
IterableSet switchBody = snlit.next().get_Body();
if (switchBody.contains( fbas)) {
switchBody.remove( fbas);
break;
}
}
}
}
}
}
SET.nest( new SETSwitchNode( as, key, body, switchNodeList, junkBody));
}
}
private IterableSet find_SubBody( AugmentedStmt switchAS, AugmentedStmt branchS)
{
IterableSet subBody = new IterableSet();
LinkedList<AugmentedStmt> worklist = new LinkedList<AugmentedStmt>();
subBody.add( branchS);
branchS = (AugmentedStmt) branchS.bsuccs.get(0);
if (branchS.get_Dominators().contains( switchAS)) {
worklist.addLast( branchS);
subBody.add( branchS);
}
while (worklist.isEmpty() == false) {
AugmentedStmt as = worklist.removeFirst();
Iterator sit = as.csuccs.iterator();
while (sit.hasNext()) {
AugmentedStmt sas = (AugmentedStmt) sit.next();
if ((subBody.contains( sas) == false) && (sas.get_Dominators().contains( branchS))) {
worklist.addLast( sas);
subBody.add( sas);
}
}
}
return subBody;
}
private void build_Bindings( AugmentedStmt swAs, Object index, AugmentedStmt target)
{
AugmentedStmt tSucc = (AugmentedStmt) target.bsuccs.get(0);
if (targetSet.add( tSucc))
targetList.addLast( tSucc);
index2target.put( index, target);
TreeSet indices = null;
if ((indices = (TreeSet) tSucc2indexSet.get( tSucc)) == null) {
indices = new TreeSet( new IndexComparator());
tSucc2indexSet.put( tSucc, indices);
tSucc2target.put( tSucc, target);
tSucc2Body.put( tSucc, find_SubBody( swAs, target));
tSuccList.add( tSucc);
}
else {
junkBody.add( target);
// break all edges between the junk body and any of it's successors
Iterator sit = target.bsuccs.iterator();
while (sit.hasNext())
((AugmentedStmt) sit.next()).bpreds.remove( target);
sit = target.csuccs.iterator();
while (sit.hasNext())
((AugmentedStmt) sit.next()).cpreds.remove( target);
target.bsuccs.clear();
target.csuccs.clear();
}
indices.add( index);
}
}