001: /* BytecodeCollector.java */
002:
003: package org.quilt.cl;
004:
005: import java.util.HashMap;
006: import java.util.HashSet;
007: import java.util.Iterator;
008: import java.util.List;
009: import java.util.Map;
010: import java.util.Set;
011: import java.util.Vector;
012:
013: import org.apache.bcel.classfile.*;
014: import org.apache.bcel.generic.*;
015:
016: import org.quilt.graph.*;
017:
018: /**
019: * Collects the bytecode for a method while walking the method
020: * control flow graph.
021: *
022: * Inspired by Quilt 0.5's BytecodeLayout,
023: * @author <a href="mailto:ddp@apache.org">David Dixon-Peugh</a> and the
024: * rest of the Quilt development team.
025: *
026: * However, errors and omissions in this version are entirely due to
027: * @author <a href="mailto:jddixon@users.sourceforge.net">Jim Dixon</a>
028: */
029:
030: public class BytecodeCollector implements org.quilt.graph.Visitor {
031:
032: private ControlFlowGraph graph = null;
033:
034: private Map startHandles = null;
035: private Map endHandles = null;
036: private Map gotoFixMeUps = null;
037: private InstructionList ilist = null;
038:
039: /** No-arg constructor. */
040: public BytecodeCollector() {
041: }
042:
043: /**
044: * Action taken upon beginning to visit the graph.
045: * @param g Directed graph to be visited.
046: */
047: public void discoverGraph(Directed g) {
048: graph = (ControlFlowGraph) g;
049: ilist = graph.getInstructionList();
050: startHandles = graph.getStartHandles();
051: endHandles = graph.getEndHandles();
052: gotoFixMeUps = graph.getGotoFixMeUps();
053: }
054:
055: /**
056: * Action taken upon beginning to visit a graph vertex. Unless
057: * this is an Entry or Exit vertex, save the starting position in
058: * the bytecode to give a offset for edges with this vertex as target.
059: * Save the end instruction in the bytecode so that we can insert any
060: * connector code their later, in finishVertex.
061: *
062: * Entry and Exit vertices carry no bytecode and never have a
063: * connecting instruction, so they can be ignored when collecting
064: * code.
065: *
066: * @param v The vertex; carries a block of code and a final
067: * instruction in its Connector.
068: */
069: public void discoverVertex(Vertex vtx) {
070: if (vtx == null) {
071: throw new IllegalArgumentException("null vertex");
072: }
073: // no code in Entry or Exit vertices
074: if (!(vtx instanceof Entry || vtx instanceof Exit)) {
075: CodeVertex v = (CodeVertex) vtx;
076: InstructionList vIList = v.getInstructionList();
077: InstructionHandle vStart = null;
078:
079: if (vIList.size() == 0) {
080: vIList = new InstructionList(new NOP());
081: }
082: vStart = ilist.append(vIList); // consumes vIList
083: if (vStart == null) {
084: System.out
085: .println("discoverVertex INTERNAL ERROR: vertex "
086: + v
087: + " has null position in bytecode\n");
088: }
089: // DEBUG
090: // System.out.println("discoverVertex " + v + " - adding vStart");
091: if (vStart == null) {
092: System.out.println(" ** vStart is null **");
093: }
094: // END
095: startHandles.put(v, vStart); // v a CodeVertex
096:
097: Instruction inst = v.getConnInst();
098: if (inst != null) {
099: // CONSOLIDATE THESE ////////////////////////////////
100: if (inst instanceof GotoInstruction) {
101: BranchHandle bh = ilist
102: .append((GotoInstruction) inst);
103: endHandles.put(v, bh);
104: } else if (inst instanceof IfInstruction) {
105: BranchHandle bh = ilist
106: .append((IfInstruction) inst);
107: endHandles.put(v, bh);
108: } else if (inst instanceof JsrInstruction) {
109: BranchHandle bh = ilist
110: .append((JsrInstruction) inst);
111: endHandles.put(v, bh);
112: } else if (inst instanceof Select) {
113: BranchHandle bh = ilist.append((Select) inst);
114: endHandles.put(v, bh);
115: } else if ((inst instanceof ExceptionThrower)
116: || (inst instanceof ReturnInstruction)
117: || (inst instanceof RET)
118: || (inst instanceof InvokeInstruction)) {
119: InstructionHandle ih = ilist.append(inst);
120: endHandles.put(v, ih);
121: } else {
122: ilist.append(inst);
123: }
124: }
125: }
126: }
127:
128: /**
129: * Action taken upon first encountering an edge.
130: * @param e A FlowControlEdge, a weighted, directed edge.
131: */
132: public void discoverEdge(Edge e) {
133: }
134:
135: /**
136: * Action taken before departing an edge.
137: *
138: * @param e The control flow graph edge.
139: */
140: public void finishEdge(Edge e) {
141: }
142:
143: /**
144: * Where the immediate target of an edge is an Entry or Exit,
145: * determine the ultimate target. That is, follow preferred
146: * edges until a code Vertex (neither Entry nor Exit) is found
147: * and return that. XXX Should throw exception if target becomes
148: * null.
149: *
150: * @param e Edge we are searching along.
151: * @return Ultimate target of the edge.
152: */
153: private CodeVertex getEffectiveTarget(final Edge e) {
154: Vertex target = e.getTarget();
155: // DEBUG
156: //System.out.println (
157: // "getEffectiveTarget: initially target is " + target);
158: // END
159: while (target != null
160: && (target instanceof Entry || target instanceof Exit)) {
161: // DEBUG
162: // System.out.print (" replacing " + target + " with");
163: // END
164:
165: target = target.getTarget();
166:
167: //System.out.println (" " + target ); // DEBUG too ;-)
168: }
169: // DEBUG
170: // System.out.println (" returning: " + target);
171: // END
172: return (CodeVertex) target;
173: }
174:
175: /**
176: * Action taken before leaving a vertex in the graph. The
177: * connection instruction, if any, is fixed up by setting
178: * its target(s).
179: *
180: * If the connection instruction is a goto, which might jump
181: * into another graph, fixup is delayed until the user asks
182: * for the instruction list, when the processing of all graphs
183: * has been completed.
184: *
185: * @see CodeVertex.getInstructionList
186: * @param v The vertex, either an Entry or Exit vertex, or a
187: * code vertex.
188: */
189: public void finishVertex(Vertex vtx) {
190: if (vtx instanceof CodeVertex) {
191: CodeVertex v = (CodeVertex) vtx;
192: Instruction inst = v.getConnInst();
193: if (inst != null) {
194: if (inst instanceof GotoInstruction) {
195: // // DEBUG
196: // Connector conn = v.getConnector();
197: // System.out.println("GotoInstruction in vertex " + v);
198: // if (conn instanceof UnaryConnector) {
199: // System.out.println(" has unary connector!!");
200: // } else if (conn instanceof BinaryConnector) {
201: // System.out.println(" has binary connector");
202: // System.out.println (" 'other' edge: "
203: // + ((BinaryConnector)conn).getOtherEdge());
204: // }
205: // // END
206: CodeVertex effTarget = getEffectiveTarget(((BinaryConnector) v
207: .getConnector()).getOtherEdge());
208: InstructionHandle target = (InstructionHandle) startHandles
209: .get(effTarget);
210: gotoFixMeUps.put(v, effTarget); // delayed fixup
211:
212: } else if (inst instanceof IfInstruction) {
213: Edge workingEdge = ((BinaryConnector) v
214: .getConnector()).getOtherEdge();
215: InstructionHandle target = (InstructionHandle) startHandles
216: .get(getEffectiveTarget(workingEdge));
217: BranchHandle bh = (BranchHandle) endHandles.get(v);
218: bh.setTarget(target);
219:
220: } else if (inst instanceof JsrInstruction) {
221: InstructionHandle target = (InstructionHandle) startHandles
222: .get(getEffectiveTarget(v.getEdge()));
223: BranchHandle bh = (BranchHandle) endHandles.get(v);
224: bh.setTarget(target);
225:
226: } else if (inst instanceof Select) {
227: // has a complex connector - deal with default edge
228: InstructionHandle target = (InstructionHandle) startHandles
229: .get(getEffectiveTarget(v.getEdge()));
230: // DEBUG
231: // if (target == null) {
232: // System.out.println(
233: // "BytecodeCollector.finishVertex "
234: // + v + " INTERNAL ERROR: \n"
235: // + " " + inst + " has null default target");
236: // }
237: // END
238: BranchHandle bh = (BranchHandle) endHandles.get(v);
239: bh.setTarget(target);
240:
241: // now take care of the other edges
242: ComplexConnector conn = (ComplexConnector) v
243: .getConnector();
244: int edgeCount = conn.size();
245: InstructionHandle[] targets = new InstructionHandle[edgeCount];
246: for (int i = 0; i < edgeCount; i++) {
247: target = (InstructionHandle) startHandles
248: .get(getEffectiveTarget(conn.getEdge(i)));
249: // DEBUG
250: if (target == null) {
251: System.out
252: .println("BytecodeCollector.finishVertex "
253: + v
254: + " INTERNAL ERROR: \n"
255: + " "
256: + inst
257: + " has null target " + i);
258: }
259: // END
260: ((Select) bh.getInstruction()).setTarget(i,
261: target);
262: }
263: }
264: }
265: }
266: }
267:
268: /**
269: * Dump the instruction list. Debug method.
270: */
271: private void dumpIList(String where) {
272: System.out.println("BytecodeCollector." + where
273: + " instruction list:");
274: int i = 0;
275: for (InstructionHandle ih = ilist.getStart(); ih != null; ih = ih
276: .getNext()) {
277: System.out.println(" " + (i++) + " "
278: + ih.getInstruction());
279: }
280: }
281:
282: /**
283: * Finish the graph.
284: *
285: * @param g The control flow graph - ignored.
286: */
287: public void finishGraph(Directed g) {
288:
289: }
290:
291: /**
292: * Get the instruction list after completing walking the graph,
293: * that is, after calling visit.
294: *
295: * @return A reference to the bytecode generated by walking
296: * the control flow graph.
297: */
298: public InstructionList getInstructionList() {
299: // maps vertices with gotos to their effective target vertices
300: Iterator k = gotoFixMeUps.keySet().iterator();
301: while (k.hasNext()) {
302: CodeVertex v = (CodeVertex) k.next();
303: // maps vertices to the handles on their connecting instructions
304: BranchHandle bh = (BranchHandle) endHandles.get(v);
305: // gets the handle on the first instruction in the target vertex
306: InstructionHandle target = (InstructionHandle) startHandles
307: .get(gotoFixMeUps.get(v));
308: bh.setTarget(target); // sets the goto target
309: }
310: // dumpIList("BytecodeCollector.getInstructions, before update");
311: ilist.update();
312: // dumpIList("BytecodeCollector.getInstructions, after update");
313: return ilist;
314: }
315:
316: /**
317: * Given an array of exception handler descriptions, return an
318: * array of CodeExceptionGen. The array may be empty.
319: *
320: * XXX This method has not been thoroughly tested.
321: *
322: * @param cd Array of exception handler descriptions in terms of vertices
323: * @return Array of BCEL exception handler structures.
324: */
325: public CodeExceptionGen[] getCEGs(CatchData[] cd) {
326: CodeExceptionGen[] ceg;
327: if (cd == null) {
328: ceg = new CodeExceptionGen[0];
329: } else {
330: ceg = new CodeExceptionGen[cd.length];
331: for (int i = 0; i < cd.length; i++) {
332: InstructionHandle start = (InstructionHandle) startHandles
333: .get(cd[i].tryStart);
334: InstructionHandle end = (InstructionHandle) startHandles
335: .get(cd[i].tryEnd);
336: InstructionHandle handler = (InstructionHandle) startHandles
337: .get(cd[i].handlerPC);
338: if (start == null || end == null || handler == null) {
339: System.out
340: .println("BytecodeCollector.getCEGs: INTERNAL ERROR - null handler\n"
341: + " CatchData["
342: + i
343: + "] start: "
344: + cd[i].tryStart
345: + " end: "
346: + cd[i].tryEnd
347: + " handler at: " + cd[i].handlerPC);
348: }
349: ceg[i] = new CodeExceptionGen(start, end, handler,
350: cd[i].exception);
351: }
352: }
353: return ceg;
354: }
355: }
|