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

soot.shimple.internal.SPatchingChain Maven / Gradle / Ivy

package soot.shimple.internal;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2003 Navindra Umanee 
 * %%
 * This program 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 program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.Body;
import soot.TrapManager;
import soot.Unit;
import soot.UnitBox;
import soot.UnitPatchingChain;
import soot.options.Options;
import soot.shimple.PhiExpr;
import soot.shimple.Shimple;
import soot.shimple.ShimpleBody;
import soot.util.Chain;
import soot.util.HashMultiMap;
import soot.util.MultiMap;

/**
 * Internal Shimple extension of PatchingChain.
 *
 * @author Navindra Umanee
 * @see soot.PatchingChain
 **/
public class SPatchingChain extends UnitPatchingChain {
  private static final Logger logger = LoggerFactory.getLogger(SPatchingChain.class);
  /**
   * 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();
    }
  }

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

  @Override
  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(out, in);
    super.insertBefore(in, out);
    super.remove(out);
  }

  @Override
  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 = 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.emptySet();
        if (body != null) {
          trappedUnits = TrapManager.getTrappedUnitsOf(body);
        }
        if (!trappedUnits.contains(unit)) {
          Shimple.redirectPointers(unit, toInsert);
          break patchpointers;
        }
      }

      /* handle each UnitBox individually */

      UnitBox[] boxes = 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) {
              logger.warn("Orphaned UnitBox to " + unit + "?  SPatchingChain will not move the pointer.");
            }
            continue;
          }
        }

        if (needsPatching) {
          box.setUnit(toInsert);
          box.setUnitChanged(false);
        }
      }
    }
  }

  @Override
  public void insertAfter(List toInsert, Unit point) {
    for (Unit unit : toInsert) {
      processPhiNode(unit);
    }
    super.insertAfter(toInsert, point);
  }

  @Override
  public void insertBefore(List toInsert, Unit point) {
    for (Unit unit : toInsert) {
      processPhiNode(unit);
    }
    super.insertBefore(toInsert, point);
  }

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

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

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

  public boolean remove(Unit obj) {
    if (contains(obj)) {
      Shimple.redirectToPreds(body, obj);
    }

    return super.remove(obj);
  }

  /**
   * Map from UnitBox to the Phi node owning it.
   **/
  protected Map boxToPhiNode = new HashMap();
  /**
   * Set of the values of boxToPhiNode. Used to allow O(1) contains() on the values.
   **/
  protected Set phiNodeSet = new HashSet();

  /**
   * 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 boxToNeedsPatching = new HashMap();

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

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

    // already processed previously, unit chain manipulations?
    if (phiNodeSet.contains(phiNode)) {
      return;
    }

    for (UnitBox box : phi.getUnitBoxes()) {
      boxToPhiNode.put(box, phiNode);
      phiNodeSet.add(phiNode);
    }
  }

  protected void reprocessPhiNodes() {
    Set phiNodes = new HashSet(boxToPhiNode.values());
    boxToPhiNode = new HashMap();
    phiNodeSet = new HashSet();
    boxToNeedsPatching = new HashMap();

    Iterator 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 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();
    for (Unit u : this) {
      // update trackedPhiToBoxes
      List boxesToTrack = u.getBoxesPointingToThis();
      if (boxesToTrack != null) {
        for (UnitBox boxToTrack : boxesToTrack) {
          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 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) {
        for (UnitBox ub : boxes) {
          SUnitBox box = getSBox(ub);

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

        trackedPhiToBoxes.remove(u);
      }
    }

    // after the iteration, the rest do not fall through
    Iterator 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);
    }

    @Override
    public void remove() {
      Unit victim = 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 = getSuccOf(victim)) == null) {
        successor = getPredOf(victim);
      }

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

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

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

  @Override
  public Iterator iterator(Unit head, Unit tail) {
    return new SPatchingIterator(innerChain, head, tail);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy