98 lines
2.6 KiB
JavaScript
98 lines
2.6 KiB
JavaScript
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||
|
|
||
|
/*global exports:true*/
|
||
|
/*jslint node:true*/
|
||
|
"use strict";
|
||
|
|
||
|
var Syntax = require('esprima-fb').Syntax;
|
||
|
|
||
|
/**
|
||
|
* Executes visitor on the object and its children (recursively).
|
||
|
* While traversing the tree, a scope chain is built and passed to the visitor.
|
||
|
*
|
||
|
* If the visitor returns false, the object's children are not traversed.
|
||
|
*
|
||
|
* @param {object} object
|
||
|
* @param {function} visitor
|
||
|
* @param {?array} scopeChain
|
||
|
*/
|
||
|
function traverse(object, visitor, scopeChain) {
|
||
|
scopeChain = scopeChain || [{}];
|
||
|
|
||
|
var scope = scopeChain[0];
|
||
|
|
||
|
switch (object.type) {
|
||
|
case Syntax.VariableDeclaration:
|
||
|
object.declarations.forEach(function(decl) {
|
||
|
scope[decl.id.name] = decl.init;
|
||
|
});
|
||
|
break;
|
||
|
case Syntax.ClassDeclaration:
|
||
|
scope[object.id.name] = object;
|
||
|
break;
|
||
|
case Syntax.FunctionDeclaration:
|
||
|
// A function declaration creates a symbol in the current scope
|
||
|
scope[object.id.name] = object;
|
||
|
/* falls through */
|
||
|
case Syntax.FunctionExpression:
|
||
|
case Syntax.Program:
|
||
|
scopeChain = [{}].concat(scopeChain);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (object.type === Syntax.FunctionExpression ||
|
||
|
object.type === Syntax.FunctionDeclaration) {
|
||
|
// add parameters to the new scope
|
||
|
object.params.forEach(function(param) {
|
||
|
// since the value of the parameters are unknown during parsing time
|
||
|
// we set the value to `undefined`.
|
||
|
scopeChain[0][param.name] = undefined;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (object.type) {
|
||
|
if (visitor.call(null, object, scopeChain) === false) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var key in object) {
|
||
|
if (object.hasOwnProperty(key)) {
|
||
|
var child = object[key];
|
||
|
if (typeof child === 'object' && child !== null) {
|
||
|
traverse(child, visitor, scopeChain);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Executes visitor on the object and its children, but only traverses into
|
||
|
* children which can be statically analyzed and don't depend on runtime
|
||
|
* information.
|
||
|
*
|
||
|
* @param {object} object
|
||
|
* @param {function} visitor
|
||
|
* @param {?array} scopeChain
|
||
|
*/
|
||
|
function traverseFlat(object, visitor, scopeChain) {
|
||
|
traverse(object, function(node, scopeChain) {
|
||
|
switch (node.type) {
|
||
|
case Syntax.FunctionDeclaration:
|
||
|
case Syntax.FunctionExpression:
|
||
|
case Syntax.IfStatement:
|
||
|
case Syntax.WithStatement:
|
||
|
case Syntax.SwitchStatement:
|
||
|
case Syntax.TryStatement:
|
||
|
case Syntax.WhileStatement:
|
||
|
case Syntax.DoWhileStatement:
|
||
|
case Syntax.ForStatement:
|
||
|
case Syntax.ForInStatement:
|
||
|
return false;
|
||
|
}
|
||
|
return visitor(node, scopeChain);
|
||
|
}, scopeChain);
|
||
|
}
|
||
|
|
||
|
module.exports = traverseFlat;
|