es**シリーズの仕様意訳第三回。前回はestraverseでした。今回はescope。
escope
escopeは、ASTを喰ってスコープを解析するためのモジュール。 estraverseと同じように、ASTをescope.analyzeに喰わせるだけで動く。 デモを見ると挙動がよく分かる。サンプルコードがモナドなあたりに玄人臭を感じますねw
使い方は前回、前々回同様、超カンタン。
escopeとASTを吐かせるためのesprimaをnpmで取ってくる。
npm install esprima npm install escope
あとはこんな感じで実行。
var esprima = require('esprima');
var escope = require('escope');
var ast = esprima.parse('console.log("Hello, world!");');
var scopes = escope.analyze(ast).scopes;
console.log(scopes);
このシリーズのモジュールは本当に質が高くて惚れ惚れする。 もうちょっとドキュメントがあると嬉しいんだけれど…。
得られるスコープオブジェクトの仕様は
/**
* スコープオブジェクト。
* @constructor
* @param {AstNode} block スコープを生成できるASTノード。FunctionExpression
* やWithStatementなどが該当する。
* @param {Object} opt オプション(ユーザがインスタンス化させるわけじゃないので割愛)。
*/
function Scope(block, opt) {
/**
* このスコープの種類。catch、with、global、functionのいずれか。
* @type {stirng}
*/
this.type;
/**
* このスコープに含まれる変数のマップ。
* @type {Map}
*/
this.set;
/**
* taints=汚れ?(よくわからない)
* @type {Map}
*/
this.taints;
/**
* このスコープが動的かどうか(type が global か with ならば true)。
* @type {boolean}
*/
this.dynamic;
/**
* このスコープを生成しているASTノード。
* @type {AstNode}
*/
this.block;
/**
* このスコープ(内包したスコープ含む)に含まれる代入式のうち、このスコープで定義されていない変数の配列。
* @type {Array.<Reference>}
*/
this.through;
/**
* このスコープによって宣言された変数の配列。arguments を含む。
* @type {Array.<Reference>}
*/
this.variables;
/**
* このスコープでアクセスされた変数の配列。
*/
this.references;
/**
* 内包する代入式の左辺が一時的に格納される。
* @type {null|AstNode}
*/
this.left;
/**
* このスコープ内で変数が定義されたとき、変数が登録されるスコープ。Withstatement などでは変数が登録されるスコープが、自身のスコープと一致しないので。
*/
this.variableScope;
/**
* Function式によるスコープかどうか。
* @type {boolean}
*/
this.functionExpressionScope;
/**
* evalが呼ばれたときにこのスコープにアクセスできるかどうか。
* @type {boolean}
*/
this.directCallToEvalScope;
/**
* thisキーワードが使われたかどうか。
* @type {boolean}
*/
this.thisFound;
/**
* ひとつ上のスコープ。
* @type {escope.Scope}
*/
this.upper = currentScope;
/**
* このスコープが strict モードかどうか。
* @type {boolean}
*/
this.isStrict = isStrictScope(this, block);
}
escope.analyze の戻り値は escope.ScopeManagerのインスタンスになっていて、スコープオブジェクトのリストはescope.ScopeManager#scopesから取得できる。
また、ASTそのものにスコープ情報を追加して欲しい場合は、escope.ScopeManager#attach() でスコープを生成しているASTノードに__$escope$__のプロパティ名でスコープオブジェクトが追加される(escope.ScopeManager#detach()で戻せる)。
また、関数宣言ノード/関数式ノードからスコープ情報が欲しい場合は、escope.ScopeManager#acquire(node)のようにしてやることで、スコープオブジェクトを得ることができる。
うーん、素晴らしいモジュールだ。