blob: 5dbed41c0c0f5a114c18bf779f31ed1e5a96bbfd [file] [log] [blame]
/*
* Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
// Visit ES6 ESTree compatible AST nodes in program order.
// <https://github.com/estree/estree>.
//
// The Node types and properties (for ES6) are described in:
// <https://github.com/estree/estree/blob/master/es6.md>
//
// The ESTree spec doesn't appear to be complete yet, so this
// currently assumes some Esprima nodes and properties.
// <http://esprima.org/demo/parse.html>
// FIXME: Add support for Import/Export/Modules nodes if we allow parsing modules.
ESTreeWalker = class ESTreeWalker
{
constructor(before, after)
{
console.assert(typeof before === "function");
console.assert(typeof after === "function");
this._before = before;
this._after = after;
}
walk(node)
{
this._walk(node, null);
}
// Private
_walk(node, parent)
{
if (!node)
return;
node.parent = parent;
this._before(node);
this._walkChildren(node);
this._after(node);
}
_walkArray(array, parent)
{
for (let i = 0; i < array.length; ++i)
this._walk(array[i], parent);
}
_walkChildren(node)
{
switch (node.type) {
case "AssignmentExpression":
this._walk(node.left, node);
this._walk(node.right, node);
break;
case "ArrayExpression":
case "ArrayPattern":
this._walkArray(node.elements, node);
break;
case "AssignmentPattern":
this._walk(node.left, node);
this._walk(node.right, node);
break;
case "AwaitExpression":
this._walk(node.argument, node);
break;
case "BlockStatement":
this._walkArray(node.body, node);
break;
case "BinaryExpression":
this._walk(node.left, node);
this._walk(node.right, node);
break;
case "BreakStatement":
case "ContinueStatement":
this._walk(node.label, node);
break;
case "CallExpression":
this._walk(node.callee, node);
this._walkArray(node.arguments, node);
break;
case "CatchClause":
this._walk(node.param, node);
this._walk(node.body, node);
break;
case "ClassBody":
this._walkArray(node.body, node);
break;
case "ClassDeclaration":
case "ClassExpression":
this._walk(node.id, node);
this._walk(node.superClass, node);
this._walk(node.body, node);
break;
case "DoWhileStatement":
this._walk(node.body, node);
this._walk(node.test, node);
break;
case "ExpressionStatement":
this._walk(node.expression, node);
break;
case "ForStatement":
this._walk(node.init, node);
this._walk(node.test, node);
this._walk(node.update, node);
this._walk(node.body, node);
break;
case "ForInStatement":
case "ForOfStatement":
this._walk(node.left, node);
this._walk(node.right, node);
this._walk(node.body, node);
break;
case "FunctionDeclaration":
case "FunctionExpression":
case "ArrowFunctionExpression":
this._walk(node.id, node);
this._walkArray(node.params, node);
this._walk(node.body, node);
break;
case "IfStatement":
this._walk(node.test, node);
this._walk(node.consequent, node);
this._walk(node.alternate, node);
break;
case "LabeledStatement":
this._walk(node.label, node);
this._walk(node.body, node);
break;
case "LogicalExpression":
this._walk(node.left, node);
this._walk(node.right, node);
break;
case "MemberExpression":
this._walk(node.object, node);
this._walk(node.property, node);
break;
case "MethodDefinition":
this._walk(node.key, node);
this._walk(node.value, node);
break;
case "NewExpression":
this._walk(node.callee, node);
this._walkArray(node.arguments, node);
break;
case "ObjectExpression":
case "ObjectPattern":
this._walkArray(node.properties, node);
break;
case "Program":
this._walkArray(node.body, node);
break;
case "Property":
this._walk(node.key, node);
this._walk(node.value, node);
break;
case "RestElement":
this._walk(node.argument, node);
break;
case "ReturnStatement":
this._walk(node.argument, node);
break;
case "SequenceExpression":
this._walkArray(node.expressions, node);
break;
case "SpreadElement":
this._walk(node.argument, node);
break;
case "SwitchStatement":
this._walk(node.discriminant, node);
this._walkArray(node.cases, node);
break;
case "SwitchCase":
this._walk(node.test, node);
this._walkArray(node.consequent, node);
break;
case "ConditionalExpression":
this._walk(node.test, node);
this._walk(node.consequent, node);
this._walk(node.alternate, node);
break;
case "TaggedTemplateExpression":
this._walk(node.tag, node);
this._walk(node.quasi, node);
break;
case "ThrowStatement":
this._walk(node.argument, node);
break;
case "TryStatement":
this._walk(node.block, node);
this._walk(node.handler, node);
this._walk(node.finalizer, node);
break;
case "UnaryExpression":
this._walk(node.argument, node);
break;
case "UpdateExpression":
this._walk(node.argument, node);
break;
case "VariableDeclaration":
this._walkArray(node.declarations, node);
break;
case "VariableDeclarator":
this._walk(node.id, node);
this._walk(node.init, node);
break;
case "WhileStatement":
this._walk(node.test, node);
this._walk(node.body, node);
break;
case "WithStatement":
this._walk(node.object, node);
this._walk(node.body, node);
break;
case "YieldExpression":
this._walk(node.argument, node);
break;
case "ExportAllDeclaration":
this._walk(node.source, node);
break;
case "ExportNamedDeclaration":
this._walk(node.declaration, node);
this._walkArray(node.specifiers, node);
this._walk(node.source, node);
break;
case "ExportDefaultDeclaration":
this._walk(node.declaration, node);
break;
case "ExportSpecifier":
this._walk(node.local, node);
this._walk(node.exported, node);
break;
case "ImportDeclaration":
this._walkArray(node.specifiers, node);
this._walk(node.source, node);
break;
case "ImportDefaultSpecifier":
this._walk(node.local, node);
break;
case "ImportNamespaceSpecifier":
this._walk(node.local, node);
break;
case "ImportSpecifier":
this._walk(node.imported, node);
this._walk(node.local, node);
break;
case "MetaProperty":
this._walk(node.meta, node);
this._walk(node.property, node);
break;
// Special case. We want to walk in program order,
// so walk quasi, expression, quasi, expression, etc.
case "TemplateLiteral":
for (var i = 0; i < node.expressions.length; ++i) {
this._walk(node.quasis[i], node);
this._walk(node.expressions[i], node);
}
break;
// Leaf nodes.
case "DebuggerStatement":
case "EmptyStatement":
case "Identifier":
case "Import":
case "Literal":
case "Super":
case "ThisExpression":
case "TemplateElement":
break;
default:
console.error("ESTreeWalker unhandled node type", node.type);
break;
}
}
};