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