-
Notifications
You must be signed in to change notification settings - Fork 16
Control flow graph API
A control flow graph of the method is represented as a JcGraph
object. To create method' JcGraph
, you can invoke the graph
function of a three-address instruction list:
fun createGraph(classpath: JcClasspath, method: JcMethod): JcGraph {
val instructionList = method.instructionList(classpath)
return instructionList.graph(classpath, method)
}
An intermediate representation of JcGraph
uses the resolved type information (i.e. JcType
hierarchy) and classpath information, therefore it requires a classpath instance.
Similar to JcRawInstList
, JcGraph
stores a list of method instructions. However, it
also tries to resolve all the execution paths in the method. JcGraph
operates with
JcInst
class hierarchy (which is similar to JcRawInst
in many cases) and provides the following API:
-
entry: JcInst
— Get the entry point of a method: there can be only one entry point. -
exits: List<JcInst>
— Get all the "normal" exit points of a method, i.e. all the return and throw instructions. -
throwExits: Map<JcType, List<JcInst>>
— All the potential exception exit points of a method. -
ref(inst: JcInst): JcInstRef
— Get theJcInstRef
for an instruction. It is a lightweight wrapper that allows to reference an instruction anytime you need. -
inst(ref: JcInstRef): JcInst
— ConvertJcInstRef
into aJcInst
. -
previous(inst: JcInst): JcInst
— Get the previous instruction in the list. -
next(inst: JcInst): JcInst
— Get the next instruction in the list. -
successors(inst: JcInst): Set<JcInst>
— Get all the successors of an instruction in a CFG. Does not include any exception control flow. -
predecessors(inst: JcInst): Set<JcInst>
— Get all the predecessors of an instruction in a CFG. Does not include any exception control flow. -
throwers(inst: JcInst): Set<JcInst>
— Get all the instructions that may throw an exception caught byinst
. Represents an exception control flow of a method. Returns an empty set for all instructions exceptJcCatchInst
. -
catchers(inst: JcInst): Set<JcCatchInst>
— Get all the instructions that may catch an exception thrown byinst
. Represents an exception control flow of a method. -
exceptionExits(inst: JcInst): Set<JcClassType>
— Get all the exception types that an instruction can throw and a method will not catch. -
blockGraph(): JcBlockGraph
— Create a basic block representation of a CFG. -
iterator(): Iterator<JcInst>
— An iterator over the instructions of a graph.
JcBlockGraph
is a basic block API for CFG. It operates with JcBasicBlock
instances — each
basic block just represents an interval of instructions with the following properties:
- Instructions of a basic block are executed consecutively during the normal execution (i.e. when no exceptions are thrown).
- All the instructions of a basic block have the same exception handlers, i.e. calling
jcGraph.catchers(inst)
for each instruction of a basic block returns the same result.
JcBlockGraph
provides the following API:
-
entry: JcBasicBlock
— Entry of a method. There can be only one entry. -
exits: List<JcBasicBlock>
— Exits of a method. -
instructions(block: JcBasicBlock): List<JcInst>
— Get the instructions of a basic block. -
predecessors(block: JcBasicBlock): Set<JcBasicBlock>
— Get all the predecessors of a basic block in a CFG. Does not include any exception control flow. -
successors(block: JcBasicBlock): Set<JcBasicBlock>
— Get all the successors of a basic block in a CFG. Does not include any exception control flow. -
throwers(block: JcBasicBlock): Set<JcBasicBlock>
— Get all the basic blocks that may throw an exception caught byblock
. Represents an exception control flow of a method. Returns an empty set for all blocks except the ones that start withJcCatchInst
. -
catchers(block: JcBasicBlock): Set<JcBasicBlock>
— Get all the basic blocks that may catch an exception thrown byblock
. Represents an exception control flow of a method.
We also provide an API to visualize JcGraph
and JcBlockGraph
:
-
JcGraph.view(dotCmd: String, viewerCmd: String, viewCatchConnections: Boolean = false)
— Generate an SVG file using DOT (dotCmd
requires a path to a DOT executable) and view it usingviewerCmd
program (specify a browser executable). TheviewCatchConnections
flag defines whether athrow...catch
connections are to be displayed in the graph. -
JcBlockGraph.view(dotCmd: String, viewerCmd: String)
— Similar, but it displaysJcBlockGraph
.
CFG API operates on JcInst
instructions. JcInst
is similar to JcRawInst
with some
small differences. The main difference is that JcInst
uses JcType
instances to represent
types. Another difference is that JcInst
does not need labels to represent connections
between instructions (as they are stored in JcGraph
). In all other cases, JcInst
hierarchy
(including JcExpr
and JcValue
) matches largely with JcRawInst
hierarchy
(including JcRawExpr
and JcRawValue
).
One more thing worth noting is that JcGraph
represents an immutable structure and provides no API for modifying it. That is made on purpose, because modifying CFG
requires a user to be aware of all the connections inside a graph. The user should correctly manage
these connections when changing the CFG. However, one can always create a new copy of
JcGraph
with all the necessary modifications.
You can find an example of CFG modification in the StringConcatSimplifier
class: it creates new
JcGraph
in which all the invokedynamic
string concatenation instructions are
replaced with simple String.concat
method call.
ReachingDefinitionsAnalysis
class is an example of using basic block API. It performs
a standard reaching definition analysis for basic blocks using a simple worklist algorithm.
As for the three-address instruction list, we provide a visitor API for traversing and
modifying JcGraph
. Visitors have a standard interface — they can be invoked using an
accept
method on instructions and expressions:
val a = jcInst.accept(MyInstVisitor())
val b = jcExpr.accept(MyExprVisitor())
We also provide "functional"-like extensions for applying visitors to JcGraph
:
filter(visitor: JcInstVisitor<Boolean>): JcGraph
filterNot(visitor: JcInstVisitor<Boolean>): JcGraph
map(visitor: JcInstVisitor<JcInst>): JcGraph
mapNotNull(visitor: JcInstVisitor<JcInst?>): JcGraph
flatMap(visitor: JcInstVisitor<Collection<JcInst>>): JcGraph
apply(visitor: JcInstVisitor<Unit>): JcGraph
applyAndGet(visitor: T, getter: (T) -> R): R
collect(visitor: JcInstVisitor<T>): Collection<T>