udało mi to za pomocą UglifyJS2 i Dot/GraphViz, w rodzaju kombinacja powyższych odpowiedzi i odpowiedzi na pytanie powiązanego.
Brakujące części, dla mnie, było, jak filtrować analizowane AST. Okazuje się, że UglifyJS ma obiekt TreeWalker, który zasadniczo stosuje funkcję do każdego węzła AST. Jest to kod mam tak daleko:
//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, '}');
go uruchomić z node, a następnie umieścić wyjście poprzez
dot -Tpng -o graph_name.png dot_file_name.dot
Uwagi:
Daje to dość podstawowy wykres - tylko czarno-biały i bez formatowania.
W ogóle nie przechwytuje ajaxa, i prawdopodobnie nie ma takich rzeczy, jak eval
lub with
albo jako others have mentioned.
Ponadto, w swoim obecnym stanie, zawiera on na wykresie: funkcje wywoływane przez inne funkcje (i w konsekwencji funkcje, które wywołują inne funkcje), funkcje, które są nazywane niezależnie, funkcje AND, które są zdefiniowane, ale nie wywoływane.
W wyniku tego wszystkiego może on ominąć rzeczy, które są istotne, lub zawierać rzeczy, które nie są istotne.Jest to jednak początek i wydaje się, że udało mi się osiągnąć to, o co prosiłem, i co doprowadziło mnie do tego pytania w pierwszej kolejności.
+1 świetne pytanie - – miku
Dlaczego nie używasz narzędzi programistycznych wbudowanych w obsługę profilowania javascript? – Tushar
Wygląda na to, że oryginalny wątek zniknął, a link jest teraz uszkodzony. :-( –