• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.bytecode.stackmap;
17 
18 import javassist.bytecode.*;
19 import java.util.HashMap;
20 import java.util.ArrayList;
21 
22 /**
23  * A basic block is a sequence of bytecode that does not contain jump/branch
24  * instructions except at the last bytecode.
25  * Since Java6 or later does not allow JSR, this class deals with JSR as a
26  * non-branch instruction.
27  */
28 public class BasicBlock {
29     public int position, length;
30     public int incoming;        // the number of incoming branches.
31     public BasicBlock[] exit;   // null if the block is a leaf.
32     public boolean stop;        // true if the block ends with an unconditional jump.
33     public Catch toCatch;
34 
BasicBlock(int pos)35     protected BasicBlock(int pos) {
36         position = pos;
37         length = 0;
38         incoming = 0;
39     }
40 
find(BasicBlock[] blocks, int pos)41     public static BasicBlock find(BasicBlock[] blocks, int pos)
42         throws BadBytecode
43     {
44         for (int i = 0; i < blocks.length; i++) {
45             int iPos = blocks[i].position;
46             if (iPos <= pos && pos < iPos + blocks[i].length)
47                 return blocks[i];
48         }
49 
50         throw new BadBytecode("no basic block at " + pos);
51     }
52 
53     public static class Catch {
54         Catch next;
55         BasicBlock body;
56         int typeIndex;
Catch(BasicBlock b, int i, Catch c)57         Catch(BasicBlock b, int i, Catch c) {
58             body = b;
59             typeIndex = i;
60             next = c;
61         }
62     }
63 
toString()64     public String toString() {
65         StringBuffer sbuf = new StringBuffer();
66         String cname = this.getClass().getName();
67         int i = cname.lastIndexOf('.');
68         sbuf.append(i < 0 ? cname : cname.substring(i + 1));
69         sbuf.append("[");
70         toString2(sbuf);
71         sbuf.append("]");
72         return sbuf.toString();
73     }
74 
75     protected void toString2(StringBuffer sbuf) {
76         sbuf.append("pos=").append(position).append(", len=")
77             .append(length).append(", in=").append(incoming)
78             .append(", exit{");
79         if (exit != null) {
80             for (int i = 0; i < exit.length; i++)
81                 sbuf.append(exit[i].position).append(", ");
82         }
83 
84         sbuf.append("}, {");
85         Catch th = toCatch;
86         while (th != null) {
87             sbuf.append("(").append(th.body.position).append(", ")
88                 .append(th.typeIndex).append("), ");
89             th = th.next;
90         }
91 
92         sbuf.append("}");
93     }
94 
95     static class Mark implements Comparable {
96         int position;
97         BasicBlock block;
98         BasicBlock[] jump;
99         boolean alwaysJmp;     // true if a unconditional branch.
100         int size;       // 0 unless the mark indicates RETURN etc.
101         Catch catcher;
102 
103         Mark(int p) {
104             position = p;
105             block = null;
106             jump = null;
107             alwaysJmp = false;
108             size = 0;
109             catcher = null;
110         }
111 
112         public int compareTo(Object obj) {
113             if (obj instanceof Mark) {
114                 int pos = ((Mark)obj).position;
115                 return position - pos;
116             }
117 
118             return -1;
119         }
120 
121         void setJump(BasicBlock[] bb, int s, boolean always) {
122             jump = bb;
123             size = s;
124             alwaysJmp = always;
125         }
126     }
127 
128     public static class Maker {
129         /* Override these two methods if a subclass of BasicBlock must be
130          * instantiated.
131          */
132         protected BasicBlock makeBlock(int pos) {
133             return new BasicBlock(pos);
134         }
135 
136         protected BasicBlock[] makeArray(int size) {
137             return new BasicBlock[size];
138         }
139 
140         private BasicBlock[] makeArray(BasicBlock b) {
141             BasicBlock[] array = makeArray(1);
142             array[0] = b;
143             return array;
144         }
145 
146         private BasicBlock[] makeArray(BasicBlock b1, BasicBlock b2) {
147             BasicBlock[] array = makeArray(2);
148             array[0] = b1;
149             array[1] = b2;
150             return array;
151         }
152 
153         public BasicBlock[] make(MethodInfo minfo) throws BadBytecode {
154             CodeAttribute ca = minfo.getCodeAttribute();
155             if (ca == null)
156                 return null;
157 
158             CodeIterator ci = ca.iterator();
159             return make(ci, 0, ci.getCodeLength(), ca.getExceptionTable());
160         }
161 
162         public BasicBlock[] make(CodeIterator ci, int begin, int end,
163                                  ExceptionTable et)
164             throws BadBytecode
165         {
166             HashMap marks = makeMarks(ci, begin, end, et);
167             BasicBlock[] bb = makeBlocks(marks);
168             addCatchers(bb, et);
169             return bb;
170         }
171 
172         /* Branch target
173          */
174         private Mark makeMark(HashMap table, int pos) {
175             return makeMark0(table, pos, true, true);
176         }
177 
178         /* Branch instruction.
179          * size > 0
180          */
181         private Mark makeMark(HashMap table, int pos, BasicBlock[] jump,
182                               int size, boolean always) {
183             Mark m = makeMark0(table, pos, false, false);
184             m.setJump(jump, size, always);
185             return m;
186         }
187 
188         private Mark makeMark0(HashMap table, int pos,
189                                boolean isBlockBegin, boolean isTarget) {
190             Integer p = new Integer(pos);
191             Mark m = (Mark)table.get(p);
192             if (m == null) {
193                 m = new Mark(pos);
194                 table.put(p, m);
195             }
196 
197             if (isBlockBegin) {
198                 if (m.block == null)
199                     m.block = makeBlock(pos);
200 
201                 if (isTarget)
202                     m.block.incoming++;
203             }
204 
205             return m;
206         }
207 
208         private HashMap makeMarks(CodeIterator ci, int begin, int end,
209                                   ExceptionTable et)
210             throws BadBytecode
211         {
212             ci.begin();
213             ci.move(begin);
214             HashMap marks = new HashMap();
215             while (ci.hasNext()) {
216                 int index = ci.next();
217                 if (index >= end)
218                     break;
219 
220                 int op = ci.byteAt(index);
221                 if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE)
222                         || op == Opcode.IFNULL || op == Opcode.IFNONNULL) {
223                     Mark to = makeMark(marks, index + ci.s16bitAt(index + 1));
224                     Mark next = makeMark(marks, index + 3);
225                     makeMark(marks, index, makeArray(to.block, next.block), 3, false);
226                 }
227                 else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH)
228                     switch (op) {
229                     case Opcode.GOTO :
230                         makeGoto(marks, index, index + ci.s16bitAt(index + 1), 3);
231                         break;
232                     case Opcode.JSR :
233                         makeJsr(marks, index, index + ci.s16bitAt(index + 1), 3);
234                         break;
235                     case Opcode.RET :
236                         makeMark(marks, index, null, 2, true);
237                         break;
238                     case Opcode.TABLESWITCH : {
239                         int pos = (index & ~3) + 4;
240                         int low = ci.s32bitAt(pos + 4);
241                         int high = ci.s32bitAt(pos + 8);
242                         int ncases = high - low + 1;
243                         BasicBlock[] to = makeArray(ncases + 1);
244                         to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block;   // default branch target
245                         int p = pos + 12;
246                         int n = p + ncases * 4;
247                         int k = 1;
248                         while (p < n) {
249                             to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block;
250                             p += 4;
251                         }
252                         makeMark(marks, index, to, n - index, true);
253                         break; }
254                     case Opcode.LOOKUPSWITCH : {
255                         int pos = (index & ~3) + 4;
256                         int ncases = ci.s32bitAt(pos + 4);
257                         BasicBlock[] to = makeArray(ncases + 1);
258                         to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block;   // default branch target
259                         int p = pos + 8 + 4;
260                         int n = p + ncases * 8 - 4;
261                         int k = 1;
262                         while (p < n) {
263                             to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block;
264                             p += 8;
265                         }
266                         makeMark(marks, index, to, n - index, true);
267                         break; }
268                     }
269                 else if ((Opcode.IRETURN <= op && op <= Opcode.RETURN) || op == Opcode.ATHROW)
270                     makeMark(marks, index, null, 1, true);
271                 else if (op == Opcode.GOTO_W)
272                     makeGoto(marks, index, index + ci.s32bitAt(index + 1), 5);
273                 else if (op == Opcode.JSR_W)
274                     makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5);
275                 else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET)
276                     makeMark(marks, index, null, 1, true);
277             }
278 
279             if (et != null) {
280                 int i = et.size();
281                 while (--i >= 0) {
282                     makeMark0(marks, et.startPc(i), true, false);
283                     makeMark(marks, et.handlerPc(i));
284                 }
285             }
286 
287             return marks;
288         }
289 
290         private void makeGoto(HashMap marks, int pos, int target, int size) {
291             Mark to = makeMark(marks, target);
292             BasicBlock[] jumps = makeArray(to.block);
293             makeMark(marks, pos, jumps, size, true);
294         }
295 
296         /**
297          * We ignore JSR since Java 6 or later does not allow it.
298          */
299         protected void makeJsr(HashMap marks, int pos, int target, int size) {
300         /*
301             Mark to = makeMark(marks, target);
302             Mark next = makeMark(marks, pos + size);
303             BasicBlock[] jumps = makeArray(to.block, next.block);
304             makeMark(marks, pos, jumps, size, false);
305         */
306         }
307 
308         private BasicBlock[] makeBlocks(HashMap markTable) {
309             Mark[] marks = (Mark[])markTable.values()
310                                             .toArray(new Mark[markTable.size()]);
311             java.util.Arrays.sort(marks);
312             ArrayList blocks = new ArrayList();
313             int i = 0;
314             BasicBlock prev;
315             if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null)
316                 prev = getBBlock(marks[i++]);
317             else
318                 prev = makeBlock(0);
319 
320             blocks.add(prev);
321             while (i < marks.length) {
322                 Mark m = marks[i++];
323                 BasicBlock bb = getBBlock(m);
324                 if (bb == null) {
325                     // the mark indicates a branch instruction
326                     if (prev.length > 0) {
327                         // the previous mark already has exits.
328                         prev = makeBlock(prev.position + prev.length);
329                         blocks.add(prev);
330                     }
331 
332                     prev.length = m.position + m.size - prev.position;
333                     prev.exit = m.jump;
334                     prev.stop = m.alwaysJmp;
335                 }
336                 else {
337                     // the mark indicates a branch target
338                     if (prev.length == 0) {
339                         prev.length = m.position - prev.position;
340                         bb.incoming++;
341                         prev.exit = makeArray(bb);
342                     }
343                     else {
344                         // the previous mark already has exits.
345                         int prevPos = prev.position;
346                         if (prevPos + prev.length < m.position) {
347                             prev = makeBlock(prevPos + prev.length);
348                             prev.length = m.position - prevPos;
349                             // the incoming flow from dead code is not counted
350                             // bb.incoming++;
351                             prev.exit = makeArray(bb);
352                         }
353                     }
354 
355                     blocks.add(bb);
356                     prev = bb;
357                 }
358             }
359 
360             return (BasicBlock[])blocks.toArray(makeArray(blocks.size()));
361         }
362 
363         private static BasicBlock getBBlock(Mark m) {
364             BasicBlock b = m.block;
365             if (b != null && m.size > 0) {
366                 b.exit = m.jump;
367                 b.length = m.size;
368                 b.stop = m.alwaysJmp;
369             }
370 
371             return b;
372         }
373 
374         private void addCatchers(BasicBlock[] blocks, ExceptionTable et)
375             throws BadBytecode
376         {
377             if (et == null)
378                 return;
379 
380             int i = et.size();
381             while (--i >= 0) {
382                 BasicBlock handler = find(blocks, et.handlerPc(i));
383                 int start = et.startPc(i);
384                 int end = et.endPc(i);
385                 int type = et.catchType(i);
386                 handler.incoming--;
387                 for (int k = 0; k < blocks.length; k++) {
388                     BasicBlock bb = blocks[k];
389                     int iPos = bb.position;
390                     if (start <= iPos && iPos < end) {
391                         bb.toCatch = new Catch(handler, type, bb.toCatch);
392                         handler.incoming++;
393                     }
394                 }
395             }
396         }
397     }
398 }
399