All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.toolkits.graph.pdg.EnhancedUnitGraph Maven / Gradle / Ivy

There is a newer version: 2.5.0-9
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 1999-2010 Hossein Sadat-Mohtasham
 *
 * 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.toolkits.graph.pdg;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import soot.Body;
import soot.Trap;
import soot.Unit;
import soot.jimple.ThrowStmt;
import soot.jimple.internal.JNopStmt;
import soot.toolkits.graph.DominatorNode;
import soot.toolkits.graph.MHGDominatorsFinder;
import soot.toolkits.graph.MHGPostDominatorsFinder;
import soot.toolkits.graph.UnitGraph;
import soot.util.Chain;

/**
 * 
 * This class represents a control flow graph which behaves like an ExceptionalUnitGraph and 
 * BriefUnitGraph when there are no exception handling construct in the method; at the presence
 * of such constructs, the CFG is constructed from a brief graph by addition a concise representation
 * of the exceptional flow as well as START/STOP auxiliary nodes. In a nutshell, the exceptional flow
 * is represented at the level of try-catch-finally blocks instead of the Unit level to allow a more
 * useful region analysis.
 * 
 * @author Hossein Sadat-Mohtasham
 *
 */

public class EnhancedUnitGraph extends UnitGraph {
	
	//This keeps a map from the beginning of each guarded block
	//to the corresponding special EHNopStmt.
	//protected Hashtable try2nop = null;
	protected Hashtable try2nop = null;
	//Keep the real header of the handler block
	protected Hashtable handler2header = null;
	
	
	public EnhancedUnitGraph(Body body)
	{
	    super(body);
	
	    //try2nop = new Hashtable();
	    try2nop = new Hashtable();
	    handler2header = new Hashtable();
	    
	        
	    //there could be a maximum of traps.size() of nop
	    //units added to the CFG plus potentially START/STOP nodes.
		int size = unitChain.size() + body.getTraps().size() + 2;

		unitToSuccs = new HashMap>(size * 2 + 1, 0.7f);
		unitToPreds = new HashMap>(size * 2 + 1, 0.7f);
		
		
		/*
		 * Compute the head and tails at each phase because other phases
		 * might rely on them.
		 */
		buildUnexceptionalEdges(unitToSuccs, unitToPreds);	
		addAuxiliaryExceptionalEdges();	
		buildHeadsAndTails();
		handleExplicitThrowEdges();
		buildHeadsAndTails();
		handleMultipleReturns();
		buildHeadsAndTails();

		/**
		 * Remove bogus heads (these are useless goto's)
		 */
	    
		removeBogusHeads();
		buildHeadsAndTails();
		
		makeMappedListsUnmodifiable(unitToSuccs);
		makeMappedListsUnmodifiable(unitToPreds);

	}

	
	/**
	 * This method adds a STOP node to the graph, if necessary, to make the CFG
	 * single-tailed.
	 */
	
	protected void handleMultipleReturns()
	{
	
		if(this.getTails().size() > 1)
		{
			Unit stop = new ExitStmt();
		    List predsOfstop = new ArrayList();
	
		    
			for(Iterator tailItr = this.getTails().iterator(); tailItr.hasNext(); )
			{
				Unit tail = tailItr.next();
				predsOfstop.add(tail);
			
				List tailSuccs = this.unitToSuccs.get(tail);
				tailSuccs.add(stop);	
			}
			
		    this.unitToPreds.put(stop, predsOfstop);
		    this.unitToSuccs.put(stop, new ArrayList());
	
		    Chain units = body.getUnits().getNonPatchingChain();
		    if(!units.contains(stop))
		    	units.addLast(stop);	    
		}
	}
	
	/**
	 * This method removes all the heads in the CFG except the one
	 * that corresponds to the first unit in the method. 
	 */
	
	protected void removeBogusHeads()
	{
		Chain units = body.getUnits();
		Unit trueHead = units.getFirst();
		
		while(this.getHeads().size() > 1)
		{
			for(Iterator headItr = this.getHeads().iterator(); headItr.hasNext(); )
			{
				Unit head = headItr.next();
				if(trueHead == head)
					continue;
				
				this.unitToPreds.remove(head);
				List succs = this.unitToSuccs.get(head);
				for(Iterator succsItr = succs.iterator(); succsItr.hasNext(); )
				{
					List tobeRemoved = new ArrayList();
					
					Unit succ = succsItr.next();
					List predOfSuccs = this.unitToPreds.get(succ);
					

					for(Iterator predItr = predOfSuccs.iterator(); predItr.hasNext(); )
					{
						Unit pred = predItr.next();
						if(pred == head)
							tobeRemoved.add(pred);	
						
					}
					
					predOfSuccs.removeAll(tobeRemoved);
				}
				
				this.unitToSuccs.remove(head);
				
				if(units.contains(head))
					units.remove(head);
			}
			
			this.buildHeadsAndTails();
		}
	}
	

	@SuppressWarnings("unchecked")
	protected void handleExplicitThrowEdges()
	{
		MHGDominatorTree dom = new MHGDominatorTree(new MHGDominatorsFinder(this));
		MHGDominatorTree pdom = new MHGDominatorTree(new MHGPostDominatorsFinder(this));
		
		//this keeps a map from the entry of a try-catch-block to a selected merge point 
		Hashtable x2mergePoint = new Hashtable();
		
		List tails = this.getTails();

		TailsLoop:		
		for(Iterator itr = tails.iterator(); itr.hasNext(); )
		{
			Unit tail = itr.next();
			if(!(tail instanceof ThrowStmt))
				continue;
			
			DominatorNode x = dom.getDode(tail);
			DominatorNode parentOfX = dom.getParentOf(x);
			Object xgode = x.getGode();
			DominatorNode xpdomDode = pdom.getDode(xgode);
			Object parentXGode = parentOfX.getGode();
			DominatorNode parentpdomDode = pdom.getDode(parentXGode);
			//while x post-dominates its dominator (parent in dom)
			while(pdom.isDominatorOf(xpdomDode, parentpdomDode))
			{
				x = parentOfX;
				parentOfX = dom.getParentOf(x);
				
				//If parent is null we must be at the head of the graph
				if(parentOfX == null)
					//throw new RuntimeException("This should never have happened!");
					break;
				
				xgode = x.getGode();
				xpdomDode = pdom.getDode(xgode);
				parentXGode = parentOfX.getGode();
				parentpdomDode = pdom.getDode(parentXGode);
			}
			
			if(parentOfX != null)
				x = parentOfX;
			
			xgode = x.getGode();
			xpdomDode = pdom.getDode(xgode);
			
			
			Unit mergePoint = null;
			
			if(x2mergePoint.containsKey(xgode))
				mergePoint = x2mergePoint.get(xgode);
			else
			{
				//Now get all the children of x in the dom
				
				List domChilds = dom.getChildrenOf(x);
								
				Object child1god = null;
				Object child2god = null;
				
				for(Iterator domItr = domChilds.iterator(); domItr.hasNext(); )
				{
					DominatorNode child = domItr.next();
					Object childGode = child.getGode();
					DominatorNode childpdomDode = pdom.getDode(childGode);
					
					
					//we don't want to make a loop! 
					List path = this.getExtendedBasicBlockPathBetween((Unit)childGode, tail);
					
					//if(dom.isDominatorOf(child, dom.getDode(tail)))
					if(!(path == null || path.size() == 0))
						continue;
					
					if(pdom.isDominatorOf(childpdomDode, xpdomDode))
					{
						mergePoint = (Unit) child.getGode();				
						break;
					}					
									
					//gather two eligible childs
					if(child1god == null)
						child1god = childGode;
					else if(child2god == null)
						child2god = childGode;
						
				}
				
				if(mergePoint == null)
				{
					if(child1god != null && child2god != null)
					{
						DominatorNode child1 = pdom.getDode(child1god);
						DominatorNode child2 = pdom.getDode(child2god);
	
						//go up the pdom tree and find the common parent of child1 and child2
						DominatorNode comParent = child1.getParent();
						while(comParent != null)
						{
							if(pdom.isDominatorOf(comParent, child2))
							{
								mergePoint = (Unit) comParent.getGode();
								break;
							}
							comParent = comParent.getParent();
						}
					}
					else if(child1god != null || child2god != null){
					
						DominatorNode y = null;
						
						if(child1god != null)
							y = pdom.getDode(child1god);
						else if(child2god != null)
							y = pdom.getDode(child2god);
						
							
						DominatorNode initialY = dom.getDode(y.getGode());
						DominatorNode yDodeInDom = initialY;
						
						while(dom.isDominatorOf(x, yDodeInDom))
						{
							y = y.getParent();
							
							//If this is a case where the childs of a conditional 
							//are all throws, or returns, just forget it!
							if(y == null)
							{
								break ;
							}
							yDodeInDom = dom.getDode(y.getGode());
						}
						if(y != null)
							mergePoint = (Unit) y.getGode();
						else
							mergePoint = (Unit) initialY.getGode();
					}
				}
				
				//This means no (dom) child of x post-dominates x, so just use the child that is
				//immediately
				/*if(mergePoint == null)
				{
					//throw new RuntimeException("No child post-dominates x.");
					mergePoint = potentialMergePoint;
					
				}*/
				//This means no (dom) child of x post-dominates x, so just use the child that is
				//immediately. this means there is no good reliable merge point. So we just fetch the succ
				//of x in CFg so that the succ does not dominate the throw, and find the first 
				//post-dom of the succ so that x does not dom it.
				//
				if(mergePoint == null)
				{
					List xSucc = this.unitToSuccs.get(x.getGode());
					for(Iterator uItr = xSucc.iterator(); uItr.hasNext(); )
					{
						Unit u = uItr.next();
						if(dom.isDominatorOf(dom.getDode(u), dom.getDode(tail)))
							continue;
						
						
						DominatorNode y = pdom.getDode(u);
						
						while(dom.isDominatorOf(x, y))
						{
							y = y.getParent();
							//If this is a case where the childs of a conditional 
							//are all throws, or returns, just forget it!
							if(y == null)
							{
								continue TailsLoop;
							}
						}
						mergePoint = (Unit) y.getGode();
						break;
					}
				}
				//the following happens if the throw is the only exit in the method (even if return stmt is present.)
				else if(dom.isDominatorOf(dom.getDode(mergePoint), dom.getDode(tail)))
					continue TailsLoop;
				
				if(mergePoint == null)
					throw new RuntimeException("This should not have happened!");
				
				x2mergePoint.put((Unit) xgode, mergePoint);
			}
			//add an edge from the tail (throw) to the merge point
			
			if(!this.unitToSuccs.containsKey(tail))
				this.unitToSuccs.put(tail, new ArrayList());
			
			List throwSuccs = this.unitToSuccs.get(tail);
			throwSuccs.add(mergePoint);
			
			List mergePreds = this.unitToPreds.get(mergePoint);
			mergePreds.add(tail);	
			
		}

	
	}

	/**
	 * Add an exceptional flow edge for each handler from the corresponding
	 * auxiliary nop node to the beginning of the handler.
	 */
	protected void addAuxiliaryExceptionalEdges()
	{		
		
		//Do some preparation for each trap in the method
		for (Iterator trapIt = body.getTraps().iterator(); trapIt.hasNext(); ) 
		{
		    Trap trap = trapIt.next();		    
		    
		    /**
		     * Find the real header of this handler block
		     * 
		     */
		    Unit handler = trap.getHandlerUnit();
		    
		    Unit pred = handler;
		    while(this.unitToPreds.get(pred).size() > 0)
		    	pred = this.unitToPreds.get(pred).get(0);
		    
		    
		    handler2header.put(handler, pred);
		    /***********/
		    
		    /*
		     * Keep this here for possible future changes.
		     */
		    /*GuardedBlock gb = new GuardedBlock(trap.getBeginUnit(), trap.getEndUnit());
		    Unit ehnop;
		    if(try2nop.containsKey(gb))
		    	ehnop = try2nop.get(gb);
		    else
		    {
		    	ehnop = new EHNopStmt();
		    	try2nop.put(gb, ehnop);
		    }*/
		    
		    
		    Unit ehnop;
		    if(try2nop.containsKey(trap.getBeginUnit()))
		    	ehnop = try2nop.get(trap.getBeginUnit());
		    else
		    {
		    	ehnop = new EHNopStmt();
		    	try2nop.put(trap.getBeginUnit(), ehnop);
		    }
		    		    
		}
		
		//Only add a nop once
		Hashtable nop2added = new Hashtable();

		// Now actually add the edge
AddExceptionalEdge:
		for (Iterator trapIt = body.getTraps().iterator(); trapIt.hasNext(); ) 
		{

		    Trap trap = trapIt.next();
		    Unit b = trap.getBeginUnit();
		    Unit handler = trap.getHandlerUnit();
		    handler = handler2header.get(handler);
		    
		    
		    /**
		     * Check if this trap is a finally trap that handles exceptions of an adjacent catch block;
		     * what differentiates such trap is that it's guarded region has the same parent as the
		     * handler of the trap itself, in the dom tree.
		     * 
		     * The problem is that we don't have a complete DOM tree at this transient state.
		     * 
		     * The work-around is to not process a trap that has already an edge pointing to it.
		     * 
		     */
		    
		    if(this.unitToPreds.containsKey(handler))
		    {
		    	List handlerPreds = this.unitToPreds.get(handler);
		    	for(Iterator preditr = handlerPreds.iterator(); preditr.hasNext(); )
		    		if(try2nop.containsValue(preditr.next()))
		    			continue AddExceptionalEdge;
		    			
		    }
		    else 
		    	continue;

		    
		    
		    //GuardedBlock gb = new GuardedBlock(b, e);
		    Unit ehnop = try2nop.get(b);
		    		    
		    if(!nop2added.containsKey(ehnop))
		    {
			    List predsOfB = getPredsOf(b);
			    List predsOfehnop = new ArrayList(predsOfB);
			    
			    for(Iterator itr = predsOfB.iterator(); itr.hasNext(); )
			    {
			    	Unit a = itr.next();
			    	List succsOfA = this.unitToSuccs.get(a);
			    	succsOfA.remove(b);
			    	succsOfA.add((Unit)ehnop);
			    }
			    
			    predsOfB.clear();
			    predsOfB.add((Unit)ehnop);
			    
			    this.unitToPreds.put((Unit)ehnop, predsOfehnop);
			    
		    }
		    
		    if(!this.unitToSuccs.containsKey(ehnop))
		    	this.unitToSuccs.put(ehnop, new ArrayList());
		    
		    List succsOfehnop = this.unitToSuccs.get(ehnop);
		    if(!succsOfehnop.contains(b))
		    	succsOfehnop.add(b);
		    
		    succsOfehnop.add(handler);
		    
		    if(!this.unitToPreds.containsKey(handler))
		    	this.unitToPreds.put(handler, new ArrayList());
		    
		    List predsOfhandler = this.unitToPreds.get(handler);
		    predsOfhandler.add((Unit)ehnop);
 		    
	
		    Chain units = body.getUnits().getNonPatchingChain();
		   
		    if(!units.contains(ehnop))
		    	units.insertBefore((Unit)ehnop, b);
	
		    
		    nop2added.put(ehnop, Boolean.TRUE);
		}
		
		
	}
}


/**
 * This class represents a block of code guarded by a trap. Currently, this 
 * is not used but it might well be put to use in later updates.
 * 
 * @author Hossein Sadat-Mohtasham
 *
 */
class GuardedBlock {
	
	Unit start, end;
	
	public GuardedBlock(Unit s, Unit e)
	{
		this.start = s;
		this.end = e;
	}
	
    public int hashCode() 
    {
		// Following Joshua Bloch's recipe in "Effective Java", Item 8:
		int result = 17;
		result = 37 * result + this.start.hashCode();
		result = 37 * result + this.end.hashCode();
		return result;
    }
    public boolean equals(Object rhs) 
    {
		if (rhs == this) 
		{
		    return true;
		}
		if (! (rhs instanceof GuardedBlock)) {
		    return false;
		}
		GuardedBlock rhsGB = (GuardedBlock) rhs;
		return ((this.start == rhsGB.start) && 
			(this.end == rhsGB.end));
	    }
}

/**
 * 
 * @author Hossein Sadat-Mohtasham
 * Feb 2010
 * 
 * This class represents a special nop statement that marks the 
 * beginning of a try block at the Jimple level. This is going
 * to be used in the CFG enhancement.
 *
 */


@SuppressWarnings("serial")
class EHNopStmt extends JNopStmt
{
    public EHNopStmt()
    {
    }
    
    public Object clone() 
    {
        return new EHNopStmt();
    } 

    public boolean fallsThrough(){return true;}        
    public boolean branches(){return false;}

}

/**
 * 
 * @author Hossein Sadat-Mohtasham
 * Feb 2010
 * 
 * This class represents a special nop statement that marks the 
 * beginning of method body at the Jimple level. This is going
 * to be used in the CFG enhancement.
 *
 */
@SuppressWarnings("serial")
class EntryStmt extends JNopStmt
{
    public EntryStmt()
    {
    }
    
    public Object clone() 
    {
        return new EntryStmt();
    } 

    public boolean fallsThrough(){return true;}        
    public boolean branches(){return false;}

}

/**
 * 
 * @author Hossein Sadat-Mohtasham
 * Feb 2010
 * 
 * This class represents a special nop statement that marks the 
 * exit of method body at the Jimple level. This is going
 * to be used in the CFG enhancement.
 *
 */

@SuppressWarnings("serial")
class ExitStmt extends JNopStmt
{
    public ExitStmt()
    {
    }
    
    public Object clone() 
    {
        return new ExitStmt();
    } 

    public boolean fallsThrough(){return true;}        
    public boolean branches(){return false;}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy