View Javadoc
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