如何为给定的javascript生成调用图? [关闭]
Posted
技术标签:
【中文标题】如何为给定的javascript生成调用图? [关闭]【英文标题】:How to generate call-graphs for given javascript? [closed] 【发布时间】:2012-04-28 06:29:42 【问题描述】:我看过“https://***.com/questions/1385335/how-to-generate-function-call-graphs-for-javascript”,并尝试过。如果你想得到一个抽象的语法树,它工作得很好。
不幸的是,闭包编译器似乎只提供--print_tree
、--print_ast
和--print_pass_graph
。它们都对我没有用。
我想看看哪个函数调用了其他函数的图表。
【问题讨论】:
为什么不使用内置支持分析 javascript 的开发工具? 看来原来的线程已经消失了,链接现在被破坏了。 :-( 看看这个。 github.com/cheburakshu/Javascript-Explorer-Callgraph 该问题上链接的帖子很久以前就被删除了。可以找到指向其最新存档版本的链接here。 嗨,beatak,在浏览了所有建议之后,您有什么解决方案或建议?请您在***.com/questions/62613726 做一个简短的总结好吗?谢谢。 【参考方案1】:code2flow 正是这样做的。完全披露,我开始了这个项目
运行
$ code2flow source1.js source2.js -o out.gv
然后,用 graphviz 打开 out.gv
编辑:目前,该项目尚未维护。我建议在使用 code2flow 之前尝试不同的解决方案。
【讨论】:
太棒了。如果它可以在 jQuery 上运行,它也必须能够处理我的项目。我一定会试试的。谢谢!!! @scottmrogowski,你的项目对我来说效果很好。对于使用此解决方案的其他任何人,我想指出 this page 它将 graphviz 转换为 yEd 可以打开的文件。斯科特,我调整了你的 python 脚本以根据函数名称命名节点,它产生了很好的 yEd 可读输出。 不幸的是,该项目似乎无人维护。我无法让 code2flow 仅在我的 Windows 和 Linux 笔记本电脑上运行。 @achille 你是对的,这是无人维护的,我现在已经更新了我的答案。我在 Mac 上开发了它,并在 Mac 和 Debian 系统上进行了测试。 Windows 用户无法使其正常工作。您可能想要验证您使用的是 Python 2.7x 而不是 3.x。 它适用于windows: 1. 安装graphviz: graphviz.gitlab.io/_pages/Download/windows/graphviz-2.38.msi 2. 在下面添加到Windows Environment PATH,即C:\Program Files (x86)\Graphviz2.38\bin 3. cd C: \code2flow-master\code2flow-master python setup.py install python code2flow myfile.js -o myfile.jpeg 但是它不理解 asyc.parallel 和 javascript 中的其他内容,因此对于这些基本场景几乎没有用处。【参考方案2】:如果你过滤closure --print_tree
的输出,你会得到你想要的。
以下面的文件为例:
var fib = function(n)
if (n < 2)
return n;
else
return fib(n - 1) + fib(n - 2);
;
console.log(fib(fib(5)));
过滤closure --print_tree
的输出
NAME fib 1
FUNCTION 1
CALL 5
NAME fib 5
SUB 5
NAME a 5
NUMBER 1.0 5
CALL 5
NAME fib 5
SUB 5
NAME a 5
NUMBER 2.0 5
EXPR_RESULT 9
CALL 9
GETPROP 9
NAME console 9
STRING log 9
CALL 9
CALL 9
NAME fib 9
CALL 9
CALL 9
NAME fib 9
NUMBER 5.0 9
你可以看到所有的调用语句。
我为此编写了以下脚本。
./call_tree
#! /usr/bin/env sh
function make_tree()
closure --print_tree $1 | grep $1
function parse_tree()
gawk -f parse_tree.awk
if [[ "$1" = "--tree" ]]; then
make_tree $2
else
make_tree $1 | parse_tree
fi
parse_tree.awk
BEGIN
lines_c = 0
indent_width = 4
indent_offset = 0
string_offset = ""
calling = 0
call_indent = 0
sub(/\[source_file.*$/, "")
sub(/\[free_call.*$/, "")
/SCRIPT/
indent_offset = calculate_indent($0)
root_indent = indent_offset - 1
/FUNCTION/
pl = get_previous_line()
if (calculate_indent(pl) < calculate_indent($0))
print pl
print
lines_v[lines_c] = $0
lines_c += 1
indent = calculate_indent($0)
if (indent <= call_indent)
calling = 0
if (calling)
print
/CALL/
calling = 1
call_indent = calculate_indent($0)
print
/EXPR/
line_indent = calculate_indent($0)
if (line_indent == root_indent)
if ($0 !~ /(FUNCTION)/)
print
function calculate_indent(line)
match(line, /^ */)
return int(RLENGTH / indent_width) - indent_offset
function get_previous_line()
return lines_v[lines_c - 1]
【讨论】:
这是非常有趣的方法。我会再挖一点,但是谢谢!! 有没有办法获取脚本中每个函数调用的行号?【参考方案3】:我最终使用UglifyJS2 和Dot/GraphViz 解决了这个问题,结合了上述答案和链接问题的答案。
对我来说,缺少的部分是如何过滤已解析的 AST。事实证明,UglifyJS 有 TreeWalker 对象,它基本上对 AST 的每个节点都应用了一个函数。这是我到目前为止的代码:
//to be run using nodejs
var UglifyJS = require('uglify-js')
var fs = require('fs');
var util = require('util');
var file = 'path/to/file...';
//read in the code
var code = fs.readFileSync(file, "utf8");
//parse it to AST
var toplevel = UglifyJS.parse(code);
//open the output DOT file
var out = fs.openSync('path/to/output/file...', 'w');
//output the start of a directed graph in DOT notation
fs.writeSync(out, 'digraph test\n');
//use a tree walker to examine each node
var walker = new UglifyJS.TreeWalker(function(node)
//check for function calls
if (node instanceof UglifyJS.AST_Call)
if(node.expression.name !== undefined)
//find where the calling function is defined
var p = walker.find_parent(UglifyJS.AST_Defun);
if(p !== undefined)
//filter out unneccessary stuff, eg calls to external libraries or constructors
if(node.expression.name == "$" || node.expression.name == "Number" || node.expression.name =="Date")
//NOTE: $ is from jquery, and causes problems if it's in the DOT file.
//It's also very frequent, so even replacing it with a safe string
//results in a very cluttered graph
else
fs.writeSync(out, p.name.name);
fs.writeSync(out, " -> ");
fs.writeSync(out, node.expression.name);
fs.writeSync(out, "\n");
else
//it's a top level function
fs.writeSync(out, node.expression.name);
fs.writeSync(out, "\n");
if(node instanceof UglifyJS.AST_Defun)
//defined but not called
fs.writeSync(out, node.name.name);
fs.writeSync(out, "\n");
);
//analyse the AST
toplevel.walk(walker);
//finally, write out the closing bracket
fs.writeSync(out, '');
我用node运行它,然后把输出通过
dot -Tpng -o graph_name.png dot_file_name.dot
注意事项:
它提供了一个非常基本的图表——只有黑白,没有格式。
它根本不捕获 ajax,而且大概也不是 eval
或 with
之类的东西,如 others have mentioned。
此外,就目前而言,它在图中包括:由其他函数调用的函数(以及因此调用其他函数的函数)、独立调用的函数以及已定义但未调用的函数。
因此,它可能会遗漏相关的内容,或包含不相关的内容。虽然这是一个开始,而且似乎完成了我所追求的目标,也是最初导致我提出这个问题的原因。
【讨论】:
有趣。它确实为一个简单的 javascript 创建了一个调用图。感谢您的努力! (旁注:最近我开始用 Esprima esprima.org 挖掘这个区域,而 Esprima 非常有趣。) @beatak 您是否设法使用 esprima 生成了类似的图表?【参考方案4】:https://github.com/mishoo/UglifyJS 允许访问 javascript 中的 ast。
ast.coffee
util = require 'util'
jsp = require('uglify-js').parser
orig_code = """
var a = function (x)
return x * x;
;
function b (x)
return a(x)
console.log(a(5));
console.log(b(5));
"""
ast = jsp.parse(orig_code)
console.log util.inspect ast, true, null, true
【讨论】:
以上是关于如何为给定的javascript生成调用图? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
如何为所有动态生成的 div 添加常用的 Javascript 函数? [复制]