v2 / vlib / v / callgraph / callgraph.v
129 lines · 122 sloc · 2.97 KB · 09f8b56904c93d79a20f3451a81ec3eeaaea42b4
Raw
1module callgraph
2
3import v.ast
4import v.ast.walker
5import v.pref
6import v.dotgraph
7
8// callgraph.show walks the AST, starting at main() and prints a DOT output describing the calls
9// that function make transitively
10pub fn show(mut table ast.Table, pref_ &pref.Preferences, ast_files []&ast.File) {
11 mut mapper := &Mapper{
12 pref: pref_
13 table: table
14 dg: dotgraph.new('CallGraph', 'CallGraph for ${pref_.path}', 'green')
15 }
16 // Node14 [shape="box",label="PrivateBase",URL="$classPrivateBase.html"];
17 // Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"];
18 for afile in ast_files {
19 walker.walk(mut mapper, afile)
20 }
21 mapper.dg.finish()
22}
23
24@[heap]
25struct Mapper {
26 pos int
27mut:
28 pref &pref.Preferences = unsafe { nil }
29 table &ast.Table = unsafe { nil }
30 file &ast.File = unsafe { nil }
31 node &ast.Node = unsafe { nil }
32 fn_decl &ast.FnDecl = unsafe { nil }
33 caller_name string
34 dot_caller_name string
35 is_caller_used bool
36 dg dotgraph.DotGraph
37}
38
39fn (mut m Mapper) dot_normalise_node_name(name string) string {
40 res := name.replace_each([
41 '.',
42 '_',
43 '==',
44 'op_eq',
45 '>=',
46 'op_greater_eq',
47 '<=',
48 'op_lesser_eq',
49 '>',
50 'op_greater',
51 '<',
52 'op_lesser',
53 '+',
54 'op_plus',
55 '-',
56 'op_minus',
57 '/',
58 'op_divide',
59 '*',
60 'op_multiply',
61 '^',
62 'op_xor',
63 '|',
64 'op_or',
65 '&',
66 'op_and',
67 ])
68 return res
69}
70
71fn (mut m Mapper) fn_name(fname string, receiver_type ast.Type, is_method bool) string {
72 if !is_method {
73 return fname
74 }
75 rec_sym := m.table.sym(receiver_type)
76 return '${rec_sym.name}.${fname}'
77}
78
79fn (mut m Mapper) dot_fn_name(fname string, recv_type ast.Type, is_method bool) string {
80 if is_method {
81 return 'Node_method_' + int(recv_type).str() + '_' + m.dot_normalise_node_name(fname)
82 }
83 return 'Node_fn_' + m.dot_normalise_node_name(fname)
84}
85
86fn (mut m Mapper) visit(node &ast.Node) ! {
87 m.node = unsafe { node }
88 match node {
89 ast.File {
90 m.file = unsafe { &node }
91 }
92 ast.Stmt {
93 match node {
94 ast.FnDecl {
95 m.is_caller_used = true
96 if m.pref.skip_unused {
97 m.is_caller_used = m.table.used_features.used_fns[node.fkey()]
98 }
99 m.fn_decl = unsafe { &node }
100 m.caller_name = m.fn_name(node.name, node.receiver.typ, node.is_method)
101 m.dot_caller_name = m.dot_fn_name(node.name, node.receiver.typ, node.is_method)
102 if m.is_caller_used {
103 m.dg.new_node(m.caller_name,
104 node_name: m.dot_caller_name
105 should_highlight: m.caller_name == 'main.main'
106 )
107 }
108 }
109 else {}
110 }
111 }
112 ast.Expr {
113 match node {
114 ast.CallExpr {
115 if m.is_caller_used {
116 dot_called_name := m.dot_fn_name(node.name, node.receiver_type,
117 node.is_method)
118 // Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"];
119 m.dg.new_edge(m.dot_caller_name, dot_called_name,
120 should_highlight: m.caller_name == 'main.main'
121 )
122 }
123 }
124 else {}
125 }
126 }
127 else {}
128 }
129}
130