/* 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); } }