1 /* BytecodeCollector.java */
2
3 package org.quilt.cl;
4
5 import java.util.HashMap;
6 import java.util.HashSet;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.Vector;
12
13 import org.apache.bcel.classfile.*;
14 import org.apache.bcel.generic.*;
15
16 import org.quilt.graph.*;
17
18 /***
19 * Collects the bytecode for a method while walking the method
20 * control flow graph.
21 *
22 * Inspired by Quilt 0.5's BytecodeLayout,
23 * @author <a href="mailto:ddp@apache.org">David Dixon-Peugh</a> and the
24 * rest of the Quilt development team.
25 *
26 * However, errors and omissions in this version are entirely due to
27 * @author <a href="mailto:jddixon@users.sourceforge.net">Jim Dixon</a>
28 */
29
30 public class BytecodeCollector implements org.quilt.graph.Visitor {
31
32 private ControlFlowGraph graph = null;
33
34 private Map startHandles = null;
35 private Map endHandles = null;
36 private Map gotoFixMeUps = null;
37 private InstructionList ilist = null;
38
39 /*** No-arg constructor. */
40 public BytecodeCollector() { }
41
42 /***
43 * Action taken upon beginning to visit the graph.
44 * @param g Directed graph to be visited.
45 */
46 public void discoverGraph( Directed g ) {
47 graph = (ControlFlowGraph) g;
48 ilist = graph.getInstructionList();
49 startHandles = graph.getStartHandles();
50 endHandles = graph.getEndHandles();
51 gotoFixMeUps = graph.getGotoFixMeUps();
52 }
53 /***
54 * Action taken upon beginning to visit a graph vertex. Unless
55 * this is an Entry or Exit vertex, save the starting position in
56 * the bytecode to give a offset for edges with this vertex as target.
57 * Save the end instruction in the bytecode so that we can insert any
58 * connector code their later, in finishVertex.
59 *
60 * Entry and Exit vertices carry no bytecode and never have a
61 * connecting instruction, so they can be ignored when collecting
62 * code.
63 *
64 * @param v The vertex; carries a block of code and a final
65 * instruction in its Connector.
66 */
67 public void discoverVertex(Vertex vtx) {
68 if (vtx == null) {
69 throw new IllegalArgumentException ("null vertex");
70 }
71 // no code in Entry or Exit vertices
72 if (! (vtx instanceof Entry || vtx instanceof Exit) ) {
73 CodeVertex v = (CodeVertex) vtx;
74 InstructionList vIList = v.getInstructionList();
75 InstructionHandle vStart = null;
76
77 if (vIList.size() == 0) {
78 vIList = new InstructionList ( new NOP() );
79 }
80 vStart = ilist.append(vIList); // consumes vIList
81 if (vStart == null) {
82 System.out.println("discoverVertex INTERNAL ERROR: vertex "
83 + v + " has null position in bytecode\n");
84 }
85 // DEBUG
86 // System.out.println("discoverVertex " + v + " - adding vStart");
87 if (vStart == null) {
88 System.out.println(" ** vStart is null **");
89 }
90 // END
91 startHandles.put(v, vStart); // v a CodeVertex
92
93 Instruction inst = v.getConnInst();
94 if (inst != null) {
95 // CONSOLIDATE THESE ////////////////////////////////
96 if (inst instanceof GotoInstruction) {
97 BranchHandle bh = ilist.append ((GotoInstruction)inst);
98 endHandles.put (v, bh);
99 } else if (inst instanceof IfInstruction) {
100 BranchHandle bh = ilist.append((IfInstruction) inst);
101 endHandles.put (v, bh);
102 } else if ( inst instanceof JsrInstruction ) {
103 BranchHandle bh = ilist.append((JsrInstruction) inst);
104 endHandles.put (v, bh);
105 } else if ( inst instanceof Select) {
106 BranchHandle bh = ilist.append((Select) inst);
107 endHandles.put (v, bh );
108 } else if ( (inst instanceof ExceptionThrower)
109 || (inst instanceof ReturnInstruction)
110 || (inst instanceof RET)
111 || (inst instanceof InvokeInstruction) ) {
112 InstructionHandle ih = ilist.append(inst);
113 endHandles.put (v, ih );
114 } else {
115 ilist.append(inst);
116 }
117 }
118 }
119 }
120
121 /***
122 * Action taken upon first encountering an edge.
123 * @param e A FlowControlEdge, a weighted, directed edge.
124 */
125 public void discoverEdge( Edge e ) {
126 }
127
128 /***
129 * Action taken before departing an edge.
130 *
131 * @param e The control flow graph edge.
132 */
133 public void finishEdge( Edge e ) {
134 }
135
136 /***
137 * Where the immediate target of an edge is an Entry or Exit,
138 * determine the ultimate target. That is, follow preferred
139 * edges until a code Vertex (neither Entry nor Exit) is found
140 * and return that. XXX Should throw exception if target becomes
141 * null.
142 *
143 * @param e Edge we are searching along.
144 * @return Ultimate target of the edge.
145 */
146 private CodeVertex getEffectiveTarget (final Edge e) {
147 Vertex target = e.getTarget();
148 // DEBUG
149 //System.out.println (
150 // "getEffectiveTarget: initially target is " + target);
151 // END
152 while (target != null
153 && (target instanceof Entry || target instanceof Exit) ) {
154 // DEBUG
155 // System.out.print (" replacing " + target + " with");
156 // END
157
158 target = target.getTarget();
159
160 //System.out.println (" " + target ); // DEBUG too ;-)
161 }
162 // DEBUG
163 // System.out.println (" returning: " + target);
164 // END
165 return (CodeVertex) target;
166 }
167 /***
168 * Action taken before leaving a vertex in the graph. The
169 * connection instruction, if any, is fixed up by setting
170 * its target(s).
171 *
172 * If the connection instruction is a goto, which might jump
173 * into another graph, fixup is delayed until the user asks
174 * for the instruction list, when the processing of all graphs
175 * has been completed.
176 *
177 * @see CodeVertex.getInstructionList
178 * @param v The vertex, either an Entry or Exit vertex, or a
179 * code vertex.
180 */
181 public void finishVertex( Vertex vtx ) {
182 if (vtx instanceof CodeVertex) {
183 CodeVertex v = (CodeVertex) vtx;
184 Instruction inst = v.getConnInst();
185 if (inst != null) {
186 if ( inst instanceof GotoInstruction ) {
187 // // DEBUG
188 // Connector conn = v.getConnector();
189 // System.out.println("GotoInstruction in vertex " + v);
190 // if (conn instanceof UnaryConnector) {
191 // System.out.println(" has unary connector!!");
192 // } else if (conn instanceof BinaryConnector) {
193 // System.out.println(" has binary connector");
194 // System.out.println (" 'other' edge: "
195 // + ((BinaryConnector)conn).getOtherEdge());
196 // }
197 // // END
198 CodeVertex effTarget = getEffectiveTarget(
199 ((BinaryConnector)v.getConnector()).getOtherEdge());
200 InstructionHandle target
201 = (InstructionHandle) startHandles.get(effTarget);
202 gotoFixMeUps.put (v, effTarget); // delayed fixup
203
204 } else if ( inst instanceof IfInstruction ) {
205 Edge workingEdge
206 = ((BinaryConnector)v.getConnector()).getOtherEdge();
207 InstructionHandle target
208 = (InstructionHandle) startHandles.get(
209 getEffectiveTarget(workingEdge));
210 BranchHandle bh = (BranchHandle) endHandles.get(v);
211 bh.setTarget(target);
212
213 } else if ( inst instanceof JsrInstruction ) {
214 InstructionHandle target
215 = (InstructionHandle) startHandles.get(
216 getEffectiveTarget(v.getEdge()));
217 BranchHandle bh = (BranchHandle) endHandles.get(v);
218 bh.setTarget(target);
219
220 } else if (inst instanceof Select) {
221 // has a complex connector - deal with default edge
222 InstructionHandle target
223 = (InstructionHandle) startHandles.get(
224 getEffectiveTarget(v.getEdge()));
225 // DEBUG
226 // if (target == null) {
227 // System.out.println(
228 // "BytecodeCollector.finishVertex "
229 // + v + " INTERNAL ERROR: \n"
230 // + " " + inst + " has null default target");
231 // }
232 // END
233 BranchHandle bh = (BranchHandle) endHandles.get(v);
234 bh.setTarget(target);
235
236 // now take care of the other edges
237 ComplexConnector conn = (ComplexConnector)v.getConnector();
238 int edgeCount = conn.size();
239 InstructionHandle[] targets = new InstructionHandle[edgeCount];
240 for (int i = 0; i < edgeCount; i++) {
241 target = (InstructionHandle) startHandles.get(
242 getEffectiveTarget(conn.getEdge(i)));
243 // DEBUG
244 if (target == null) {
245 System.out.println(
246 "BytecodeCollector.finishVertex "
247 + v + " INTERNAL ERROR: \n"
248 + " " + inst + " has null target " + i);
249 }
250 // END
251 ((Select)bh.getInstruction()).setTarget(i, target);
252 }
253 }
254 }
255 }
256 }
257
258 /***
259 * Dump the instruction list. Debug method.
260 */
261 private void dumpIList(String where) {
262 System.out.println("BytecodeCollector."
263 + where + " instruction list:");
264 int i = 0;
265 for (InstructionHandle ih = ilist.getStart(); ih != null;
266 ih = ih.getNext() ) {
267 System.out.println( " " + (i++) + " " + ih.getInstruction());
268 }
269 }
270 /***
271 * Finish the graph.
272 *
273 * @param g The control flow graph - ignored.
274 */
275 public void finishGraph( Directed g ) {
276
277 }
278
279 /***
280 * Get the instruction list after completing walking the graph,
281 * that is, after calling visit.
282 *
283 * @return A reference to the bytecode generated by walking
284 * the control flow graph.
285 */
286 public InstructionList getInstructionList() {
287 // maps vertices with gotos to their effective target vertices
288 Iterator k = gotoFixMeUps.keySet().iterator();
289 while (k.hasNext()) {
290 CodeVertex v = (CodeVertex) k.next();
291 // maps vertices to the handles on their connecting instructions
292 BranchHandle bh = (BranchHandle) endHandles.get(v);
293 // gets the handle on the first instruction in the target vertex
294 InstructionHandle target
295 = (InstructionHandle) startHandles.get(
296 gotoFixMeUps.get (v));
297 bh.setTarget(target); // sets the goto target
298 }
299 // dumpIList("BytecodeCollector.getInstructions, before update");
300 ilist.update();
301 // dumpIList("BytecodeCollector.getInstructions, after update");
302 return ilist;
303 }
304
305 /***
306 * Given an array of exception handler descriptions, return an
307 * array of CodeExceptionGen. The array may be empty.
308 *
309 * XXX This method has not been thoroughly tested.
310 *
311 * @param cd Array of exception handler descriptions in terms of vertices
312 * @return Array of BCEL exception handler structures.
313 */
314 public CodeExceptionGen[] getCEGs ( CatchData[] cd) {
315 CodeExceptionGen [] ceg;
316 if (cd == null) {
317 ceg = new CodeExceptionGen[ 0 ];
318 } else {
319 ceg = new CodeExceptionGen[ cd.length ];
320 for (int i = 0; i < cd.length; i++) {
321 InstructionHandle start
322 = (InstructionHandle) startHandles.get(cd[i].tryStart);
323 InstructionHandle end
324 = (InstructionHandle) startHandles.get(cd[i].tryEnd);
325 InstructionHandle handler
326 = (InstructionHandle) startHandles.get(cd[i].handlerPC);
327 if ( start == null || end == null || handler == null) {
328 System.out.println (
329 "BytecodeCollector.getCEGs: INTERNAL ERROR - null handler\n"
330 + " CatchData[" + i + "] start: " + cd[i].tryStart
331 + " end: " + cd[i].tryEnd + " handler at: "
332 + cd[i].handlerPC);
333 }
334 ceg[i] = new CodeExceptionGen( start, end, handler,
335 cd[i].exception );
336 }
337 }
338 return ceg;
339 }
340 }
This page was automatically generated by Maven