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