Constructs a CFG for JavaScript source code.
This module will read one or more JavaScript source files and produce CFGs of the code.
npm i ast-flow-graph
const
CFG = require( 'ast-flow-graph' ),
fs = require( 'fs' ),
src = fs.readFileSync( 'some-javascript.js', 'utf8' ),
cfg = new CFG( src, {
parser: {
loc: true,
range: true,
comment: true,
tokens: true,
ecmaVersion: 9,
sourceType: 'module',
ecmaFeatures: {
impliedStrict: true,
experimentalObjectRestSpread: true
}
}
} );
cfg.generate(); // Generate a CFG for all functions (and main module)
// or for just one function
const graph = cfg.generate( 'my_function' );
// Create all graphs and then get one of them
cfg.generate(); // You only need to do this once.
const myFunc = cfg.by_name( 'my_function' );
// ...
console.log( cfg.toTable() ); // Display all functions as tables
// Create a graph-viz .dot file
console.log( cfg.create_dot( myFunc ) );
cfg version 1.0.0
Usage
cfg [-d] [-g] [-s ...sources] [-t] [-n ...names] [-r] [-v] [-h] [...files]
Options
-d, --debug Turn on debugging mode. Warning: This will generate a lot of output.
-g, --graph Create a .dot file for graph-viz
-o, --output string If this option is present, save the .dot files to this directory.
-s, --source string[] Input source file. Can also be specified at the end of the command line.
-t, --table Output a table showing all CFG blocks
-l, --lines Output CFG blocks as text
-n, --name string[] Specify a function name to only display information for that function.
-v, --verbose Output additional information
-h, --help Display this help message
Description
Creates a CFG from one or more source files.
The CFG it outputs can be in one of several formats. Two of them are for easy text displays in the console and can be displayed using the -t, --table
or -l, --lines
options for tabular or line oriented output, respectively. These displays are of limited use and are mostly used for quick reference or checks. The program will also output a .dot
file for use with Graphviz or, optionally, graph-easy
or similar programs that can read standard .dot
files.
Since I like to include some explanatory graphs in source or README.md files, I like to create graphs in line ascii. For this, I use the perl module graph-easy
.
Example usage:
ast-flow-graph -g -s test-data/cfg-test-04.js | graph-easy --as_boxart
Or in two steps.
ast-flow-graph -gs test-data/cfg-test-04.js > cfg-04.dot
graph-easy cfg-04.dot --as_boxart >flow-04.txt
which will produce something like this:
blah:12-28
╭─────────────╮
│ entry:0 │
╰─────────────╯
│
│
▼
╭─────────────╮
│ NORMAL:1@14 │
╰─────────────╯
│
│
▼
╭────────────────────────────────────────────╮ CONTINUE
│ NORMAL:2@16-20 │ ◀─────────────┐
╰────────────────────────────────────────────╯ │
│ ▲ │ │
│ │ │ │
▼ │ ▼ │
╭─────────────╮ TRUE ╭─────────────╮ │ ╭───────────────────╮ ╭───────────╮
│ NORMAL:6@23 │ ◀╴╴╴╴╴╴ │ TEST:5@22 │ └────── │ TEST|LOOP:3@18-19 │ ──────────▶ │ LOOP:4@19 │
╰─────────────╯ ╰─────────────╯ ╰───────────────────╯ ╰───────────╯
│ ╵
│ ╵ FALSE
│ ▼
│ ╭─────────────╮
│ │ TEST:7@24 │ ╴┐
│ ╰─────────────╯ ╵
│ ╵ ╵
│ ╵ TRUE ╵
│ ▼ ╵
│ ╭─────────────╮ ╵
│ │ NORMAL:8@25 │ ╵ FALSE
│ ╰─────────────╯ ╵
│ │ ╵
│ │ ╵
│ ▼ ╵
│ ╭─────────────╮ ╵
└───────────────────▶ │ NORMAL:9@27 │ ◀┘
╰─────────────╯
│
│
▼
╭─────────────╮
│ NORMAL:10@0 │
╰─────────────╯
│
│
▼
╭─────────────╮
│ exit:11 │
╰─────────────╯
The annotations, like 3@18-19
, for example, means (CFG) block number 3
, covering lines 18 through 19 in the source code. A TEST
is a conditional branch, probably an if
statement, and TEST|LOOP
is a loop condition test, and so on.
You can also send the output Graphviz and use one of its myriad outputs. We can generate a .png
of the graph above with a command like this:
ast-flow-graph -gs test-data/cfg-test-04.js > cfg-04.dot
dot -Tpng tmp-04.dot
which produces something the image below (I scaled it down for size reasons):
Finally, note that the .dot
file is simple text, so you can edit the labels, colors, and whatnot if you care or need to do so.
This section is a work in progress. The headers
, rows
, and json
are not implemented, the others are but not tested.
postLoad
for general initializationpreExit
for general cleanup
init
before anything is donepostInit
afterAST
but before graphfinish
after graph as part of block finishingheaders
rows
json
init
before parsingpostInit
after parsingfinish
after parsing and traversalpostFinish
after allAST
functions are done
init
after the block is initializedfinish
after the block has been visitedpostFinish
after the block has been added and the walker is moving onheaders
rows
json
init
after theBlockManager
has been initializedfinish
after theBlockManager
has added all blockspostFinish
after theclean()
stageheaders
rows
json
parse
called to parse the source code
- succesors_as_indices ⇒
Array.<number>
- successors ⇒
Array.<CFGBlock>
- succs ⇒
Array.<CFGBlock>
- preds ⇒
Array.<CFGBlock>
- preds ⇒
Array.<number>
Get all predecessors for a given CFGBlock
- defaultOutputOptions
The default display options for table and string output.
- pluginManager :
PluginManager
- node_to_scope(node) ⇒
*
- forFunctions() :
Iterable.<FunctionInfo>
- traverse(ast, [enter], [leave])
- walker(node, [enter], [leave])
- flat_walker(nodes, cb)
Iterate over all nodes in a block without recursing into sub-nodes.
- call_visitors(node, cb)
Callback for each visitor key for a given node.
- add_line(lineNumber, sourceLine)
Add a new line to the source code.
- rename(inode, newName)
- as_source() ⇒
string
Return the AST nodes as source code, including any modifications made.
- get_from_function(node, [whatToGet]) ⇒
Array.<Node>
|string
|CFGInfo
- isEmpty() ⇒
boolean
- classify(to, type) ⇒
CFGBlock
- follows(cb) ⇒
CFGBlock
- from(cb) ⇒
CFGBlock
- to(cb) ⇒
CFGBlock
- remove_succs() ⇒
CFGBlock
- remove_succ(kill) ⇒
CFGBlock
- as(nodeType) ⇒
CFGBlock
- edge_as(edgeType, [to]) ⇒
CFGBlock
Sets the last edge to type.
- not(nodeType) ⇒
CFGBlock
Removes a type from this block.
- whenTrue(block) ⇒
CFGBlock
For test nodes, this adds the edge taken when the condition is true.
- whenFalse(block) ⇒
CFGBlock
For test nodes, this adds the edge taken when the condition is false.
- add(node) ⇒
CFGBlock
Add a current-level AST node to this block.
- first() ⇒
AnnotatedNode
|BaseNode
|Node
Returns the first AST node (if any) of this block.
- last() ⇒
AnnotatedNode
|BaseNode
|Node
Returns the last AST node (if any) of this block.
- by(txt) ⇒
CFGBlock
Free-text field indicating the manner of of creation of this node. For information in graphs and printouts only.
- isa(typeName) ⇒
boolean
Check if this block has a particular type.
- eliminate() ⇒
boolean
Remove itself if it's an empty node and isn't the start or exit node.
- defer_edge_type(type)
- graph_label() ⇒
string
For the vertices.
- lines() ⇒
string
Stringified line numbers for this block.
- pred_edge_types() ⇒
Array.<string>
- succ_edge_types() ⇒
Array.<string>
- split_by(arr, chunkSize) ⇒
ArrayArray.<string>
- toRow() ⇒
Array.<string>
Headers are TYPE / LINES / LEFT EDGES / NODE / RIGHT EDGES / CREATED BY / AST
- toString() ⇒
Array.<string>
- toString() ⇒
string
- toTable() ⇒
string
- generate([name]) ⇒
Array.<CFGInfo>
|CFG
- by_name(name) ⇒
CFGInfo
- forEach(fn)
- create_dot(cfg, [title]) ⇒
string
- _as_table(hdr, [headers], [rows])
- reindex(from) ⇒
Edges
- add(from, to, type) ⇒
Edges
Add an edge between to CFGBlocks.
- classify(from, to, ctype) ⇒
Edges
Set a type on an arbitrary edge.
- not(from, to, type) ⇒
Edges
Remove a type from an arbitrary edge.
- retarget_multiple(node) ⇒
Edges
Point one or more edges to a new CFGBlock, used in block removal.
- remove_succ(from, to) ⇒
Edges
- get_succs(from) ⇒
Array.<CFGBlock>
Get all successors for a given CFGBlock.
- get_preds(from) ⇒
Array.<CFGBlock>
Get all predecessors for a given CFGBlock
- renumber(newOffsets)
Renumber all indices (
id
field) because of removed CFGBlocks.- successors() :
Iterable.<number>
- has(from, type) ⇒
boolean
Is there an edge of the gievn type?
- edges(from) ⇒
Array.<Connection>
Get edge information for a given CFGBlock, i.e. successors.
- pred_edges(_from) ⇒
Array.<Connection>
Get all predecessor edge information for a given CFGBlock.
- forEach(fn)
- map(fn)
- get(index) ⇒
CFGBlock
- toString() ⇒
string
- toTable() ⇒
Array.<string>
- create_dot(title) ⇒
string
- callback(topKey, subKey, ...args) ⇒
*
- output(options)
Override display options.
- CFGInfo :
object
- VisitorHelper :
object
- AnnotatedNode :
Statement
|function
|Expression
|Pattern
|Declaration
|Node
|BaseNode
|Esprima.Node
It's damn near impossible to make WebStorm understand a class hierarchy.
- CFGOptions :
object
- DotOptions :
object
- FunctionInfo :
object
- Connection :
object
Get all predecessors for a given CFGBlock
The default display options for table and string output.
Kind: global function
Param | Type |
---|---|
node | AnnotatedNode |
forFunctions() : Iterable.<FunctionInfo>
Kind: global function
Param | Type |
---|---|
ast | Node | function |
[enter] | function |
[leave] | function |
Kind: global function
Param | Type |
---|---|
node | AnnotatedNode | BaseNode | Node |
[enter] | function |
[leave] | function |
Iterate over all nodes in a block without recursing into sub-nodes.
Kind: global function
Param | Type |
---|---|
nodes | Array.<AnnotatedNode> | AnnotatedNode |
cb | function |
Callback for each visitor key for a given node.
Kind: global function
Param | Type |
---|---|
node | AnnotatedNode |
cb | function |
Add a new line to the source code.
Kind: global function
Param | Type | Description |
---|---|---|
lineNumber | number |
0-based line number |
sourceLine | string |
The source line to add |
Kind: global function
Param | Type | Description |
---|---|---|
inode | Identifier | AnnotatedNode |
A node of type Syntax.Identifier |
newName | string |
The new name of the identifier |
Return the AST nodes as source code, including any modifications made.
Kind: global function
Returns: string
- - The lossy source code
get_from_function(node, [whatToGet]) ⇒ Array.<Node>
| string
| CFGInfo
Kind: global function Access: protected
Param | Type | Default |
---|---|---|
node | FunctionDeclaration | FunctionExpression | MethodDefinition | ArrowFunctionExpression | Property | Node |
|
[whatToGet] | string |
"'all'" |
Kind: global function
Param | Type |
---|---|
to | number | CFGBlock |
type | string |
Kind: global function
Param | Type |
---|---|
cb | CFGBlock | Array.<CFGBlock> |
Kind: global function
Param | Type |
---|---|
cb | CFGBlock | Array.<CFGBlock> |
Kind: global function
Param | Type |
---|---|
cb | CFGBlock | Array.<CFGBlock> |
Kind: global function
Param | Type |
---|---|
kill | number | CFGBlock |
Kind: global function
Param | Type |
---|---|
nodeType | number |
Sets the last edge to type.
Kind: global function
Param | Type |
---|---|
edgeType | Edge |
[to] | number | CFGBlock |
Removes a type from this block.
Kind: global function
Param | Type |
---|---|
nodeType | Edge |
For test nodes, this adds the edge taken when the condition is true.
Kind: global function
Param | Type |
---|---|
block | CFGBlock |
For test nodes, this adds the edge taken when the condition is false.
Kind: global function
Param | Type |
---|---|
block | CFGBlock |
Add a current-level AST node to this block.
Kind: global function
Param | Type |
---|---|
node | AnnotatedNode | BaseNode | Node |
first() ⇒ AnnotatedNode
| BaseNode
| Node
Returns the first AST node (if any) of this block.
last() ⇒ AnnotatedNode
| BaseNode
| Node
Returns the last AST node (if any) of this block.
Free-text field indicating the manner of of creation of this node. For information in graphs and printouts only.
Kind: global function
Param | Type |
---|---|
txt | string |
Check if this block has a particular type.
Kind: global function
Param | Type |
---|---|
typeName | number |
Remove itself if it's an empty node and isn't the start or exit node.
Kind: global function
Returns: boolean
- - true if it was deleted
Kind: global function
Param | Type |
---|---|
type | Edge |
For the vertices.
Stringified line numbers for this block.
Kind: global function
Param | Type |
---|---|
arr | Array.<*> |
chunkSize | number |
Headers are TYPE / LINES / LEFT EDGES / NODE / RIGHT EDGES / CREATED BY / AST
generate([name]) ⇒ Array.<CFGInfo>
| CFG
Kind: global function
Param | Type |
---|---|
[name] | string |
by_name(name) ⇒ CFGInfo
Kind: global function
Param | Type |
---|---|
name | string |
Kind: global function
Param | Type |
---|---|
fn | function |
Kind: global function
Param | Type |
---|---|
cfg | CFGInfo |
[title] | string |
Kind: global function
Param | Type |
---|---|
hdr | string | Array.<string> | Array.<Array.<string>> |
[headers] | Array.<string> | Array.<Array.<string>> |
[rows] | Array.<Array.<string>> |
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
Add an edge between to CFGBlocks.
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
to | CFGBlock | number |
type | Edge |
Set a type on an arbitrary edge.
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
to | CFGBlock | number |
ctype | Edge |
Remove a type from an arbitrary edge.
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
to | CFGBlock | number |
type | Edge |
Point one or more edges to a new CFGBlock, used in block removal.
Kind: global function
Param | Type |
---|---|
node | CFGBlock | number |
Remove a successor CFGBlock from a CFGBlock
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
to | CFGBlock | number |
Get all successors for a given CFGBlock.
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
Get all predecessors for a given CFGBlock
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
Renumber all indices (id
field) because of removed CFGBlocks.
Kind: global function
Param | Type |
---|---|
newOffsets | Array.<number> |
Is there an edge of the gievn type?
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
type | Edge |
edges(from) ⇒ Array.<Connection>
Get edge information for a given CFGBlock, i.e. successors.
Kind: global function
Param | Type |
---|---|
from | CFGBlock | number |
pred_edges(_from) ⇒ Array.<Connection>
Get all predecessor edge information for a given CFGBlock.
Kind: global function
Param | Type |
---|---|
_from | CFGBlock | number |
Kind: global function
Param | Type |
---|---|
fn | function |
Kind: global function
Param | Type |
---|---|
fn | function |
Kind: global function
Param | Type |
---|---|
index | number |
Kind: global function
Param | Type |
---|---|
title | string |
Kind: global function
Param | Type |
---|---|
topKey | string |
subKey | string |
...args | * |
Override display options.
Kind: global function
Param |
---|
options |
Kind: global typedef Properties
Name | Type |
---|---|
name | string |
params | Array.<AnnotatedNode> |
body | AnnotatedNode | Array.<AnnotatedNode> |
lines | Array.<Number> |
[bm] | BlockManager |
[trailing] | CFGBlock |
node, | AnnotatedNode | Node | BaseNode |
ast | AST |
topScope | Scope |
toString | function |
toTable | function |
Kind: global typedef Properties
Name | Type |
---|---|
BlockManager | BlockManager |
bm | BlockManager |
ast | AST |
prev | CFGBlock |
block | CFGBlock |
newBlock | function |
toExit | Array.<CFGBlock> |
[flatWalk] | function |
[scanWalk] | function |
breakTargets | Array.<CFGBlock> |
addBreakTarget | function |
addLoopTarget | function |
popTarget | function |
getBreakTarget | function |
getLoopTarget | function |
AnnotatedNode : Statement
| function
| Expression
| Pattern
| Declaration
| Node
| BaseNode
| Esprima.Node
It's damn near impossible to make WebStorm understand a class hierarchy.
Kind: global typedef
Extends: BaseNode
, Node
, VariableDeclarator
, Statement
, Declaration
, Pattern
, Expression
, Function
, BlockStatement
, espree.Node
Properties
Name | Type |
---|---|
[index] | number |
[parent] | AnnotatedNode |
[cfg] | CFGBlock |
[toString] | function |
scope | Scope |
level | number |
field | string |
fieldIndex | number |
Kind: global typedef Properties
Name | Type |
---|---|
ssaSource | boolean |
parser | object |
Kind: global typedef Properties
Name | Type | Default | Description |
---|---|---|---|
title | string |
||
nodeLabels | Array.<string> |
||
edgeLabels | Array.<string> |
// was graph_label | |
[start] | number |
||
[end] | number |
||
conditional | Array.<Array.<number>> |
Actually an array of a tuple length 2: [ number, number ] | |
unconditional | Array.<Array.<number>> |
Actually an array of a tuple length 2: [ number, number ] | |
[dotOptions] | object |
{} |
|
blocks | Array.<CFGBlock> |
Kind: global typedef Properties
Name | Type | Description |
---|---|---|
name | string |
|
body | Array.<AnnotatedNode> | AnnotatedNode |
|
[params] | Array.<AnnotatedNode> |
|
node | AnnotatedNode |
|
lines | Array.<number> |
A tuple with length 2 |
Kind: global typedef Properties
Name | Type |
---|---|
from | number |
to | number |
type | EdgeInfo |
MIT © Julian Jensen