browser/devtools/shared/Parser.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/shared/Parser.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2337 @@
     1.4 +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +"use strict";
    1.10 +
    1.11 +const Ci = Components.interfaces;
    1.12 +const Cu = Components.utils;
    1.13 +
    1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.15 +const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
    1.16 +
    1.17 +XPCOMUtils.defineLazyModuleGetter(this,
    1.18 +  "Reflect", "resource://gre/modules/reflect.jsm");
    1.19 +
    1.20 +this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
    1.21 +
    1.22 +/**
    1.23 + * A JS parser using the reflection API.
    1.24 + */
    1.25 +this.Parser = function Parser() {
    1.26 +  this._cache = new Map();
    1.27 +  this.errors = [];
    1.28 +};
    1.29 +
    1.30 +Parser.prototype = {
    1.31 +  /**
    1.32 +   * Gets a collection of parser methods for a specified source.
    1.33 +   *
    1.34 +   * @param string aSource
    1.35 +   *        The source text content.
    1.36 +   * @param string aUrl [optional]
    1.37 +   *        The source url. The AST nodes will be cached, so you can use this
    1.38 +   *        identifier to avoid parsing the whole source again.
    1.39 +   */
    1.40 +  get: function(aSource, aUrl = "") {
    1.41 +    // Try to use the cached AST nodes, to avoid useless parsing operations.
    1.42 +    if (this._cache.has(aUrl)) {
    1.43 +      return this._cache.get(aUrl);
    1.44 +    }
    1.45 +
    1.46 +    // The source may not necessarily be JS, in which case we need to extract
    1.47 +    // all the scripts. Fastest/easiest way is with a regular expression.
    1.48 +    // Don't worry, the rules of using a <script> tag are really strict,
    1.49 +    // this will work.
    1.50 +    let regexp = /<script[^>]*>([^]*?)<\/script\s*>/gim;
    1.51 +    let syntaxTrees = [];
    1.52 +    let scriptMatches = [];
    1.53 +    let scriptMatch;
    1.54 +
    1.55 +    if (aSource.match(/^\s*</)) {
    1.56 +      // First non whitespace character is &lt, so most definitely HTML.
    1.57 +      while (scriptMatch = regexp.exec(aSource)) {
    1.58 +        scriptMatches.push(scriptMatch[1]); // Contents are captured at index 1.
    1.59 +      }
    1.60 +    }
    1.61 +
    1.62 +    // If there are no script matches, send the whole source directly to the
    1.63 +    // reflection API to generate the AST nodes.
    1.64 +    if (!scriptMatches.length) {
    1.65 +      // Reflect.parse throws when encounters a syntax error.
    1.66 +      try {
    1.67 +        let nodes = Reflect.parse(aSource);
    1.68 +        let length = aSource.length;
    1.69 +        syntaxTrees.push(new SyntaxTree(nodes, aUrl, length));
    1.70 +      } catch (e) {
    1.71 +        this.errors.push(e);
    1.72 +        DevToolsUtils.reportException(aUrl, e);
    1.73 +      }
    1.74 +    }
    1.75 +    // Generate the AST nodes for each script.
    1.76 +    else {
    1.77 +      for (let script of scriptMatches) {
    1.78 +        // Reflect.parse throws when encounters a syntax error.
    1.79 +        try {
    1.80 +          let nodes = Reflect.parse(script);
    1.81 +          let offset = aSource.indexOf(script);
    1.82 +          let length = script.length;
    1.83 +          syntaxTrees.push(new SyntaxTree(nodes, aUrl, length, offset));
    1.84 +        } catch (e) {
    1.85 +          this.errors.push(e);
    1.86 +          DevToolsUtils.reportException(aUrl, e);
    1.87 +        }
    1.88 +      }
    1.89 +    }
    1.90 +
    1.91 +    let pool = new SyntaxTreesPool(syntaxTrees, aUrl);
    1.92 +
    1.93 +    // Cache the syntax trees pool by the specified url. This is entirely
    1.94 +    // optional, but it's strongly encouraged to cache ASTs because
    1.95 +    // generating them can be costly with big/complex sources.
    1.96 +    if (aUrl) {
    1.97 +      this._cache.set(aUrl, pool);
    1.98 +    }
    1.99 +
   1.100 +    return pool;
   1.101 +  },
   1.102 +
   1.103 +  /**
   1.104 +   * Clears all the parsed sources from cache.
   1.105 +   */
   1.106 +  clearCache: function() {
   1.107 +    this._cache.clear();
   1.108 +  },
   1.109 +
   1.110 +  /**
   1.111 +   * Clears the AST for a particular source.
   1.112 +   *
   1.113 +   * @param String aUrl
   1.114 +   *        The URL of the source that is being cleared.
   1.115 +   */
   1.116 +  clearSource: function(aUrl) {
   1.117 +    this._cache.delete(aUrl);
   1.118 +  },
   1.119 +
   1.120 +  _cache: null,
   1.121 +  errors: null
   1.122 +};
   1.123 +
   1.124 +/**
   1.125 + * A pool handling a collection of AST nodes generated by the reflection API.
   1.126 + *
   1.127 + * @param object aSyntaxTrees
   1.128 + *        A collection of AST nodes generated for a source.
   1.129 + * @param string aUrl [optional]
   1.130 + *        The source url.
   1.131 + */
   1.132 +function SyntaxTreesPool(aSyntaxTrees, aUrl = "<unknown>") {
   1.133 +  this._trees = aSyntaxTrees;
   1.134 +  this._url = aUrl;
   1.135 +  this._cache = new Map();
   1.136 +}
   1.137 +
   1.138 +SyntaxTreesPool.prototype = {
   1.139 +  /**
   1.140 +   * @see SyntaxTree.prototype.getIdentifierAt
   1.141 +   */
   1.142 +  getIdentifierAt: function({ line, column, scriptIndex, ignoreLiterals }) {
   1.143 +    return this._call("getIdentifierAt", scriptIndex, line, column, ignoreLiterals)[0];
   1.144 +  },
   1.145 +
   1.146 +  /**
   1.147 +   * @see SyntaxTree.prototype.getNamedFunctionDefinitions
   1.148 +   */
   1.149 +  getNamedFunctionDefinitions: function(aSubstring) {
   1.150 +    return this._call("getNamedFunctionDefinitions", -1, aSubstring);
   1.151 +  },
   1.152 +
   1.153 +  /**
   1.154 +   * Gets the total number of scripts in the parent source.
   1.155 +   * @return number
   1.156 +   */
   1.157 +  get scriptCount() {
   1.158 +    return this._trees.length;
   1.159 +  },
   1.160 +
   1.161 +  /**
   1.162 +   * Finds the start and length of the script containing the specified offset
   1.163 +   * relative to its parent source.
   1.164 +   *
   1.165 +   * @param number aOffset
   1.166 +   *        The offset relative to the parent source.
   1.167 +   * @return object
   1.168 +   *         The offset and length relative to the enclosing script.
   1.169 +   */
   1.170 +  getScriptInfo: function(aOffset) {
   1.171 +    let info = { start: -1, length: -1, index: -1 };
   1.172 +
   1.173 +    for (let { offset, length } of this._trees) {
   1.174 +      info.index++;
   1.175 +      if (offset <= aOffset && offset + length >= aOffset) {
   1.176 +        info.start = offset;
   1.177 +        info.length = length;
   1.178 +        return info;
   1.179 +      }
   1.180 +    }
   1.181 +
   1.182 +    info.index = -1;
   1.183 +    return info;
   1.184 +  },
   1.185 +
   1.186 +  /**
   1.187 +   * Handles a request for a specific or all known syntax trees.
   1.188 +   *
   1.189 +   * @param string aFunction
   1.190 +   *        The function name to call on the SyntaxTree instances.
   1.191 +   * @param number aSyntaxTreeIndex
   1.192 +   *        The syntax tree for which to handle the request. If the tree at
   1.193 +   *        the specified index isn't found, the accumulated results for all
   1.194 +   *        syntax trees are returned.
   1.195 +   * @param any aParams
   1.196 +   *        Any kind params to pass to the request function.
   1.197 +   * @return array
   1.198 +   *         The results given by all known syntax trees.
   1.199 +   */
   1.200 +  _call: function(aFunction, aSyntaxTreeIndex, ...aParams) {
   1.201 +    let results = [];
   1.202 +    let requestId = [aFunction, aSyntaxTreeIndex, aParams].toSource();
   1.203 +
   1.204 +    if (this._cache.has(requestId)) {
   1.205 +      return this._cache.get(requestId);
   1.206 +    }
   1.207 +
   1.208 +    let requestedTree = this._trees[aSyntaxTreeIndex];
   1.209 +    let targettedTrees = requestedTree ? [requestedTree] : this._trees;
   1.210 +
   1.211 +    for (let syntaxTree of targettedTrees) {
   1.212 +      try {
   1.213 +        let parseResults = syntaxTree[aFunction].apply(syntaxTree, aParams);
   1.214 +        if (parseResults) {
   1.215 +          parseResults.sourceUrl = syntaxTree.url;
   1.216 +          parseResults.scriptLength = syntaxTree.length;
   1.217 +          parseResults.scriptOffset = syntaxTree.offset;
   1.218 +          results.push(parseResults);
   1.219 +        }
   1.220 +      } catch (e) {
   1.221 +        // Can't guarantee that the tree traversal logic is forever perfect :)
   1.222 +        // Language features may be added, in which case the recursive methods
   1.223 +        // need to be updated. If an exception is thrown here, file a bug.
   1.224 +        DevToolsUtils.reportException("Syntax tree visitor for " + aUrl, e);
   1.225 +      }
   1.226 +    }
   1.227 +    this._cache.set(requestId, results);
   1.228 +    return results;
   1.229 +  },
   1.230 +
   1.231 +  _trees: null,
   1.232 +  _cache: null
   1.233 +};
   1.234 +
   1.235 +/**
   1.236 + * A collection of AST nodes generated by the reflection API.
   1.237 + *
   1.238 + * @param object aNodes
   1.239 + *        The AST nodes.
   1.240 + * @param string aUrl
   1.241 + *        The source url.
   1.242 + * @param number aLength
   1.243 + *        The total number of chars of the parsed script in the parent source.
   1.244 + * @param number aOffset [optional]
   1.245 + *        The char offset of the parsed script in the parent source.
   1.246 + */
   1.247 +function SyntaxTree(aNodes, aUrl, aLength, aOffset = 0) {
   1.248 +  this.AST = aNodes;
   1.249 +  this.url = aUrl;
   1.250 +  this.length = aLength;
   1.251 +  this.offset = aOffset;
   1.252 +};
   1.253 +
   1.254 +SyntaxTree.prototype = {
   1.255 +  /**
   1.256 +   * Gets the identifier at the specified location.
   1.257 +   *
   1.258 +   * @param number aLine
   1.259 +   *        The line in the source.
   1.260 +   * @param number aColumn
   1.261 +   *        The column in the source.
   1.262 +   * @param boolean aIgnoreLiterals
   1.263 +   *        Specifies if alone literals should be ignored.
   1.264 +   * @return object
   1.265 +   *         An object containing identifier information as { name, location,
   1.266 +   *         evalString } properties, or null if nothing is found.
   1.267 +   */
   1.268 +  getIdentifierAt: function(aLine, aColumn, aIgnoreLiterals) {
   1.269 +    let info = null;
   1.270 +
   1.271 +    SyntaxTreeVisitor.walk(this.AST, {
   1.272 +      /**
   1.273 +       * Callback invoked for each identifier node.
   1.274 +       * @param Node aNode
   1.275 +       */
   1.276 +      onIdentifier: function(aNode) {
   1.277 +        if (ParserHelpers.nodeContainsPoint(aNode, aLine, aColumn)) {
   1.278 +          info = {
   1.279 +            name: aNode.name,
   1.280 +            location: ParserHelpers.getNodeLocation(aNode),
   1.281 +            evalString: ParserHelpers.getIdentifierEvalString(aNode)
   1.282 +          };
   1.283 +
   1.284 +          // Abruptly halt walking the syntax tree.
   1.285 +          SyntaxTreeVisitor.break = true;
   1.286 +        }
   1.287 +      },
   1.288 +
   1.289 +      /**
   1.290 +       * Callback invoked for each literal node.
   1.291 +       * @param Node aNode
   1.292 +       */
   1.293 +      onLiteral: function(aNode) {
   1.294 +        if (!aIgnoreLiterals) {
   1.295 +          this.onIdentifier(aNode);
   1.296 +        }
   1.297 +      },
   1.298 +
   1.299 +      /**
   1.300 +       * Callback invoked for each 'this' node.
   1.301 +       * @param Node aNode
   1.302 +       */
   1.303 +      onThisExpression: function(aNode) {
   1.304 +        this.onIdentifier(aNode);
   1.305 +      }
   1.306 +    });
   1.307 +
   1.308 +    return info;
   1.309 +  },
   1.310 +
   1.311 +  /**
   1.312 +   * Searches for all function definitions (declarations and expressions)
   1.313 +   * whose names (or inferred names) contain a string.
   1.314 +   *
   1.315 +   * @param string aSubstring
   1.316 +   *        The string to be contained in the function name (or inferred name).
   1.317 +   *        Can be an empty string to match all functions.
   1.318 +   * @return array
   1.319 +   *         All the matching function declarations and expressions, as
   1.320 +   *         { functionName, functionLocation ... } object hashes.
   1.321 +   */
   1.322 +  getNamedFunctionDefinitions: function(aSubstring) {
   1.323 +    let lowerCaseToken = aSubstring.toLowerCase();
   1.324 +    let store = [];
   1.325 +
   1.326 +    SyntaxTreeVisitor.walk(this.AST, {
   1.327 +      /**
   1.328 +       * Callback invoked for each function declaration node.
   1.329 +       * @param Node aNode
   1.330 +       */
   1.331 +      onFunctionDeclaration: function(aNode) {
   1.332 +        let functionName = aNode.id.name;
   1.333 +        if (functionName.toLowerCase().contains(lowerCaseToken)) {
   1.334 +          store.push({
   1.335 +            functionName: functionName,
   1.336 +            functionLocation: ParserHelpers.getNodeLocation(aNode)
   1.337 +          });
   1.338 +        }
   1.339 +      },
   1.340 +
   1.341 +      /**
   1.342 +       * Callback invoked for each function expression node.
   1.343 +       * @param Node aNode
   1.344 +       */
   1.345 +      onFunctionExpression: function(aNode) {
   1.346 +        // Function expressions don't necessarily have a name.
   1.347 +        let functionName = aNode.id ? aNode.id.name : "";
   1.348 +        let functionLocation = ParserHelpers.getNodeLocation(aNode);
   1.349 +
   1.350 +        // Infer the function's name from an enclosing syntax tree node.
   1.351 +        let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
   1.352 +        let inferredName = inferredInfo.name;
   1.353 +        let inferredChain = inferredInfo.chain;
   1.354 +        let inferredLocation = inferredInfo.loc;
   1.355 +
   1.356 +        // Current node may be part of a larger assignment expression stack.
   1.357 +        if (aNode._parent.type == "AssignmentExpression") {
   1.358 +          this.onFunctionExpression(aNode._parent);
   1.359 +        }
   1.360 +
   1.361 +        if ((functionName && functionName.toLowerCase().contains(lowerCaseToken)) ||
   1.362 +            (inferredName && inferredName.toLowerCase().contains(lowerCaseToken))) {
   1.363 +          store.push({
   1.364 +            functionName: functionName,
   1.365 +            functionLocation: functionLocation,
   1.366 +            inferredName: inferredName,
   1.367 +            inferredChain: inferredChain,
   1.368 +            inferredLocation: inferredLocation
   1.369 +          });
   1.370 +        }
   1.371 +      },
   1.372 +
   1.373 +      /**
   1.374 +       * Callback invoked for each arrow expression node.
   1.375 +       * @param Node aNode
   1.376 +       */
   1.377 +      onArrowExpression: function(aNode) {
   1.378 +        // Infer the function's name from an enclosing syntax tree node.
   1.379 +        let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
   1.380 +        let inferredName = inferredInfo.name;
   1.381 +        let inferredChain = inferredInfo.chain;
   1.382 +        let inferredLocation = inferredInfo.loc;
   1.383 +
   1.384 +        // Current node may be part of a larger assignment expression stack.
   1.385 +        if (aNode._parent.type == "AssignmentExpression") {
   1.386 +          this.onFunctionExpression(aNode._parent);
   1.387 +        }
   1.388 +
   1.389 +        if (inferredName && inferredName.toLowerCase().contains(lowerCaseToken)) {
   1.390 +          store.push({
   1.391 +            inferredName: inferredName,
   1.392 +            inferredChain: inferredChain,
   1.393 +            inferredLocation: inferredLocation
   1.394 +          });
   1.395 +        }
   1.396 +      }
   1.397 +    });
   1.398 +
   1.399 +    return store;
   1.400 +  },
   1.401 +
   1.402 +  AST: null,
   1.403 +  url: "",
   1.404 +  length: 0,
   1.405 +  offset: 0
   1.406 +};
   1.407 +
   1.408 +/**
   1.409 + * Parser utility methods.
   1.410 + */
   1.411 +let ParserHelpers = {
   1.412 +  /**
   1.413 +   * Gets the location information for a node. Not all nodes have a
   1.414 +   * location property directly attached, or the location information
   1.415 +   * is incorrect, in which cases it's accessible via the parent.
   1.416 +   *
   1.417 +   * @param Node aNode
   1.418 +   *        The node who's location needs to be retrieved.
   1.419 +   * @return object
   1.420 +   *         An object containing { line, column } information.
   1.421 +   */
   1.422 +  getNodeLocation: function(aNode) {
   1.423 +    if (aNode.type != "Identifier") {
   1.424 +      return aNode.loc;
   1.425 +    }
   1.426 +    // Work around the fact that some identifier nodes don't have the
   1.427 +    // correct location attached.
   1.428 +    let { loc: parentLocation, type: parentType } = aNode._parent;
   1.429 +    let { loc: nodeLocation } = aNode;
   1.430 +    if (!nodeLocation) {
   1.431 +      if (parentType == "FunctionDeclaration" ||
   1.432 +          parentType == "FunctionExpression") {
   1.433 +        // e.g. "function foo() {}" or "{ bar: function foo() {} }"
   1.434 +        // The location is unavailable for the identifier node "foo".
   1.435 +        let loc = JSON.parse(JSON.stringify(parentLocation));
   1.436 +        loc.end.line = loc.start.line;
   1.437 +        loc.end.column = loc.start.column + aNode.name.length;
   1.438 +        return loc;
   1.439 +      }
   1.440 +      if (parentType == "MemberExpression") {
   1.441 +        // e.g. "foo.bar"
   1.442 +        // The location is unavailable for the identifier node "bar".
   1.443 +        let loc = JSON.parse(JSON.stringify(parentLocation));
   1.444 +        loc.start.line = loc.end.line;
   1.445 +        loc.start.column = loc.end.column - aNode.name.length;
   1.446 +        return loc;
   1.447 +      }
   1.448 +      if (parentType == "LabeledStatement") {
   1.449 +        // e.g. label: ...
   1.450 +        // The location is unavailable for the identifier node "label".
   1.451 +        let loc = JSON.parse(JSON.stringify(parentLocation));
   1.452 +        loc.end.line = loc.start.line;
   1.453 +        loc.end.column = loc.start.column + aNode.name.length;
   1.454 +        return loc;
   1.455 +      }
   1.456 +      if (parentType == "ContinueStatement") {
   1.457 +        // e.g. continue label
   1.458 +        // The location is unavailable for the identifier node "label".
   1.459 +        let loc = JSON.parse(JSON.stringify(parentLocation));
   1.460 +        loc.start.line = loc.end.line;
   1.461 +        loc.start.column = loc.end.column - aNode.name.length;
   1.462 +        return loc;
   1.463 +      }
   1.464 +    } else {
   1.465 +      if (parentType == "VariableDeclarator") {
   1.466 +        // e.g. "let foo = 42"
   1.467 +        // The location incorrectly spans across the whole variable declaration,
   1.468 +        // not just the identifier node "foo".
   1.469 +        let loc = JSON.parse(JSON.stringify(nodeLocation));
   1.470 +        loc.end.line = loc.start.line;
   1.471 +        loc.end.column = loc.start.column + aNode.name.length;
   1.472 +        return loc;
   1.473 +      }
   1.474 +    }
   1.475 +    return aNode.loc;
   1.476 +  },
   1.477 +
   1.478 +  /**
   1.479 +   * Checks if a node's bounds contains a specified line.
   1.480 +   *
   1.481 +   * @param Node aNode
   1.482 +   *        The node's bounds used as reference.
   1.483 +   * @param number aLine
   1.484 +   *        The line number to check.
   1.485 +   * @return boolean
   1.486 +   *         True if the line and column is contained in the node's bounds.
   1.487 +   */
   1.488 +  nodeContainsLine: function(aNode, aLine) {
   1.489 +    let { start: s, end: e } = this.getNodeLocation(aNode);
   1.490 +    return s.line <= aLine && e.line >= aLine;
   1.491 +  },
   1.492 +
   1.493 +  /**
   1.494 +   * Checks if a node's bounds contains a specified line and column.
   1.495 +   *
   1.496 +   * @param Node aNode
   1.497 +   *        The node's bounds used as reference.
   1.498 +   * @param number aLine
   1.499 +   *        The line number to check.
   1.500 +   * @param number aColumn
   1.501 +   *        The column number to check.
   1.502 +   * @return boolean
   1.503 +   *         True if the line and column is contained in the node's bounds.
   1.504 +   */
   1.505 +  nodeContainsPoint: function(aNode, aLine, aColumn) {
   1.506 +    let { start: s, end: e } = this.getNodeLocation(aNode);
   1.507 +    return s.line == aLine && e.line == aLine &&
   1.508 +           s.column <= aColumn && e.column >= aColumn;
   1.509 +  },
   1.510 +
   1.511 +  /**
   1.512 +   * Try to infer a function expression's name & other details based on the
   1.513 +   * enclosing VariableDeclarator, AssignmentExpression or ObjectExpression.
   1.514 +   *
   1.515 +   * @param Node aNode
   1.516 +   *        The function expression node to get the name for.
   1.517 +   * @return object
   1.518 +   *         The inferred function name, or empty string can't infer the name,
   1.519 +   *         along with the chain (a generic "context", like a prototype chain)
   1.520 +   *         and location if available.
   1.521 +   */
   1.522 +  inferFunctionExpressionInfo: function(aNode) {
   1.523 +    let parent = aNode._parent;
   1.524 +
   1.525 +    // A function expression may be defined in a variable declarator,
   1.526 +    // e.g. var foo = function(){}, in which case it is possible to infer
   1.527 +    // the variable name.
   1.528 +    if (parent.type == "VariableDeclarator") {
   1.529 +      return {
   1.530 +        name: parent.id.name,
   1.531 +        chain: null,
   1.532 +        loc: this.getNodeLocation(parent.id)
   1.533 +      };
   1.534 +    }
   1.535 +
   1.536 +    // Function expressions can also be defined in assignment expressions,
   1.537 +    // e.g. foo = function(){} or foo.bar = function(){}, in which case it is
   1.538 +    // possible to infer the assignee name ("foo" and "bar" respectively).
   1.539 +    if (parent.type == "AssignmentExpression") {
   1.540 +      let propertyChain = this._getMemberExpressionPropertyChain(parent.left);
   1.541 +      let propertyLeaf = propertyChain.pop();
   1.542 +      return {
   1.543 +        name: propertyLeaf,
   1.544 +        chain: propertyChain,
   1.545 +        loc: this.getNodeLocation(parent.left)
   1.546 +      };
   1.547 +    }
   1.548 +
   1.549 +    // If a function expression is defined in an object expression,
   1.550 +    // e.g. { foo: function(){} }, then it is possible to infer the name
   1.551 +    // from the corresponding property.
   1.552 +    if (parent.type == "ObjectExpression") {
   1.553 +      let propertyKey = this._getObjectExpressionPropertyKeyForValue(aNode);
   1.554 +      let propertyChain = this._getObjectExpressionPropertyChain(parent);
   1.555 +      let propertyLeaf = propertyKey.name;
   1.556 +      return {
   1.557 +        name: propertyLeaf,
   1.558 +        chain: propertyChain,
   1.559 +        loc: this.getNodeLocation(propertyKey)
   1.560 +      };
   1.561 +    }
   1.562 +
   1.563 +    // Can't infer the function expression's name.
   1.564 +    return {
   1.565 +      name: "",
   1.566 +      chain: null,
   1.567 +      loc: null
   1.568 +    };
   1.569 +  },
   1.570 +
   1.571 +  /**
   1.572 +   * Gets the name of an object expression's property to which a specified
   1.573 +   * value is assigned.
   1.574 +   *
   1.575 +   * Used for inferring function expression information and retrieving
   1.576 +   * an identifier evaluation string.
   1.577 +   *
   1.578 +   * For example, if aNode represents the "bar" identifier in a hypothetical
   1.579 +   * "{ foo: bar }" object expression, the returned node is the "foo" identifier.
   1.580 +   *
   1.581 +   * @param Node aNode
   1.582 +   *        The value node in an object expression.
   1.583 +   * @return object
   1.584 +   *         The key identifier node in the object expression.
   1.585 +   */
   1.586 +  _getObjectExpressionPropertyKeyForValue: function(aNode) {
   1.587 +    let parent = aNode._parent;
   1.588 +    if (parent.type != "ObjectExpression") {
   1.589 +      return null;
   1.590 +    }
   1.591 +    for (let property of parent.properties) {
   1.592 +      if (property.value == aNode) {
   1.593 +        return property.key;
   1.594 +      }
   1.595 +    }
   1.596 +  },
   1.597 +
   1.598 +  /**
   1.599 +   * Gets an object expression's property chain to its parent
   1.600 +   * variable declarator or assignment expression, if available.
   1.601 +   *
   1.602 +   * Used for inferring function expression information and retrieving
   1.603 +   * an identifier evaluation string.
   1.604 +   *
   1.605 +   * For example, if aNode represents the "baz: {}" object expression in a
   1.606 +   * hypothetical "foo = { bar: { baz: {} } }" assignment expression, the
   1.607 +   * returned chain is ["foo", "bar", "baz"].
   1.608 +   *
   1.609 +   * @param Node aNode
   1.610 +   *        The object expression node to begin the scan from.
   1.611 +   * @param array aStore [optional]
   1.612 +   *        The chain to store the nodes into.
   1.613 +   * @return array
   1.614 +   *         The chain to the parent variable declarator, as strings.
   1.615 +   */
   1.616 +  _getObjectExpressionPropertyChain: function(aNode, aStore = []) {
   1.617 +    switch (aNode.type) {
   1.618 +      case "ObjectExpression":
   1.619 +        this._getObjectExpressionPropertyChain(aNode._parent, aStore);
   1.620 +        let propertyKey = this._getObjectExpressionPropertyKeyForValue(aNode);
   1.621 +        if (propertyKey) {
   1.622 +          aStore.push(propertyKey.name);
   1.623 +        }
   1.624 +        break;
   1.625 +      // Handle "var foo = { ... }" variable declarators.
   1.626 +      case "VariableDeclarator":
   1.627 +        aStore.push(aNode.id.name);
   1.628 +        break;
   1.629 +      // Handle "foo.bar = { ... }" assignment expressions, since they're
   1.630 +      // commonly used when defining an object's prototype methods; e.g:
   1.631 +      // "Foo.prototype = { ... }".
   1.632 +      case "AssignmentExpression":
   1.633 +        this._getMemberExpressionPropertyChain(aNode.left, aStore);
   1.634 +        break;
   1.635 +      // Additionally handle stuff like "foo = bar.baz({ ... })", because it's
   1.636 +      // commonly used in prototype-based inheritance in many libraries; e.g:
   1.637 +      // "Foo = Bar.extend({ ... })".
   1.638 +      case "NewExpression":
   1.639 +      case "CallExpression":
   1.640 +        this._getObjectExpressionPropertyChain(aNode._parent, aStore);
   1.641 +        break;
   1.642 +    }
   1.643 +    return aStore;
   1.644 +  },
   1.645 +
   1.646 +  /**
   1.647 +   * Gets a member expression's property chain.
   1.648 +   *
   1.649 +   * Used for inferring function expression information and retrieving
   1.650 +   * an identifier evaluation string.
   1.651 +   *
   1.652 +   * For example, if aNode represents a hypothetical "foo.bar.baz"
   1.653 +   * member expression, the returned chain ["foo", "bar", "baz"].
   1.654 +   *
   1.655 +   * More complex expressions like foo.bar().baz are intentionally not handled.
   1.656 +   *
   1.657 +   * @param Node aNode
   1.658 +   *        The member expression node to begin the scan from.
   1.659 +   * @param array aStore [optional]
   1.660 +   *        The chain to store the nodes into.
   1.661 +   * @return array
   1.662 +   *         The full member chain, as strings.
   1.663 +   */
   1.664 +  _getMemberExpressionPropertyChain: function(aNode, aStore = []) {
   1.665 +    switch (aNode.type) {
   1.666 +      case "MemberExpression":
   1.667 +        this._getMemberExpressionPropertyChain(aNode.object, aStore);
   1.668 +        this._getMemberExpressionPropertyChain(aNode.property, aStore);
   1.669 +        break;
   1.670 +      case "ThisExpression":
   1.671 +        aStore.push("this");
   1.672 +        break;
   1.673 +      case "Identifier":
   1.674 +        aStore.push(aNode.name);
   1.675 +        break;
   1.676 +    }
   1.677 +    return aStore;
   1.678 +  },
   1.679 +
   1.680 +  /**
   1.681 +   * Returns an evaluation string which can be used to obtain the
   1.682 +   * current value for the respective identifier.
   1.683 +   *
   1.684 +   * @param Node aNode
   1.685 +   *        The leaf node (e.g. Identifier, Literal) to begin the scan from.
   1.686 +   * @return string
   1.687 +   *         The corresponding evaluation string, or empty string if
   1.688 +   *         the specified leaf node can't be used.
   1.689 +   */
   1.690 +  getIdentifierEvalString: function(aNode) {
   1.691 +    switch (aNode._parent.type) {
   1.692 +      case "ObjectExpression":
   1.693 +        // If the identifier is the actual property value, it can be used
   1.694 +        // directly as an evaluation string. Otherwise, construct the property
   1.695 +        // access chain, since the value might have changed.
   1.696 +        if (!this._getObjectExpressionPropertyKeyForValue(aNode)) {
   1.697 +          let propertyChain = this._getObjectExpressionPropertyChain(aNode._parent);
   1.698 +          let propertyLeaf = aNode.name;
   1.699 +          return [...propertyChain, propertyLeaf].join(".");
   1.700 +        }
   1.701 +        break;
   1.702 +      case "MemberExpression":
   1.703 +        // Make sure this is a property identifier, not the parent object.
   1.704 +        if (aNode._parent.property == aNode) {
   1.705 +          return this._getMemberExpressionPropertyChain(aNode._parent).join(".");
   1.706 +        }
   1.707 +        break;
   1.708 +    }
   1.709 +    switch (aNode.type) {
   1.710 +      case "ThisExpression":
   1.711 +        return "this";
   1.712 +      case "Identifier":
   1.713 +        return aNode.name;
   1.714 +      case "Literal":
   1.715 +        return uneval(aNode.value);
   1.716 +      default:
   1.717 +        return "";
   1.718 +    }
   1.719 +  }
   1.720 +};
   1.721 +
   1.722 +/**
   1.723 + * A visitor for a syntax tree generated by the reflection API.
   1.724 + * See https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API.
   1.725 + *
   1.726 + * All node types implement the following interface:
   1.727 + * interface Node {
   1.728 + *   type: string;
   1.729 + *   loc: SourceLocation | null;
   1.730 + * }
   1.731 + */
   1.732 +let SyntaxTreeVisitor = {
   1.733 +  /**
   1.734 +   * Walks a syntax tree.
   1.735 +   *
   1.736 +   * @param object aTree
   1.737 +   *        The AST nodes generated by the reflection API
   1.738 +   * @param object aCallbacks
   1.739 +   *        A map of all the callbacks to invoke when passing through certain
   1.740 +   *        types of noes (e.g: onFunctionDeclaration, onBlockStatement etc.).
   1.741 +   */
   1.742 +  walk: function(aTree, aCallbacks) {
   1.743 +    this.break = false;
   1.744 +    this[aTree.type](aTree, aCallbacks);
   1.745 +  },
   1.746 +
   1.747 +  /**
   1.748 +   * Filters all the nodes in this syntax tree based on a predicate.
   1.749 +   *
   1.750 +   * @param object aTree
   1.751 +   *        The AST nodes generated by the reflection API
   1.752 +   * @param function aPredicate
   1.753 +   *        The predicate ran on each node.
   1.754 +   * @return array
   1.755 +   *         An array of nodes validating the predicate.
   1.756 +   */
   1.757 +  filter: function(aTree, aPredicate) {
   1.758 +    let store = [];
   1.759 +    this.walk(aTree, { onNode: e => { if (aPredicate(e)) store.push(e); } });
   1.760 +    return store;
   1.761 +  },
   1.762 +
   1.763 +  /**
   1.764 +   * A flag checked on each node in the syntax tree. If true, walking is
   1.765 +   * abruptly halted.
   1.766 +   */
   1.767 +  break: false,
   1.768 +
   1.769 +  /**
   1.770 +   * A complete program source tree.
   1.771 +   *
   1.772 +   * interface Program <: Node {
   1.773 +   *   type: "Program";
   1.774 +   *   body: [ Statement ];
   1.775 +   * }
   1.776 +   */
   1.777 +  Program: function(aNode, aCallbacks) {
   1.778 +    if (aCallbacks.onProgram) {
   1.779 +      aCallbacks.onProgram(aNode);
   1.780 +    }
   1.781 +    for (let statement of aNode.body) {
   1.782 +      this[statement.type](statement, aNode, aCallbacks);
   1.783 +    }
   1.784 +  },
   1.785 +
   1.786 +  /**
   1.787 +   * Any statement.
   1.788 +   *
   1.789 +   * interface Statement <: Node { }
   1.790 +   */
   1.791 +  Statement: function(aNode, aParent, aCallbacks) {
   1.792 +    aNode._parent = aParent;
   1.793 +
   1.794 +    if (this.break) {
   1.795 +      return;
   1.796 +    }
   1.797 +    if (aCallbacks.onNode) {
   1.798 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.799 +        return;
   1.800 +      }
   1.801 +    }
   1.802 +    if (aCallbacks.onStatement) {
   1.803 +      aCallbacks.onStatement(aNode);
   1.804 +    }
   1.805 +  },
   1.806 +
   1.807 +  /**
   1.808 +   * An empty statement, i.e., a solitary semicolon.
   1.809 +   *
   1.810 +   * interface EmptyStatement <: Statement {
   1.811 +   *   type: "EmptyStatement";
   1.812 +   * }
   1.813 +   */
   1.814 +  EmptyStatement: function(aNode, aParent, aCallbacks) {
   1.815 +    aNode._parent = aParent;
   1.816 +
   1.817 +    if (this.break) {
   1.818 +      return;
   1.819 +    }
   1.820 +    if (aCallbacks.onNode) {
   1.821 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.822 +        return;
   1.823 +      }
   1.824 +    }
   1.825 +    if (aCallbacks.onEmptyStatement) {
   1.826 +      aCallbacks.onEmptyStatement(aNode);
   1.827 +    }
   1.828 +  },
   1.829 +
   1.830 +  /**
   1.831 +   * A block statement, i.e., a sequence of statements surrounded by braces.
   1.832 +   *
   1.833 +   * interface BlockStatement <: Statement {
   1.834 +   *   type: "BlockStatement";
   1.835 +   *   body: [ Statement ];
   1.836 +   * }
   1.837 +   */
   1.838 +  BlockStatement: function(aNode, aParent, aCallbacks) {
   1.839 +    aNode._parent = aParent;
   1.840 +
   1.841 +    if (this.break) {
   1.842 +      return;
   1.843 +    }
   1.844 +    if (aCallbacks.onNode) {
   1.845 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.846 +        return;
   1.847 +      }
   1.848 +    }
   1.849 +    if (aCallbacks.onBlockStatement) {
   1.850 +      aCallbacks.onBlockStatement(aNode);
   1.851 +    }
   1.852 +    for (let statement of aNode.body) {
   1.853 +      this[statement.type](statement, aNode, aCallbacks);
   1.854 +    }
   1.855 +  },
   1.856 +
   1.857 +  /**
   1.858 +   * An expression statement, i.e., a statement consisting of a single expression.
   1.859 +   *
   1.860 +   * interface ExpressionStatement <: Statement {
   1.861 +   *   type: "ExpressionStatement";
   1.862 +   *   expression: Expression;
   1.863 +   * }
   1.864 +   */
   1.865 +  ExpressionStatement: function(aNode, aParent, aCallbacks) {
   1.866 +    aNode._parent = aParent;
   1.867 +
   1.868 +    if (this.break) {
   1.869 +      return;
   1.870 +    }
   1.871 +    if (aCallbacks.onNode) {
   1.872 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.873 +        return;
   1.874 +      }
   1.875 +    }
   1.876 +    if (aCallbacks.onExpressionStatement) {
   1.877 +      aCallbacks.onExpressionStatement(aNode);
   1.878 +    }
   1.879 +    this[aNode.expression.type](aNode.expression, aNode, aCallbacks);
   1.880 +  },
   1.881 +
   1.882 +  /**
   1.883 +   * An if statement.
   1.884 +   *
   1.885 +   * interface IfStatement <: Statement {
   1.886 +   *   type: "IfStatement";
   1.887 +   *   test: Expression;
   1.888 +   *   consequent: Statement;
   1.889 +   *   alternate: Statement | null;
   1.890 +   * }
   1.891 +   */
   1.892 +  IfStatement: function(aNode, aParent, aCallbacks) {
   1.893 +    aNode._parent = aParent;
   1.894 +
   1.895 +    if (this.break) {
   1.896 +      return;
   1.897 +    }
   1.898 +    if (aCallbacks.onNode) {
   1.899 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.900 +        return;
   1.901 +      }
   1.902 +    }
   1.903 +    if (aCallbacks.onIfStatement) {
   1.904 +      aCallbacks.onIfStatement(aNode);
   1.905 +    }
   1.906 +    this[aNode.test.type](aNode.test, aNode, aCallbacks);
   1.907 +    this[aNode.consequent.type](aNode.consequent, aNode, aCallbacks);
   1.908 +    if (aNode.alternate) {
   1.909 +      this[aNode.alternate.type](aNode.alternate, aNode, aCallbacks);
   1.910 +    }
   1.911 +  },
   1.912 +
   1.913 +  /**
   1.914 +   * A labeled statement, i.e., a statement prefixed by a break/continue label.
   1.915 +   *
   1.916 +   * interface LabeledStatement <: Statement {
   1.917 +   *   type: "LabeledStatement";
   1.918 +   *   label: Identifier;
   1.919 +   *   body: Statement;
   1.920 +   * }
   1.921 +   */
   1.922 +  LabeledStatement: function(aNode, aParent, aCallbacks) {
   1.923 +    aNode._parent = aParent;
   1.924 +
   1.925 +    if (this.break) {
   1.926 +      return;
   1.927 +    }
   1.928 +    if (aCallbacks.onNode) {
   1.929 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.930 +        return;
   1.931 +      }
   1.932 +    }
   1.933 +    if (aCallbacks.onLabeledStatement) {
   1.934 +      aCallbacks.onLabeledStatement(aNode);
   1.935 +    }
   1.936 +    this[aNode.label.type](aNode.label, aNode, aCallbacks);
   1.937 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
   1.938 +  },
   1.939 +
   1.940 +  /**
   1.941 +   * A break statement.
   1.942 +   *
   1.943 +   * interface BreakStatement <: Statement {
   1.944 +   *   type: "BreakStatement";
   1.945 +   *   label: Identifier | null;
   1.946 +   * }
   1.947 +   */
   1.948 +  BreakStatement: function(aNode, aParent, aCallbacks) {
   1.949 +    aNode._parent = aParent;
   1.950 +
   1.951 +    if (this.break) {
   1.952 +      return;
   1.953 +    }
   1.954 +    if (aCallbacks.onNode) {
   1.955 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.956 +        return;
   1.957 +      }
   1.958 +    }
   1.959 +    if (aCallbacks.onBreakStatement) {
   1.960 +      aCallbacks.onBreakStatement(aNode);
   1.961 +    }
   1.962 +    if (aNode.label) {
   1.963 +      this[aNode.label.type](aNode.label, aNode, aCallbacks);
   1.964 +    }
   1.965 +  },
   1.966 +
   1.967 +  /**
   1.968 +   * A continue statement.
   1.969 +   *
   1.970 +   * interface ContinueStatement <: Statement {
   1.971 +   *   type: "ContinueStatement";
   1.972 +   *   label: Identifier | null;
   1.973 +   * }
   1.974 +   */
   1.975 +  ContinueStatement: function(aNode, aParent, aCallbacks) {
   1.976 +    aNode._parent = aParent;
   1.977 +
   1.978 +    if (this.break) {
   1.979 +      return;
   1.980 +    }
   1.981 +    if (aCallbacks.onNode) {
   1.982 +      if (aCallbacks.onNode(aNode, aParent) === false) {
   1.983 +        return;
   1.984 +      }
   1.985 +    }
   1.986 +    if (aCallbacks.onContinueStatement) {
   1.987 +      aCallbacks.onContinueStatement(aNode);
   1.988 +    }
   1.989 +    if (aNode.label) {
   1.990 +      this[aNode.label.type](aNode.label, aNode, aCallbacks);
   1.991 +    }
   1.992 +  },
   1.993 +
   1.994 +  /**
   1.995 +   * A with statement.
   1.996 +   *
   1.997 +   * interface WithStatement <: Statement {
   1.998 +   *   type: "WithStatement";
   1.999 +   *   object: Expression;
  1.1000 +   *   body: Statement;
  1.1001 +   * }
  1.1002 +   */
  1.1003 +  WithStatement: function(aNode, aParent, aCallbacks) {
  1.1004 +    aNode._parent = aParent;
  1.1005 +
  1.1006 +    if (this.break) {
  1.1007 +      return;
  1.1008 +    }
  1.1009 +    if (aCallbacks.onNode) {
  1.1010 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1011 +        return;
  1.1012 +      }
  1.1013 +    }
  1.1014 +    if (aCallbacks.onWithStatement) {
  1.1015 +      aCallbacks.onWithStatement(aNode);
  1.1016 +    }
  1.1017 +    this[aNode.object.type](aNode.object, aNode, aCallbacks);
  1.1018 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1019 +  },
  1.1020 +
  1.1021 +  /**
  1.1022 +   * A switch statement. The lexical flag is metadata indicating whether the
  1.1023 +   * switch statement contains any unnested let declarations (and therefore
  1.1024 +   * introduces a new lexical scope).
  1.1025 +   *
  1.1026 +   * interface SwitchStatement <: Statement {
  1.1027 +   *   type: "SwitchStatement";
  1.1028 +   *   discriminant: Expression;
  1.1029 +   *   cases: [ SwitchCase ];
  1.1030 +   *   lexical: boolean;
  1.1031 +   * }
  1.1032 +   */
  1.1033 +  SwitchStatement: function(aNode, aParent, aCallbacks) {
  1.1034 +    aNode._parent = aParent;
  1.1035 +
  1.1036 +    if (this.break) {
  1.1037 +      return;
  1.1038 +    }
  1.1039 +    if (aCallbacks.onNode) {
  1.1040 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1041 +        return;
  1.1042 +      }
  1.1043 +    }
  1.1044 +    if (aCallbacks.onSwitchStatement) {
  1.1045 +      aCallbacks.onSwitchStatement(aNode);
  1.1046 +    }
  1.1047 +    this[aNode.discriminant.type](aNode.discriminant, aNode, aCallbacks);
  1.1048 +    for (let _case of aNode.cases) {
  1.1049 +      this[_case.type](_case, aNode, aCallbacks);
  1.1050 +    }
  1.1051 +  },
  1.1052 +
  1.1053 +  /**
  1.1054 +   * A return statement.
  1.1055 +   *
  1.1056 +   * interface ReturnStatement <: Statement {
  1.1057 +   *   type: "ReturnStatement";
  1.1058 +   *   argument: Expression | null;
  1.1059 +   * }
  1.1060 +   */
  1.1061 +  ReturnStatement: function(aNode, aParent, aCallbacks) {
  1.1062 +    aNode._parent = aParent;
  1.1063 +
  1.1064 +    if (this.break) {
  1.1065 +      return;
  1.1066 +    }
  1.1067 +    if (aCallbacks.onNode) {
  1.1068 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1069 +        return;
  1.1070 +      }
  1.1071 +    }
  1.1072 +    if (aCallbacks.onReturnStatement) {
  1.1073 +      aCallbacks.onReturnStatement(aNode);
  1.1074 +    }
  1.1075 +    if (aNode.argument) {
  1.1076 +      this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1.1077 +    }
  1.1078 +  },
  1.1079 +
  1.1080 +  /**
  1.1081 +   * A throw statement.
  1.1082 +   *
  1.1083 +   * interface ThrowStatement <: Statement {
  1.1084 +   *   type: "ThrowStatement";
  1.1085 +   *   argument: Expression;
  1.1086 +   * }
  1.1087 +   */
  1.1088 +  ThrowStatement: function(aNode, aParent, aCallbacks) {
  1.1089 +    aNode._parent = aParent;
  1.1090 +
  1.1091 +    if (this.break) {
  1.1092 +      return;
  1.1093 +    }
  1.1094 +    if (aCallbacks.onNode) {
  1.1095 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1096 +        return;
  1.1097 +      }
  1.1098 +    }
  1.1099 +    if (aCallbacks.onThrowStatement) {
  1.1100 +      aCallbacks.onThrowStatement(aNode);
  1.1101 +    }
  1.1102 +    this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1.1103 +  },
  1.1104 +
  1.1105 +  /**
  1.1106 +   * A try statement.
  1.1107 +   *
  1.1108 +   * interface TryStatement <: Statement {
  1.1109 +   *   type: "TryStatement";
  1.1110 +   *   block: BlockStatement;
  1.1111 +   *   handler: CatchClause | null;
  1.1112 +   *   guardedHandlers: [ CatchClause ];
  1.1113 +   *   finalizer: BlockStatement | null;
  1.1114 +   * }
  1.1115 +   */
  1.1116 +  TryStatement: function(aNode, aParent, aCallbacks) {
  1.1117 +    aNode._parent = aParent;
  1.1118 +
  1.1119 +    if (this.break) {
  1.1120 +      return;
  1.1121 +    }
  1.1122 +    if (aCallbacks.onNode) {
  1.1123 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1124 +        return;
  1.1125 +      }
  1.1126 +    }
  1.1127 +    if (aCallbacks.onTryStatement) {
  1.1128 +      aCallbacks.onTryStatement(aNode);
  1.1129 +    }
  1.1130 +    this[aNode.block.type](aNode.block, aNode, aCallbacks);
  1.1131 +    if (aNode.handler) {
  1.1132 +      this[aNode.handler.type](aNode.handler, aNode, aCallbacks);
  1.1133 +    }
  1.1134 +    for (let guardedHandler of aNode.guardedHandlers) {
  1.1135 +      this[guardedHandler.type](guardedHandler, aNode, aCallbacks);
  1.1136 +    }
  1.1137 +    if (aNode.finalizer) {
  1.1138 +      this[aNode.finalizer.type](aNode.finalizer, aNode, aCallbacks);
  1.1139 +    }
  1.1140 +  },
  1.1141 +
  1.1142 +  /**
  1.1143 +   * A while statement.
  1.1144 +   *
  1.1145 +   * interface WhileStatement <: Statement {
  1.1146 +   *   type: "WhileStatement";
  1.1147 +   *   test: Expression;
  1.1148 +   *   body: Statement;
  1.1149 +   * }
  1.1150 +   */
  1.1151 +  WhileStatement: function(aNode, aParent, aCallbacks) {
  1.1152 +    aNode._parent = aParent;
  1.1153 +
  1.1154 +    if (this.break) {
  1.1155 +      return;
  1.1156 +    }
  1.1157 +    if (aCallbacks.onNode) {
  1.1158 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1159 +        return;
  1.1160 +      }
  1.1161 +    }
  1.1162 +    if (aCallbacks.onWhileStatement) {
  1.1163 +      aCallbacks.onWhileStatement(aNode);
  1.1164 +    }
  1.1165 +    this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1.1166 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1167 +  },
  1.1168 +
  1.1169 +  /**
  1.1170 +   * A do/while statement.
  1.1171 +   *
  1.1172 +   * interface DoWhileStatement <: Statement {
  1.1173 +   *   type: "DoWhileStatement";
  1.1174 +   *   body: Statement;
  1.1175 +   *   test: Expression;
  1.1176 +   * }
  1.1177 +   */
  1.1178 +  DoWhileStatement: function(aNode, aParent, aCallbacks) {
  1.1179 +    aNode._parent = aParent;
  1.1180 +
  1.1181 +    if (this.break) {
  1.1182 +      return;
  1.1183 +    }
  1.1184 +    if (aCallbacks.onNode) {
  1.1185 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1186 +        return;
  1.1187 +      }
  1.1188 +    }
  1.1189 +    if (aCallbacks.onDoWhileStatement) {
  1.1190 +      aCallbacks.onDoWhileStatement(aNode);
  1.1191 +    }
  1.1192 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1193 +    this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1.1194 +  },
  1.1195 +
  1.1196 +  /**
  1.1197 +   * A for statement.
  1.1198 +   *
  1.1199 +   * interface ForStatement <: Statement {
  1.1200 +   *   type: "ForStatement";
  1.1201 +   *   init: VariableDeclaration | Expression | null;
  1.1202 +   *   test: Expression | null;
  1.1203 +   *   update: Expression | null;
  1.1204 +   *   body: Statement;
  1.1205 +   * }
  1.1206 +   */
  1.1207 +  ForStatement: function(aNode, aParent, aCallbacks) {
  1.1208 +    aNode._parent = aParent;
  1.1209 +
  1.1210 +    if (this.break) {
  1.1211 +      return;
  1.1212 +    }
  1.1213 +    if (aCallbacks.onNode) {
  1.1214 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1215 +        return;
  1.1216 +      }
  1.1217 +    }
  1.1218 +    if (aCallbacks.onForStatement) {
  1.1219 +      aCallbacks.onForStatement(aNode);
  1.1220 +    }
  1.1221 +    if (aNode.init) {
  1.1222 +      this[aNode.init.type](aNode.init, aNode, aCallbacks);
  1.1223 +    }
  1.1224 +    if (aNode.test) {
  1.1225 +      this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1.1226 +    }
  1.1227 +    if (aNode.update) {
  1.1228 +      this[aNode.update.type](aNode.update, aNode, aCallbacks);
  1.1229 +    }
  1.1230 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1231 +  },
  1.1232 +
  1.1233 +  /**
  1.1234 +   * A for/in statement, or, if each is true, a for each/in statement.
  1.1235 +   *
  1.1236 +   * interface ForInStatement <: Statement {
  1.1237 +   *   type: "ForInStatement";
  1.1238 +   *   left: VariableDeclaration | Expression;
  1.1239 +   *   right: Expression;
  1.1240 +   *   body: Statement;
  1.1241 +   *   each: boolean;
  1.1242 +   * }
  1.1243 +   */
  1.1244 +  ForInStatement: function(aNode, aParent, aCallbacks) {
  1.1245 +    aNode._parent = aParent;
  1.1246 +
  1.1247 +    if (this.break) {
  1.1248 +      return;
  1.1249 +    }
  1.1250 +    if (aCallbacks.onNode) {
  1.1251 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1252 +        return;
  1.1253 +      }
  1.1254 +    }
  1.1255 +    if (aCallbacks.onForInStatement) {
  1.1256 +      aCallbacks.onForInStatement(aNode);
  1.1257 +    }
  1.1258 +    this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1.1259 +    this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1.1260 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1261 +  },
  1.1262 +
  1.1263 +  /**
  1.1264 +   * A for/of statement.
  1.1265 +   *
  1.1266 +   * interface ForOfStatement <: Statement {
  1.1267 +   *   type: "ForOfStatement";
  1.1268 +   *   left: VariableDeclaration | Expression;
  1.1269 +   *   right: Expression;
  1.1270 +   *   body: Statement;
  1.1271 +   * }
  1.1272 +   */
  1.1273 +  ForOfStatement: function(aNode, aParent, aCallbacks) {
  1.1274 +    aNode._parent = aParent;
  1.1275 +
  1.1276 +    if (this.break) {
  1.1277 +      return;
  1.1278 +    }
  1.1279 +    if (aCallbacks.onNode) {
  1.1280 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1281 +        return;
  1.1282 +      }
  1.1283 +    }
  1.1284 +    if (aCallbacks.onForOfStatement) {
  1.1285 +      aCallbacks.onForOfStatement(aNode);
  1.1286 +    }
  1.1287 +    this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1.1288 +    this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1.1289 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1290 +  },
  1.1291 +
  1.1292 +  /**
  1.1293 +   * A let statement.
  1.1294 +   *
  1.1295 +   * interface LetStatement <: Statement {
  1.1296 +   *   type: "LetStatement";
  1.1297 +   *   head: [ { id: Pattern, init: Expression | null } ];
  1.1298 +   *   body: Statement;
  1.1299 +   * }
  1.1300 +   */
  1.1301 +  LetStatement: function(aNode, aParent, aCallbacks) {
  1.1302 +    aNode._parent = aParent;
  1.1303 +
  1.1304 +    if (this.break) {
  1.1305 +      return;
  1.1306 +    }
  1.1307 +    if (aCallbacks.onNode) {
  1.1308 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1309 +        return;
  1.1310 +      }
  1.1311 +    }
  1.1312 +    if (aCallbacks.onLetStatement) {
  1.1313 +      aCallbacks.onLetStatement(aNode);
  1.1314 +    }
  1.1315 +    for (let { id, init } of aNode.head) {
  1.1316 +      this[id.type](id, aNode, aCallbacks);
  1.1317 +      if (init) {
  1.1318 +        this[init.type](init, aNode, aCallbacks);
  1.1319 +      }
  1.1320 +    }
  1.1321 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1322 +  },
  1.1323 +
  1.1324 +  /**
  1.1325 +   * A debugger statement.
  1.1326 +   *
  1.1327 +   * interface DebuggerStatement <: Statement {
  1.1328 +   *   type: "DebuggerStatement";
  1.1329 +   * }
  1.1330 +   */
  1.1331 +  DebuggerStatement: function(aNode, aParent, aCallbacks) {
  1.1332 +    aNode._parent = aParent;
  1.1333 +
  1.1334 +    if (this.break) {
  1.1335 +      return;
  1.1336 +    }
  1.1337 +    if (aCallbacks.onNode) {
  1.1338 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1339 +        return;
  1.1340 +      }
  1.1341 +    }
  1.1342 +    if (aCallbacks.onDebuggerStatement) {
  1.1343 +      aCallbacks.onDebuggerStatement(aNode);
  1.1344 +    }
  1.1345 +  },
  1.1346 +
  1.1347 +  /**
  1.1348 +   * Any declaration node. Note that declarations are considered statements;
  1.1349 +   * this is because declarations can appear in any statement context in the
  1.1350 +   * language recognized by the SpiderMonkey parser.
  1.1351 +   *
  1.1352 +   * interface Declaration <: Statement { }
  1.1353 +   */
  1.1354 +  Declaration: function(aNode, aParent, aCallbacks) {
  1.1355 +    aNode._parent = aParent;
  1.1356 +
  1.1357 +    if (this.break) {
  1.1358 +      return;
  1.1359 +    }
  1.1360 +    if (aCallbacks.onNode) {
  1.1361 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1362 +        return;
  1.1363 +      }
  1.1364 +    }
  1.1365 +    if (aCallbacks.onDeclaration) {
  1.1366 +      aCallbacks.onDeclaration(aNode);
  1.1367 +    }
  1.1368 +  },
  1.1369 +
  1.1370 +  /**
  1.1371 +   * A function declaration.
  1.1372 +   *
  1.1373 +   * interface FunctionDeclaration <: Function, Declaration {
  1.1374 +   *   type: "FunctionDeclaration";
  1.1375 +   *   id: Identifier;
  1.1376 +   *   params: [ Pattern ];
  1.1377 +   *   defaults: [ Expression ];
  1.1378 +   *   rest: Identifier | null;
  1.1379 +   *   body: BlockStatement | Expression;
  1.1380 +   *   generator: boolean;
  1.1381 +   *   expression: boolean;
  1.1382 +   * }
  1.1383 +   */
  1.1384 +  FunctionDeclaration: function(aNode, aParent, aCallbacks) {
  1.1385 +    aNode._parent = aParent;
  1.1386 +
  1.1387 +    if (this.break) {
  1.1388 +      return;
  1.1389 +    }
  1.1390 +    if (aCallbacks.onNode) {
  1.1391 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1392 +        return;
  1.1393 +      }
  1.1394 +    }
  1.1395 +    if (aCallbacks.onFunctionDeclaration) {
  1.1396 +      aCallbacks.onFunctionDeclaration(aNode);
  1.1397 +    }
  1.1398 +    this[aNode.id.type](aNode.id, aNode, aCallbacks);
  1.1399 +    for (let param of aNode.params) {
  1.1400 +      this[param.type](param, aNode, aCallbacks);
  1.1401 +    }
  1.1402 +    for (let _default of aNode.defaults) {
  1.1403 +      this[_default.type](_default, aNode, aCallbacks);
  1.1404 +    }
  1.1405 +    if (aNode.rest) {
  1.1406 +      this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
  1.1407 +    }
  1.1408 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1409 +  },
  1.1410 +
  1.1411 +  /**
  1.1412 +   * A variable declaration, via one of var, let, or const.
  1.1413 +   *
  1.1414 +   * interface VariableDeclaration <: Declaration {
  1.1415 +   *   type: "VariableDeclaration";
  1.1416 +   *   declarations: [ VariableDeclarator ];
  1.1417 +   *   kind: "var" | "let" | "const";
  1.1418 +   * }
  1.1419 +   */
  1.1420 +  VariableDeclaration: function(aNode, aParent, aCallbacks) {
  1.1421 +    aNode._parent = aParent;
  1.1422 +
  1.1423 +    if (this.break) {
  1.1424 +      return;
  1.1425 +    }
  1.1426 +    if (aCallbacks.onNode) {
  1.1427 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1428 +        return;
  1.1429 +      }
  1.1430 +    }
  1.1431 +    if (aCallbacks.onVariableDeclaration) {
  1.1432 +      aCallbacks.onVariableDeclaration(aNode);
  1.1433 +    }
  1.1434 +    for (let declaration of aNode.declarations) {
  1.1435 +      this[declaration.type](declaration, aNode, aCallbacks);
  1.1436 +    }
  1.1437 +  },
  1.1438 +
  1.1439 +  /**
  1.1440 +   * A variable declarator.
  1.1441 +   *
  1.1442 +   * interface VariableDeclarator <: Node {
  1.1443 +   *   type: "VariableDeclarator";
  1.1444 +   *   id: Pattern;
  1.1445 +   *   init: Expression | null;
  1.1446 +   * }
  1.1447 +   */
  1.1448 +  VariableDeclarator: function(aNode, aParent, aCallbacks) {
  1.1449 +    aNode._parent = aParent;
  1.1450 +
  1.1451 +    if (this.break) {
  1.1452 +      return;
  1.1453 +    }
  1.1454 +    if (aCallbacks.onNode) {
  1.1455 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1456 +        return;
  1.1457 +      }
  1.1458 +    }
  1.1459 +    if (aCallbacks.onVariableDeclarator) {
  1.1460 +      aCallbacks.onVariableDeclarator(aNode);
  1.1461 +    }
  1.1462 +    this[aNode.id.type](aNode.id, aNode, aCallbacks);
  1.1463 +    if (aNode.init) {
  1.1464 +      this[aNode.init.type](aNode.init, aNode, aCallbacks);
  1.1465 +    }
  1.1466 +  },
  1.1467 +
  1.1468 +  /**
  1.1469 +   * Any expression node. Since the left-hand side of an assignment may be any
  1.1470 +   * expression in general, an expression can also be a pattern.
  1.1471 +   *
  1.1472 +   * interface Expression <: Node, Pattern { }
  1.1473 +   */
  1.1474 +  Expression: function(aNode, aParent, aCallbacks) {
  1.1475 +    aNode._parent = aParent;
  1.1476 +
  1.1477 +    if (this.break) {
  1.1478 +      return;
  1.1479 +    }
  1.1480 +    if (aCallbacks.onNode) {
  1.1481 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1482 +        return;
  1.1483 +      }
  1.1484 +    }
  1.1485 +    if (aCallbacks.onExpression) {
  1.1486 +      aCallbacks.onExpression(aNode);
  1.1487 +    }
  1.1488 +  },
  1.1489 +
  1.1490 +  /**
  1.1491 +   * A this expression.
  1.1492 +   *
  1.1493 +   * interface ThisExpression <: Expression {
  1.1494 +   *   type: "ThisExpression";
  1.1495 +   * }
  1.1496 +   */
  1.1497 +  ThisExpression: function(aNode, aParent, aCallbacks) {
  1.1498 +    aNode._parent = aParent;
  1.1499 +
  1.1500 +    if (this.break) {
  1.1501 +      return;
  1.1502 +    }
  1.1503 +    if (aCallbacks.onNode) {
  1.1504 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1505 +        return;
  1.1506 +      }
  1.1507 +    }
  1.1508 +    if (aCallbacks.onThisExpression) {
  1.1509 +      aCallbacks.onThisExpression(aNode);
  1.1510 +    }
  1.1511 +  },
  1.1512 +
  1.1513 +  /**
  1.1514 +   * An array expression.
  1.1515 +   *
  1.1516 +   * interface ArrayExpression <: Expression {
  1.1517 +   *   type: "ArrayExpression";
  1.1518 +   *   elements: [ Expression | null ];
  1.1519 +   * }
  1.1520 +   */
  1.1521 +  ArrayExpression: function(aNode, aParent, aCallbacks) {
  1.1522 +    aNode._parent = aParent;
  1.1523 +
  1.1524 +    if (this.break) {
  1.1525 +      return;
  1.1526 +    }
  1.1527 +    if (aCallbacks.onNode) {
  1.1528 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1529 +        return;
  1.1530 +      }
  1.1531 +    }
  1.1532 +    if (aCallbacks.onArrayExpression) {
  1.1533 +      aCallbacks.onArrayExpression(aNode);
  1.1534 +    }
  1.1535 +    for (let element of aNode.elements) {
  1.1536 +      // TODO: remove the typeof check when support for SpreadExpression is
  1.1537 +      // added (bug 890913).
  1.1538 +      if (element && typeof this[element.type] == "function") {
  1.1539 +        this[element.type](element, aNode, aCallbacks);
  1.1540 +      }
  1.1541 +    }
  1.1542 +  },
  1.1543 +
  1.1544 +  /**
  1.1545 +   * An object expression. A literal property in an object expression can have
  1.1546 +   * either a string or number as its value. Ordinary property initializers
  1.1547 +   * have a kind value "init"; getters and setters have the kind values "get"
  1.1548 +   * and "set", respectively.
  1.1549 +   *
  1.1550 +   * interface ObjectExpression <: Expression {
  1.1551 +   *   type: "ObjectExpression";
  1.1552 +   *   properties: [ { key: Literal | Identifier,
  1.1553 +   *                   value: Expression,
  1.1554 +   *                   kind: "init" | "get" | "set" } ];
  1.1555 +   * }
  1.1556 +   */
  1.1557 +  ObjectExpression: function(aNode, aParent, aCallbacks) {
  1.1558 +    aNode._parent = aParent;
  1.1559 +
  1.1560 +    if (this.break) {
  1.1561 +      return;
  1.1562 +    }
  1.1563 +    if (aCallbacks.onNode) {
  1.1564 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1565 +        return;
  1.1566 +      }
  1.1567 +    }
  1.1568 +    if (aCallbacks.onObjectExpression) {
  1.1569 +      aCallbacks.onObjectExpression(aNode);
  1.1570 +    }
  1.1571 +    for (let { key, value } of aNode.properties) {
  1.1572 +      this[key.type](key, aNode, aCallbacks);
  1.1573 +      this[value.type](value, aNode, aCallbacks);
  1.1574 +    }
  1.1575 +  },
  1.1576 +
  1.1577 +  /**
  1.1578 +   * A function expression.
  1.1579 +   *
  1.1580 +   * interface FunctionExpression <: Function, Expression {
  1.1581 +   *   type: "FunctionExpression";
  1.1582 +   *   id: Identifier | null;
  1.1583 +   *   params: [ Pattern ];
  1.1584 +   *   defaults: [ Expression ];
  1.1585 +   *   rest: Identifier | null;
  1.1586 +   *   body: BlockStatement | Expression;
  1.1587 +   *   generator: boolean;
  1.1588 +   *   expression: boolean;
  1.1589 +   * }
  1.1590 +   */
  1.1591 +  FunctionExpression: function(aNode, aParent, aCallbacks) {
  1.1592 +    aNode._parent = aParent;
  1.1593 +
  1.1594 +    if (this.break) {
  1.1595 +      return;
  1.1596 +    }
  1.1597 +    if (aCallbacks.onNode) {
  1.1598 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1599 +        return;
  1.1600 +      }
  1.1601 +    }
  1.1602 +    if (aCallbacks.onFunctionExpression) {
  1.1603 +      aCallbacks.onFunctionExpression(aNode);
  1.1604 +    }
  1.1605 +    if (aNode.id) {
  1.1606 +      this[aNode.id.type](aNode.id, aNode, aCallbacks);
  1.1607 +    }
  1.1608 +    for (let param of aNode.params) {
  1.1609 +      this[param.type](param, aNode, aCallbacks);
  1.1610 +    }
  1.1611 +    for (let _default of aNode.defaults) {
  1.1612 +      this[_default.type](_default, aNode, aCallbacks);
  1.1613 +    }
  1.1614 +    if (aNode.rest) {
  1.1615 +      this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
  1.1616 +    }
  1.1617 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1618 +  },
  1.1619 +
  1.1620 +  /**
  1.1621 +   * An arrow expression.
  1.1622 +   *
  1.1623 +   * interface ArrowExpression <: Function, Expression {
  1.1624 +   *   type: "ArrowExpression";
  1.1625 +   *   params: [ Pattern ];
  1.1626 +   *   defaults: [ Expression ];
  1.1627 +   *   rest: Identifier | null;
  1.1628 +   *   body: BlockStatement | Expression;
  1.1629 +   *   generator: boolean;
  1.1630 +   *   expression: boolean;
  1.1631 +   * }
  1.1632 +   */
  1.1633 +  ArrowExpression: function(aNode, aParent, aCallbacks) {
  1.1634 +    aNode._parent = aParent;
  1.1635 +
  1.1636 +    if (this.break) {
  1.1637 +      return;
  1.1638 +    }
  1.1639 +    if (aCallbacks.onNode) {
  1.1640 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1641 +        return;
  1.1642 +      }
  1.1643 +    }
  1.1644 +    if (aCallbacks.onArrowExpression) {
  1.1645 +      aCallbacks.onArrowExpression(aNode);
  1.1646 +    }
  1.1647 +    for (let param of aNode.params) {
  1.1648 +      this[param.type](param, aNode, aCallbacks);
  1.1649 +    }
  1.1650 +    for (let _default of aNode.defaults) {
  1.1651 +      this[_default.type](_default, aNode, aCallbacks);
  1.1652 +    }
  1.1653 +    if (aNode.rest) {
  1.1654 +      this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
  1.1655 +    }
  1.1656 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1657 +  },
  1.1658 +
  1.1659 +  /**
  1.1660 +   * A sequence expression, i.e., a comma-separated sequence of expressions.
  1.1661 +   *
  1.1662 +   * interface SequenceExpression <: Expression {
  1.1663 +   *   type: "SequenceExpression";
  1.1664 +   *   expressions: [ Expression ];
  1.1665 +   * }
  1.1666 +   */
  1.1667 +  SequenceExpression: function(aNode, aParent, aCallbacks) {
  1.1668 +    aNode._parent = aParent;
  1.1669 +
  1.1670 +    if (this.break) {
  1.1671 +      return;
  1.1672 +    }
  1.1673 +    if (aCallbacks.onNode) {
  1.1674 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1675 +        return;
  1.1676 +      }
  1.1677 +    }
  1.1678 +    if (aCallbacks.onSequenceExpression) {
  1.1679 +      aCallbacks.onSequenceExpression(aNode);
  1.1680 +    }
  1.1681 +    for (let expression of aNode.expressions) {
  1.1682 +      this[expression.type](expression, aNode, aCallbacks);
  1.1683 +    }
  1.1684 +  },
  1.1685 +
  1.1686 +  /**
  1.1687 +   * A unary operator expression.
  1.1688 +   *
  1.1689 +   * interface UnaryExpression <: Expression {
  1.1690 +   *   type: "UnaryExpression";
  1.1691 +   *   operator: UnaryOperator;
  1.1692 +   *   prefix: boolean;
  1.1693 +   *   argument: Expression;
  1.1694 +   * }
  1.1695 +   */
  1.1696 +  UnaryExpression: function(aNode, aParent, aCallbacks) {
  1.1697 +    aNode._parent = aParent;
  1.1698 +
  1.1699 +    if (this.break) {
  1.1700 +      return;
  1.1701 +    }
  1.1702 +    if (aCallbacks.onNode) {
  1.1703 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1704 +        return;
  1.1705 +      }
  1.1706 +    }
  1.1707 +    if (aCallbacks.onUnaryExpression) {
  1.1708 +      aCallbacks.onUnaryExpression(aNode);
  1.1709 +    }
  1.1710 +    this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1.1711 +  },
  1.1712 +
  1.1713 +  /**
  1.1714 +   * A binary operator expression.
  1.1715 +   *
  1.1716 +   * interface BinaryExpression <: Expression {
  1.1717 +   *   type: "BinaryExpression";
  1.1718 +   *   operator: BinaryOperator;
  1.1719 +   *   left: Expression;
  1.1720 +   *   right: Expression;
  1.1721 +   * }
  1.1722 +   */
  1.1723 +  BinaryExpression: function(aNode, aParent, aCallbacks) {
  1.1724 +    aNode._parent = aParent;
  1.1725 +
  1.1726 +    if (this.break) {
  1.1727 +      return;
  1.1728 +    }
  1.1729 +    if (aCallbacks.onNode) {
  1.1730 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1731 +        return;
  1.1732 +      }
  1.1733 +    }
  1.1734 +    if (aCallbacks.onBinaryExpression) {
  1.1735 +      aCallbacks.onBinaryExpression(aNode);
  1.1736 +    }
  1.1737 +    this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1.1738 +    this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1.1739 +  },
  1.1740 +
  1.1741 +  /**
  1.1742 +   * An assignment operator expression.
  1.1743 +   *
  1.1744 +   * interface AssignmentExpression <: Expression {
  1.1745 +   *   type: "AssignmentExpression";
  1.1746 +   *   operator: AssignmentOperator;
  1.1747 +   *   left: Expression;
  1.1748 +   *   right: Expression;
  1.1749 +   * }
  1.1750 +   */
  1.1751 +  AssignmentExpression: function(aNode, aParent, aCallbacks) {
  1.1752 +    aNode._parent = aParent;
  1.1753 +
  1.1754 +    if (this.break) {
  1.1755 +      return;
  1.1756 +    }
  1.1757 +    if (aCallbacks.onNode) {
  1.1758 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1759 +        return;
  1.1760 +      }
  1.1761 +    }
  1.1762 +    if (aCallbacks.onAssignmentExpression) {
  1.1763 +      aCallbacks.onAssignmentExpression(aNode);
  1.1764 +    }
  1.1765 +    this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1.1766 +    this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1.1767 +  },
  1.1768 +
  1.1769 +  /**
  1.1770 +   * An update (increment or decrement) operator expression.
  1.1771 +   *
  1.1772 +   * interface UpdateExpression <: Expression {
  1.1773 +   *   type: "UpdateExpression";
  1.1774 +   *   operator: UpdateOperator;
  1.1775 +   *   argument: Expression;
  1.1776 +   *   prefix: boolean;
  1.1777 +   * }
  1.1778 +   */
  1.1779 +  UpdateExpression: function(aNode, aParent, aCallbacks) {
  1.1780 +    aNode._parent = aParent;
  1.1781 +
  1.1782 +    if (this.break) {
  1.1783 +      return;
  1.1784 +    }
  1.1785 +    if (aCallbacks.onNode) {
  1.1786 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1787 +        return;
  1.1788 +      }
  1.1789 +    }
  1.1790 +    if (aCallbacks.onUpdateExpression) {
  1.1791 +      aCallbacks.onUpdateExpression(aNode);
  1.1792 +    }
  1.1793 +    this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1.1794 +  },
  1.1795 +
  1.1796 +  /**
  1.1797 +   * A logical operator expression.
  1.1798 +   *
  1.1799 +   * interface LogicalExpression <: Expression {
  1.1800 +   *   type: "LogicalExpression";
  1.1801 +   *   operator: LogicalOperator;
  1.1802 +   *   left: Expression;
  1.1803 +   *   right: Expression;
  1.1804 +   * }
  1.1805 +   */
  1.1806 +  LogicalExpression: function(aNode, aParent, aCallbacks) {
  1.1807 +    aNode._parent = aParent;
  1.1808 +
  1.1809 +    if (this.break) {
  1.1810 +      return;
  1.1811 +    }
  1.1812 +    if (aCallbacks.onNode) {
  1.1813 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1814 +        return;
  1.1815 +      }
  1.1816 +    }
  1.1817 +    if (aCallbacks.onLogicalExpression) {
  1.1818 +      aCallbacks.onLogicalExpression(aNode);
  1.1819 +    }
  1.1820 +    this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1.1821 +    this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1.1822 +  },
  1.1823 +
  1.1824 +  /**
  1.1825 +   * A conditional expression, i.e., a ternary ?/: expression.
  1.1826 +   *
  1.1827 +   * interface ConditionalExpression <: Expression {
  1.1828 +   *   type: "ConditionalExpression";
  1.1829 +   *   test: Expression;
  1.1830 +   *   alternate: Expression;
  1.1831 +   *   consequent: Expression;
  1.1832 +   * }
  1.1833 +   */
  1.1834 +  ConditionalExpression: function(aNode, aParent, aCallbacks) {
  1.1835 +    aNode._parent = aParent;
  1.1836 +
  1.1837 +    if (this.break) {
  1.1838 +      return;
  1.1839 +    }
  1.1840 +    if (aCallbacks.onNode) {
  1.1841 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1842 +        return;
  1.1843 +      }
  1.1844 +    }
  1.1845 +    if (aCallbacks.onConditionalExpression) {
  1.1846 +      aCallbacks.onConditionalExpression(aNode);
  1.1847 +    }
  1.1848 +    this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1.1849 +    this[aNode.alternate.type](aNode.alternate, aNode, aCallbacks);
  1.1850 +    this[aNode.consequent.type](aNode.consequent, aNode, aCallbacks);
  1.1851 +  },
  1.1852 +
  1.1853 +  /**
  1.1854 +   * A new expression.
  1.1855 +   *
  1.1856 +   * interface NewExpression <: Expression {
  1.1857 +   *   type: "NewExpression";
  1.1858 +   *   callee: Expression;
  1.1859 +   *   arguments: [ Expression | null ];
  1.1860 +   * }
  1.1861 +   */
  1.1862 +  NewExpression: function(aNode, aParent, aCallbacks) {
  1.1863 +    aNode._parent = aParent;
  1.1864 +
  1.1865 +    if (this.break) {
  1.1866 +      return;
  1.1867 +    }
  1.1868 +    if (aCallbacks.onNode) {
  1.1869 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1870 +        return;
  1.1871 +      }
  1.1872 +    }
  1.1873 +    if (aCallbacks.onNewExpression) {
  1.1874 +      aCallbacks.onNewExpression(aNode);
  1.1875 +    }
  1.1876 +    this[aNode.callee.type](aNode.callee, aNode, aCallbacks);
  1.1877 +    for (let argument of aNode.arguments) {
  1.1878 +      if (argument) {
  1.1879 +        this[argument.type](argument, aNode, aCallbacks);
  1.1880 +      }
  1.1881 +    }
  1.1882 +  },
  1.1883 +
  1.1884 +  /**
  1.1885 +   * A function or method call expression.
  1.1886 +   *
  1.1887 +   * interface CallExpression <: Expression {
  1.1888 +   *   type: "CallExpression";
  1.1889 +   *   callee: Expression;
  1.1890 +   *   arguments: [ Expression | null ];
  1.1891 +   * }
  1.1892 +   */
  1.1893 +  CallExpression: function(aNode, aParent, aCallbacks) {
  1.1894 +    aNode._parent = aParent;
  1.1895 +
  1.1896 +    if (this.break) {
  1.1897 +      return;
  1.1898 +    }
  1.1899 +    if (aCallbacks.onNode) {
  1.1900 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1901 +        return;
  1.1902 +      }
  1.1903 +    }
  1.1904 +    if (aCallbacks.onCallExpression) {
  1.1905 +      aCallbacks.onCallExpression(aNode);
  1.1906 +    }
  1.1907 +    this[aNode.callee.type](aNode.callee, aNode, aCallbacks);
  1.1908 +    for (let argument of aNode.arguments) {
  1.1909 +      if (argument) {
  1.1910 +        this[argument.type](argument, aNode, aCallbacks);
  1.1911 +      }
  1.1912 +    }
  1.1913 +  },
  1.1914 +
  1.1915 +  /**
  1.1916 +   * A member expression. If computed is true, the node corresponds to a
  1.1917 +   * computed e1[e2] expression and property is an Expression. If computed is
  1.1918 +   * false, the node corresponds to a static e1.x expression and property is an
  1.1919 +   * Identifier.
  1.1920 +   *
  1.1921 +   * interface MemberExpression <: Expression {
  1.1922 +   *   type: "MemberExpression";
  1.1923 +   *   object: Expression;
  1.1924 +   *   property: Identifier | Expression;
  1.1925 +   *   computed: boolean;
  1.1926 +   * }
  1.1927 +   */
  1.1928 +  MemberExpression: function(aNode, aParent, aCallbacks) {
  1.1929 +    aNode._parent = aParent;
  1.1930 +
  1.1931 +    if (this.break) {
  1.1932 +      return;
  1.1933 +    }
  1.1934 +    if (aCallbacks.onNode) {
  1.1935 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1936 +        return;
  1.1937 +      }
  1.1938 +    }
  1.1939 +    if (aCallbacks.onMemberExpression) {
  1.1940 +      aCallbacks.onMemberExpression(aNode);
  1.1941 +    }
  1.1942 +    this[aNode.object.type](aNode.object, aNode, aCallbacks);
  1.1943 +    this[aNode.property.type](aNode.property, aNode, aCallbacks);
  1.1944 +  },
  1.1945 +
  1.1946 +  /**
  1.1947 +   * A yield expression.
  1.1948 +   *
  1.1949 +   * interface YieldExpression <: Expression {
  1.1950 +   *   argument: Expression | null;
  1.1951 +   * }
  1.1952 +   */
  1.1953 +  YieldExpression: function(aNode, aParent, aCallbacks) {
  1.1954 +    aNode._parent = aParent;
  1.1955 +
  1.1956 +    if (this.break) {
  1.1957 +      return;
  1.1958 +    }
  1.1959 +    if (aCallbacks.onNode) {
  1.1960 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1961 +        return;
  1.1962 +      }
  1.1963 +    }
  1.1964 +    if (aCallbacks.onYieldExpression) {
  1.1965 +      aCallbacks.onYieldExpression(aNode);
  1.1966 +    }
  1.1967 +    if (aNode.argument) {
  1.1968 +      this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1.1969 +    }
  1.1970 +  },
  1.1971 +
  1.1972 +  /**
  1.1973 +   * An array comprehension. The blocks array corresponds to the sequence of
  1.1974 +   * for and for each blocks. The optional filter expression corresponds to the
  1.1975 +   * final if clause, if present.
  1.1976 +   *
  1.1977 +   * interface ComprehensionExpression <: Expression {
  1.1978 +   *   body: Expression;
  1.1979 +   *   blocks: [ ComprehensionBlock ];
  1.1980 +   *   filter: Expression | null;
  1.1981 +   * }
  1.1982 +   */
  1.1983 +  ComprehensionExpression: function(aNode, aParent, aCallbacks) {
  1.1984 +    aNode._parent = aParent;
  1.1985 +
  1.1986 +    if (this.break) {
  1.1987 +      return;
  1.1988 +    }
  1.1989 +    if (aCallbacks.onNode) {
  1.1990 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.1991 +        return;
  1.1992 +      }
  1.1993 +    }
  1.1994 +    if (aCallbacks.onComprehensionExpression) {
  1.1995 +      aCallbacks.onComprehensionExpression(aNode);
  1.1996 +    }
  1.1997 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.1998 +    for (let block of aNode.blocks) {
  1.1999 +      this[block.type](block, aNode, aCallbacks);
  1.2000 +    }
  1.2001 +    if (aNode.filter) {
  1.2002 +      this[aNode.filter.type](aNode.filter, aNode, aCallbacks);
  1.2003 +    }
  1.2004 +  },
  1.2005 +
  1.2006 +  /**
  1.2007 +   * A generator expression. As with array comprehensions, the blocks array
  1.2008 +   * corresponds to the sequence of for and for each blocks, and the optional
  1.2009 +   * filter expression corresponds to the final if clause, if present.
  1.2010 +   *
  1.2011 +   * interface GeneratorExpression <: Expression {
  1.2012 +   *   body: Expression;
  1.2013 +   *   blocks: [ ComprehensionBlock ];
  1.2014 +   *   filter: Expression | null;
  1.2015 +   * }
  1.2016 +   */
  1.2017 +  GeneratorExpression: function(aNode, aParent, aCallbacks) {
  1.2018 +    aNode._parent = aParent;
  1.2019 +
  1.2020 +    if (this.break) {
  1.2021 +      return;
  1.2022 +    }
  1.2023 +    if (aCallbacks.onNode) {
  1.2024 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2025 +        return;
  1.2026 +      }
  1.2027 +    }
  1.2028 +    if (aCallbacks.onGeneratorExpression) {
  1.2029 +      aCallbacks.onGeneratorExpression(aNode);
  1.2030 +    }
  1.2031 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.2032 +    for (let block of aNode.blocks) {
  1.2033 +      this[block.type](block, aNode, aCallbacks);
  1.2034 +    }
  1.2035 +    if (aNode.filter) {
  1.2036 +      this[aNode.filter.type](aNode.filter, aNode, aCallbacks);
  1.2037 +    }
  1.2038 +  },
  1.2039 +
  1.2040 +  /**
  1.2041 +   * A graph expression, aka "sharp literal," such as #1={ self: #1# }.
  1.2042 +   *
  1.2043 +   * interface GraphExpression <: Expression {
  1.2044 +   *   index: uint32;
  1.2045 +   *   expression: Literal;
  1.2046 +   * }
  1.2047 +   */
  1.2048 +  GraphExpression: function(aNode, aParent, aCallbacks) {
  1.2049 +    aNode._parent = aParent;
  1.2050 +
  1.2051 +    if (this.break) {
  1.2052 +      return;
  1.2053 +    }
  1.2054 +    if (aCallbacks.onNode) {
  1.2055 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2056 +        return;
  1.2057 +      }
  1.2058 +    }
  1.2059 +    if (aCallbacks.onGraphExpression) {
  1.2060 +      aCallbacks.onGraphExpression(aNode);
  1.2061 +    }
  1.2062 +    this[aNode.expression.type](aNode.expression, aNode, aCallbacks);
  1.2063 +  },
  1.2064 +
  1.2065 +  /**
  1.2066 +   * A graph index expression, aka "sharp variable," such as #1#.
  1.2067 +   *
  1.2068 +   * interface GraphIndexExpression <: Expression {
  1.2069 +   *   index: uint32;
  1.2070 +   * }
  1.2071 +   */
  1.2072 +  GraphIndexExpression: function(aNode, aParent, aCallbacks) {
  1.2073 +    aNode._parent = aParent;
  1.2074 +
  1.2075 +    if (this.break) {
  1.2076 +      return;
  1.2077 +    }
  1.2078 +    if (aCallbacks.onNode) {
  1.2079 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2080 +        return;
  1.2081 +      }
  1.2082 +    }
  1.2083 +    if (aCallbacks.onGraphIndexExpression) {
  1.2084 +      aCallbacks.onGraphIndexExpression(aNode);
  1.2085 +    }
  1.2086 +  },
  1.2087 +
  1.2088 +  /**
  1.2089 +   * A let expression.
  1.2090 +   *
  1.2091 +   * interface LetExpression <: Expression {
  1.2092 +   *   type: "LetExpression";
  1.2093 +   *   head: [ { id: Pattern, init: Expression | null } ];
  1.2094 +   *   body: Expression;
  1.2095 +   * }
  1.2096 +   */
  1.2097 +  LetExpression: function(aNode, aParent, aCallbacks) {
  1.2098 +    aNode._parent = aParent;
  1.2099 +
  1.2100 +    if (this.break) {
  1.2101 +      return;
  1.2102 +    }
  1.2103 +    if (aCallbacks.onNode) {
  1.2104 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2105 +        return;
  1.2106 +      }
  1.2107 +    }
  1.2108 +    if (aCallbacks.onLetExpression) {
  1.2109 +      aCallbacks.onLetExpression(aNode);
  1.2110 +    }
  1.2111 +    for (let { id, init } of aNode.head) {
  1.2112 +      this[id.type](id, aNode, aCallbacks);
  1.2113 +      if (init) {
  1.2114 +        this[init.type](init, aNode, aCallbacks);
  1.2115 +      }
  1.2116 +    }
  1.2117 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.2118 +  },
  1.2119 +
  1.2120 +  /**
  1.2121 +   * Any pattern.
  1.2122 +   *
  1.2123 +   * interface Pattern <: Node { }
  1.2124 +   */
  1.2125 +  Pattern: function(aNode, aParent, aCallbacks) {
  1.2126 +    aNode._parent = aParent;
  1.2127 +
  1.2128 +    if (this.break) {
  1.2129 +      return;
  1.2130 +    }
  1.2131 +    if (aCallbacks.onNode) {
  1.2132 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2133 +        return;
  1.2134 +      }
  1.2135 +    }
  1.2136 +    if (aCallbacks.onPattern) {
  1.2137 +      aCallbacks.onPattern(aNode);
  1.2138 +    }
  1.2139 +  },
  1.2140 +
  1.2141 +  /**
  1.2142 +   * An object-destructuring pattern. A literal property in an object pattern
  1.2143 +   * can have either a string or number as its value.
  1.2144 +   *
  1.2145 +   * interface ObjectPattern <: Pattern {
  1.2146 +   *   type: "ObjectPattern";
  1.2147 +   *   properties: [ { key: Literal | Identifier, value: Pattern } ];
  1.2148 +   * }
  1.2149 +   */
  1.2150 +  ObjectPattern: function(aNode, aParent, aCallbacks) {
  1.2151 +    aNode._parent = aParent;
  1.2152 +
  1.2153 +    if (this.break) {
  1.2154 +      return;
  1.2155 +    }
  1.2156 +    if (aCallbacks.onNode) {
  1.2157 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2158 +        return;
  1.2159 +      }
  1.2160 +    }
  1.2161 +    if (aCallbacks.onObjectPattern) {
  1.2162 +      aCallbacks.onObjectPattern(aNode);
  1.2163 +    }
  1.2164 +    for (let { key, value } of aNode.properties) {
  1.2165 +      this[key.type](key, aNode, aCallbacks);
  1.2166 +      this[value.type](value, aNode, aCallbacks);
  1.2167 +    }
  1.2168 +  },
  1.2169 +
  1.2170 +  /**
  1.2171 +   * An array-destructuring pattern.
  1.2172 +   *
  1.2173 +   * interface ArrayPattern <: Pattern {
  1.2174 +   *   type: "ArrayPattern";
  1.2175 +   *   elements: [ Pattern | null ];
  1.2176 +   * }
  1.2177 +   */
  1.2178 +  ArrayPattern: function(aNode, aParent, aCallbacks) {
  1.2179 +    aNode._parent = aParent;
  1.2180 +
  1.2181 +    if (this.break) {
  1.2182 +      return;
  1.2183 +    }
  1.2184 +    if (aCallbacks.onNode) {
  1.2185 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2186 +        return;
  1.2187 +      }
  1.2188 +    }
  1.2189 +    if (aCallbacks.onArrayPattern) {
  1.2190 +      aCallbacks.onArrayPattern(aNode);
  1.2191 +    }
  1.2192 +    for (let element of aNode.elements) {
  1.2193 +      if (element) {
  1.2194 +        this[element.type](element, aNode, aCallbacks);
  1.2195 +      }
  1.2196 +    }
  1.2197 +  },
  1.2198 +
  1.2199 +  /**
  1.2200 +   * A case (if test is an Expression) or default (if test is null) clause in
  1.2201 +   * the body of a switch statement.
  1.2202 +   *
  1.2203 +   * interface SwitchCase <: Node {
  1.2204 +   *   type: "SwitchCase";
  1.2205 +   *   test: Expression | null;
  1.2206 +   *   consequent: [ Statement ];
  1.2207 +   * }
  1.2208 +   */
  1.2209 +  SwitchCase: function(aNode, aParent, aCallbacks) {
  1.2210 +    aNode._parent = aParent;
  1.2211 +
  1.2212 +    if (this.break) {
  1.2213 +      return;
  1.2214 +    }
  1.2215 +    if (aCallbacks.onNode) {
  1.2216 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2217 +        return;
  1.2218 +      }
  1.2219 +    }
  1.2220 +    if (aCallbacks.onSwitchCase) {
  1.2221 +      aCallbacks.onSwitchCase(aNode);
  1.2222 +    }
  1.2223 +    if (aNode.test) {
  1.2224 +      this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1.2225 +    }
  1.2226 +    for (let consequent of aNode.consequent) {
  1.2227 +      this[consequent.type](consequent, aNode, aCallbacks);
  1.2228 +    }
  1.2229 +  },
  1.2230 +
  1.2231 +  /**
  1.2232 +   * A catch clause following a try block. The optional guard property
  1.2233 +   * corresponds to the optional expression guard on the bound variable.
  1.2234 +   *
  1.2235 +   * interface CatchClause <: Node {
  1.2236 +   *   type: "CatchClause";
  1.2237 +   *   param: Pattern;
  1.2238 +   *   guard: Expression | null;
  1.2239 +   *   body: BlockStatement;
  1.2240 +   * }
  1.2241 +   */
  1.2242 +  CatchClause: function(aNode, aParent, aCallbacks) {
  1.2243 +    aNode._parent = aParent;
  1.2244 +
  1.2245 +    if (this.break) {
  1.2246 +      return;
  1.2247 +    }
  1.2248 +    if (aCallbacks.onNode) {
  1.2249 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2250 +        return;
  1.2251 +      }
  1.2252 +    }
  1.2253 +    if (aCallbacks.onCatchClause) {
  1.2254 +      aCallbacks.onCatchClause(aNode);
  1.2255 +    }
  1.2256 +    this[aNode.param.type](aNode.param, aNode, aCallbacks);
  1.2257 +    if (aNode.guard) {
  1.2258 +      this[aNode.guard.type](aNode.guard, aNode, aCallbacks);
  1.2259 +    }
  1.2260 +    this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1.2261 +  },
  1.2262 +
  1.2263 +  /**
  1.2264 +   * A for or for each block in an array comprehension or generator expression.
  1.2265 +   *
  1.2266 +   * interface ComprehensionBlock <: Node {
  1.2267 +   *   left: Pattern;
  1.2268 +   *   right: Expression;
  1.2269 +   *   each: boolean;
  1.2270 +   * }
  1.2271 +   */
  1.2272 +  ComprehensionBlock: function(aNode, aParent, aCallbacks) {
  1.2273 +    aNode._parent = aParent;
  1.2274 +
  1.2275 +    if (this.break) {
  1.2276 +      return;
  1.2277 +    }
  1.2278 +    if (aCallbacks.onNode) {
  1.2279 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2280 +        return;
  1.2281 +      }
  1.2282 +    }
  1.2283 +    if (aCallbacks.onComprehensionBlock) {
  1.2284 +      aCallbacks.onComprehensionBlock(aNode);
  1.2285 +    }
  1.2286 +    this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1.2287 +    this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1.2288 +  },
  1.2289 +
  1.2290 +  /**
  1.2291 +   * An identifier. Note that an identifier may be an expression or a
  1.2292 +   * destructuring pattern.
  1.2293 +   *
  1.2294 +   * interface Identifier <: Node, Expression, Pattern {
  1.2295 +   *   type: "Identifier";
  1.2296 +   *   name: string;
  1.2297 +   * }
  1.2298 +   */
  1.2299 +  Identifier: function(aNode, aParent, aCallbacks) {
  1.2300 +    aNode._parent = aParent;
  1.2301 +
  1.2302 +    if (this.break) {
  1.2303 +      return;
  1.2304 +    }
  1.2305 +    if (aCallbacks.onNode) {
  1.2306 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2307 +        return;
  1.2308 +      }
  1.2309 +    }
  1.2310 +    if (aCallbacks.onIdentifier) {
  1.2311 +      aCallbacks.onIdentifier(aNode);
  1.2312 +    }
  1.2313 +  },
  1.2314 +
  1.2315 +  /**
  1.2316 +   * A literal token. Note that a literal can be an expression.
  1.2317 +   *
  1.2318 +   * interface Literal <: Node, Expression {
  1.2319 +   *   type: "Literal";
  1.2320 +   *   value: string | boolean | null | number | RegExp;
  1.2321 +   * }
  1.2322 +   */
  1.2323 +  Literal: function(aNode, aParent, aCallbacks) {
  1.2324 +    aNode._parent = aParent;
  1.2325 +
  1.2326 +    if (this.break) {
  1.2327 +      return;
  1.2328 +    }
  1.2329 +    if (aCallbacks.onNode) {
  1.2330 +      if (aCallbacks.onNode(aNode, aParent) === false) {
  1.2331 +        return;
  1.2332 +      }
  1.2333 +    }
  1.2334 +    if (aCallbacks.onLiteral) {
  1.2335 +      aCallbacks.onLiteral(aNode);
  1.2336 +    }
  1.2337 +  }
  1.2338 +};
  1.2339 +
  1.2340 +XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect);

mercurial