-
Notifications
You must be signed in to change notification settings - Fork 18
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 theJcInstReffor an instruction. It is a lightweight wrapper that allows to reference an instruction anytime you need. -
inst(ref: JcInstRef): JcInst— ConvertJcInstRefinto 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 (dotCmdrequires a path to a DOT executable) and view it usingviewerCmdprogram (specify a browser executable). TheviewCatchConnectionsflag defines whether athrow...catchconnections 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>): JcGraphfilterNot(visitor: JcInstVisitor<Boolean>): JcGraphmap(visitor: JcInstVisitor<JcInst>): JcGraphmapNotNull(visitor: JcInstVisitor<JcInst?>): JcGraphflatMap(visitor: JcInstVisitor<Collection<JcInst>>): JcGraphapply(visitor: JcInstVisitor<Unit>): JcGraphapplyAndGet(visitor: T, getter: (T) -> R): Rcollect(visitor: JcInstVisitor<T>): Collection<T>