mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
130 lines
2.8 KiB
V
130 lines
2.8 KiB
V
module callgraph
|
|
|
|
import v.ast
|
|
import v.ast.walker
|
|
import v.pref
|
|
import v.dotgraph
|
|
|
|
// callgraph.show walks the AST, starting at main() and prints a DOT output describing the calls
|
|
// that function make transitively
|
|
pub fn show(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.File) {
|
|
mut mapper := &Mapper{
|
|
pref: pref
|
|
table: table
|
|
dg: dotgraph.new('CallGraph', 'CallGraph for $pref.path', 'green')
|
|
}
|
|
// Node14 [shape="box",label="PrivateBase",URL="$classPrivateBase.html"];
|
|
// Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"];
|
|
for afile in ast_files {
|
|
walker.walk(mut mapper, afile)
|
|
}
|
|
mapper.dg.finish()
|
|
}
|
|
|
|
[heap]
|
|
struct Mapper {
|
|
pos int
|
|
mut:
|
|
pref &pref.Preferences
|
|
table &ast.Table
|
|
file &ast.File = 0
|
|
node &ast.Node = 0
|
|
fn_decl &ast.FnDecl = 0
|
|
caller_name string
|
|
dot_caller_name string
|
|
is_caller_used bool
|
|
dg dotgraph.DotGraph
|
|
}
|
|
|
|
fn (mut m Mapper) dot_normalise_node_name(name string) string {
|
|
res := name.replace_each([
|
|
'.',
|
|
'_',
|
|
'==',
|
|
'op_eq',
|
|
'>=',
|
|
'op_greater_eq',
|
|
'<=',
|
|
'op_lesser_eq',
|
|
'>',
|
|
'op_greater',
|
|
'<',
|
|
'op_lesser',
|
|
'+',
|
|
'op_plus',
|
|
'-',
|
|
'op_minus',
|
|
'/',
|
|
'op_divide',
|
|
'*',
|
|
'op_multiply',
|
|
'^',
|
|
'op_xor',
|
|
'|',
|
|
'op_or',
|
|
'&',
|
|
'op_and',
|
|
])
|
|
return res
|
|
}
|
|
|
|
fn (mut m Mapper) fn_name(fname string, receiver_type ast.Type, is_method bool) string {
|
|
if !is_method {
|
|
return fname
|
|
}
|
|
rec_sym := m.table.sym(receiver_type)
|
|
return '${rec_sym.name}.$fname'
|
|
}
|
|
|
|
fn (mut m Mapper) dot_fn_name(fname string, recv_type ast.Type, is_method bool) string {
|
|
if is_method {
|
|
return 'Node_method_' + int(recv_type).str() + '_' + m.dot_normalise_node_name(fname)
|
|
}
|
|
return 'Node_fn_' + m.dot_normalise_node_name(fname)
|
|
}
|
|
|
|
fn (mut m Mapper) visit(node &ast.Node) ? {
|
|
m.node = unsafe { node }
|
|
match node {
|
|
ast.File {
|
|
m.file = unsafe { &node }
|
|
}
|
|
ast.Stmt {
|
|
match node {
|
|
ast.FnDecl {
|
|
m.is_caller_used = true
|
|
if m.pref.skip_unused {
|
|
m.is_caller_used = m.table.used_fns[node.fkey()]
|
|
}
|
|
m.fn_decl = unsafe { &node }
|
|
m.caller_name = m.fn_name(node.name, node.receiver.typ, node.is_method)
|
|
m.dot_caller_name = m.dot_fn_name(node.name, node.receiver.typ, node.is_method)
|
|
if m.is_caller_used {
|
|
m.dg.new_node(m.caller_name,
|
|
node_name: m.dot_caller_name
|
|
should_highlight: m.caller_name == 'main.main'
|
|
)
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
ast.Expr {
|
|
match node {
|
|
ast.CallExpr {
|
|
if m.is_caller_used {
|
|
dot_called_name := m.dot_fn_name(node.name, node.receiver_type,
|
|
node.is_method)
|
|
// Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"];
|
|
m.dg.new_edge(m.dot_caller_name, dot_called_name,
|
|
should_highlight: m.caller_name == 'main.main'
|
|
)
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|