You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
4.5 KiB
138 lines
4.5 KiB
// sample on how to use the parser and walker API to instrument some code |
|
|
|
var jsp = require("uglify-js").parser; |
|
var pro = require("uglify-js").uglify; |
|
|
|
function instrument(code) { |
|
var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want |
|
// to have start/end tokens embedded in the |
|
// statements |
|
var w = pro.ast_walker(); |
|
|
|
function trace (line, comment) { |
|
var code = pro.gen_code(line, { beautify: true }); |
|
var data = line[0] |
|
|
|
var args = [] |
|
if (!comment) comment = "" |
|
if (typeof data === "object") { |
|
code = code.split(/\n/).shift() |
|
args = [ [ "string", data.toString() ], |
|
[ "string", code ], |
|
[ "num", data.start.line ], |
|
[ "num", data.start.col ], |
|
[ "num", data.end.line ], |
|
[ "num", data.end.col ]] |
|
} else { |
|
args = [ [ "string", data ], |
|
[ "string", code ]] |
|
|
|
} |
|
return [ "call", [ "name", "trace" ], args ]; |
|
} |
|
|
|
// we're gonna need this to push elements that we're currently looking at, to avoid |
|
// endless recursion. |
|
var analyzing = []; |
|
function do_stat() { |
|
var ret; |
|
if (this[0].start && analyzing.indexOf(this) < 0) { |
|
// without the `analyzing' hack, w.walk(this) would re-enter here leading |
|
// to infinite recursion |
|
analyzing.push(this); |
|
ret = [ "splice", |
|
[ [ "stat", trace(this) ], |
|
w.walk(this) ]]; |
|
analyzing.pop(this); |
|
} |
|
return ret; |
|
} |
|
|
|
function do_cond(c, t, f) { |
|
return [ this[0], w.walk(c), |
|
["seq", trace(t), w.walk(t) ], |
|
["seq", trace(f), w.walk(f) ]]; |
|
} |
|
|
|
function do_binary(c, l, r) { |
|
if (c !== "&&" && c !== "||") { |
|
return [this[0], c, w.walk(l), w.walk(r)]; |
|
} |
|
return [ this[0], c, |
|
["seq", trace(l), w.walk(l) ], |
|
["seq", trace(r), w.walk(r) ]]; |
|
} |
|
|
|
var new_ast = w.with_walkers({ |
|
"stat" : do_stat, |
|
"label" : do_stat, |
|
"break" : do_stat, |
|
"continue" : do_stat, |
|
"debugger" : do_stat, |
|
"var" : do_stat, |
|
"const" : do_stat, |
|
"return" : do_stat, |
|
"throw" : do_stat, |
|
"try" : do_stat, |
|
"defun" : do_stat, |
|
"if" : do_stat, |
|
"while" : do_stat, |
|
"do" : do_stat, |
|
"for" : do_stat, |
|
"for-in" : do_stat, |
|
"switch" : do_stat, |
|
"with" : do_stat, |
|
"conditional" : do_cond, |
|
"binary" : do_binary |
|
}, function(){ |
|
return w.walk(ast); |
|
}); |
|
return pro.gen_code(new_ast, { beautify: true }); |
|
} |
|
|
|
|
|
////// test code follows. |
|
|
|
var code = instrument(test.toString()); |
|
console.log(code); |
|
|
|
function test() { |
|
// simple stats |
|
a = 5; |
|
c += a + b; |
|
"foo"; |
|
|
|
// var |
|
var foo = 5; |
|
const bar = 6, baz = 7; |
|
|
|
// switch block. note we can't track case lines the same way. |
|
switch ("foo") { |
|
case "foo": |
|
return 1; |
|
case "bar": |
|
return 2; |
|
} |
|
|
|
// for/for in |
|
for (var i = 0; i < 5; ++i) { |
|
console.log("Hello " + i); |
|
} |
|
for (var i in [ 1, 2, 3]) { |
|
console.log(i); |
|
} |
|
|
|
for (var i = 0; i < 5; ++i) |
|
console.log("foo"); |
|
|
|
for (var i = 0; i < 5; ++i) { |
|
console.log("foo"); |
|
} |
|
|
|
var k = plurp() ? 1 : 0; |
|
var x = a ? doX(y) && goZoo("zoo") |
|
: b ? blerg({ x: y }) |
|
: null; |
|
|
|
var x = X || Y; |
|
}
|
|
|