001: /* TryStacks.java */
002:
003: package org.quilt.cl;
004:
005: import java.util.*;
006: import org.apache.bcel.generic.*;
007:
008: import org.quilt.graph.*;
009:
010: /**
011: * Manages try/catch blocks. Adds subgraphs to the method
012: * graph for each exception handler, building the graph that
013: * GraphTransformer hangs bytecode off.
014: *
015: * This module must cope with the fact that the compiler allocates exception
016: * handlers in no particular order.
017: *
018: * Hacked from earlier 0.5-compatible code.
019: *
020: * @author < a href="jdd@dixons.org">Jim Dixon</a>
021: */
022: public class TryStacks {
023:
024: private ControlFlowGraph graph = null;
025: private SortedBlocks blox = null;
026:
027: private int handlerCount = 0;
028:
029: private int index; // index into the arrays that follow
030: private int tryStart[]; // bytecount position ...
031: private int tryEnd[]; // inclusive
032: private int handlerPC[]; // start of catch blocks
033: private ObjectType exception[];
034: private boolean done[]; // debugging only?
035:
036: /** Hash of bytecode offsets of end of try blocks. */
037: private Map tryEndNdx = new HashMap();
038:
039: /**
040: * Comparator for exception handlers. These need to be sorted
041: * by tryStart (offset of beginning of try block) in ascending
042: * order, then by tryEnd (offset of end of try block) in
043: * descending order, then by handlerPC in ascending order.
044: */
045: private class CmpHandlers implements Comparator {
046: /** Implementation of compare.
047: * @param o1 first handler in comparison
048: * @param o2 second
049: * @return -1 if o1 < o2, 0 if 01 == o2, 1 if o1 > o2
050: */
051: public int compare(Object o1, Object o2) {
052: CodeExceptionGen a = (CodeExceptionGen) o1;
053: CodeExceptionGen b = (CodeExceptionGen) o2;
054:
055: // -1 if a < b, 0 if a = b, 1 if a > b, in some sense
056: int aStart = a.getStartPC().getPosition();
057: int bStart = b.getStartPC().getPosition();
058: // ascending order of start offset
059: if (aStart < bStart) {
060: return -1;
061: } else if (aStart > bStart) {
062: return 1;
063: }
064: // descending order of end offset
065: int aEnd = a.getEndPC().getPosition();
066: int bEnd = b.getEndPC().getPosition();
067: if (aEnd < bEnd) {
068: return 1;
069: } else if (aEnd > bEnd) {
070: return -1;
071: }
072: // ascending order of handler offset
073: int aHandler = a.getHandlerPC().getPosition();
074: int bHandler = b.getHandlerPC().getPosition();
075: if (aHandler < bHandler) {
076: return -1;
077: } else if (aHandler > bHandler) {
078: return 1;
079: } else {
080: return 0;
081: }
082: }
083: }
084:
085: // CONSTRUCTOR //////////////////////////////////////////////////
086: /**
087: * Constructor setting up try/catch arrays. Sorts the exception
088: * handlers and then builds a nested control flow graph, including
089: * the first code vertex in each try block who first vertex is a
090: * code vertex.
091: *
092: * @param handlers Array of exception handlers for a method.
093: * @param blocks Vertices indexed by position in bytecode (?).
094: * @param g Graph for the method.
095: */
096: public TryStacks(final CodeExceptionGen[] handlers,
097: SortedBlocks blocks, ControlFlowGraph g) {
098: if (handlers == null || blocks == null || g == null) {
099: throw new IllegalArgumentException(
100: "null constructor argument");
101: }
102: blox = blocks;
103: graph = g;
104:
105: handlerCount = handlers.length;
106: if (handlerCount > 0) {
107: tryStart = new int[handlerCount];
108: tryEnd = new int[handlerCount];
109: handlerPC = new int[handlerCount];
110: exception = new ObjectType[handlerCount];
111: done = new boolean[handlerCount];
112:
113: // sort the handlers first by position of beginning of
114: // try block, then by position of end of try block, then
115: // by handler address
116: SortedMap sm = new TreeMap(new CmpHandlers());
117: for (int i = 0; i < handlerCount; i++) {
118: sm.put(handlers[i], new Integer(i));
119: }
120: Iterator it = sm.keySet().iterator();
121: for (int j = 0; it.hasNext(); j++) {
122: Integer iInt = (Integer) sm.get((CodeExceptionGen) it
123: .next());
124: int i = iInt.intValue();
125: tryStart[j] = handlers[i].getStartPC().getPosition();
126: tryEnd[j] = handlers[i].getEndPC().getPosition();
127: handlerPC[j] = handlers[i].getHandlerPC().getPosition();
128: exception[j] = handlers[i].getCatchType();
129:
130: done[j] = false;
131: }
132: Edge edge = graph.getEntry().getEdge();
133: for (int i = 0; i < handlerCount && !done[i]; /* */) {
134: ControlFlowGraph sub = handleTry(graph, edge);
135: edge = sub.getExit().getEdge();
136: }
137: } // if handlerCount > 0
138: }
139:
140: /**
141: * Initialize the graph and set up catch blocks for the i-th
142: * try block.
143: *
144: * @param index Index into table of exception handlers, updated by this
145: * method.
146: * @param g Graph which will be parent of the graph created.
147: * @param e On entry, edge along which graph is created
148: * @return Subgraph created
149: */
150: private ControlFlowGraph handleTry(final ControlFlowGraph g,
151: final Edge parentEdge) {
152: int start = tryStart[index];
153: int end = tryEnd[index];
154: if (parentEdge == null) {
155: throw new IllegalArgumentException("null edge");
156: }
157: // deal with tries with multiple exception handlers
158: ControlFlowGraph subgraph = handleTryGroup(g, parentEdge);
159: Vertex subEntry = subgraph.getEntry();
160: Edge currEdge = subEntry.getEdge();
161:
162: // deal with trys starting at the same bytecode offset
163: ControlFlowGraph subsub;
164: if ((index < handlerCount) && (tryStart[index] == start)) {
165: subsub = handleTry(subgraph, currEdge);
166: currEdge = subsub.getExit().getEdge();
167: } else {
168: // this was the most deeply nested try block starting at
169: // this offset, so bytecode gets assigned to vertex
170: // hanging off the Entry's preferred edge. Create that
171: // vertex along currEdge.
172: currEdge = blox.add(tryStart[index - 1], currEdge)
173: .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(
195: final ControlFlowGraph parent, 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 && tryStart[j] == pos
201: && tryEnd[j] == end; j++) {
202: // try blocks are identical
203: k++;
204: }
205: // create a subgraph with a k-sized connector
206: ControlFlowGraph subgraph = (ControlFlowGraph) parent.subgraph(
207: parentEdge, k);
208: Edge currentEdge = subgraph.getExit().getEdge();
209:
210: // connect to catch blocks
211: ComplexConnector conn = (ComplexConnector) subgraph.getEntry()
212: .getConnector();
213: for (int j = 0; j < k; j++) {
214: done[index + j] = true;
215: Edge edge = conn.getEdge(j);
216: CodeVertex v = subgraph.insertCodeVertex(edge);
217: v.setPos(handlerPC[index + j]);
218: // v.getConnector().setData ( new ConnData() );
219: blox.add(v);
220: }
221: index += k;
222: return subgraph;
223: }
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
236: .get(tryEnd[i]), blox.get(handlerPC[i]),
237: exception[i]);
238: }
239: return cd;
240: }
241:
242: /**
243: * Return the class TryStack uses to sort exception handlers.
244: *
245: * @return The comparator used to sort handlers.
246: */
247: public Comparator getComparator() {
248: return new CmpHandlers();
249: }
250:
251: /**
252: * Get the bytecode offset of end of the try block for this graph.
253: *
254: * @param graph A subgraph created by this package.
255: * @return The bytecode offset of the end of the try block for this
256: * or -1 if there is no such graph.
257: */
258: int getEndTry(final ControlFlowGraph graph) {
259: if (tryEndNdx.containsKey(graph)) {
260: Integer i = (Integer) tryEndNdx.get(graph);
261: return i.intValue();
262: }
263: return -1;
264: }
265:
266: /**
267: * @return Newline-terminated string description of try blocks and
268: * handlers.
269: */
270: public String toString() {
271: String s = "";
272: if (handlerCount > 0) {
273: // columns will not align properly for non-trivial cases
274: s = " index start end handler pc\n";
275: for (int i = 0; i < handlerCount; i++) {
276: s += " " + i + " [" + tryStart[i] + ".."
277: + tryEnd[i] + "] --> " + handlerPC[i] + "\n";
278: }
279: }
280: return s;
281: }
282: }
|