2017-07-07 16:36:45 +00:00
|
|
|
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @unrestricted
|
|
|
|
*/
|
|
|
|
class ESTreeWalker {
|
|
|
|
/**
|
|
|
|
* @param {function(!ESTree.Node):(!Object|undefined)} beforeVisit
|
|
|
|
* @param {function(!ESTree.Node)=} afterVisit
|
|
|
|
*/
|
|
|
|
constructor(beforeVisit, afterVisit) {
|
|
|
|
this._beforeVisit = beforeVisit;
|
|
|
|
this._afterVisit = afterVisit || new Function();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!ESTree.Node} ast
|
|
|
|
*/
|
|
|
|
walk(ast) {
|
|
|
|
this._innerWalk(ast, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!ESTree.Node} node
|
|
|
|
* @param {?ESTree.Node} parent
|
|
|
|
*/
|
|
|
|
_innerWalk(node, parent) {
|
2022-06-14 11:55:35 +00:00
|
|
|
if (!node) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-07 16:36:45 +00:00
|
|
|
node.parent = parent;
|
|
|
|
|
|
|
|
if (this._beforeVisit.call(null, node) === ESTreeWalker.SkipSubtree) {
|
|
|
|
this._afterVisit.call(null, node);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-21 23:39:04 +00:00
|
|
|
const walkOrder = ESTreeWalker._walkOrder[node.type];
|
2022-06-14 11:55:35 +00:00
|
|
|
if (!walkOrder) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-07 16:36:45 +00:00
|
|
|
|
|
|
|
if (node.type === 'TemplateLiteral') {
|
2017-08-21 23:39:04 +00:00
|
|
|
const templateLiteral = /** @type {!ESTree.TemplateLiteralNode} */ (node);
|
|
|
|
const expressionsLength = templateLiteral.expressions.length;
|
2017-07-07 16:36:45 +00:00
|
|
|
for (let i = 0; i < expressionsLength; ++i) {
|
|
|
|
this._innerWalk(templateLiteral.quasis[i], templateLiteral);
|
|
|
|
this._innerWalk(templateLiteral.expressions[i], templateLiteral);
|
|
|
|
}
|
2020-05-07 10:54:55 +00:00
|
|
|
this._innerWalk(
|
|
|
|
templateLiteral.quasis[expressionsLength],
|
|
|
|
templateLiteral
|
|
|
|
);
|
2017-07-07 16:36:45 +00:00
|
|
|
} else {
|
|
|
|
for (let i = 0; i < walkOrder.length; ++i) {
|
2017-08-21 23:39:04 +00:00
|
|
|
const entity = node[walkOrder[i]];
|
2022-06-14 11:55:35 +00:00
|
|
|
if (Array.isArray(entity)) {
|
|
|
|
this._walkArray(entity, node);
|
|
|
|
} else {
|
|
|
|
this._innerWalk(entity, node);
|
|
|
|
}
|
2017-07-07 16:36:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this._afterVisit.call(null, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!Array.<!ESTree.Node>} nodeArray
|
|
|
|
* @param {?ESTree.Node} parentNode
|
|
|
|
*/
|
|
|
|
_walkArray(nodeArray, parentNode) {
|
2022-06-14 11:55:35 +00:00
|
|
|
for (let i = 0; i < nodeArray.length; ++i) {
|
2017-07-07 16:36:45 +00:00
|
|
|
this._innerWalk(nodeArray[i], parentNode);
|
2022-06-14 11:55:35 +00:00
|
|
|
}
|
2017-07-07 16:36:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @typedef {!Object} ESTreeWalker.SkipSubtree */
|
|
|
|
ESTreeWalker.SkipSubtree = {};
|
|
|
|
|
|
|
|
/** @enum {!Array.<string>} */
|
|
|
|
ESTreeWalker._walkOrder = {
|
2020-05-07 10:54:55 +00:00
|
|
|
AwaitExpression: ['argument'],
|
|
|
|
ArrayExpression: ['elements'],
|
|
|
|
ArrowFunctionExpression: ['params', 'body'],
|
|
|
|
AssignmentExpression: ['left', 'right'],
|
|
|
|
AssignmentPattern: ['left', 'right'],
|
|
|
|
BinaryExpression: ['left', 'right'],
|
|
|
|
BlockStatement: ['body'],
|
|
|
|
BreakStatement: ['label'],
|
|
|
|
CallExpression: ['callee', 'arguments'],
|
|
|
|
CatchClause: ['param', 'body'],
|
|
|
|
ClassBody: ['body'],
|
|
|
|
ClassDeclaration: ['id', 'superClass', 'body'],
|
|
|
|
ClassExpression: ['id', 'superClass', 'body'],
|
|
|
|
ConditionalExpression: ['test', 'consequent', 'alternate'],
|
|
|
|
ContinueStatement: ['label'],
|
|
|
|
DebuggerStatement: [],
|
|
|
|
DoWhileStatement: ['body', 'test'],
|
|
|
|
EmptyStatement: [],
|
|
|
|
ExpressionStatement: ['expression'],
|
|
|
|
ForInStatement: ['left', 'right', 'body'],
|
|
|
|
ForOfStatement: ['left', 'right', 'body'],
|
|
|
|
ForStatement: ['init', 'test', 'update', 'body'],
|
|
|
|
FunctionDeclaration: ['id', 'params', 'body'],
|
|
|
|
FunctionExpression: ['id', 'params', 'body'],
|
|
|
|
Identifier: [],
|
|
|
|
IfStatement: ['test', 'consequent', 'alternate'],
|
|
|
|
LabeledStatement: ['label', 'body'],
|
|
|
|
Literal: [],
|
|
|
|
LogicalExpression: ['left', 'right'],
|
|
|
|
MemberExpression: ['object', 'property'],
|
|
|
|
MethodDefinition: ['key', 'value'],
|
|
|
|
NewExpression: ['callee', 'arguments'],
|
|
|
|
ObjectExpression: ['properties'],
|
|
|
|
ObjectPattern: ['properties'],
|
|
|
|
ParenthesizedExpression: ['expression'],
|
|
|
|
Program: ['body'],
|
|
|
|
Property: ['key', 'value'],
|
|
|
|
ReturnStatement: ['argument'],
|
|
|
|
SequenceExpression: ['expressions'],
|
|
|
|
Super: [],
|
|
|
|
SwitchCase: ['test', 'consequent'],
|
|
|
|
SwitchStatement: ['discriminant', 'cases'],
|
|
|
|
TaggedTemplateExpression: ['tag', 'quasi'],
|
|
|
|
TemplateElement: [],
|
|
|
|
TemplateLiteral: ['quasis', 'expressions'],
|
|
|
|
ThisExpression: [],
|
|
|
|
ThrowStatement: ['argument'],
|
|
|
|
TryStatement: ['block', 'handler', 'finalizer'],
|
|
|
|
UnaryExpression: ['argument'],
|
|
|
|
UpdateExpression: ['argument'],
|
|
|
|
VariableDeclaration: ['declarations'],
|
|
|
|
VariableDeclarator: ['id', 'init'],
|
|
|
|
WhileStatement: ['test', 'body'],
|
|
|
|
WithStatement: ['object', 'body'],
|
|
|
|
YieldExpression: ['argument'],
|
2017-07-07 16:36:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = ESTreeWalker;
|