/* 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.util.AbstractCollection; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import soot.util.Chain; /** An implementation of a Chain which can contain only Units, * and handles patching to deal with element insertions and removals. * This is done by calling Unit.redirectJumpsToThisTo at strategic * times. */ public class PatchingChain<E extends Unit> extends AbstractCollection<E> implements Chain<E> { protected Chain<E> innerChain; /** Constructs a PatchingChain from the given Chain. */ public PatchingChain(Chain<E> aChain) { innerChain = aChain; } /** * Returns the inner chain used by the PatchingChain. In general, * this should not be used. However, direct access to the inner * chain may be necessary if you wish to perform certain * operations (such as control-flow manipulations) without * interference from the patching algorithms. **/ public Chain<E> getNonPatchingChain() { return innerChain; } /** Adds the given object to this Chain. */ public boolean add(E o) { return innerChain.add(o); } /** Replaces <code>out</code> in the Chain by <code>in</code>. */ public void swapWith(E out, E in) { insertBefore(in, out); remove(out); } /** Inserts <code>toInsert</code> in the Chain after <code>point</code>. */ public void insertAfter(E toInsert, E point) { innerChain.insertAfter(toInsert, point); } /** Inserts <code>toInsert</code> in the Chain after <code>point</code>. */ public void insertAfter(List<E> toInsert, E point) { innerChain.insertAfter(toInsert, point); } public void insertAfter(Chain<E> toInsert, E point) { innerChain.insertAfter(toInsert, point); } /** Inserts <code>toInsert</code> in the Chain before <code>point</code>. */ public void insertBefore(List<E> toInsert, E point) { LinkedList<E> backwardList = new LinkedList<E>(); // Insert toInsert backwards into the list { Iterator<E> it = toInsert.iterator(); while(it.hasNext()) backwardList.addFirst(it.next()); } E previousPoint = point; Iterator<E> it = backwardList.iterator(); while (it.hasNext()) { E o = it.next(); insertBeforeNoRedirect(o, previousPoint); previousPoint = o; } point.redirectJumpsToThisTo(toInsert.get(0)); } /** Inserts <code>toInsert</code> in the Chain before <code>point</code>. */ public void insertBefore(Chain<E> toInsert, E point) { LinkedList<E> backwardList = new LinkedList<E>(); // Insert toInsert backwards into the list { Iterator<E> it = toInsert.iterator(); while(it.hasNext()) backwardList.addFirst(it.next()); } E previousPoint = point; Iterator<E> it = backwardList.iterator(); while (it.hasNext()) { E o = it.next(); insertBefore(o, previousPoint); previousPoint = o; } } /** Inserts <code>toInsert</code> in the Chain before <code>point</code>. */ public void insertBefore(E toInsert, E point) { point.redirectJumpsToThisTo(toInsert); innerChain.insertBefore(toInsert, point); } /** Inserts <code>toInsert</code> in the Chain before <code>point</code> WITHOUT redirecting jumps. */ public void insertBeforeNoRedirect(E toInsert, E point) { innerChain.insertBefore(toInsert, point); } /** Returns true if object <code>a</code> follows object <code>b</code> in the Chain. */ public boolean follows(E a, E b) { return innerChain.follows(a,b); } /** Removes the given object from this Chain. */ @SuppressWarnings("unchecked") public boolean remove(Object obj) { boolean res = false; if(contains(obj)) { Unit successor; if((successor = getSuccOf((E) obj)) == null) successor = getPredOf((E) obj); // Note that redirecting to the last unit in the method // like this is probably incorrect when dealing with a Trap. // I.e., let's say that the final unit in the method used to // be U10, preceded by U9, and that there was a Trap which // returned U10 as getEndUnit(). I.e., before the trap covered U9. // When we redirect the Trap's end unit to U9, the trap will no // longer cover U9. I know this is incorrect, but I'm not sure how // to fix it, so I'm leaving this comment in the hopes that some // future maintainer will see the right course to take. res = innerChain.remove(obj); ((E)obj).redirectJumpsToThisTo(successor); } return res; } /** Returns true if this patching chain contains the specified element. */ public boolean contains(Object u) { return innerChain.contains(u); } /** Adds the given object at the beginning of the Chain. */ public void addFirst(E u) { innerChain.addFirst(u); } /** Adds the given object at the end of the Chain. */ public void addLast(E u) { innerChain.addLast(u); } /** Removes the first object from this Chain. */ public void removeFirst() { remove(innerChain.getFirst()); } /** Removes the last object from this Chain. */ public void removeLast() { remove(innerChain.getLast()); } /** Returns the first object in this Chain. */ public E getFirst() { return innerChain.getFirst(); } /** Returns the last object in this Chain. */ public E getLast() { return innerChain.getLast(); } /** Returns the object immediately following <code>point</code>. */ public E getSuccOf(E point){return innerChain.getSuccOf(point);} /** Returns the object immediately preceding <code>point</code>. */ public E getPredOf(E point){return innerChain.getPredOf(point);} protected class PatchingIterator implements Iterator<E> { protected Iterator<E> innerIterator = null; protected E lastObject; protected boolean state = false; protected PatchingIterator (Chain<E> innerChain) { innerIterator = innerChain.iterator(); } protected PatchingIterator (Chain<E> innerChain, E u) { innerIterator = innerChain.iterator(u); } protected PatchingIterator (Chain<E> innerChain, E head, E tail) { innerIterator = innerChain.iterator(head, tail); } public boolean hasNext() { return innerIterator.hasNext(); } public E next() { lastObject = innerIterator.next(); state = true; return lastObject; } public void remove() { if (!state) throw new IllegalStateException("remove called before first next() call"); Unit successor; if((successor = getSuccOf(lastObject)) == null) successor = getPredOf(lastObject); // Note that redirecting to the last unit in the method // like this is probably incorrect when dealing with a Trap. // I.e., let's say that the final unit in the method used to // be U10, preceded by U9, and that there was a Trap which // returned U10 as getEndUnit(). I.e., before the trap covered U9. // When we redirect the Trap's end unit to U9, the trap will no // longer cover U9. I know this is incorrect, but I'm not sure how // to fix it, so I'm leaving this comment in the hopes that some // future maintainer will see the right course to take. innerIterator.remove(); lastObject.redirectJumpsToThisTo(successor); } } /** Returns an iterator over a copy of this chain. * This avoids ConcurrentModificationExceptions from being thrown * if the underlying Chain is modified during iteration. * Do not use this to remove elements which have not yet been * iterated over! */ public Iterator<E> snapshotIterator() { List<E> l = new ArrayList<E>(); l.addAll(this); return l.iterator(); } /** Returns an iterator over this Chain. */ public Iterator<E> iterator() { return new PatchingIterator(innerChain); } /** Returns an iterator over this Chain, starting at the given object. */ public Iterator<E> iterator(E u) { return new PatchingIterator(innerChain, u); } /** Returns an iterator over this Chain, starting at head and reaching tail (inclusive). */ public Iterator<E> iterator(E head, E tail) { return new PatchingIterator(innerChain, head, tail); } /** Returns the size of this Chain. */ public int size(){return innerChain.size(); } }