関数のトレースをとる必要があったのですが、
esprimaを使えばいけるじゃないかと思って試してみました。
ASTをたどるのは面倒いなーと思っていたら、
estraverseでさくっといけました。
関数がオブジェクトのメンバのときの名前がまだうまくとれていないですが、素の関数定義のときは関数名をとってきてます。
未知のソースの解析とかに利用しようと思います。
サーバー側でコードを注入して各関数の実行時間をとれば
chrome developer toolとか使えない環境向けのプロファイラも作れそうだなっとも思っています。
あと、アスペクト指向的にコード注入できると嬉しいだろうかとも思ってみたり。
logger_injection.coffee
esprima = require 'esprima'
escodegen = require 'escodegen'
estraverse = require 'estraverse'
fs = require 'fs'
_funcID = 0
logger = (funcID, fileName, node, parent, loc) ->
name = 'anonymous'
if node.id?
name = node.id.name
else if parent.id
name = parent.id.name
logItem = {
id: funcID,
file: fileName,
func: name,
line: node.loc[loc].line
phase: loc
}
esprima.parse('logger.trace(' + JSON.stringify(logItem) + ' );').body
injectLogger = (source, fileName) ->
ast = esprima.parse source, {
loc: true
}
estraverse.traverse ast, {
enter: (node, parent) ->
if (node.type == 'FunctionExpression' ||
node.type == 'FunctionDeclaration')
_funcID++
node.body.body = logger(_funcID,
fileName,
node,
parent,
'start').concat node.body.body
node.body.body = node.body.body.concat logger(_funcID,
fileName,
node,
parent,
'end')
}
escodegen.generate ast
fs.readFile process.argv[2], (err, source) ->
console.log injectLogger source
追記:
esprimaの作者の方のesmorphというのを使えばもっと簡単にできるようでした。
ただし、npmにあがっているのは少しバージョン古いようなので、
https://github.com/ariya/esmorph
からとってきて使いました。
logger_injection.coffee esmorph版
esprima = require 'esprima'
escodegen = require 'escodegen'
esmorph = require 'esmorph'
fs = require 'fs'
logger = (fileName, fn, phase) ->
logItem = {
file: fileName,
func: fn.name,
line: fn.line,
range: fn.range,
phase: phase,
return: fn.return ? false
}
'logger.trace(' + JSON.stringify(logItem) + ' );'
injectLogger = (source, fileName) ->
tracers = [
(esmorph.Tracer.FunctionEntrance (fn) -> logger(fileName, fn, 'start')),
(esmorph.Tracer.FunctionExit (fn) -> logger(fileName, fn, 'end'))
]
code = esmorph.modify(source, tracers)
fs.readFile process.argv[2], (err, source) ->
console.log injectLogger source.toString()