You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
504 lines
16 KiB
504 lines
16 KiB
/* |
|
* Javassist, a Java-bytecode translator toolkit. |
|
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. |
|
* |
|
* The contents of this file are subject to the Mozilla Public License Version |
|
* 1.1 (the "License"); you may not use this file except in compliance with |
|
* the License. Alternatively, the contents of this file may be used under |
|
* the terms of the GNU Lesser General Public License Version 2.1 or later, |
|
* or the Apache License Version 2.0. |
|
* |
|
* Software distributed under the License is distributed on an "AS IS" basis, |
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
|
* for the specific language governing rights and limitations under the |
|
* License. |
|
*/ |
|
|
|
package com.fr.third.javassist.bytecode.analysis; |
|
|
|
import java.util.ArrayList; |
|
import com.fr.third.javassist.CtClass; |
|
import com.fr.third.javassist.CtMethod; |
|
import com.fr.third.javassist.bytecode.BadBytecode; |
|
import com.fr.third.javassist.bytecode.MethodInfo; |
|
import com.fr.third.javassist.bytecode.stackmap.BasicBlock; |
|
|
|
/** |
|
* Represents the control flow graph of a given method. |
|
* |
|
* <p>To obtain the control flow graph, do the following:</p> |
|
* |
|
* <pre>CtMethod m = ... |
|
* ControlFlow cf = new ControlFlow(m); |
|
* Block[] blocks = cf.basicBlocks(); |
|
* </pre> |
|
* |
|
* <p><code>blocks</code> is an array of basic blocks in |
|
* that method body.</p> |
|
* |
|
* @see CtMethod |
|
* @see Block |
|
* @see com.fr.third.javassist.bytecode.analysis.Frame |
|
* @see com.fr.third.javassist.bytecode.analysis.Analyzer |
|
* @author Shigeru Chiba |
|
* @since 3.16 |
|
*/ |
|
public class ControlFlow { |
|
private CtClass clazz; |
|
private MethodInfo methodInfo; |
|
private Block[] basicBlocks; |
|
private com.fr.third.javassist.bytecode.analysis.Frame[] frames; |
|
|
|
/** |
|
* Constructs a control-flow analyzer for the given method. |
|
*/ |
|
public ControlFlow(CtMethod method) throws BadBytecode { |
|
this(method.getDeclaringClass(), method.getMethodInfo2()); |
|
} |
|
|
|
/** |
|
* Constructs a control-flow analyzer. |
|
*/ |
|
public ControlFlow(CtClass ctclazz, MethodInfo minfo) throws BadBytecode { |
|
clazz = ctclazz; |
|
methodInfo = minfo; |
|
frames = null; |
|
basicBlocks = (Block[])new BasicBlock.Maker() { |
|
protected BasicBlock makeBlock(int pos) { |
|
return new Block(pos, methodInfo); |
|
} |
|
protected BasicBlock[] makeArray(int size) { |
|
return new Block[size]; |
|
} |
|
}.make(minfo); |
|
int size = basicBlocks.length; |
|
int[] counters = new int[size]; |
|
for (int i = 0; i < size; i++) { |
|
Block b = basicBlocks[i]; |
|
b.index = i; |
|
b.entrances = new Block[b.incomings()]; |
|
counters[i] = 0; |
|
} |
|
|
|
for (int i = 0; i < size; i++) { |
|
Block b = basicBlocks[i]; |
|
for (int k = 0; k < b.exits(); k++) { |
|
Block e = b.exit(k); |
|
e.entrances[counters[e.index]++] = b; |
|
} |
|
|
|
ControlFlow.Catcher[] catchers = b.catchers(); |
|
for (int k = 0; k < catchers.length; k++) { |
|
Block catchBlock = catchers[k].node; |
|
catchBlock.entrances[counters[catchBlock.index]++] = b; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Returns all the basic blocks in the method body. |
|
*/ |
|
public Block[] basicBlocks() { |
|
return basicBlocks; |
|
} |
|
|
|
/** |
|
* Returns the types of the local variables and stack frame entries |
|
* available at the given position. If the byte at the position is |
|
* not the first byte of an instruction, then this method returns |
|
* null. |
|
* |
|
* @param pos the position. |
|
*/ |
|
public Frame frameAt(int pos) throws BadBytecode { |
|
if (frames == null) |
|
frames = new Analyzer().analyze(clazz, methodInfo); |
|
|
|
return frames[pos]; |
|
} |
|
|
|
/** |
|
* Constructs a dominator tree. This method returns an array of |
|
* the tree nodes. The first element of the array is the root |
|
* of the tree. |
|
* |
|
* <p> The order of the elements is the same as that |
|
* of the elements in the <code>Block</code> array returned |
|
* by the <code>basicBlocks</code> |
|
* method. If a <code>Block</code> object is at the i-th position |
|
* in the <code>Block</code> array, then |
|
* the <code>Node</code> object referring to that |
|
* <code>Block</code> object is at the i-th position in the |
|
* array returned by this method. |
|
* For every array element <code>node</code>, its index in the |
|
* array is equivalent to <code>node.block().index()</code>. |
|
* |
|
* @return an array of the tree nodes, or null if the method is abstract. |
|
* @see Node#block() |
|
* @see Block#index() |
|
*/ |
|
public Node[] dominatorTree() { |
|
int size = basicBlocks.length; |
|
if (size == 0) |
|
return null; |
|
|
|
Node[] nodes = new Node[size]; |
|
boolean[] visited = new boolean[size]; |
|
int[] distance = new int[size]; |
|
for (int i = 0; i < size; i++) { |
|
nodes[i] = new Node(basicBlocks[i]); |
|
visited[i] = false; |
|
} |
|
|
|
Access access = new Access(nodes) { |
|
BasicBlock[] exits(Node n) { return n.block.getExit(); } |
|
BasicBlock[] entrances(Node n) { return n.block.entrances; } |
|
}; |
|
nodes[0].makeDepth1stTree(null, visited, 0, distance, access); |
|
do { |
|
for (int i = 0; i < size; i++) |
|
visited[i] = false; |
|
} while (nodes[0].makeDominatorTree(visited, distance, access)); |
|
Node.setChildren(nodes); |
|
return nodes; |
|
} |
|
|
|
/** |
|
* Constructs a post dominator tree. This method returns an array of |
|
* the tree nodes. Note that the tree has multiple roots. |
|
* The parent of the root nodes is null. |
|
* |
|
* <p> The order of the elements is the same as that |
|
* of the elements in the <code>Block</code> array returned |
|
* by the <code>basicBlocks</code> |
|
* method. If a <code>Block</code> object is at the i-th position |
|
* in the <code>Block</code> array, then |
|
* the <code>Node</code> object referring to that |
|
* <code>Block</code> object is at the i-th position in the |
|
* array returned by this method. |
|
* For every array element <code>node</code>, its index in the |
|
* array is equivalent to <code>node.block().index()</code>. |
|
* |
|
* @return an array of the tree nodes, or null if the method is abstract. |
|
* @see Node#block() |
|
* @see Block#index() |
|
*/ |
|
public Node[] postDominatorTree() { |
|
int size = basicBlocks.length; |
|
if (size == 0) |
|
return null; |
|
|
|
Node[] nodes = new Node[size]; |
|
boolean[] visited = new boolean[size]; |
|
int[] distance = new int[size]; |
|
for (int i = 0; i < size; i++) { |
|
nodes[i] = new Node(basicBlocks[i]); |
|
visited[i] = false; |
|
} |
|
|
|
Access access = new Access(nodes) { |
|
BasicBlock[] exits(Node n) { return n.block.entrances; } |
|
BasicBlock[] entrances(Node n) { return n.block.getExit(); } |
|
}; |
|
|
|
int counter = 0; |
|
for (int i = 0; i < size; i++) |
|
if (nodes[i].block.exits() == 0) |
|
counter = nodes[i].makeDepth1stTree(null, visited, counter, distance, access); |
|
|
|
boolean changed; |
|
do { |
|
for (int i = 0; i < size; i++) |
|
visited[i] = false; |
|
|
|
changed = false; |
|
for (int i = 0; i < size; i++) |
|
if (nodes[i].block.exits() == 0) |
|
if (nodes[i].makeDominatorTree(visited, distance, access)) |
|
changed = true; |
|
} while (changed); |
|
|
|
Node.setChildren(nodes); |
|
return nodes; |
|
} |
|
|
|
/** |
|
* Basic block. |
|
* It is a sequence of contiguous instructions that do not contain |
|
* jump/branch instructions except the last one. |
|
* Since Java6 or later does not allow <code>JSR</code>, |
|
* we deal with <code>JSR</code> as a non-branch instruction. |
|
*/ |
|
public static class Block extends BasicBlock { |
|
/** |
|
* A field that can be freely used for storing extra data. |
|
* A client program of this control-flow analyzer can append |
|
* an additional attribute to a <code>Block</code> object. |
|
* The Javassist library never accesses this field. |
|
*/ |
|
public Object clientData = null; |
|
|
|
int index; |
|
MethodInfo method; |
|
Block[] entrances; |
|
|
|
Block(int pos, MethodInfo minfo) { |
|
super(pos); |
|
method = minfo; |
|
} |
|
|
|
protected void toString2(StringBuffer sbuf) { |
|
super.toString2(sbuf); |
|
sbuf.append(", incoming{"); |
|
for (int i = 0; i < entrances.length; i++) |
|
sbuf.append(entrances[i].position).append(", "); |
|
|
|
sbuf.append("}"); |
|
} |
|
|
|
BasicBlock[] getExit() { return exit; } |
|
|
|
/** |
|
* Returns the position of this block in the array of |
|
* basic blocks that the <code>basicBlocks</code> method |
|
* returns. |
|
* |
|
* @see #basicBlocks() |
|
*/ |
|
public int index() { return index; } |
|
|
|
/** |
|
* Returns the position of the first instruction |
|
* in this block. |
|
*/ |
|
public int position() { return position; } |
|
|
|
/** |
|
* Returns the length of this block. |
|
*/ |
|
public int length() { return length; } |
|
|
|
/** |
|
* Returns the number of the control paths entering this block. |
|
*/ |
|
public int incomings() { return incoming; } |
|
|
|
/** |
|
* Returns the block that the control may jump into this block from. |
|
*/ |
|
public Block incoming(int n) { |
|
return entrances[n]; |
|
} |
|
|
|
/** |
|
* Return the number of the blocks that may be executed |
|
* after this block. |
|
*/ |
|
public int exits() { return exit == null ? 0 : exit.length; } |
|
|
|
/** |
|
* Returns the n-th block that may be executed after this |
|
* block. |
|
* |
|
* @param n an index in the array of exit blocks. |
|
*/ |
|
public Block exit(int n) { return (Block)exit[n]; } |
|
|
|
/** |
|
* Returns catch clauses that will catch an exception thrown |
|
* in this block. |
|
*/ |
|
public Catcher[] catchers() { |
|
ArrayList catchers = new ArrayList(); |
|
BasicBlock.Catch c = toCatch; |
|
while (c != null) { |
|
catchers.add(new Catcher(c)); |
|
c = c.next; |
|
} |
|
|
|
return (Catcher[])catchers.toArray(new Catcher[catchers.size()]); |
|
} |
|
} |
|
|
|
static abstract class Access { |
|
Node[] all; |
|
Access(Node[] nodes) { all = nodes; } |
|
Node node(BasicBlock b) { return all[((Block)b).index]; } |
|
abstract BasicBlock[] exits(Node n); |
|
abstract BasicBlock[] entrances(Node n); |
|
} |
|
|
|
/** |
|
* A node of (post) dominator trees. |
|
*/ |
|
public static class Node { |
|
private Block block; |
|
private Node parent; |
|
private Node[] children; |
|
|
|
Node(Block b) { |
|
block = b; |
|
parent = null; |
|
} |
|
|
|
/** |
|
* Returns a <code>String</code> representation. |
|
*/ |
|
public String toString() { |
|
StringBuffer sbuf = new StringBuffer(); |
|
sbuf.append("Node[pos=").append(block().position()); |
|
sbuf.append(", parent="); |
|
sbuf.append(parent == null ? "*" : Integer.toString(parent.block().position())); |
|
sbuf.append(", children{"); |
|
for (int i = 0; i < children.length; i++) |
|
sbuf.append(children[i].block().position()).append(", "); |
|
|
|
sbuf.append("}]"); |
|
return sbuf.toString(); |
|
} |
|
|
|
/** |
|
* Returns the basic block indicated by this node. |
|
*/ |
|
public Block block() { return block; } |
|
|
|
/** |
|
* Returns the parent of this node. |
|
*/ |
|
public Node parent() { return parent; } |
|
|
|
/** |
|
* Returns the number of the children of this node. |
|
*/ |
|
public int children() { return children.length; } |
|
|
|
/** |
|
* Returns the n-th child of this node. |
|
* |
|
* @param n an index in the array of children. |
|
*/ |
|
public Node child(int n) { return children[n]; } |
|
|
|
/* |
|
* After executing this method, distance[] represents the post order of the tree nodes. |
|
* It also represents distances from the root; a bigger number represents a shorter |
|
* distance. parent is set to its parent in the depth first spanning tree. |
|
*/ |
|
int makeDepth1stTree(Node caller, boolean[] visited, int counter, int[] distance, Access access) { |
|
int index = block.index; |
|
if (visited[index]) |
|
return counter; |
|
|
|
visited[index] = true; |
|
parent = caller; |
|
BasicBlock[] exits = access.exits(this); |
|
if (exits != null) |
|
for (int i = 0; i < exits.length; i++) { |
|
Node n = access.node(exits[i]); |
|
counter = n.makeDepth1stTree(this, visited, counter, distance, access); |
|
} |
|
|
|
distance[index] = counter++; |
|
return counter; |
|
} |
|
|
|
boolean makeDominatorTree(boolean[] visited, int[] distance, Access access) { |
|
int index = block.index; |
|
if (visited[index]) |
|
return false; |
|
|
|
visited[index] = true; |
|
boolean changed = false; |
|
BasicBlock[] exits = access.exits(this); |
|
if (exits != null) |
|
for (int i = 0; i < exits.length; i++) { |
|
Node n = access.node(exits[i]); |
|
if (n.makeDominatorTree(visited, distance, access)) |
|
changed = true; |
|
} |
|
|
|
BasicBlock[] entrances = access.entrances(this); |
|
if (entrances != null) |
|
for (int i = 0; i < entrances.length; i++) { |
|
if (parent != null) { |
|
Node n = getAncestor(parent, access.node(entrances[i]), distance); |
|
if (n != parent) { |
|
parent = n; |
|
changed = true; |
|
} |
|
} |
|
} |
|
|
|
return changed; |
|
} |
|
|
|
private static Node getAncestor(Node n1, Node n2, int[] distance) { |
|
while (n1 != n2) { |
|
if (distance[n1.block.index] < distance[n2.block.index]) |
|
n1 = n1.parent; |
|
else |
|
n2 = n2.parent; |
|
|
|
if (n1 == null || n2 == null) |
|
return null; |
|
} |
|
|
|
return n1; |
|
} |
|
|
|
private static void setChildren(Node[] all) { |
|
int size = all.length; |
|
int[] nchildren = new int[size]; |
|
for (int i = 0; i < size; i++) |
|
nchildren[i] = 0; |
|
|
|
for (int i = 0; i < size; i++) { |
|
Node p = all[i].parent; |
|
if (p != null) |
|
nchildren[p.block.index]++; |
|
} |
|
|
|
for (int i = 0; i < size; i++) |
|
all[i].children = new Node[nchildren[i]]; |
|
|
|
for (int i = 0; i < size; i++) |
|
nchildren[i] = 0; |
|
|
|
for (int i = 0; i < size; i++) { |
|
Node n = all[i]; |
|
Node p = n.parent; |
|
if (p != null) |
|
p.children[nchildren[p.block.index]++] = n; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Represents a catch clause. |
|
*/ |
|
public static class Catcher { |
|
private Block node; |
|
private int typeIndex; |
|
|
|
Catcher(BasicBlock.Catch c) { |
|
node = (Block)c.body; |
|
typeIndex = c.typeIndex; |
|
} |
|
|
|
/** |
|
* Returns the first block of the catch clause. |
|
*/ |
|
public Block block() { return node; } |
|
|
|
/** |
|
* Returns the name of the exception type that |
|
* this catch clause catches. |
|
*/ |
|
public String type() { |
|
if (typeIndex == 0) |
|
return "java.lang.Throwable"; |
|
else |
|
return node.method.getConstPool().getClassInfo(typeIndex); |
|
} |
|
} |
|
}
|
|
|