1   /* TryStacks.java */
2   
3   package org.quilt.cl;
4   
5   import java.util.*;
6   import org.apache.bcel.generic.*;
7   
8   import org.quilt.graph.*;
9   
10  /*** 
11   * Manages try/catch blocks.  Adds subgraphs to the method 
12   * graph for each exception handler, building the graph that 
13   * GraphTransformer hangs bytecode off.  
14   *
15   * This module must cope with the fact that the compiler allocates exception 
16   * handlers in no particular order.
17   * 
18   * Hacked from earlier 0.5-compatible code.  
19   *
20   * @author < a href="jdd@dixons.org">Jim Dixon</a>
21   */
22  public class TryStacks {
23  
24      private ControlFlowGraph     graph = null;
25      private SortedBlocks blox  = null;
26  
27      private int handlerCount = 0;
28     
29      private int index;                  // index into the arrays that follow
30      private int tryStart[];             // bytecount position ...
31      private int tryEnd[];               //      inclusive
32      private int handlerPC[];            // start of catch blocks
33      private ObjectType exception[];    
34      private boolean done [];            // debugging only?
35  
36      /*** Hash of bytecode offsets of end of try blocks. */
37      private Map tryEndNdx = new HashMap();
38  
39      /*** 
40       * Comparator for exception handlers.  These need to be sorted
41       * by tryStart (offset of beginning of try block) in ascending
42       * order, then by tryEnd (offset of end of try block) in
43       * descending order, then by handlerPC in ascending order.  
44       */
45      private class CmpHandlers implements Comparator {
46          /*** Implementation of compare.
47           * @param o1 first handler in comparison
48           * @param o2 second
49           * @return -1 if o1 < o2, 0 if 01 == o2, 1 if o1 > o2
50           */
51          public int compare (Object o1, Object o2) {
52              CodeExceptionGen a = (CodeExceptionGen) o1;
53              CodeExceptionGen b = (CodeExceptionGen) o2;
54  
55              // -1 if a < b, 0 if a = b, 1 if a > b, in some sense
56              int aStart = a.getStartPC().getPosition();
57              int bStart = b.getStartPC().getPosition();
58              // ascending order of start offset
59              if (aStart < bStart) {
60                  return -1;
61              } else if (aStart > bStart) {
62                  return 1; 
63              }
64              // descending order of end offset
65              int aEnd   = a.getEndPC().getPosition();
66              int bEnd   = b.getEndPC().getPosition();
67              if (aEnd < bEnd) {
68                  return 1;
69              } else if (aEnd > bEnd) {
70                  return -1;
71              }  
72              // ascending order of handler offset
73              int aHandler = a.getHandlerPC().getPosition();
74              int bHandler = b.getHandlerPC().getPosition();
75              if (aHandler < bHandler) {
76                  return -1;
77              } else if (aHandler > bHandler) {
78                  return 1;
79              } else {
80                  return 0;
81              }
82          }
83      }
84      
85      // CONSTRUCTOR //////////////////////////////////////////////////
86      /*** 
87       * Constructor setting up try/catch arrays.  Sorts the exception
88       * handlers and then builds a nested control flow graph, including
89       * the first code vertex in each try block who first vertex is a
90       * code vertex.
91       *
92       * @param handlers Array of exception handlers for a method.
93       * @param blocks   Vertices indexed by position in bytecode (?).
94       * @param g        Graph for the method.
95       */
96      public TryStacks (
97              final CodeExceptionGen[] handlers, 
98              SortedBlocks blocks, 
99              ControlFlowGraph g) {
100         if (handlers == null || blocks == null || g == null) {
101             throw new IllegalArgumentException("null constructor argument");
102         }
103         blox    = blocks;
104         graph   = g;
105 
106         handlerCount = handlers.length;
107         if (handlerCount > 0) {
108             tryStart  = new int[handlerCount];
109             tryEnd    = new int[handlerCount];
110             handlerPC = new int[handlerCount];
111             exception = new ObjectType[handlerCount];
112             done      = new boolean [handlerCount];
113             
114             // sort the handlers first by position of beginning of
115             // try block, then by position of end of try block, then
116             // by handler address
117             SortedMap sm = new TreeMap(new CmpHandlers());
118             for (int i=0; i<handlerCount; i++) {
119                 sm.put ( handlers[i], new Integer(i) );
120             }
121             Iterator it = sm.keySet().iterator();
122             for (int j = 0; it.hasNext(); j++ ) {
123                 Integer iInt = (Integer) sm.get ( 
124                                             (CodeExceptionGen) it.next() );
125                 int i = iInt.intValue();
126                 tryStart[j]   = handlers[i].getStartPC().getPosition();
127                 tryEnd[j]     = handlers[i].getEndPC().getPosition();
128                 handlerPC[j]  = handlers[i].getHandlerPC().getPosition();
129                 exception[j]  = handlers[i].getCatchType();
130 
131                 done[j]       = false;
132             }
133             Edge edge = graph.getEntry().getEdge();
134             for (int i = 0; i < handlerCount && !done[i]; /* */ ) {
135                 ControlFlowGraph sub = handleTry(graph, edge) ;
136                 edge = sub.getExit().getEdge();
137             }
138         } // if handlerCount > 0
139     } 
140 
141     /*** 
142      * Initialize the graph and set up catch blocks for the i-th 
143      * try block. 
144      *
145      * @param index Index into table of exception handlers, updated by this
146      *                method.
147      * @param g     Graph which will be parent of the graph created.
148      * @param e     On entry, edge along which graph is created
149      * @return      Subgraph created
150      */
151     private ControlFlowGraph handleTry (
152                     final ControlFlowGraph g, final Edge parentEdge ) {
153         int start = tryStart[index];
154         int end   = tryEnd[index];
155         if ( parentEdge == null) {
156             throw new IllegalArgumentException("null edge");
157         }
158         // deal with tries with multiple exception handlers
159         ControlFlowGraph subgraph   = handleTryGroup (g, parentEdge);
160         Vertex   subEntry   = subgraph.getEntry();
161         Edge     currEdge  = subEntry.getEdge();
162         
163         // deal with trys starting at the same bytecode offset
164         ControlFlowGraph subsub;
165         if ((index < handlerCount) && (tryStart[index] == start)) {
166             subsub = handleTry (subgraph, currEdge);
167             currEdge = subsub.getExit().getEdge();
168         } else {
169             // this was the most deeply nested try block starting at 
170             // this offset, so bytecode gets assigned to vertex
171             // hanging off the Entry's preferred edge.  Create that
172             // vertex along currEdge.
173             currEdge = blox.add (tryStart[index - 1], currEdge ).getEdge();
174         }
175         // other tries nested within this try block
176         int nested = 0;
177         while ((index < handlerCount) && (tryStart[index] < end)) {
178             subsub = handleTry (subgraph, currEdge);
179             currEdge = subsub.getExit().getEdge();
180         }
181         // set up tryEnd index by graph
182         tryEndNdx.put(subgraph, new Integer(start));
183         return subgraph;
184     } 
185 
186     /***
187      * Deal with a try block with one or more catch blocks. 
188      *
189      * @param i          Index into handler table, updated by this method.
190      * @param parent     Parent graph.
191      * @param parentEdge Edge along which subgraph is created.
192      * @returns          Subgraph created.
193      */
194     private ControlFlowGraph handleTryGroup (final ControlFlowGraph parent, 
195                                             final Edge parentEdge) {
196        
197         int k = 1;                  // number of catch blocks
198         int pos = tryStart[index];
199         int end = tryEnd[index];
200         for (int j = index + 1; j < handlerCount 
201                          && tryStart[j] == pos && tryEnd[j] == end;
202                                                                 j++) {
203             // try blocks are identical
204               k++;
205         }
206         // create a subgraph with a k-sized connector
207         ControlFlowGraph subgraph 
208                     = (ControlFlowGraph) parent.subgraph (parentEdge, k);
209         Edge currentEdge  = subgraph.getExit().getEdge();
210 
211         // connect to catch blocks
212         ComplexConnector conn 
213                     = (ComplexConnector)subgraph.getEntry().getConnector();
214         for (int j = 0; j < k; j++) {
215             done[index + j] = true;
216             Edge edge = conn.getEdge(j);
217             CodeVertex v = subgraph.insertCodeVertex (edge);
218             v.setPos (handlerPC[index + j] );
219             // v.getConnector().setData ( new ConnData() );
220             blox.add (v);
221         }
222         index += k; 
223         return subgraph;
224     }
225     /*** 
226      * Return an array of CatchData, with vertices for the beginning
227      * and end of the try block, a vertex for the handler, and the 
228      * exception handled.
229      *
230      * @return Catch handler descriptions for the graph.
231      */
232     public CatchData [] getCatchData() {
233         CatchData [] cd = new CatchData[tryStart.length];
234         for (int i = 0; i < tryStart.length; i++) {
235             cd [i] = new CatchData (blox.get(tryStart[i]), blox.get(tryEnd[i]),
236                                     blox.get(handlerPC[i]), exception[i] );
237         }
238         return cd;
239     }
240     /***
241      * Return the class TryStack uses to sort exception handlers.
242      *
243      * @return The comparator used to sort handlers. 
244      */
245     public Comparator getComparator() {
246         return new CmpHandlers();
247     }
248     /***
249      * Get the bytecode offset of end of the try block for this graph.
250      *
251      * @param graph A subgraph created by this package.
252      * @return      The bytecode offset of the end of the try block for this
253      *                  or -1 if there is no such graph.
254      */
255     int getEndTry (final ControlFlowGraph graph) {
256         if (tryEndNdx.containsKey(graph)) {
257             Integer i = (Integer)tryEndNdx.get(graph);
258             return i.intValue();
259         }
260         return -1;
261     }
262     /*** 
263      * @return Newline-terminated string description of try blocks and 
264      * handlers. 
265      */
266     public String toString() {
267         String s = "";
268         if (handlerCount > 0) {
269             // columns will not align properly for non-trivial cases
270             s = "  index start end handler pc\n";
271             for (int i = 0; i < handlerCount; i++) {
272                 s += "    " + i + "    [" + tryStart[i] 
273                    + ".." + tryEnd[i] 
274                    + "] --> " + handlerPC[i] + "\n";
275             }
276         }
277         return s;
278     }
279 }
This page was automatically generated by Maven