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

soot.jimple.toolkits.annotation.arraycheck.RectangularArrayFinder Maven / Gradle / Ivy

/* Soot - a J*va Optimization Framework
 * Copyright (C) 2000 Feng Qian
 *
 * 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.
 */

/*
 * Modified by the Sable Research Group and others 1997-1999.  
 * See the 'credits' file distributed with Soot for the complete list of
 * contributors.  (Soot is distributed at http://www.sable.mcgill.ca/soot)
 */

package soot.jimple.toolkits.annotation.arraycheck;
import soot.options.*;

import soot.*;
import soot.util.*;
import soot.jimple.*;
import soot.jimple.internal.*;
import soot.jimple.toolkits.callgraph.*;
import java.util.*;

/** Interprocedural analysis to identify rectangular multi-dimension array
 * locals. It is based on the call graph. 
 */

public class RectangularArrayFinder extends SceneTransformer
{
    public RectangularArrayFinder( Singletons.Global g ) {}
    public static RectangularArrayFinder v() { return G.v().soot_jimple_toolkits_annotation_arraycheck_RectangularArrayFinder(); }

    private final ExtendedHashMutableDirectedGraph agraph =
	new ExtendedHashMutableDirectedGraph();

    private final Set falseSet = new HashSet();
    private final Set trueSet = new HashSet();
    private CallGraph cg;

    protected void internalTransform(String phaseName, Map opts)
    {
	Scene sc = Scene.v();

        cg = sc.getCallGraph();

	Date start = new Date();
	if (Options.v().verbose())
	    G.v().out.println("[ra] Finding rectangular arrays, start on "+start);

	Chain appClasses = sc.getApplicationClasses();

	Iterator classIt = appClasses.iterator();

	while (classIt.hasNext())
	{
	    SootClass c = (SootClass)classIt.next();

	    Iterator methodIt = c.methodIterator();
	    
	    while (methodIt.hasNext())
	    {
		SootMethod method = (SootMethod)methodIt.next();
		
		if (!method.isConcrete())
		    continue;
		
		if (!sc.getReachableMethods().contains(method))
		    continue;

		recoverRectArray(method);
		addInfoFromMethod(method);
	    }
	}
	
	/*
	MutableDirectedGraph methodGraph = ig.newMethodGraph();
	HashSet visitedMethods = new HashSet();
	LinkedList tovisitMethods = new LinkedList();

	List heads = methodGraph.getHeads();
	Iterator headIt = heads.iterator();
	while (headIt.hasNext())
	{
	    SootMethod entry = (SootMethod)headIt.next();
	    String sig = entry.getSubSignature();

	    if (sig.equals(mainSignature))
		tovisitMethods.add(entry);
	}

	while (!tovisitMethods.isEmpty())
	{
	    SootMethod visiting = (SootMethod)tovisitMethods.removeFirst();
	    visitedMethods.add(visiting);

	    recoverRectArray(visiting);
	    addInfoFromMethod(visiting);

	    List succs = methodGraph.getSuccsOf(visiting);
	    Iterator succIt = succs.iterator();
	    while (succIt.hasNext())
	    {
		Object succ = succIt.next();
		if (!visitedMethods.contains(succ))
		    tovisitMethods.add(succ);
	    }
	}
	*/

	/* propagate the graph info from FALSE node. */
	if (agraph.containsNode(BoolValue.v(false)))
	{
	    List changedNodeList = new ArrayList();

	    List startNodes = agraph.getSuccsOf(BoolValue.v(false));

	    falseSet.addAll(startNodes);
	    changedNodeList.addAll(startNodes);
	
	    while (!changedNodeList.isEmpty())
	    {
		Object node = changedNodeList.remove(0);

		List succs = agraph.getSuccsOf(node);
	    
		Iterator succsIt = succs.iterator();

		while (succsIt.hasNext())
		{
		    Object succ = succsIt.next();

		    if (!falseSet.contains(succ))
		    {
			falseSet.add(succ);
			changedNodeList.add(succ);
		    }
		}
	    }
	}

        /* propagate graph info from TRUE node then. */
	if (agraph.containsNode(BoolValue.v(true)))
	{
	    List changedNodeList = new ArrayList();

	    List startNodes = agraph.getSuccsOf(BoolValue.v(true));

	    Iterator nodesIt = startNodes.iterator();
	    while (nodesIt.hasNext())
	    {
		Object node = nodesIt.next();
		if (falseSet.contains(node))
		    continue;

		changedNodeList.add(node);
		trueSet.add(node);
	    }

	    while (!changedNodeList.isEmpty())
	    {
		Object node = changedNodeList.remove(0);
		
		List succs = agraph.getSuccsOf(node);

		Iterator succsIt = succs.iterator();

		while (succsIt.hasNext())
		{
		    Object succ = succsIt.next();

		    if (falseSet.contains(succ))
			continue;

		    if (trueSet.contains(succ))
			continue;

		    trueSet.add(succ);
		    changedNodeList.add(succ);
		}
	    }
	}

	/* For verification, print out true set and false set. */
	
	if (Options.v().debug())
	{
	    G.v().out.println("Rectangular Array :");
	    {
		Iterator nodeIt = trueSet.iterator();
		while (nodeIt.hasNext())
		{
		    Object node = nodeIt.next();

		    G.v().out.println(node);
		}
	    }

	    G.v().out.println("\nNon-rectangular Array :");
	    {
		Iterator nodeIt = falseSet.iterator();
		while (nodeIt.hasNext())
		{
		    Object node = nodeIt.next();
		    
		    G.v().out.println(node);
		}
	    }
	}
	
	Date finish = new Date();
	if (Options.v().verbose()) 
	{
	    long runtime = finish.getTime() - start.getTime();
	    long mins = runtime/60000;
	    long secs = (runtime%60000)/1000;
	    G.v().out.println("[ra] Rectangular array finder finishes."
			       +" It took "+mins+" mins and "+secs+" secs.");
	}
    }

    private void addInfoFromMethod(SootMethod method)
    {
	if (Options.v().verbose()) 
	    G.v().out.println("[ra] Operating "+method.getSignature());

	boolean needTransfer = true;

	Body body = method.getActiveBody();

	Set tmpNode = new HashSet();

	/* check the return type of method, if it is multi-array. */

	boolean trackReturn = false;
	Type rtnType = method.getReturnType();
	
	if (rtnType instanceof ArrayType)
	{
	    if (((ArrayType)rtnType).numDimensions > 1)
	    {
		trackReturn = true;
		needTransfer = true;
	    }
	}      

	Set arrayLocal = new HashSet();

	/* Collect the multi-array locals */

	Chain locals = body.getLocals();
	Iterator localIt = locals.iterator();
	while (localIt.hasNext())
	{
	    Local local = (Local)localIt.next();
	    
	    Type type = local.getType();

	    if (type instanceof ArrayType)
	    {
		if ( ((ArrayType)type).numDimensions > 1)
		{
		    arrayLocal.add(local);
		}
		else
		    tmpNode.add(new MethodLocal(method, local));
	    }
	}

	/* The method has a local graph. It will be merged to the whole graph after simplification. */
	ExtendedHashMutableDirectedGraph ehmdg = new ExtendedHashMutableDirectedGraph();	

	Iterator unitIt = body.getUnits().snapshotIterator();

	while (unitIt.hasNext())
	{
	    Stmt s = (Stmt)unitIt.next();

	    /* for each invoke site, add edges from local parameter to the target methods' parameter node. */
	    if (s.containsInvokeExpr())
	    {
		InvokeExpr iexpr = s.getInvokeExpr();
		
		int argnum = iexpr.getArgCount();
		
		for (int i=0; i tmpNodeIt = tmpNode.iterator();
	
	    while (tmpNodeIt.hasNext())
	    {	    
      		ehmdg.skipNode(tmpNodeIt.next());
	    }
	
	    /* Add local graph to whole graph */	
	    agraph.mergeWith(ehmdg);
	}

    }

    private void recoverRectArray(SootMethod method)
    {
	Body body = method.getActiveBody();
	HashSet malocal = new HashSet();

	Chain locals = body.getLocals();
	Iterator localsIt = locals.iterator();
	while (localsIt.hasNext())
	{
	    Local local = (Local)localsIt.next();
	    Type type = local.getType();
	    if (!(type instanceof ArrayType))
		continue;
	    
	    if (((ArrayType)type).numDimensions == 2)
		malocal.add(local);
	}

	if (malocal.size() == 0)
	    return;

	Chain units = body.getUnits();

	Stmt stmt = (Stmt)units.getFirst();

	while (true)
	{
	    if (stmt == null)
		break;

	    /* only deal with the first block */
	    if (!stmt.fallsThrough())
		break;

	searchblock:
	    {
	    /* possible candidates */
		if (!(stmt instanceof AssignStmt))
		    break searchblock;
		    
		Value leftOp = ((AssignStmt)stmt).getLeftOp();
		Value rightOp = ((AssignStmt)stmt).getRightOp();

		if (!malocal.contains(leftOp) || !(rightOp instanceof NewArrayExpr))
		    break searchblock;

		Local local = (Local)leftOp;
 
		NewArrayExpr naexpr = (NewArrayExpr)rightOp;
		
		Value size = naexpr.getSize();
		if (!(size instanceof IntConstant))
		    break searchblock;

		int firstdim = ((IntConstant)size).value;
		if (firstdim > 100)
		    break searchblock;

		ArrayType localtype = (ArrayType)local.getType();
		Type basetype = localtype.baseType;

		Local[] tmplocals = new Local[firstdim];

		int seconddim = lookforPattern(units, stmt, 
					       firstdim, local,
					       basetype, tmplocals);

		if (seconddim >= 0)
		    transferPattern(units, stmt, 
				    firstdim, seconddim, 
				    local, basetype, tmplocals);
	    }

	    stmt = (Stmt)units.getSuccOf(stmt);
	}
    }

    /* if the local is assigned a rect array, return back the second dimension length,
       else return -1
    */
    private int lookforPattern(Chain units, Stmt startpoint, 
			       int firstdim, Local local,
			       Type basetype, Local[] tmplocals)
    {
	/* It is a state machine to match the pattern */
	/*  state        input               goto
	    start        r1 = new(A[])[c]      1
	    1            r2 = newA[d]          2
	    2            r2[?] = ...           2
	                 r1[e] = r2 (e = c-1)  3
			 r1[e] = r2 (e = e'+1) 2
	    3            end
	*/

	int seconddim = -1;
	int curdim = 0;
	Object curtmp = local; // Local, I have to initialize it. It should not be this value.

	Stmt curstmt = startpoint;

	int fault = 99;
	int state = 1;

	while (true)
	{
	    curstmt = (Stmt)units.getSuccOf(curstmt);
	    if (curstmt == null)
		return -1;

	    if (!(curstmt instanceof AssignStmt))
		return -1;

	    Value leftOp = ((AssignStmt)curstmt).getLeftOp();
	    Value rightOp = ((AssignStmt)curstmt).getRightOp();

	    switch (state)
	    {
		/* we already did state 0 outside */
	    case 0:
		break;
		
	    case 1:
		/* make sure it is a new array expr */
		{
		    state = fault;

		    if (!(rightOp instanceof NewArrayExpr))
			break;
		    
		    NewArrayExpr naexpr = (NewArrayExpr)rightOp;
		    Type type = naexpr.getBaseType();
		    Value size = naexpr.getSize();

		    if (!type.equals(basetype))
		        break;

		    if (!(size instanceof IntConstant))
			break;

		    if (curdim == 0)
			seconddim = ((IntConstant)size).value;
		    else
		    {
			if (((IntConstant)size).value != seconddim)
			    break;
		    }
		    
		    curtmp = leftOp;

		    state = 2;
		}
		break;

	    case 2:
		{
		    state = fault;

		    if (!(leftOp instanceof ArrayRef))
			break;

		    Value base = ((ArrayRef)leftOp).getBase();
		    Value idx = ((ArrayRef)leftOp).getIndex();

		    /* curtmp[?] = ? */
		    if (base.equals(curtmp))
			state = 2;
		    else
		    /* local[?] = curtmp? */
		    if (base.equals(local))
		    {
			if (!(idx instanceof IntConstant))
			    break;
			
			if (curdim != ((IntConstant)idx).value)
			    break;

			if (!rightOp.equals(curtmp))
			    break;

			tmplocals[curdim] = (Local)curtmp;

			curdim++;

			if (curdim >= firstdim)
			    state = 3;
			else
			    state = 1;
		    }
		}
		break;
		
	    case 3:
		return seconddim;
      

	    default:
		return -1;
	    }
	}
    }
    
    private void transferPattern(Chain units, Stmt startpoint,
				 int firstdim, int seconddim,
				 Local local, Type basetype,
				 Local[] tmplocals)
    {
	/* sequentially search and replace the sub dimension assignment */
	{
	    /* change the first one, reset the right op */
	    ArrayType atype = (ArrayType)local.getType();
	    List sizes = new ArrayList(2);
	    sizes.add(IntConstant.v(firstdim));
	    sizes.add(IntConstant.v(seconddim));
	    Value nmexpr = new JNewMultiArrayExpr(atype, sizes);

	    ((AssignStmt)startpoint).setRightOp(nmexpr);
	}

	{
	    int curdim = 0;
	    Local tmpcur = local;

	    Stmt curstmt = (Stmt)units.getSuccOf(startpoint);

	    while (curdim < firstdim)
	    {	        
		Value leftOp = ((AssignStmt)curstmt).getLeftOp();
		Value rightOp = ((AssignStmt)curstmt).getRightOp();
		
		if (tmplocals[curdim].equals(leftOp) && (rightOp instanceof NewArrayExpr))
		{
		    ArrayRef arexpr = new JArrayRef(local, IntConstant.v(curdim));
		    ((AssignStmt)curstmt).setRightOp(arexpr);
		    tmpcur = (Local)leftOp;
		}
		else
		if ((leftOp instanceof ArrayRef) && (rightOp.equals(tmpcur)))
		{		    
		    /* delete current stmt */
		    Stmt tmpstmt = curstmt;
		    curstmt = (Stmt)units.getSuccOf(curstmt);
		    units.remove(tmpstmt);

		    curdim++;
		}
		else
		    curstmt = (Stmt)units.getSuccOf(curstmt);		
	    }
	}
    }


    public boolean isRectangular(Object obj)
    {
	if (trueSet.contains(obj))
	    return true;
	else
	    return false;
    }
}