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

soot.Body Maven / Gradle / Ivy

There is a newer version: 2.5.0-9
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 1997-1999 Raja Vallee-Rai
 *
 * 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;

import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import soot.jimple.CaughtExceptionRef;
import soot.jimple.DefinitionStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.ParameterRef;
import soot.jimple.ThisRef;
import soot.options.Options;
import soot.tagkit.AbstractHost;
import soot.tagkit.Tag;
import soot.toolkits.exceptions.PedanticThrowAnalysis;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.toolkits.scalar.FlowSet;
import soot.toolkits.scalar.InitAnalysis;
import soot.toolkits.scalar.LocalDefs;
import soot.toolkits.scalar.SimpleLiveLocals;
import soot.toolkits.scalar.SmartLocalDefs;
import soot.util.Chain;
import soot.util.EscapedWriter;
import soot.util.HashChain;


/**
 *   Abstract base class that models the body (code attribute) of a Java method.
 *   Classes that implement an Intermediate Representation for a method body should subclass it.
 *   In particular the classes GrimpBody, JimpleBody and BafBody all extend this
 *   class. This class provides methods that are common to any IR, such as methods
 *   to get the body's units (statements), traps, and locals.
 *
 *  @see soot.grimp.GrimpBody
 *  @see soot.jimple.JimpleBody
 *  @see soot.baf.BafBody
 */
public abstract class Body extends AbstractHost implements Serializable
{
    /** The method associated with this Body. */
    protected transient SootMethod method = null;

    /** The chain of locals for this Body. */
    protected Chain localChain = new HashChain();

    /** The chain of traps for this Body. */
    protected Chain trapChain = new HashChain();

    // RoboVM note: Added
    protected List localVariables = new ArrayList<>();

    /** The chain of units for this Body. */
    protected PatchingChain unitChain = new PatchingChain(new HashChain());

    /** Creates a deep copy of this Body. */
    abstract public Object clone();

    /** Creates a Body associated to the given method.  Used by subclasses during initialization.
     *  Creation of a Body is triggered by e.g. Jimple.v().newBody(options).
     */
    protected Body(SootMethod m)
    {
        this.method = m;
    }

    /** Creates an extremely empty Body.  The Body is not associated to any method. */
    protected Body()
    {
    }

    /**
     * Returns the method associated with this Body.
     * @return the method that owns this body.
     */
    public SootMethod getMethod()
    {
        if(method == null)
            throw new RuntimeException("no method associated w/ body");
        return method;
    }


    /**
     * Sets the method associated with this Body.
     * @param method the method that owns this body.
     *
     */
    public void setMethod(SootMethod method)
    {
        this.method = method;
    }

    /** Returns the number of locals declared in this body. */
    public int getLocalCount()
    {
        return localChain.size();
    }

    /** Copies the contents of the given Body into this one. */
    public Map importBodyContentsFrom(Body b)
    {
        HashMap bindings = new HashMap();

        {
	        Iterator it = b.getUnits().iterator();
	
	        // Clone units in body's statement list
	        while(it.hasNext()) {
	            Unit original = it.next();
	            Unit copy = (Unit) original.clone();
	
	            copy.addAllTagsOf(original);
	
	            // Add cloned unit to our unitChain.
	            unitChain.addLast(copy);
	
	            // Build old <-> new map to be able to patch up references to other units
	            // within the cloned units. (these are still refering to the original
	            // unit objects).
	            bindings.put(original, copy);
	        }
        }

        {
	        // Clone trap units.
	        Iterator it = b.getTraps().iterator();
	        while(it.hasNext()) {
	            Trap original = it.next();
	            Trap copy = (Trap) original.clone();
	
	            // Add cloned unit to our trap list.
	            trapChain.addLast(copy);
	
	            // Store old <-> new mapping.
	            bindings.put(original, copy);
	        }
        }

        {
	        // Clone local units.
	        Iterator it = b.getLocals().iterator();
	        while(it.hasNext()) {
	            Local original = it.next();
	            Local copy = (Local) original.clone();
	
	            // Add cloned unit to our trap list.
	            localChain.addLast(copy);
	
	            // Build old <-> new mapping.
	            bindings.put(original, copy);
	        }
        }

        {
	        // Patch up references within units using our (old <-> new) map.
	        Iterator it = getAllUnitBoxes().iterator();
	        while(it.hasNext()) {
	            UnitBox box = it.next();
	            Unit newObject, oldObject = box.getUnit();
	
	            // if we have a reference to an old object, replace it
	            // it's clone.
	            if( (newObject = (Unit)  bindings.get(oldObject)) != null )
	                box.setUnit(newObject);
	
	        }
        }


        {
	        // backpatching all local variables.
	        Iterator it = getUseBoxes().iterator();
	        while(it.hasNext()) {
	            ValueBox vb = it.next();
	            if(vb.getValue() instanceof Local)
	                vb.setValue((Value) bindings.get(vb.getValue()));
	        }
	        it = getDefBoxes().iterator();
	        while(it.hasNext()) {
	            ValueBox vb = it.next();
	            if(vb.getValue() instanceof Local)
	                vb.setValue((Value) bindings.get(vb.getValue()));
	        }
        }
        return bindings;
    }

    /** Verifies a few sanity conditions on the contents on this body. */
    public void validate()
    {
        //System.out.println("body: "+this.getUnits());
        validateLocals();
        validateTraps();
        validateUnitBoxes();
        validateLocalVariables(); // RoboVM note: Added
        if (Options.v().debug() || Options.v().validate()) {
            validateUses();
            validateValueBoxes();
            checkInit();
            checkTypes();
            checkLocals();
        }
    }

    /** Verifies that a ValueBox is not used in more than one place. */
    public void validateValueBoxes()
    {
        List l = getUseAndDefBoxes();
        for( int i = 0; i < l.size(); i++ ) {
            for( int j = 0; j < l.size(); j++ ) {
                if( i == j ) continue;
                if( l.get(i) == l.get(j) ) {
                    System.err.println("Aliased value box : "+l.get(i)+" in "+getMethod());
                    for( Iterator uIt = getUnits().iterator(); uIt.hasNext(); ) {
                        final Unit u = uIt.next();
                        System.err.println(""+u);
                    }
                    throw new RuntimeException("Aliased value box : "+l.get(i)+" in "+getMethod());
                }
            }
        }
    }

    /** Verifies that each Local of getUseAndDefBoxes() is in this body's locals Chain. */
    public void validateLocals()
    {
        Iterator it = getUseBoxes().iterator();
        while(it.hasNext()){
            validateLocal( it.next() );
        }
        it = getDefBoxes().iterator();
        while(it.hasNext()){
            validateLocal( it.next() );
        }
    }
    private void validateLocal( ValueBox vb ) {
        Value value;
        if( (value = vb.getValue()) instanceof Local) {
            //System.out.println("localChain: "+localChain);
            if(!localChain.contains(value))
                throw new RuntimeException("Local not in chain : "+value+" in "+getMethod());
        }
    }

    /** Verifies that the begin, end and handler units of each trap are in this body. */
    public void validateTraps()
    {
        Iterator it = getTraps().iterator();
        while (it.hasNext())
        {
            Trap t = it.next();
            if (!unitChain.contains(t.getBeginUnit()))
                throw new RuntimeException("begin not in chain"+" in "+getMethod());

            if (!unitChain.contains(t.getEndUnit()))
                throw new RuntimeException("end not in chain"+" in "+getMethod());

            if (!unitChain.contains(t.getHandlerUnit()))
                throw new RuntimeException("handler not in chain"+" in "+getMethod());
        }
    }

    // RoboVM note: Added
    public void validateLocalVariables()
    {
        Iterator it = getLocalVariables().iterator();
        while (it.hasNext())
        {
            LocalVariable lv = it.next();
            if (lv.getStartUnit() != null && !unitChain.contains(lv.getStartUnit()))
                throw new RuntimeException("start not in chain"+" in "+getMethod());

            if (lv.getEndUnit() != null && !unitChain.contains(lv.getEndUnit()))
                throw new RuntimeException("end not in chain"+" in "+getMethod());
        }
    }

    /** Verifies that the UnitBoxes of this Body all point to a Unit contained within this body. */
    public void validateUnitBoxes()
    {
        Iterator it = getAllUnitBoxes().iterator();
        while (it.hasNext())
        {
            UnitBox ub = it.next();
            if (!unitChain.contains(ub.getUnit()))
                throw new RuntimeException
                    ("Unitbox points outside unitChain! to unit : "+ub.getUnit()+" in "+getMethod());
        }
    }

    /** Verifies that each use in this Body has a def. */
    public void validateUses()
    {
        UnitGraph g = new ExceptionalUnitGraph(this);
        LocalDefs ld = new SmartLocalDefs(g, new SimpleLiveLocals(g));

        Iterator unitsIt = getUnits().iterator();
        while (unitsIt.hasNext())
        {
            Unit u = unitsIt.next();
            Iterator useBoxIt = u.getUseBoxes().iterator();
            while (useBoxIt.hasNext())
            {
                Value v = (useBoxIt.next()).getValue();
                if (v instanceof Local)
                {
                    // This throws an exception if there is
                    // no def already; we check anyhow.
                    List l = ld.getDefsOfAt((Local)v, u);
                    if (l.size() == 0){
                        for( Iterator uuIt = getUnits().iterator(); uuIt.hasNext(); ) {
                            final Unit uu = uuIt.next();
                            System.err.println(""+uu);
                        }
                        throw new RuntimeException("no defs for value: "+v+"!"+" in "+getMethod());
                    }
                }
            }
        }
    }

    /** Returns a backed chain of the locals declared in this Body. */
    public Chain getLocals() {return localChain;}

    /** Returns a backed view of the traps found in this Body. */
    public Chain getTraps() {return trapChain;}

    // RoboVM note: Added
    public List getLocalVariables() {
        return localVariables;
    }

    /** Return LHS of the first identity stmt assigning from \@this. **/
    public Local getThisLocal()
    {
        Iterator unitsIt = getUnits().iterator();

        while (unitsIt.hasNext())
        {
            Unit s = unitsIt.next();
            if (s instanceof IdentityStmt &&
                ((IdentityStmt)s).getRightOp() instanceof ThisRef)
                return (Local)(((IdentityStmt)s).getLeftOp());
        }

        throw new RuntimeException("couldn't find identityref!"+" in "+getMethod());
    }

    /** Return LHS of the first identity stmt assigning from \@parameter i. **/
    public Local getParameterLocal(int i)
    {
        Iterator unitsIt = getUnits().iterator();
        while (unitsIt.hasNext())
        {
            Unit s = unitsIt.next();
            if (s instanceof IdentityStmt &&
                ((IdentityStmt)s).getRightOp() instanceof ParameterRef)
            {
                IdentityStmt is = (IdentityStmt)s;
                ParameterRef pr = (ParameterRef)is.getRightOp();
                if (pr.getIndex() == i)
                    return (Local)is.getLeftOp();
            }
        }

        throw new RuntimeException("couldn't find parameterref!"+" in "+getMethod());
    }

    /**
     *  Returns the Chain of Units that make up this body. The units are
     *  returned as a PatchingChain. The client can then manipulate the chain,
     *  adding and removing units, and the changes will be reflected in the body.
     *  Since a PatchingChain is returned the client need not worry about removing exception
     *  boundary units or otherwise corrupting the chain.
     *
     *  @return the units in this Body
     *
     *  @see PatchingChain
     *  @see Unit
     */
    public PatchingChain getUnits()
    {
        return unitChain;
    }

    /**
     * Returns the result of iterating through all Units in this body
     * and querying them for their UnitBoxes.  All UnitBoxes thus
     * found are returned.  Branching Units and statements which use
     * PhiExpr will have UnitBoxes; a UnitBox contains a Unit that is
     * either a target of a branch or is being used as a pointer to
     * the end of a CFG block.
     *
     * 

This method is typically used for pointer patching, eg when * the unit chain is cloned. * * @return A list of all the UnitBoxes held by this body's units. * @see UnitBox * @see #getUnitBoxes(boolean) * @see Unit#getUnitBoxes() * @see soot.shimple.PhiExpr#getUnitBoxes() **/ public List getAllUnitBoxes() { ArrayList unitBoxList = new ArrayList(); { Iterator it = unitChain.iterator(); while(it.hasNext()) { Unit item = it.next(); unitBoxList.addAll(item.getUnitBoxes()); } } { Iterator it = trapChain.iterator(); while(it.hasNext()) { Trap item = it.next(); unitBoxList.addAll(item.getUnitBoxes()); } } // RoboVM note: Added { Iterator it = localVariables.iterator(); while(it.hasNext()) { LocalVariable item = it.next(); unitBoxList.addAll(item.getUnitBoxes()); } } return unitBoxList; } /** * If branchTarget is true, returns the result of iterating * through all branching Units in this body and querying them for * their UnitBoxes. These UnitBoxes contain Units that are the * target of a branch. This is useful for, say, labeling blocks * or updating the targets of branching statements. * *

If branchTarget is false, returns the result of iterating * through the non-branching Units in this body and querying them * for their UnitBoxes. Any such UnitBoxes (typically from * PhiExpr) contain a Unit that indicates the end of a CFG block. * * @return a list of all the UnitBoxes held by this body's * branching units. * * @see UnitBox * @see #getAllUnitBoxes() * @see Unit#getUnitBoxes() * @see soot.shimple.PhiExpr#getUnitBoxes() **/ public List getUnitBoxes(boolean branchTarget) { ArrayList unitBoxList = new ArrayList(); { Iterator it = unitChain.iterator(); while(it.hasNext()) { Unit item = it.next(); if(branchTarget){ if(item.branches()) unitBoxList.addAll(item.getUnitBoxes()); } else{ if(!item.branches()) unitBoxList.addAll(item.getUnitBoxes()); } } } { Iterator it = trapChain.iterator(); while(it.hasNext()) { Trap item = it.next(); unitBoxList.addAll(item.getUnitBoxes()); } } return unitBoxList; } /** * Returns the result of iterating through all Units in this * body and querying them for ValueBoxes used. * All of the ValueBoxes found are then returned as a List. * * @return a list of all the ValueBoxes for the Values used this body's units. * * @see Value * @see Unit#getUseBoxes * @see ValueBox * @see Value * */ public List getUseBoxes() { ArrayList useBoxList = new ArrayList(); Iterator it = unitChain.iterator(); while(it.hasNext()) { Unit item = it.next(); useBoxList.addAll(item.getUseBoxes()); } return useBoxList; } /** * Returns the result of iterating through all Units in this * body and querying them for ValueBoxes defined. * All of the ValueBoxes found are then returned as a List. * * @return a list of all the ValueBoxes for Values defined by this body's units. * * @see Value * @see Unit#getDefBoxes * @see ValueBox * @see Value */ public List getDefBoxes() { ArrayList defBoxList = new ArrayList(); Iterator it = unitChain.iterator(); while(it.hasNext()) { Unit item = it.next(); defBoxList.addAll(item.getDefBoxes()); } return defBoxList; } /** * Returns a list of boxes corresponding to Values * either used or defined in any unit of this Body. * * @return a list of ValueBoxes for held by the body's Units. * * @see Value * @see Unit#getUseAndDefBoxes * @see ValueBox * @see Value */ public List getUseAndDefBoxes() { ArrayList useAndDefBoxList = new ArrayList(); Iterator it = unitChain.iterator(); while(it.hasNext()) { Unit item = it.next(); useAndDefBoxList.addAll(item.getUseBoxes()); useAndDefBoxList.addAll(item.getDefBoxes()); } return useAndDefBoxList; } private void checkLocals() { Chain locals=getLocals(); Iterator it=locals.iterator(); while(it.hasNext()) { Local l=it.next(); if(l.getType() instanceof VoidType) throw new RuntimeException("Local "+l+" in "+method+" defined with void type"); } } private void checkTypes() { Chain units=getUnits(); Iterator it=units.iterator(); while(it.hasNext()) { Unit stmt=(it.next()); InvokeExpr iexpr=null; String errorSuffix=" at "+stmt+" in "+getMethod(); if(stmt instanceof DefinitionStmt) { DefinitionStmt astmt=(DefinitionStmt) stmt; if( !(astmt.getRightOp() instanceof CaughtExceptionRef ) ) { Type leftType=Type.toMachineType(astmt.getLeftOp().getType()); Type rightType=Type.toMachineType(astmt.getRightOp().getType()); checkCopy(leftType,rightType,errorSuffix); if(astmt.getRightOp() instanceof InvokeExpr) iexpr=(InvokeExpr) (astmt.getRightOp()); } } if(stmt instanceof InvokeStmt) iexpr=((InvokeStmt) stmt).getInvokeExpr(); if(iexpr!=null) { SootMethodRef called=iexpr.getMethodRef(); if(iexpr instanceof InstanceInvokeExpr) { InstanceInvokeExpr iiexpr=(InstanceInvokeExpr) iexpr; checkCopy(called.declaringClass().getType(), iiexpr.getBase().getType(), " in receiver of call"+errorSuffix); } if(called.parameterTypes().size() != iexpr.getArgCount()) throw new RuntimeException("Warning: Argument count doesn't match up with signature in call"+errorSuffix+" in "+getMethod()); else for(int i=0;i units=getUnits(); ExceptionalUnitGraph g = new ExceptionalUnitGraph (this, PedanticThrowAnalysis.v(), false); // FIXME: Work around for bug in soot Scene.v().releaseActiveHierarchy(); InitAnalysis analysis=new InitAnalysis(g); Iterator it=units.iterator(); while(it.hasNext()) { Unit s=(it.next()); FlowSet init=(FlowSet) analysis.getFlowBefore(s); List uses=s.getUseBoxes(); Iterator usesIt=uses.iterator(); while(usesIt.hasNext()) { Value v=((usesIt.next())).getValue(); if(v instanceof Local) { Local l=(Local) v; if(!init.contains(l)) throw new RuntimeException("Warning: Local variable "+l +" not definitely defined at "+s +" in "+method); } } } } /** * {@inheritDoc} */ @Override public String toString() { ByteArrayOutputStream streamOut = new ByteArrayOutputStream(); PrintWriter writerOut = new PrintWriter(new EscapedWriter(new OutputStreamWriter(streamOut))); try { Printer.v().printTo(this, writerOut); } catch (RuntimeException e) { e.printStackTrace(writerOut); } writerOut.flush(); writerOut.close(); return streamOut.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy