View Javadoc

1   // QuadIterator.java, created Fri Jan 11 16:42:38 2002 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Compiler.Quad;
5   import java.util.Collection;
6   import java.util.NoSuchElementException;
7   import joeq.Class.jq_Class;
8   import joeq.Util.Templates.List;
9   import joeq.Util.Templates.ListIterator;
10  import joeq.Util.Templates.UnmodifiableList;
11  import jwutil.graphs.Navigator;
12  
13  /***
14   *
15   * @author  John Whaley <jwhaley@alum.mit.edu>
16   * @version $Id: QuadIterator.java 1931 2004-09-22 22:17:47Z joewhaley $
17   */
18  public class QuadIterator implements ListIterator.Quad {
19  
20      /*** A reference to the control flow graph that we are iterating over. */
21      protected final ControlFlowGraph cfg;
22      /*** The reverse post order iteration of basic blocks in the control flow graph.
23       * When going forward, nextBasicBlock should always be the last basic block
24       * returned by this iterator. */
25      protected final ListIterator.BasicBlock rpoBasicBlocks;
26      /*** References to the previous non-empty basic block, the current basic block,
27       * and the next non-empty basic block. */
28      protected BasicBlock previousBasicBlock, currentBasicBlock, nextBasicBlock;
29      /*** An iteration of the quads in the current basic block. */
30      protected ListIterator.Quad quadsInCurrentBasicBlock;
31      
32      /*** The index of the last quad that was returned. */
33      protected int lastIndex;
34      /*** The last quad that was returned. */
35      protected Quad lastQuad;
36      
37      /***
38       * Initialize the iterator to iterate over the quads in the
39       * given control flow graph in reverse post order.
40       * @param cfg  the control flow graph
41       */
42      public QuadIterator(ControlFlowGraph cfg) { this(cfg, true); }
43      /***
44       * Initialize the iterator to iterate over the quads in the
45       * given control flow graph.
46       * If direction is true, the order is reverse post order and the
47       * iteration starts at the first quad.
48       * If direction is false, the order is post order on the
49       * reverse graph and the iteration starts at the last quad.
50       * @param direction  the direction to iterate, forward==true
51       * @param cfg  the control flow graph
52       */
53      public QuadIterator(ControlFlowGraph cfg, boolean direction) {
54          // cache control flow graph
55          this.cfg = cfg;
56          // initialize list of basic blocks in reverse post order
57          this.rpoBasicBlocks = direction ? cfg.reversePostOrderIterator() : cfg.postOrderOnReverseGraphIterator();
58          // skip entry basic block
59          this.rpoBasicBlocks.nextBasicBlock();
60          this.previousBasicBlock = null;
61          this.currentBasicBlock = this.rpoBasicBlocks.nextBasicBlock();
62          updateNextBB();
63          // initialize quad iterator
64          this.quadsInCurrentBasicBlock = this.currentBasicBlock.iterator();
65          // go to the end, if we are doing the reverse direction.
66          if (!direction) {
67              while (hasNext()) nextQuad();
68          }
69          // initialize the last index and the last quad
70          this.lastIndex = -1; this.lastQuad = null;
71      }
72      
73      /*** Update the nextBasicBlock field to point to the next non-empty basic
74       * block from the reverse post order, or null if there are no more
75       * non-empty basic blocks. */
76      protected void updateNextBB() {
77          for (;;) {
78              if (!this.rpoBasicBlocks.hasNext()) {
79                  this.nextBasicBlock = null;
80                  break;
81              }
82              this.nextBasicBlock = this.rpoBasicBlocks.nextBasicBlock();
83              if (this.nextBasicBlock.size() > 0) break;
84          }
85      }
86      /*** Update the previousBasicBlock field to point to the previous non-empty basic
87       * block from the reverse post order, or null if there are no more previous
88       * non-empty basic blocks. */
89      protected void updatePreviousBB() {
90          // the rpoBasicBlocks iterator points just past nextBasicBlock, so
91          // we need to back up to before previousBasicBlock.
92          for (;;) {
93              BasicBlock p = this.rpoBasicBlocks.previousBasicBlock();
94              if (p == this.previousBasicBlock) break;
95          }
96          for (;;) {
97              if (!this.rpoBasicBlocks.hasPrevious()) {
98                  this.previousBasicBlock = null;
99                  break;
100             }
101             this.previousBasicBlock = this.rpoBasicBlocks.previousBasicBlock();
102             if (this.previousBasicBlock.size() > 0) break;
103         }
104         // now reset the rpoBasicBlocks iterator to point just past nextBasicBlock.
105         for (;;) {
106             BasicBlock p = this.rpoBasicBlocks.nextBasicBlock();
107             if (p == this.nextBasicBlock) break;
108         }
109     }
110     
111     public BasicBlock getCurrentBasicBlock() { return this.currentBasicBlock; }
112     
113     public Quad getCurrentQuad() { return lastQuad; }
114     
115     /*** Return the next quad in the iteration. */
116     public Quad nextQuad() {
117         if (!this.quadsInCurrentBasicBlock.hasNext()) {
118             // end of basic block, go to next basic block.
119             if (this.nextBasicBlock == null)
120                 throw new NoSuchElementException();
121             this.previousBasicBlock = this.currentBasicBlock;
122             this.currentBasicBlock = this.nextBasicBlock;
123             this.quadsInCurrentBasicBlock = this.currentBasicBlock.iterator();
124             // update nextBasicBlock
125             updateNextBB();
126         }
127         ++this.lastIndex;
128         return this.lastQuad = this.quadsInCurrentBasicBlock.nextQuad();
129     }
130     /*** Return the next quad in the iteration.  Use nextQuad to avoid the type cast. */
131     public Object next() { return nextQuad(); }
132     /*** Returns whether there is a next quad in this iteration. */
133     public boolean hasNext() {
134         if (this.quadsInCurrentBasicBlock.hasNext()) return true;
135         return this.nextBasicBlock != null;
136     }
137     
138     /*** Returns the first quad reachable from the start of the given basic block. */
139     protected Quad getFirstQuad(BasicBlock bb) {
140         for (;;) {
141             if (bb.isExit()) return null;
142             if (bb.size() > 0) return bb.getQuad(0);
143             bb = bb.getFallthroughSuccessor();
144         }
145     }
146 
147     /*** Returns the last quad reachable from the end of the given basic block. */
148     protected Quad getLastQuad(BasicBlock bb) {
149         for (;;) {
150             if (bb.isEntry()) return null;
151             if (bb.size() > 0) return bb.getLastQuad();
152             if (bb.getPredecessors().isEmpty()) return null; // block is unreachable.
153             bb = bb.getFallthroughPredecessor();
154         }
155     }
156     
157     /*** Sets the current quad. */
158     public void set(Object obj) { this.quadsInCurrentBasicBlock.set(obj); }
159     /*** Returns the index of the next quad to be returned. */
160     public int nextIndex() { return this.lastIndex+1; }
161     
162     /*** Returns the previous quad in the iteration. */
163     public Quad previousQuad() {
164         if (!this.quadsInCurrentBasicBlock.hasPrevious()) {
165             // beginning of basic block, go to previous basic block.
166             if (this.previousBasicBlock == null)
167                 throw new NoSuchElementException();
168             this.nextBasicBlock = this.currentBasicBlock;
169             this.currentBasicBlock = this.previousBasicBlock;
170             this.quadsInCurrentBasicBlock = this.currentBasicBlock.iterator();
171             // go to end of iterator
172             while (this.quadsInCurrentBasicBlock.hasNext())
173                 this.quadsInCurrentBasicBlock.nextQuad();
174             // update previousBasicBlock
175             updatePreviousBB();
176         }
177         this.lastQuad = this.quadsInCurrentBasicBlock.previousQuad();
178         --this.lastIndex;
179         return this.lastQuad;
180     }
181     /*** Returns the previous quad in the iteration.  Use previousQuad to avoid the type cast. */
182     public Object previous() { return previousQuad(); }
183     
184     /*** Removes the last-returned-quad from the underlying list. */
185     public void remove() { this.quadsInCurrentBasicBlock.remove(); lastQuad = null; --lastIndex; }
186 
187     /*** Returns the index of the previous quad. */
188     public int previousIndex() { return this.lastIndex; }
189     
190     /*** Returns whether this iteration has a previous quad. */
191     public boolean hasPrevious() {
192         if (this.quadsInCurrentBasicBlock.hasPrevious()) return true;
193         return this.previousBasicBlock != null;
194     }
195     
196     /*** Adds a quad to the underlying quad list. */
197     public void add(Object obj) { this.quadsInCurrentBasicBlock.add(obj); lastQuad = null; ++lastIndex; }
198     
199     /***
200      * Return an iterator of the possible successor quads of the most recently returned quad.
201      * If a possible successor is the method exit, it includes the "null" value in the iteration.
202      * @throws IllegalStateException  if the nextQuad method has not yet been called.
203      */
204     public java.util.Iterator/*<Quad>*/ successors() {
205         return successors1().iterator();
206     }
207     public Collection/*<Quad>*/ successors1() {
208         // if lastQuad is invalid, throw an exception.
209         if (lastQuad == null) throw new IllegalStateException();
210         // allocate the result set.
211         java.util.Set/*<Quad>*/ result = new java.util.HashSet/*<Quad>*/();
212 
213         // Start with case 3:
214         // Case 3: Iterate through the types of exceptions that this quad
215         //         may throw.
216         ListIterator.jq_Class exceptions = lastQuad.getThrownExceptions().classIterator();
217         while (exceptions.hasNext()) {
218             jq_Class exception = exceptions.nextClass();
219             // Iterate over the list of exception handlers that may catch an
220             // exception of this type.
221             ListIterator.ExceptionHandler mayCatch = currentBasicBlock.getExceptionHandlers().mayCatch(exception).exceptionHandlerIterator();
222             while (mayCatch.hasNext()) {
223                 ExceptionHandler exceptionHandler = mayCatch.nextExceptionHandler();
224                 // add the first quad of the exception handler entry to the set.
225                 result.add(getFirstQuad(exceptionHandler.getEntry()));
226             }
227             // if the exception is not definitely caught, add "null" for the exit.
228             if (currentBasicBlock.getExceptionHandlers().mustCatch(exception) == null) {
229                 result.add(null);
230             }
231         }
232 
233         // note: we use next() and previous() rather than using
234         // nextIndex() and getQuad(index), because accessing a linked list
235         // via an index is not a constant time operation.
236 
237         // we need to check if we last called previous or next, because
238         // the position of the iterator depends on which was most recently
239         // called.
240         if (this.quadsInCurrentBasicBlock.hasNext()) {
241             Quad next = this.quadsInCurrentBasicBlock.nextQuad();
242             if (next != lastQuad) {
243                 // We called next() last.
244                 // Case 1: if this is not the end of the basic block, add the next
245                 //         quad in the basic block.
246                 result.add(next); // add next quad.
247                 this.quadsInCurrentBasicBlock.previousQuad(); // reset iterator position.
248                 return result;
249             } else {
250                 // We called previous() last.
251                 if (this.quadsInCurrentBasicBlock.hasNext()) {
252                     // Case 1: if this is not the end of the basic block, add the next
253                     //         quad in the basic block.
254                     next = this.quadsInCurrentBasicBlock.nextQuad();
255                     result.add(next); // add next quad.
256                     // reset iterator position.
257                     this.quadsInCurrentBasicBlock.previousQuad();
258                     this.quadsInCurrentBasicBlock.previousQuad();
259                     return result;
260                 } else {
261                     // this is the last quad in the basic block.
262                     // reset iterator position and fallthrough to case 2, below.
263                     this.quadsInCurrentBasicBlock.previousQuad();
264                 }
265             }
266         }
267         // Case 2: end of basic block, add the first quad of every
268         //         successor basic block.
269         ListIterator.BasicBlock succs = this.currentBasicBlock.getSuccessors().basicBlockIterator();
270         while (succs.hasNext())
271             result.add(getFirstQuad(succs.nextBasicBlock()));
272         
273         return result;
274     }
275 
276     public java.util.Iterator/*<Quad>*/ predecessors() {
277         return predecessors1().iterator();
278     }
279     public Collection/*<Quad>*/ predecessors1() {
280         // if lastQuad is invalid, throw an exception.
281         if (lastQuad == null) throw new IllegalStateException();
282 
283         // figure out if we last called previous() or next().
284         Quad previous;
285         if (this.quadsInCurrentBasicBlock.hasPrevious()) {
286             previous = this.quadsInCurrentBasicBlock.previousQuad();
287             if (previous != lastQuad) {
288                 // we called previous() last;
289                 // Case 1: if this is not the beginning of the basic block, add the previous
290                 //         quad in the basic block.
291                 this.quadsInCurrentBasicBlock.nextQuad(); // reset iterator position.
292                 return new UnmodifiableList.Quad(previous);
293             } else {
294                 // we called next() last.
295                 if (this.quadsInCurrentBasicBlock.hasPrevious()) {
296                     // Case 1: if this is not the beginning of the basic block, add the previous
297                     //         quad in the basic block.
298                     previous = this.quadsInCurrentBasicBlock.previousQuad();
299                     // reset iterator position.
300                     this.quadsInCurrentBasicBlock.nextQuad(); 
301                     this.quadsInCurrentBasicBlock.nextQuad();
302                     return new UnmodifiableList.Quad(previous);
303                 } else {
304                     // this is the first quad in the basic block.
305                     // reset iterator position and fallthrough to case 2 and 3, below.
306                     this.quadsInCurrentBasicBlock.nextQuad();
307                 }
308             }
309         }
310 
311         // allocate the result set.
312         java.util.Set/*<Quad>*/ result = new java.util.HashSet/*<Quad>*/();
313 
314         // Case 2: beginning of basic block, add the first quad of every
315         //         predecessor basic block.
316         ListIterator.BasicBlock preds = this.currentBasicBlock.getPredecessors().basicBlockIterator();
317         while (preds.hasNext())
318             result.add(getLastQuad(preds.nextBasicBlock()));
319         // Case 3: if this is the entry point to an exception handler, find
320         //         all quads that can trigger this handler.
321         if (currentBasicBlock.isExceptionHandlerEntry()) {
322             java.util.Iterator ex_handlers = cfg.getExceptionHandlersMatchingEntry(currentBasicBlock);
323             while (ex_handlers.hasNext()) {
324                 ExceptionHandler eh = (ExceptionHandler)ex_handlers.next();
325                 ListIterator.BasicBlock handled = eh.getHandledBasicBlocks().basicBlockIterator();
326                 while (handled.hasNext()) {
327                     BasicBlock bb = handled.nextBasicBlock();
328                     addQuadsThatReachHandler(bb, result, eh);
329                 }
330             }
331         }
332         return result;
333     }
334 
335     private static void addQuadsThatReachHandler(BasicBlock bb, java.util.Set result, ExceptionHandler eh) {
336         ListIterator.Quad quads = bb.iterator();
337         while (quads.hasNext()) {
338             Quad q = quads.nextQuad();
339             ListIterator.jq_Class exceptions = q.getThrownExceptions().classIterator();
340             while (exceptions.hasNext()) {
341                 jq_Class exception = (jq_Class)exceptions.next();
342                 List.ExceptionHandler mayCatch = bb.getExceptionHandlers().mayCatch(exception);
343                 if (mayCatch.contains(eh))
344                     result.add(q);
345             }
346         }
347     }
348     
349     public Navigator getNavigator() {
350         return new Navigator() {
351             public Collection next(Object node) {
352                 if (node == lastQuad) return successors1();
353                 return search(node, true);
354             }
355             public Collection prev(Object node) {
356                 if (node == lastQuad) return predecessors1();
357                 return search(node, false);
358             }
359             private Collection search(Object node, boolean dir) {
360                 Object oldLocation = lastQuad;
361                 Collection result = null;
362                 if (searchBackward(node)) {
363                     result = dir?successors1():predecessors1();
364                 }
365                 searchForward(oldLocation);
366                 if (result == null) {
367                     if (searchForward(node)) {
368                         result = dir?successors1():predecessors1();
369                     }
370                     if (result == null) {
371                         throw new UnsupportedOperationException();
372                     }
373                     searchBackward(oldLocation);
374                 }
375                 return result;
376             }
377         };
378     }
379     public boolean searchForward(Object node) {
380         while (hasNext()) {
381             if (node == nextQuad()) return true;
382         }
383         return false;
384     }
385     public boolean searchBackward(Object node) {
386         while (hasPrevious()) {
387             if (node == previousQuad()) return true;
388         }
389         return false;
390     }
391 }