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)
のようにしてやることで、スコープオブジェクトを得ることができる。
うーん、素晴らしいモジュールだ。