| /* |
| * 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; |
| } |
| } |
| }; |