• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- 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  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */
16 
17 package javassist.bytecode.analysis;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 
22 import javassist.CtClass;
23 import javassist.CtMethod;
24 import javassist.bytecode.BadBytecode;
25 import javassist.bytecode.MethodInfo;
26 import javassist.bytecode.stackmap.BasicBlock;
27 
28 /**
29  * Represents the control flow graph of a given method.
30  *
31  * <p>To obtain the control flow graph, do the following:</p>
32  *
33  * <pre>CtMethod m = ...
34  * ControlFlow cf = new ControlFlow(m);
35  * Block[] blocks = cf.basicBlocks();
36  * </pre>
37  *
38  * <p><code>blocks</code> is an array of basic blocks in
39  * that method body.</p>
40  *
41  * @see javassist.CtMethod
42  * @see Block
43  * @see Frame
44  * @see Analyzer
45  * @author Shigeru Chiba
46  * @since 3.16
47  */
48 public class ControlFlow {
49     private CtClass clazz;
50     private MethodInfo methodInfo;
51     private Block[] basicBlocks;
52     private Frame[] frames;
53 
54     /**
55      * Constructs a control-flow analyzer for the given method.
56      */
ControlFlow(CtMethod method)57     public ControlFlow(CtMethod method) throws BadBytecode {
58         this(method.getDeclaringClass(), method.getMethodInfo2());
59     }
60 
61     /**
62      * Constructs a control-flow analyzer.
63      */
ControlFlow(CtClass ctclazz, MethodInfo minfo)64     public ControlFlow(CtClass ctclazz, MethodInfo minfo) throws BadBytecode {
65         clazz = ctclazz;
66         methodInfo = minfo;
67         frames = null;
68         basicBlocks = (Block[])new BasicBlock.Maker() {
69             @Override
70             protected BasicBlock makeBlock(int pos) {
71                 return new Block(pos, methodInfo);
72             }
73             @Override
74             protected BasicBlock[] makeArray(int size) {
75                 return new Block[size];
76             }
77         }.make(minfo);
78         if (basicBlocks == null)
79             basicBlocks = new Block[0];
80         int size = basicBlocks.length;
81         int[] counters = new int[size];
82         for (int i = 0; i < size; i++) {
83             Block b = basicBlocks[i];
84             b.index = i;
85             b.entrances = new Block[b.incomings()];
86             counters[i] = 0;
87         }
88 
89         for (int i = 0; i < size; i++) {
90             Block b = basicBlocks[i];
91             for (int k = 0; k < b.exits(); k++) {
92                 Block e = b.exit(k);
93                 e.entrances[counters[e.index]++] = b;
94             }
95 
96             ControlFlow.Catcher[] catchers = b.catchers();
97             for (int k = 0; k < catchers.length; k++) {
98                 Block catchBlock = catchers[k].node;
99                 catchBlock.entrances[counters[catchBlock.index]++] = b;
100             }
101         }
102     }
103 
104     /**
105      * Returns all the basic blocks in the method body.
106      *
107      * @return an array of basic blocks, the array has length 0 if
108      * the method doesn't have code.
109      */
basicBlocks()110     public Block[] basicBlocks() {
111         return basicBlocks;
112     }
113 
114     /**
115      * Returns the types of the local variables and stack frame entries
116      * available at the given position.  If the byte at the position is
117      * not the first byte of an instruction, then this method returns
118      * null.
119      *
120      * @param pos       the position.
121      */
frameAt(int pos)122     public Frame frameAt(int pos) throws BadBytecode {
123         if (frames == null)
124             frames = new Analyzer().analyze(clazz, methodInfo);
125 
126         return frames[pos];
127     }
128 
129     /**
130      * Constructs a dominator tree.  This method returns an array of
131      * the tree nodes.  The first element of the array is the root
132      * of the tree.
133      *
134      * <p> The order of the elements is the same as that
135      * of the elements in the <code>Block</code> array returned
136      * by the <code>basicBlocks</code>
137      * method.  If a <code>Block</code> object is at the i-th position
138      * in the <code>Block</code> array, then
139      * the <code>Node</code> object referring to that
140      * <code>Block</code> object is at the i-th position in the
141      * array returned by this method.
142      * For every array element <code>node</code>, its index in the
143      * array is equivalent to <code>node.block().index()</code>.
144      *
145      * @return an array of the tree nodes, or null if the method doesn't have code.
146      * @see Node#block()
147      * @see Block#index()
148      */
dominatorTree()149     public Node[] dominatorTree() {
150         int size = basicBlocks.length;
151         if (size == 0)
152             return null;
153 
154         Node[] nodes = new Node[size];
155         boolean[] visited = new boolean[size];
156         int[] distance = new int[size];
157         for (int i = 0; i < size; i++) {
158             nodes[i] = new Node(basicBlocks[i]);
159             visited[i] = false;
160         }
161 
162         Access access = new Access(nodes) {
163             @Override
164             BasicBlock[] exits(Node n) { return n.block.getExit(); }
165             @Override
166             BasicBlock[] entrances(Node n) { return n.block.entrances; }
167         };
168         nodes[0].makeDepth1stTree(null, visited, 0, distance, access);
169         do {
170             for (int i = 0; i < size; i++)
171                 visited[i] = false;
172         } while (nodes[0].makeDominatorTree(visited, distance, access));
173         Node.setChildren(nodes);
174         return nodes;
175     }
176 
177     /**
178      * Constructs a post dominator tree.  This method returns an array of
179      * the tree nodes.  Note that the tree has multiple roots.
180      * The parent of the root nodes is null.
181      *
182      * <p> The order of the elements is the same as that
183      * of the elements in the <code>Block</code> array returned
184      * by the <code>basicBlocks</code>
185      * method.  If a <code>Block</code> object is at the i-th position
186      * in the <code>Block</code> array, then
187      * the <code>Node</code> object referring to that
188      * <code>Block</code> object is at the i-th position in the
189      * array returned by this method.
190      * For every array element <code>node</code>, its index in the
191      * array is equivalent to <code>node.block().index()</code>.
192      *
193      * @return an array of the tree nodes, or null if the method doesn't have code.
194      * @see Node#block()
195      * @see Block#index()
196      */
postDominatorTree()197     public Node[] postDominatorTree() {
198         int size = basicBlocks.length;
199         if (size == 0)
200             return null;
201 
202         Node[] nodes = new Node[size];
203         boolean[] visited = new boolean[size];
204         int[] distance = new int[size];
205         for (int i = 0; i < size; i++) {
206             nodes[i] = new Node(basicBlocks[i]);
207             visited[i] = false;
208         }
209 
210         Access access = new Access(nodes) {
211             @Override
212             BasicBlock[] exits(Node n) { return n.block.entrances; }
213             @Override
214             BasicBlock[] entrances(Node n) { return n.block.getExit(); }
215         };
216 
217         int counter = 0;
218         for (int i = 0; i < size; i++)
219             if (nodes[i].block.exits() == 0)
220                 counter = nodes[i].makeDepth1stTree(null, visited, counter, distance, access);
221 
222         boolean changed;
223         do {
224             for (int i = 0; i < size; i++)
225                 visited[i] = false;
226 
227             changed = false;
228             for (int i = 0; i < size; i++)
229                 if (nodes[i].block.exits() == 0)
230                     if (nodes[i].makeDominatorTree(visited, distance, access))
231                         changed = true;
232         } while (changed);
233 
234         Node.setChildren(nodes);
235         return nodes;
236     }
237 
238     /**
239      * Basic block.
240      * It is a sequence of contiguous instructions that do not contain
241      * jump/branch instructions except the last one.
242      * Since Java6 or later does not allow <code>JSR</code>,
243      * we deal with <code>JSR</code> as a non-branch instruction.
244      */
245     public static class Block extends BasicBlock {
246         /**
247          * A field that can be freely used for storing extra data.
248          * A client program of this control-flow analyzer can append
249          * an additional attribute to a <code>Block</code> object.
250          * The Javassist library never accesses this field.
251          */
252         public Object clientData = null;
253 
254         int index;
255         MethodInfo method;
256         Block[] entrances;
257 
Block(int pos, MethodInfo minfo)258         Block(int pos, MethodInfo minfo) {
259             super(pos);
260             method = minfo;
261         }
262 
263         @Override
toString2(StringBuffer sbuf)264         protected void toString2(StringBuffer sbuf) {
265             super.toString2(sbuf);
266             sbuf.append(", incoming{");
267             for (int i = 0; i < entrances.length; i++)
268                     sbuf.append(entrances[i].position).append(", ");
269 
270             sbuf.append("}");
271         }
272 
getExit()273         BasicBlock[] getExit() { return exit; }
274 
275         /**
276          * Returns the position of this block in the array of
277          * basic blocks that the <code>basicBlocks</code> method
278          * returns.
279          *
280          * @see #basicBlocks()
281          */
index()282         public int index() { return index; }
283 
284         /**
285          * Returns the position of the first instruction
286          * in this block.
287          */
position()288         public int position() { return position; }
289 
290         /**
291          * Returns the length of this block.
292          */
length()293         public int length() { return length; }
294 
295         /**
296          * Returns the number of the control paths entering this block.
297          */
incomings()298         public int incomings() { return incoming; }
299 
300         /**
301          * Returns the block that the control may jump into this block from.
302          */
incoming(int n)303         public Block incoming(int n) {
304             return entrances[n];
305         }
306 
307         /**
308          * Return the number of the blocks that may be executed
309          * after this block.
310          */
exits()311         public int exits() { return exit == null ? 0 : exit.length; }
312 
313         /**
314          * Returns the n-th block that may be executed after this
315          * block.
316          *
317          * @param n     an index in the array of exit blocks.
318          */
exit(int n)319         public Block exit(int n) { return (Block)exit[n]; }
320 
321         /**
322          * Returns catch clauses that will catch an exception thrown
323          * in this block.
324          */
catchers()325         public Catcher[] catchers() {
326             List<Catcher> catchers = new ArrayList<Catcher>();
327             BasicBlock.Catch c = toCatch;
328             while (c != null) {
329                 catchers.add(new Catcher(c));
330                 c = c.next;
331             }
332 
333             return catchers.toArray(new Catcher[catchers.size()]);
334         }
335     }
336 
337     static abstract class Access {
338         Node[] all;
Access(Node[] nodes)339         Access(Node[] nodes) { all = nodes; }
node(BasicBlock b)340         Node node(BasicBlock b) { return all[((Block)b).index]; }
exits(Node n)341         abstract BasicBlock[] exits(Node n);
entrances(Node n)342         abstract BasicBlock[] entrances(Node n);
343     }
344 
345     /**
346      * A node of (post) dominator trees.
347      */
348     public static class Node {
349         private Block block;
350         private Node parent;
351         private Node[] children;
352 
Node(Block b)353         Node(Block b) {
354             block = b;
355             parent = null;
356         }
357 
358         /**
359          * Returns a <code>String</code> representation.
360          */
361         @Override
toString()362         public String toString() {
363             StringBuffer sbuf = new StringBuffer();
364             sbuf.append("Node[pos=").append(block().position());
365             sbuf.append(", parent=");
366             sbuf.append(parent == null ? "*" : Integer.toString(parent.block().position()));
367             sbuf.append(", children{");
368             for (int i = 0; i < children.length; i++)
369                 sbuf.append(children[i].block().position()).append(", ");
370 
371             sbuf.append("}]");
372             return sbuf.toString();
373         }
374 
375         /**
376          * Returns the basic block indicated by this node.
377          */
block()378         public Block block() { return block; }
379 
380         /**
381          * Returns the parent of this node.
382          */
parent()383         public Node parent() { return parent; }
384 
385         /**
386          * Returns the number of the children of this node.
387          */
children()388         public int children() { return children.length; }
389 
390         /**
391          * Returns the n-th child of this node.
392          *
393          * @param n     an index in the array of children.
394          */
child(int n)395         public Node child(int n) { return children[n]; }
396 
397         /*
398          * After executing this method, distance[] represents the post order of the tree nodes.
399          * It also represents distances from the root; a bigger number represents a shorter
400          * distance.  parent is set to its parent in the depth first spanning tree.
401          */
makeDepth1stTree(Node caller, boolean[] visited, int counter, int[] distance, Access access)402         int makeDepth1stTree(Node caller, boolean[] visited, int counter, int[] distance, Access access) {
403             int index = block.index;
404             if (visited[index])
405                 return counter;
406 
407             visited[index] = true;
408             parent = caller;
409             BasicBlock[] exits = access.exits(this);
410             if (exits != null)
411                 for (int i = 0; i < exits.length; i++) {
412                     Node n = access.node(exits[i]);
413                     counter = n.makeDepth1stTree(this, visited, counter, distance, access);
414                 }
415 
416             distance[index] = counter++;
417             return counter;
418         }
419 
makeDominatorTree(boolean[] visited, int[] distance, Access access)420         boolean makeDominatorTree(boolean[] visited, int[] distance, Access access) {
421             int index = block.index;
422             if (visited[index])
423                 return false;
424 
425             visited[index] = true;
426             boolean changed = false;
427             BasicBlock[] exits = access.exits(this);
428             if (exits != null)
429                 for (int i = 0; i < exits.length; i++) {
430                     Node n = access.node(exits[i]);
431                     if (n.makeDominatorTree(visited, distance, access))
432                         changed = true;
433                 }
434 
435             BasicBlock[] entrances = access.entrances(this);
436             if (entrances != null)
437                 for (int i = 0; i < entrances.length; i++) {
438                     if (parent != null) {
439                         Node n = getAncestor(parent, access.node(entrances[i]), distance);
440                         if (n != parent) {
441                             parent = n;
442                             changed = true;
443                         }
444                     }
445                 }
446 
447             return changed;
448         }
449 
getAncestor(Node n1, Node n2, int[] distance)450         private static Node getAncestor(Node n1, Node n2, int[] distance) {
451             while (n1 != n2) {
452                 if (distance[n1.block.index] < distance[n2.block.index])
453                     n1 = n1.parent;
454                 else
455                     n2 = n2.parent;
456 
457                 if (n1 == null || n2 == null)
458                     return null;
459             }
460 
461             return n1;
462         }
463 
setChildren(Node[] all)464         private static void setChildren(Node[] all) {
465             int size = all.length;
466             int[] nchildren = new int[size];
467             for (int i = 0; i < size; i++)
468                 nchildren[i] = 0;
469 
470             for (int i = 0; i < size; i++) {
471                 Node p = all[i].parent;
472                 if (p != null)
473                     nchildren[p.block.index]++;
474             }
475 
476             for (int i = 0; i < size; i++)
477                 all[i].children = new Node[nchildren[i]];
478 
479             for (int i = 0; i < size; i++)
480                 nchildren[i] = 0;
481 
482             for (int i = 0; i < size; i++) {
483                 Node n = all[i];
484                 Node p = n.parent;
485                 if (p != null)
486                     p.children[nchildren[p.block.index]++] = n;
487             }
488         }
489     }
490 
491     /**
492      * Represents a catch clause.
493      */
494     public static class Catcher {
495         private Block node;
496         private int typeIndex;
497 
Catcher(BasicBlock.Catch c)498         Catcher(BasicBlock.Catch c) {
499             node = (Block)c.body;
500             typeIndex = c.typeIndex;
501         }
502 
503         /**
504          * Returns the first block of the catch clause.
505          */
block()506         public Block block() { return node; }
507 
508         /**
509          * Returns the name of the exception type that
510          * this catch clause catches.
511          */
type()512         public String type() {
513             if (typeIndex == 0)
514                 return "java.lang.Throwable";
515             else
516                 return node.method.getConstPool().getClassInfo(typeIndex);
517         }
518     }
519 }
520