SPatchingChain.java :  » Code-Analyzer » soot » soot » shimple » internal » Java Open Source

Java Open Source » Code Analyzer » soot 
soot » soot » shimple » internal » SPatchingChain.java
/* Soot - a J*va Optimization Framework
 * Copyright (C) 2003 Navindra Umanee <navindra@cs.mcgill.ca>
 *
 * 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.shimple.internal;

import soot.*;
import soot.options.Options;
import soot.util.*;
import soot.shimple.*;
import java.util.*;

/**
 * Internal Shimple extension of PatchingChain.
 *
 * @author Navindra Umanee
 * @see soot.PatchingChain
 **/
public class SPatchingChain extends PatchingChain<Unit>
{
    /**
     * Needed to find non-trapped Units of the body.
     **/
    Body body = null;
    boolean debug;
    
    public SPatchingChain(Body aBody, Chain aChain)
    {
        super(aChain);
        this.body = aBody;
        this.debug = Options.v().debug();
        if(aBody instanceof ShimpleBody)
            debug |= ((ShimpleBody)aBody).getOptions().debug();
    }

    public boolean add(Unit o)
    {
        processPhiNode(o);
        return super.add(o);
    }

    public void swapWith(Unit out, Unit in)
    {
        // Ensure that branching statements are swapped correctly.
        // The normal swapWith implementation would still work
        // correctly but redirectToPreds performed during the remove
        // would be more expensive and might print warnings if no
        // actual CFG predecessors for out was found due to the
        // insertion of branching statement in.
        processPhiNode(in);
        Shimple.redirectPointers((Unit) out, (Unit) in);
        super.insertBefore(in, out);
        super.remove(out);
    }
    
    public void insertAfter(Unit toInsert, Unit point)
    {
        // important to do these before the patching, so that
        // computeNeedsPatching works properly
        processPhiNode(toInsert);
        super.insertAfter(toInsert, point);

        Unit unit = (Unit) point;

        // update any pointers from Phi nodes only if the unit
        // being inserted is in the same basic block as point, or if
        // control flows through to the Phi node
        patchpointers:
        {
            // no need to move the pointers
            if(!unit.fallsThrough())
                break patchpointers;

            // move pointers unconditionally, needed as a special case
            if(!unit.branches()){
                Set trappedUnits = Collections.EMPTY_SET;
                if(body != null)
                    trappedUnits = TrapManager.getTrappedUnitsOf(body);
                if(!trappedUnits.contains(unit)){
                    Shimple.redirectPointers(unit, (Unit) toInsert);
                    break patchpointers;
                }
            }
            
            /* handle each UnitBox individually */

            UnitBox[] boxes = (UnitBox[]) unit.getBoxesPointingToThis().toArray(new UnitBox[0]);

            for (UnitBox ub : boxes) {

                if(ub.getUnit() != unit)
                    throw new RuntimeException("Assertion failed.");
                if(ub.isBranchTarget())
                    continue;

                SUnitBox box = getSBox(ub);
                Boolean needsPatching = boxToNeedsPatching.get(box);
                
                if(needsPatching == null || box.isUnitChanged()){
                    // if boxes were added or removed to the known Phi
                    if(!boxToPhiNode.containsKey(box)){
                        reprocessPhiNodes();

                        // *** FIXME: Disabling this allows us to have
                        // PiExpr that have UnitBox pointers.
                        // I think this means that any changes 
                        // to the relevant Unit will be ignored by
                        //  SPatchingChain.
                        //
                        // Hopefully this also means that any
                        // transformation that moves/removes/modifies
                        // a Unit pointed at by a PiExpr knows what
                        // it's doing.
                        if(!boxToPhiNode.containsKey(box) && debug)
                            throw new RuntimeException("SPatchingChain has pointers from a Phi node that has never been seen.");
                    }
                    
                    computeNeedsPatching();
                    needsPatching = boxToNeedsPatching.get(box);

                    if(needsPatching == null){
                        // maybe the user forgot to clearUnitBoxes()
                        // when removing a Phi node, or the user removed
                        // a Phi node and hasn't put it back yet
                        if(debug)
                            G.v().out.println("Warning: Orphaned UnitBox to " + unit + "?  SPatchingChain will not move the pointer.");
                        continue;
                    }
                }
                    
                if(needsPatching.booleanValue()){
                    box.setUnit((Unit)toInsert);
                    box.setUnitChanged(false);
                }
            }
        }
    }

    public void insertAfter(List<Unit> toInsert, Unit point)
    {
        for (Unit unit : toInsert) {
            processPhiNode(unit);
        }
        super.insertAfter(toInsert, point);
    }
    
    public void insertBefore(List<Unit> toInsert, Unit point)
    {
        for (Unit unit : toInsert) {
            processPhiNode(unit);
        }
        super.insertBefore(toInsert, point);
    }

    public void insertBefore(Unit toInsert, Unit point)
    {
        processPhiNode(toInsert);
        super.insertBefore(toInsert, point);
    }

    public void addFirst(Unit u)
    {
        processPhiNode(u);
        super.addFirst(u);
    }
    
    public void addLast(Unit u)
    {
        processPhiNode(u);
        super.addLast(u);
    }

    public boolean remove(Unit obj)
    {
        if(contains(obj)){
            Shimple.redirectToPreds(body, (Unit)obj);
        }
        
        return super.remove(obj);
    }
    
    /**
     * Map from UnitBox to the Phi node owning it.
     **/
    protected Map<UnitBox, Unit> boxToPhiNode = new HashMap<UnitBox, Unit>();

    /**
     * Flag that indicates whether control flow falls through from the
     * box to the Phi node.  null indicates we probably need a call to
     * computeInternal().
     **/
    protected Map<SUnitBox, Boolean> boxToNeedsPatching = new HashMap<SUnitBox, Boolean>();

    
    protected void processPhiNode(Unit o)
    {
        Unit phiNode = (Unit) o;
        PhiExpr phi = Shimple.getPhiExpr(phiNode);

        // not a Phi node
        if(phi == null)
            return;

        // already processed previously, unit chain manipulations?
        if(boxToPhiNode.values().contains(phiNode))
            return;

        Iterator boxesIt = phi.getUnitBoxes().iterator();
        while(boxesIt.hasNext()){
            UnitBox box = (UnitBox) boxesIt.next();
            boxToPhiNode.put(box, phiNode);
        }
    }

    protected void reprocessPhiNodes()
    {
        Set<Unit> phiNodes = new HashSet<Unit>(boxToPhiNode.values());
        boxToPhiNode = new HashMap<UnitBox, Unit>();
        boxToNeedsPatching = new HashMap<SUnitBox, Boolean>();

        Iterator<Unit> phiNodesIt = phiNodes.iterator();
        while(phiNodesIt.hasNext())
            processPhiNode(phiNodesIt.next());
    }
    
    /**
     * NOTE: This will *miss* all the Phi nodes outside a chain.  So
     * make sure you know what you are doing if you remove a Phi node
     * from a chain and don't put it back or call clearUnitBoxes() on
     * it.
     **/
    protected void computeNeedsPatching()
    {
        {
            Set<UnitBox> boxes = boxToPhiNode.keySet();

            if(boxes.isEmpty())
                return;
        }

        // we track the fallthrough control flow from boxes to the
        // corresponding Phi statements.  trackedPhi provides a
        // mapping from the Phi being tracked to its relevant boxes.
        MultiMap trackedPhiToBoxes = new HashMultiMap();

        // consider:
        //
        // if blah goto label1
        // label1:
        //
        // Here control flow both fallsthrough and branches to label1.
        // If such an if statement is encountered, we do not want to
        // move any UnitBox pointers beyond the if statement.
        Set trackedBranchTargets = new HashSet();
        
        Iterator unitsIt = iterator();
        while(unitsIt.hasNext()){
            Unit u = (Unit) unitsIt.next();

            // update trackedPhiToBoxes
            List boxesToTrack = u.getBoxesPointingToThis();
            if(boxesToTrack != null){
                Iterator boxesToTrackIt = boxesToTrack.iterator();
                while(boxesToTrackIt.hasNext()){
                    UnitBox boxToTrack = (UnitBox) boxesToTrackIt.next();

                    if(!boxToTrack.isBranchTarget())
                        trackedPhiToBoxes.put(boxToPhiNode.get(boxToTrack),
                                              boxToTrack);
                }
            }

            // update trackedBranchTargets
            if(u.fallsThrough() && u.branches())
                trackedBranchTargets.addAll(u.getUnitBoxes());
            
            // the tracked Phi nodes may be reached through branching.
            // (note: if u is a Phi node and not a trackedBranchTarget,
            // this is not triggered since u would fall through in that
            // case.)
            if(!u.fallsThrough() || trackedBranchTargets.contains(u)){
                Iterator<UnitBox> boxesIt = trackedPhiToBoxes.values().iterator();
                while(boxesIt.hasNext()){
                    SUnitBox box = getSBox(boxesIt.next());
                    boxToNeedsPatching.put(box, Boolean.FALSE);
                    box.setUnitChanged(false);
                }

                trackedPhiToBoxes = new HashMultiMap();
                continue;
            }

            // we found one of the Phi nodes pointing to a Unit
            Set boxes = trackedPhiToBoxes.get(u);
            if(boxes != null){
                Iterator<UnitBox> boxesIt = boxes.iterator();
                while(boxesIt.hasNext()){
                    SUnitBox box = getSBox(boxesIt.next());

                    // falls through
                    boxToNeedsPatching.put(box, Boolean.TRUE);
                    box.setUnitChanged(false);
                }

                trackedPhiToBoxes.remove(u);
            }
        }

        // after the iteration, the rest do not fall through
        Iterator<UnitBox> boxesIt = trackedPhiToBoxes.values().iterator();
        while(boxesIt.hasNext()){
            SUnitBox box = getSBox(boxesIt.next());
            boxToNeedsPatching.put(box, Boolean.FALSE);
            box.setUnitChanged(false);
        }
    }

    protected SUnitBox getSBox(UnitBox box)
    {
        if(!(box instanceof SUnitBox))
            throw new RuntimeException("Shimple box not an SUnitBox?");

        return (SUnitBox) box;
    }

    protected class SPatchingIterator extends PatchingIterator
    {
        SPatchingIterator(Chain innerChain)
        {
            super(innerChain);
        }

        SPatchingIterator(Chain innerChain, Unit u)
        {
            super(innerChain, u);
        }

        SPatchingIterator(Chain innerChain, Unit head, Unit tail)
        {
            super(innerChain, head, tail);
        }
        
        public void remove()
        {
            Unit victim = (Unit) lastObject;
            
            if(!state)
                throw new IllegalStateException("remove called before first next() call");
            Shimple.redirectToPreds(SPatchingChain.this.body, victim);

            // work around for inadequate inner class support in javac 1.2
            // super.remove();
            Unit successor;

            if((successor = (Unit)getSuccOf(victim)) == null)
                successor = (Unit)getPredOf(victim);

            innerIterator.remove();
            victim.redirectJumpsToThisTo(successor);
        }
    }

    public Iterator iterator()
    {
        return new SPatchingIterator(innerChain);
    }

    public Iterator iterator(Unit u)
    {
        return new SPatchingIterator(innerChain, u);
    }

    public Iterator iterator(Unit head, Unit tail)
    {
        return new SPatchingIterator(innerChain, head, tail);
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.