browser/devtools/shared/Parser.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 "use strict";
     8 const Ci = Components.interfaces;
     9 const Cu = Components.utils;
    11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    12 const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
    14 XPCOMUtils.defineLazyModuleGetter(this,
    15   "Reflect", "resource://gre/modules/reflect.jsm");
    17 this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
    19 /**
    20  * A JS parser using the reflection API.
    21  */
    22 this.Parser = function Parser() {
    23   this._cache = new Map();
    24   this.errors = [];
    25 };
    27 Parser.prototype = {
    28   /**
    29    * Gets a collection of parser methods for a specified source.
    30    *
    31    * @param string aSource
    32    *        The source text content.
    33    * @param string aUrl [optional]
    34    *        The source url. The AST nodes will be cached, so you can use this
    35    *        identifier to avoid parsing the whole source again.
    36    */
    37   get: function(aSource, aUrl = "") {
    38     // Try to use the cached AST nodes, to avoid useless parsing operations.
    39     if (this._cache.has(aUrl)) {
    40       return this._cache.get(aUrl);
    41     }
    43     // The source may not necessarily be JS, in which case we need to extract
    44     // all the scripts. Fastest/easiest way is with a regular expression.
    45     // Don't worry, the rules of using a <script> tag are really strict,
    46     // this will work.
    47     let regexp = /<script[^>]*>([^]*?)<\/script\s*>/gim;
    48     let syntaxTrees = [];
    49     let scriptMatches = [];
    50     let scriptMatch;
    52     if (aSource.match(/^\s*</)) {
    53       // First non whitespace character is &lt, so most definitely HTML.
    54       while (scriptMatch = regexp.exec(aSource)) {
    55         scriptMatches.push(scriptMatch[1]); // Contents are captured at index 1.
    56       }
    57     }
    59     // If there are no script matches, send the whole source directly to the
    60     // reflection API to generate the AST nodes.
    61     if (!scriptMatches.length) {
    62       // Reflect.parse throws when encounters a syntax error.
    63       try {
    64         let nodes = Reflect.parse(aSource);
    65         let length = aSource.length;
    66         syntaxTrees.push(new SyntaxTree(nodes, aUrl, length));
    67       } catch (e) {
    68         this.errors.push(e);
    69         DevToolsUtils.reportException(aUrl, e);
    70       }
    71     }
    72     // Generate the AST nodes for each script.
    73     else {
    74       for (let script of scriptMatches) {
    75         // Reflect.parse throws when encounters a syntax error.
    76         try {
    77           let nodes = Reflect.parse(script);
    78           let offset = aSource.indexOf(script);
    79           let length = script.length;
    80           syntaxTrees.push(new SyntaxTree(nodes, aUrl, length, offset));
    81         } catch (e) {
    82           this.errors.push(e);
    83           DevToolsUtils.reportException(aUrl, e);
    84         }
    85       }
    86     }
    88     let pool = new SyntaxTreesPool(syntaxTrees, aUrl);
    90     // Cache the syntax trees pool by the specified url. This is entirely
    91     // optional, but it's strongly encouraged to cache ASTs because
    92     // generating them can be costly with big/complex sources.
    93     if (aUrl) {
    94       this._cache.set(aUrl, pool);
    95     }
    97     return pool;
    98   },
   100   /**
   101    * Clears all the parsed sources from cache.
   102    */
   103   clearCache: function() {
   104     this._cache.clear();
   105   },
   107   /**
   108    * Clears the AST for a particular source.
   109    *
   110    * @param String aUrl
   111    *        The URL of the source that is being cleared.
   112    */
   113   clearSource: function(aUrl) {
   114     this._cache.delete(aUrl);
   115   },
   117   _cache: null,
   118   errors: null
   119 };
   121 /**
   122  * A pool handling a collection of AST nodes generated by the reflection API.
   123  *
   124  * @param object aSyntaxTrees
   125  *        A collection of AST nodes generated for a source.
   126  * @param string aUrl [optional]
   127  *        The source url.
   128  */
   129 function SyntaxTreesPool(aSyntaxTrees, aUrl = "<unknown>") {
   130   this._trees = aSyntaxTrees;
   131   this._url = aUrl;
   132   this._cache = new Map();
   133 }
   135 SyntaxTreesPool.prototype = {
   136   /**
   137    * @see SyntaxTree.prototype.getIdentifierAt
   138    */
   139   getIdentifierAt: function({ line, column, scriptIndex, ignoreLiterals }) {
   140     return this._call("getIdentifierAt", scriptIndex, line, column, ignoreLiterals)[0];
   141   },
   143   /**
   144    * @see SyntaxTree.prototype.getNamedFunctionDefinitions
   145    */
   146   getNamedFunctionDefinitions: function(aSubstring) {
   147     return this._call("getNamedFunctionDefinitions", -1, aSubstring);
   148   },
   150   /**
   151    * Gets the total number of scripts in the parent source.
   152    * @return number
   153    */
   154   get scriptCount() {
   155     return this._trees.length;
   156   },
   158   /**
   159    * Finds the start and length of the script containing the specified offset
   160    * relative to its parent source.
   161    *
   162    * @param number aOffset
   163    *        The offset relative to the parent source.
   164    * @return object
   165    *         The offset and length relative to the enclosing script.
   166    */
   167   getScriptInfo: function(aOffset) {
   168     let info = { start: -1, length: -1, index: -1 };
   170     for (let { offset, length } of this._trees) {
   171       info.index++;
   172       if (offset <= aOffset && offset + length >= aOffset) {
   173         info.start = offset;
   174         info.length = length;
   175         return info;
   176       }
   177     }
   179     info.index = -1;
   180     return info;
   181   },
   183   /**
   184    * Handles a request for a specific or all known syntax trees.
   185    *
   186    * @param string aFunction
   187    *        The function name to call on the SyntaxTree instances.
   188    * @param number aSyntaxTreeIndex
   189    *        The syntax tree for which to handle the request. If the tree at
   190    *        the specified index isn't found, the accumulated results for all
   191    *        syntax trees are returned.
   192    * @param any aParams
   193    *        Any kind params to pass to the request function.
   194    * @return array
   195    *         The results given by all known syntax trees.
   196    */
   197   _call: function(aFunction, aSyntaxTreeIndex, ...aParams) {
   198     let results = [];
   199     let requestId = [aFunction, aSyntaxTreeIndex, aParams].toSource();
   201     if (this._cache.has(requestId)) {
   202       return this._cache.get(requestId);
   203     }
   205     let requestedTree = this._trees[aSyntaxTreeIndex];
   206     let targettedTrees = requestedTree ? [requestedTree] : this._trees;
   208     for (let syntaxTree of targettedTrees) {
   209       try {
   210         let parseResults = syntaxTree[aFunction].apply(syntaxTree, aParams);
   211         if (parseResults) {
   212           parseResults.sourceUrl = syntaxTree.url;
   213           parseResults.scriptLength = syntaxTree.length;
   214           parseResults.scriptOffset = syntaxTree.offset;
   215           results.push(parseResults);
   216         }
   217       } catch (e) {
   218         // Can't guarantee that the tree traversal logic is forever perfect :)
   219         // Language features may be added, in which case the recursive methods
   220         // need to be updated. If an exception is thrown here, file a bug.
   221         DevToolsUtils.reportException("Syntax tree visitor for " + aUrl, e);
   222       }
   223     }
   224     this._cache.set(requestId, results);
   225     return results;
   226   },
   228   _trees: null,
   229   _cache: null
   230 };
   232 /**
   233  * A collection of AST nodes generated by the reflection API.
   234  *
   235  * @param object aNodes
   236  *        The AST nodes.
   237  * @param string aUrl
   238  *        The source url.
   239  * @param number aLength
   240  *        The total number of chars of the parsed script in the parent source.
   241  * @param number aOffset [optional]
   242  *        The char offset of the parsed script in the parent source.
   243  */
   244 function SyntaxTree(aNodes, aUrl, aLength, aOffset = 0) {
   245   this.AST = aNodes;
   246   this.url = aUrl;
   247   this.length = aLength;
   248   this.offset = aOffset;
   249 };
   251 SyntaxTree.prototype = {
   252   /**
   253    * Gets the identifier at the specified location.
   254    *
   255    * @param number aLine
   256    *        The line in the source.
   257    * @param number aColumn
   258    *        The column in the source.
   259    * @param boolean aIgnoreLiterals
   260    *        Specifies if alone literals should be ignored.
   261    * @return object
   262    *         An object containing identifier information as { name, location,
   263    *         evalString } properties, or null if nothing is found.
   264    */
   265   getIdentifierAt: function(aLine, aColumn, aIgnoreLiterals) {
   266     let info = null;
   268     SyntaxTreeVisitor.walk(this.AST, {
   269       /**
   270        * Callback invoked for each identifier node.
   271        * @param Node aNode
   272        */
   273       onIdentifier: function(aNode) {
   274         if (ParserHelpers.nodeContainsPoint(aNode, aLine, aColumn)) {
   275           info = {
   276             name: aNode.name,
   277             location: ParserHelpers.getNodeLocation(aNode),
   278             evalString: ParserHelpers.getIdentifierEvalString(aNode)
   279           };
   281           // Abruptly halt walking the syntax tree.
   282           SyntaxTreeVisitor.break = true;
   283         }
   284       },
   286       /**
   287        * Callback invoked for each literal node.
   288        * @param Node aNode
   289        */
   290       onLiteral: function(aNode) {
   291         if (!aIgnoreLiterals) {
   292           this.onIdentifier(aNode);
   293         }
   294       },
   296       /**
   297        * Callback invoked for each 'this' node.
   298        * @param Node aNode
   299        */
   300       onThisExpression: function(aNode) {
   301         this.onIdentifier(aNode);
   302       }
   303     });
   305     return info;
   306   },
   308   /**
   309    * Searches for all function definitions (declarations and expressions)
   310    * whose names (or inferred names) contain a string.
   311    *
   312    * @param string aSubstring
   313    *        The string to be contained in the function name (or inferred name).
   314    *        Can be an empty string to match all functions.
   315    * @return array
   316    *         All the matching function declarations and expressions, as
   317    *         { functionName, functionLocation ... } object hashes.
   318    */
   319   getNamedFunctionDefinitions: function(aSubstring) {
   320     let lowerCaseToken = aSubstring.toLowerCase();
   321     let store = [];
   323     SyntaxTreeVisitor.walk(this.AST, {
   324       /**
   325        * Callback invoked for each function declaration node.
   326        * @param Node aNode
   327        */
   328       onFunctionDeclaration: function(aNode) {
   329         let functionName = aNode.id.name;
   330         if (functionName.toLowerCase().contains(lowerCaseToken)) {
   331           store.push({
   332             functionName: functionName,
   333             functionLocation: ParserHelpers.getNodeLocation(aNode)
   334           });
   335         }
   336       },
   338       /**
   339        * Callback invoked for each function expression node.
   340        * @param Node aNode
   341        */
   342       onFunctionExpression: function(aNode) {
   343         // Function expressions don't necessarily have a name.
   344         let functionName = aNode.id ? aNode.id.name : "";
   345         let functionLocation = ParserHelpers.getNodeLocation(aNode);
   347         // Infer the function's name from an enclosing syntax tree node.
   348         let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
   349         let inferredName = inferredInfo.name;
   350         let inferredChain = inferredInfo.chain;
   351         let inferredLocation = inferredInfo.loc;
   353         // Current node may be part of a larger assignment expression stack.
   354         if (aNode._parent.type == "AssignmentExpression") {
   355           this.onFunctionExpression(aNode._parent);
   356         }
   358         if ((functionName && functionName.toLowerCase().contains(lowerCaseToken)) ||
   359             (inferredName && inferredName.toLowerCase().contains(lowerCaseToken))) {
   360           store.push({
   361             functionName: functionName,
   362             functionLocation: functionLocation,
   363             inferredName: inferredName,
   364             inferredChain: inferredChain,
   365             inferredLocation: inferredLocation
   366           });
   367         }
   368       },
   370       /**
   371        * Callback invoked for each arrow expression node.
   372        * @param Node aNode
   373        */
   374       onArrowExpression: function(aNode) {
   375         // Infer the function's name from an enclosing syntax tree node.
   376         let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
   377         let inferredName = inferredInfo.name;
   378         let inferredChain = inferredInfo.chain;
   379         let inferredLocation = inferredInfo.loc;
   381         // Current node may be part of a larger assignment expression stack.
   382         if (aNode._parent.type == "AssignmentExpression") {
   383           this.onFunctionExpression(aNode._parent);
   384         }
   386         if (inferredName && inferredName.toLowerCase().contains(lowerCaseToken)) {
   387           store.push({
   388             inferredName: inferredName,
   389             inferredChain: inferredChain,
   390             inferredLocation: inferredLocation
   391           });
   392         }
   393       }
   394     });
   396     return store;
   397   },
   399   AST: null,
   400   url: "",
   401   length: 0,
   402   offset: 0
   403 };
   405 /**
   406  * Parser utility methods.
   407  */
   408 let ParserHelpers = {
   409   /**
   410    * Gets the location information for a node. Not all nodes have a
   411    * location property directly attached, or the location information
   412    * is incorrect, in which cases it's accessible via the parent.
   413    *
   414    * @param Node aNode
   415    *        The node who's location needs to be retrieved.
   416    * @return object
   417    *         An object containing { line, column } information.
   418    */
   419   getNodeLocation: function(aNode) {
   420     if (aNode.type != "Identifier") {
   421       return aNode.loc;
   422     }
   423     // Work around the fact that some identifier nodes don't have the
   424     // correct location attached.
   425     let { loc: parentLocation, type: parentType } = aNode._parent;
   426     let { loc: nodeLocation } = aNode;
   427     if (!nodeLocation) {
   428       if (parentType == "FunctionDeclaration" ||
   429           parentType == "FunctionExpression") {
   430         // e.g. "function foo() {}" or "{ bar: function foo() {} }"
   431         // The location is unavailable for the identifier node "foo".
   432         let loc = JSON.parse(JSON.stringify(parentLocation));
   433         loc.end.line = loc.start.line;
   434         loc.end.column = loc.start.column + aNode.name.length;
   435         return loc;
   436       }
   437       if (parentType == "MemberExpression") {
   438         // e.g. "foo.bar"
   439         // The location is unavailable for the identifier node "bar".
   440         let loc = JSON.parse(JSON.stringify(parentLocation));
   441         loc.start.line = loc.end.line;
   442         loc.start.column = loc.end.column - aNode.name.length;
   443         return loc;
   444       }
   445       if (parentType == "LabeledStatement") {
   446         // e.g. label: ...
   447         // The location is unavailable for the identifier node "label".
   448         let loc = JSON.parse(JSON.stringify(parentLocation));
   449         loc.end.line = loc.start.line;
   450         loc.end.column = loc.start.column + aNode.name.length;
   451         return loc;
   452       }
   453       if (parentType == "ContinueStatement") {
   454         // e.g. continue label
   455         // The location is unavailable for the identifier node "label".
   456         let loc = JSON.parse(JSON.stringify(parentLocation));
   457         loc.start.line = loc.end.line;
   458         loc.start.column = loc.end.column - aNode.name.length;
   459         return loc;
   460       }
   461     } else {
   462       if (parentType == "VariableDeclarator") {
   463         // e.g. "let foo = 42"
   464         // The location incorrectly spans across the whole variable declaration,
   465         // not just the identifier node "foo".
   466         let loc = JSON.parse(JSON.stringify(nodeLocation));
   467         loc.end.line = loc.start.line;
   468         loc.end.column = loc.start.column + aNode.name.length;
   469         return loc;
   470       }
   471     }
   472     return aNode.loc;
   473   },
   475   /**
   476    * Checks if a node's bounds contains a specified line.
   477    *
   478    * @param Node aNode
   479    *        The node's bounds used as reference.
   480    * @param number aLine
   481    *        The line number to check.
   482    * @return boolean
   483    *         True if the line and column is contained in the node's bounds.
   484    */
   485   nodeContainsLine: function(aNode, aLine) {
   486     let { start: s, end: e } = this.getNodeLocation(aNode);
   487     return s.line <= aLine && e.line >= aLine;
   488   },
   490   /**
   491    * Checks if a node's bounds contains a specified line and column.
   492    *
   493    * @param Node aNode
   494    *        The node's bounds used as reference.
   495    * @param number aLine
   496    *        The line number to check.
   497    * @param number aColumn
   498    *        The column number to check.
   499    * @return boolean
   500    *         True if the line and column is contained in the node's bounds.
   501    */
   502   nodeContainsPoint: function(aNode, aLine, aColumn) {
   503     let { start: s, end: e } = this.getNodeLocation(aNode);
   504     return s.line == aLine && e.line == aLine &&
   505            s.column <= aColumn && e.column >= aColumn;
   506   },
   508   /**
   509    * Try to infer a function expression's name & other details based on the
   510    * enclosing VariableDeclarator, AssignmentExpression or ObjectExpression.
   511    *
   512    * @param Node aNode
   513    *        The function expression node to get the name for.
   514    * @return object
   515    *         The inferred function name, or empty string can't infer the name,
   516    *         along with the chain (a generic "context", like a prototype chain)
   517    *         and location if available.
   518    */
   519   inferFunctionExpressionInfo: function(aNode) {
   520     let parent = aNode._parent;
   522     // A function expression may be defined in a variable declarator,
   523     // e.g. var foo = function(){}, in which case it is possible to infer
   524     // the variable name.
   525     if (parent.type == "VariableDeclarator") {
   526       return {
   527         name: parent.id.name,
   528         chain: null,
   529         loc: this.getNodeLocation(parent.id)
   530       };
   531     }
   533     // Function expressions can also be defined in assignment expressions,
   534     // e.g. foo = function(){} or foo.bar = function(){}, in which case it is
   535     // possible to infer the assignee name ("foo" and "bar" respectively).
   536     if (parent.type == "AssignmentExpression") {
   537       let propertyChain = this._getMemberExpressionPropertyChain(parent.left);
   538       let propertyLeaf = propertyChain.pop();
   539       return {
   540         name: propertyLeaf,
   541         chain: propertyChain,
   542         loc: this.getNodeLocation(parent.left)
   543       };
   544     }
   546     // If a function expression is defined in an object expression,
   547     // e.g. { foo: function(){} }, then it is possible to infer the name
   548     // from the corresponding property.
   549     if (parent.type == "ObjectExpression") {
   550       let propertyKey = this._getObjectExpressionPropertyKeyForValue(aNode);
   551       let propertyChain = this._getObjectExpressionPropertyChain(parent);
   552       let propertyLeaf = propertyKey.name;
   553       return {
   554         name: propertyLeaf,
   555         chain: propertyChain,
   556         loc: this.getNodeLocation(propertyKey)
   557       };
   558     }
   560     // Can't infer the function expression's name.
   561     return {
   562       name: "",
   563       chain: null,
   564       loc: null
   565     };
   566   },
   568   /**
   569    * Gets the name of an object expression's property to which a specified
   570    * value is assigned.
   571    *
   572    * Used for inferring function expression information and retrieving
   573    * an identifier evaluation string.
   574    *
   575    * For example, if aNode represents the "bar" identifier in a hypothetical
   576    * "{ foo: bar }" object expression, the returned node is the "foo" identifier.
   577    *
   578    * @param Node aNode
   579    *        The value node in an object expression.
   580    * @return object
   581    *         The key identifier node in the object expression.
   582    */
   583   _getObjectExpressionPropertyKeyForValue: function(aNode) {
   584     let parent = aNode._parent;
   585     if (parent.type != "ObjectExpression") {
   586       return null;
   587     }
   588     for (let property of parent.properties) {
   589       if (property.value == aNode) {
   590         return property.key;
   591       }
   592     }
   593   },
   595   /**
   596    * Gets an object expression's property chain to its parent
   597    * variable declarator or assignment expression, if available.
   598    *
   599    * Used for inferring function expression information and retrieving
   600    * an identifier evaluation string.
   601    *
   602    * For example, if aNode represents the "baz: {}" object expression in a
   603    * hypothetical "foo = { bar: { baz: {} } }" assignment expression, the
   604    * returned chain is ["foo", "bar", "baz"].
   605    *
   606    * @param Node aNode
   607    *        The object expression node to begin the scan from.
   608    * @param array aStore [optional]
   609    *        The chain to store the nodes into.
   610    * @return array
   611    *         The chain to the parent variable declarator, as strings.
   612    */
   613   _getObjectExpressionPropertyChain: function(aNode, aStore = []) {
   614     switch (aNode.type) {
   615       case "ObjectExpression":
   616         this._getObjectExpressionPropertyChain(aNode._parent, aStore);
   617         let propertyKey = this._getObjectExpressionPropertyKeyForValue(aNode);
   618         if (propertyKey) {
   619           aStore.push(propertyKey.name);
   620         }
   621         break;
   622       // Handle "var foo = { ... }" variable declarators.
   623       case "VariableDeclarator":
   624         aStore.push(aNode.id.name);
   625         break;
   626       // Handle "foo.bar = { ... }" assignment expressions, since they're
   627       // commonly used when defining an object's prototype methods; e.g:
   628       // "Foo.prototype = { ... }".
   629       case "AssignmentExpression":
   630         this._getMemberExpressionPropertyChain(aNode.left, aStore);
   631         break;
   632       // Additionally handle stuff like "foo = bar.baz({ ... })", because it's
   633       // commonly used in prototype-based inheritance in many libraries; e.g:
   634       // "Foo = Bar.extend({ ... })".
   635       case "NewExpression":
   636       case "CallExpression":
   637         this._getObjectExpressionPropertyChain(aNode._parent, aStore);
   638         break;
   639     }
   640     return aStore;
   641   },
   643   /**
   644    * Gets a member expression's property chain.
   645    *
   646    * Used for inferring function expression information and retrieving
   647    * an identifier evaluation string.
   648    *
   649    * For example, if aNode represents a hypothetical "foo.bar.baz"
   650    * member expression, the returned chain ["foo", "bar", "baz"].
   651    *
   652    * More complex expressions like foo.bar().baz are intentionally not handled.
   653    *
   654    * @param Node aNode
   655    *        The member expression node to begin the scan from.
   656    * @param array aStore [optional]
   657    *        The chain to store the nodes into.
   658    * @return array
   659    *         The full member chain, as strings.
   660    */
   661   _getMemberExpressionPropertyChain: function(aNode, aStore = []) {
   662     switch (aNode.type) {
   663       case "MemberExpression":
   664         this._getMemberExpressionPropertyChain(aNode.object, aStore);
   665         this._getMemberExpressionPropertyChain(aNode.property, aStore);
   666         break;
   667       case "ThisExpression":
   668         aStore.push("this");
   669         break;
   670       case "Identifier":
   671         aStore.push(aNode.name);
   672         break;
   673     }
   674     return aStore;
   675   },
   677   /**
   678    * Returns an evaluation string which can be used to obtain the
   679    * current value for the respective identifier.
   680    *
   681    * @param Node aNode
   682    *        The leaf node (e.g. Identifier, Literal) to begin the scan from.
   683    * @return string
   684    *         The corresponding evaluation string, or empty string if
   685    *         the specified leaf node can't be used.
   686    */
   687   getIdentifierEvalString: function(aNode) {
   688     switch (aNode._parent.type) {
   689       case "ObjectExpression":
   690         // If the identifier is the actual property value, it can be used
   691         // directly as an evaluation string. Otherwise, construct the property
   692         // access chain, since the value might have changed.
   693         if (!this._getObjectExpressionPropertyKeyForValue(aNode)) {
   694           let propertyChain = this._getObjectExpressionPropertyChain(aNode._parent);
   695           let propertyLeaf = aNode.name;
   696           return [...propertyChain, propertyLeaf].join(".");
   697         }
   698         break;
   699       case "MemberExpression":
   700         // Make sure this is a property identifier, not the parent object.
   701         if (aNode._parent.property == aNode) {
   702           return this._getMemberExpressionPropertyChain(aNode._parent).join(".");
   703         }
   704         break;
   705     }
   706     switch (aNode.type) {
   707       case "ThisExpression":
   708         return "this";
   709       case "Identifier":
   710         return aNode.name;
   711       case "Literal":
   712         return uneval(aNode.value);
   713       default:
   714         return "";
   715     }
   716   }
   717 };
   719 /**
   720  * A visitor for a syntax tree generated by the reflection API.
   721  * See https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API.
   722  *
   723  * All node types implement the following interface:
   724  * interface Node {
   725  *   type: string;
   726  *   loc: SourceLocation | null;
   727  * }
   728  */
   729 let SyntaxTreeVisitor = {
   730   /**
   731    * Walks a syntax tree.
   732    *
   733    * @param object aTree
   734    *        The AST nodes generated by the reflection API
   735    * @param object aCallbacks
   736    *        A map of all the callbacks to invoke when passing through certain
   737    *        types of noes (e.g: onFunctionDeclaration, onBlockStatement etc.).
   738    */
   739   walk: function(aTree, aCallbacks) {
   740     this.break = false;
   741     this[aTree.type](aTree, aCallbacks);
   742   },
   744   /**
   745    * Filters all the nodes in this syntax tree based on a predicate.
   746    *
   747    * @param object aTree
   748    *        The AST nodes generated by the reflection API
   749    * @param function aPredicate
   750    *        The predicate ran on each node.
   751    * @return array
   752    *         An array of nodes validating the predicate.
   753    */
   754   filter: function(aTree, aPredicate) {
   755     let store = [];
   756     this.walk(aTree, { onNode: e => { if (aPredicate(e)) store.push(e); } });
   757     return store;
   758   },
   760   /**
   761    * A flag checked on each node in the syntax tree. If true, walking is
   762    * abruptly halted.
   763    */
   764   break: false,
   766   /**
   767    * A complete program source tree.
   768    *
   769    * interface Program <: Node {
   770    *   type: "Program";
   771    *   body: [ Statement ];
   772    * }
   773    */
   774   Program: function(aNode, aCallbacks) {
   775     if (aCallbacks.onProgram) {
   776       aCallbacks.onProgram(aNode);
   777     }
   778     for (let statement of aNode.body) {
   779       this[statement.type](statement, aNode, aCallbacks);
   780     }
   781   },
   783   /**
   784    * Any statement.
   785    *
   786    * interface Statement <: Node { }
   787    */
   788   Statement: function(aNode, aParent, aCallbacks) {
   789     aNode._parent = aParent;
   791     if (this.break) {
   792       return;
   793     }
   794     if (aCallbacks.onNode) {
   795       if (aCallbacks.onNode(aNode, aParent) === false) {
   796         return;
   797       }
   798     }
   799     if (aCallbacks.onStatement) {
   800       aCallbacks.onStatement(aNode);
   801     }
   802   },
   804   /**
   805    * An empty statement, i.e., a solitary semicolon.
   806    *
   807    * interface EmptyStatement <: Statement {
   808    *   type: "EmptyStatement";
   809    * }
   810    */
   811   EmptyStatement: function(aNode, aParent, aCallbacks) {
   812     aNode._parent = aParent;
   814     if (this.break) {
   815       return;
   816     }
   817     if (aCallbacks.onNode) {
   818       if (aCallbacks.onNode(aNode, aParent) === false) {
   819         return;
   820       }
   821     }
   822     if (aCallbacks.onEmptyStatement) {
   823       aCallbacks.onEmptyStatement(aNode);
   824     }
   825   },
   827   /**
   828    * A block statement, i.e., a sequence of statements surrounded by braces.
   829    *
   830    * interface BlockStatement <: Statement {
   831    *   type: "BlockStatement";
   832    *   body: [ Statement ];
   833    * }
   834    */
   835   BlockStatement: function(aNode, aParent, aCallbacks) {
   836     aNode._parent = aParent;
   838     if (this.break) {
   839       return;
   840     }
   841     if (aCallbacks.onNode) {
   842       if (aCallbacks.onNode(aNode, aParent) === false) {
   843         return;
   844       }
   845     }
   846     if (aCallbacks.onBlockStatement) {
   847       aCallbacks.onBlockStatement(aNode);
   848     }
   849     for (let statement of aNode.body) {
   850       this[statement.type](statement, aNode, aCallbacks);
   851     }
   852   },
   854   /**
   855    * An expression statement, i.e., a statement consisting of a single expression.
   856    *
   857    * interface ExpressionStatement <: Statement {
   858    *   type: "ExpressionStatement";
   859    *   expression: Expression;
   860    * }
   861    */
   862   ExpressionStatement: function(aNode, aParent, aCallbacks) {
   863     aNode._parent = aParent;
   865     if (this.break) {
   866       return;
   867     }
   868     if (aCallbacks.onNode) {
   869       if (aCallbacks.onNode(aNode, aParent) === false) {
   870         return;
   871       }
   872     }
   873     if (aCallbacks.onExpressionStatement) {
   874       aCallbacks.onExpressionStatement(aNode);
   875     }
   876     this[aNode.expression.type](aNode.expression, aNode, aCallbacks);
   877   },
   879   /**
   880    * An if statement.
   881    *
   882    * interface IfStatement <: Statement {
   883    *   type: "IfStatement";
   884    *   test: Expression;
   885    *   consequent: Statement;
   886    *   alternate: Statement | null;
   887    * }
   888    */
   889   IfStatement: function(aNode, aParent, aCallbacks) {
   890     aNode._parent = aParent;
   892     if (this.break) {
   893       return;
   894     }
   895     if (aCallbacks.onNode) {
   896       if (aCallbacks.onNode(aNode, aParent) === false) {
   897         return;
   898       }
   899     }
   900     if (aCallbacks.onIfStatement) {
   901       aCallbacks.onIfStatement(aNode);
   902     }
   903     this[aNode.test.type](aNode.test, aNode, aCallbacks);
   904     this[aNode.consequent.type](aNode.consequent, aNode, aCallbacks);
   905     if (aNode.alternate) {
   906       this[aNode.alternate.type](aNode.alternate, aNode, aCallbacks);
   907     }
   908   },
   910   /**
   911    * A labeled statement, i.e., a statement prefixed by a break/continue label.
   912    *
   913    * interface LabeledStatement <: Statement {
   914    *   type: "LabeledStatement";
   915    *   label: Identifier;
   916    *   body: Statement;
   917    * }
   918    */
   919   LabeledStatement: function(aNode, aParent, aCallbacks) {
   920     aNode._parent = aParent;
   922     if (this.break) {
   923       return;
   924     }
   925     if (aCallbacks.onNode) {
   926       if (aCallbacks.onNode(aNode, aParent) === false) {
   927         return;
   928       }
   929     }
   930     if (aCallbacks.onLabeledStatement) {
   931       aCallbacks.onLabeledStatement(aNode);
   932     }
   933     this[aNode.label.type](aNode.label, aNode, aCallbacks);
   934     this[aNode.body.type](aNode.body, aNode, aCallbacks);
   935   },
   937   /**
   938    * A break statement.
   939    *
   940    * interface BreakStatement <: Statement {
   941    *   type: "BreakStatement";
   942    *   label: Identifier | null;
   943    * }
   944    */
   945   BreakStatement: function(aNode, aParent, aCallbacks) {
   946     aNode._parent = aParent;
   948     if (this.break) {
   949       return;
   950     }
   951     if (aCallbacks.onNode) {
   952       if (aCallbacks.onNode(aNode, aParent) === false) {
   953         return;
   954       }
   955     }
   956     if (aCallbacks.onBreakStatement) {
   957       aCallbacks.onBreakStatement(aNode);
   958     }
   959     if (aNode.label) {
   960       this[aNode.label.type](aNode.label, aNode, aCallbacks);
   961     }
   962   },
   964   /**
   965    * A continue statement.
   966    *
   967    * interface ContinueStatement <: Statement {
   968    *   type: "ContinueStatement";
   969    *   label: Identifier | null;
   970    * }
   971    */
   972   ContinueStatement: function(aNode, aParent, aCallbacks) {
   973     aNode._parent = aParent;
   975     if (this.break) {
   976       return;
   977     }
   978     if (aCallbacks.onNode) {
   979       if (aCallbacks.onNode(aNode, aParent) === false) {
   980         return;
   981       }
   982     }
   983     if (aCallbacks.onContinueStatement) {
   984       aCallbacks.onContinueStatement(aNode);
   985     }
   986     if (aNode.label) {
   987       this[aNode.label.type](aNode.label, aNode, aCallbacks);
   988     }
   989   },
   991   /**
   992    * A with statement.
   993    *
   994    * interface WithStatement <: Statement {
   995    *   type: "WithStatement";
   996    *   object: Expression;
   997    *   body: Statement;
   998    * }
   999    */
  1000   WithStatement: function(aNode, aParent, aCallbacks) {
  1001     aNode._parent = aParent;
  1003     if (this.break) {
  1004       return;
  1006     if (aCallbacks.onNode) {
  1007       if (aCallbacks.onNode(aNode, aParent) === false) {
  1008         return;
  1011     if (aCallbacks.onWithStatement) {
  1012       aCallbacks.onWithStatement(aNode);
  1014     this[aNode.object.type](aNode.object, aNode, aCallbacks);
  1015     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1016   },
  1018   /**
  1019    * A switch statement. The lexical flag is metadata indicating whether the
  1020    * switch statement contains any unnested let declarations (and therefore
  1021    * introduces a new lexical scope).
  1023    * interface SwitchStatement <: Statement {
  1024    *   type: "SwitchStatement";
  1025    *   discriminant: Expression;
  1026    *   cases: [ SwitchCase ];
  1027    *   lexical: boolean;
  1028    * }
  1029    */
  1030   SwitchStatement: function(aNode, aParent, aCallbacks) {
  1031     aNode._parent = aParent;
  1033     if (this.break) {
  1034       return;
  1036     if (aCallbacks.onNode) {
  1037       if (aCallbacks.onNode(aNode, aParent) === false) {
  1038         return;
  1041     if (aCallbacks.onSwitchStatement) {
  1042       aCallbacks.onSwitchStatement(aNode);
  1044     this[aNode.discriminant.type](aNode.discriminant, aNode, aCallbacks);
  1045     for (let _case of aNode.cases) {
  1046       this[_case.type](_case, aNode, aCallbacks);
  1048   },
  1050   /**
  1051    * A return statement.
  1053    * interface ReturnStatement <: Statement {
  1054    *   type: "ReturnStatement";
  1055    *   argument: Expression | null;
  1056    * }
  1057    */
  1058   ReturnStatement: function(aNode, aParent, aCallbacks) {
  1059     aNode._parent = aParent;
  1061     if (this.break) {
  1062       return;
  1064     if (aCallbacks.onNode) {
  1065       if (aCallbacks.onNode(aNode, aParent) === false) {
  1066         return;
  1069     if (aCallbacks.onReturnStatement) {
  1070       aCallbacks.onReturnStatement(aNode);
  1072     if (aNode.argument) {
  1073       this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1075   },
  1077   /**
  1078    * A throw statement.
  1080    * interface ThrowStatement <: Statement {
  1081    *   type: "ThrowStatement";
  1082    *   argument: Expression;
  1083    * }
  1084    */
  1085   ThrowStatement: function(aNode, aParent, aCallbacks) {
  1086     aNode._parent = aParent;
  1088     if (this.break) {
  1089       return;
  1091     if (aCallbacks.onNode) {
  1092       if (aCallbacks.onNode(aNode, aParent) === false) {
  1093         return;
  1096     if (aCallbacks.onThrowStatement) {
  1097       aCallbacks.onThrowStatement(aNode);
  1099     this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1100   },
  1102   /**
  1103    * A try statement.
  1105    * interface TryStatement <: Statement {
  1106    *   type: "TryStatement";
  1107    *   block: BlockStatement;
  1108    *   handler: CatchClause | null;
  1109    *   guardedHandlers: [ CatchClause ];
  1110    *   finalizer: BlockStatement | null;
  1111    * }
  1112    */
  1113   TryStatement: function(aNode, aParent, aCallbacks) {
  1114     aNode._parent = aParent;
  1116     if (this.break) {
  1117       return;
  1119     if (aCallbacks.onNode) {
  1120       if (aCallbacks.onNode(aNode, aParent) === false) {
  1121         return;
  1124     if (aCallbacks.onTryStatement) {
  1125       aCallbacks.onTryStatement(aNode);
  1127     this[aNode.block.type](aNode.block, aNode, aCallbacks);
  1128     if (aNode.handler) {
  1129       this[aNode.handler.type](aNode.handler, aNode, aCallbacks);
  1131     for (let guardedHandler of aNode.guardedHandlers) {
  1132       this[guardedHandler.type](guardedHandler, aNode, aCallbacks);
  1134     if (aNode.finalizer) {
  1135       this[aNode.finalizer.type](aNode.finalizer, aNode, aCallbacks);
  1137   },
  1139   /**
  1140    * A while statement.
  1142    * interface WhileStatement <: Statement {
  1143    *   type: "WhileStatement";
  1144    *   test: Expression;
  1145    *   body: Statement;
  1146    * }
  1147    */
  1148   WhileStatement: function(aNode, aParent, aCallbacks) {
  1149     aNode._parent = aParent;
  1151     if (this.break) {
  1152       return;
  1154     if (aCallbacks.onNode) {
  1155       if (aCallbacks.onNode(aNode, aParent) === false) {
  1156         return;
  1159     if (aCallbacks.onWhileStatement) {
  1160       aCallbacks.onWhileStatement(aNode);
  1162     this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1163     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1164   },
  1166   /**
  1167    * A do/while statement.
  1169    * interface DoWhileStatement <: Statement {
  1170    *   type: "DoWhileStatement";
  1171    *   body: Statement;
  1172    *   test: Expression;
  1173    * }
  1174    */
  1175   DoWhileStatement: function(aNode, aParent, aCallbacks) {
  1176     aNode._parent = aParent;
  1178     if (this.break) {
  1179       return;
  1181     if (aCallbacks.onNode) {
  1182       if (aCallbacks.onNode(aNode, aParent) === false) {
  1183         return;
  1186     if (aCallbacks.onDoWhileStatement) {
  1187       aCallbacks.onDoWhileStatement(aNode);
  1189     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1190     this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1191   },
  1193   /**
  1194    * A for statement.
  1196    * interface ForStatement <: Statement {
  1197    *   type: "ForStatement";
  1198    *   init: VariableDeclaration | Expression | null;
  1199    *   test: Expression | null;
  1200    *   update: Expression | null;
  1201    *   body: Statement;
  1202    * }
  1203    */
  1204   ForStatement: function(aNode, aParent, aCallbacks) {
  1205     aNode._parent = aParent;
  1207     if (this.break) {
  1208       return;
  1210     if (aCallbacks.onNode) {
  1211       if (aCallbacks.onNode(aNode, aParent) === false) {
  1212         return;
  1215     if (aCallbacks.onForStatement) {
  1216       aCallbacks.onForStatement(aNode);
  1218     if (aNode.init) {
  1219       this[aNode.init.type](aNode.init, aNode, aCallbacks);
  1221     if (aNode.test) {
  1222       this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1224     if (aNode.update) {
  1225       this[aNode.update.type](aNode.update, aNode, aCallbacks);
  1227     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1228   },
  1230   /**
  1231    * A for/in statement, or, if each is true, a for each/in statement.
  1233    * interface ForInStatement <: Statement {
  1234    *   type: "ForInStatement";
  1235    *   left: VariableDeclaration | Expression;
  1236    *   right: Expression;
  1237    *   body: Statement;
  1238    *   each: boolean;
  1239    * }
  1240    */
  1241   ForInStatement: function(aNode, aParent, aCallbacks) {
  1242     aNode._parent = aParent;
  1244     if (this.break) {
  1245       return;
  1247     if (aCallbacks.onNode) {
  1248       if (aCallbacks.onNode(aNode, aParent) === false) {
  1249         return;
  1252     if (aCallbacks.onForInStatement) {
  1253       aCallbacks.onForInStatement(aNode);
  1255     this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1256     this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1257     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1258   },
  1260   /**
  1261    * A for/of statement.
  1263    * interface ForOfStatement <: Statement {
  1264    *   type: "ForOfStatement";
  1265    *   left: VariableDeclaration | Expression;
  1266    *   right: Expression;
  1267    *   body: Statement;
  1268    * }
  1269    */
  1270   ForOfStatement: function(aNode, aParent, aCallbacks) {
  1271     aNode._parent = aParent;
  1273     if (this.break) {
  1274       return;
  1276     if (aCallbacks.onNode) {
  1277       if (aCallbacks.onNode(aNode, aParent) === false) {
  1278         return;
  1281     if (aCallbacks.onForOfStatement) {
  1282       aCallbacks.onForOfStatement(aNode);
  1284     this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1285     this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1286     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1287   },
  1289   /**
  1290    * A let statement.
  1292    * interface LetStatement <: Statement {
  1293    *   type: "LetStatement";
  1294    *   head: [ { id: Pattern, init: Expression | null } ];
  1295    *   body: Statement;
  1296    * }
  1297    */
  1298   LetStatement: function(aNode, aParent, aCallbacks) {
  1299     aNode._parent = aParent;
  1301     if (this.break) {
  1302       return;
  1304     if (aCallbacks.onNode) {
  1305       if (aCallbacks.onNode(aNode, aParent) === false) {
  1306         return;
  1309     if (aCallbacks.onLetStatement) {
  1310       aCallbacks.onLetStatement(aNode);
  1312     for (let { id, init } of aNode.head) {
  1313       this[id.type](id, aNode, aCallbacks);
  1314       if (init) {
  1315         this[init.type](init, aNode, aCallbacks);
  1318     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1319   },
  1321   /**
  1322    * A debugger statement.
  1324    * interface DebuggerStatement <: Statement {
  1325    *   type: "DebuggerStatement";
  1326    * }
  1327    */
  1328   DebuggerStatement: function(aNode, aParent, aCallbacks) {
  1329     aNode._parent = aParent;
  1331     if (this.break) {
  1332       return;
  1334     if (aCallbacks.onNode) {
  1335       if (aCallbacks.onNode(aNode, aParent) === false) {
  1336         return;
  1339     if (aCallbacks.onDebuggerStatement) {
  1340       aCallbacks.onDebuggerStatement(aNode);
  1342   },
  1344   /**
  1345    * Any declaration node. Note that declarations are considered statements;
  1346    * this is because declarations can appear in any statement context in the
  1347    * language recognized by the SpiderMonkey parser.
  1349    * interface Declaration <: Statement { }
  1350    */
  1351   Declaration: function(aNode, aParent, aCallbacks) {
  1352     aNode._parent = aParent;
  1354     if (this.break) {
  1355       return;
  1357     if (aCallbacks.onNode) {
  1358       if (aCallbacks.onNode(aNode, aParent) === false) {
  1359         return;
  1362     if (aCallbacks.onDeclaration) {
  1363       aCallbacks.onDeclaration(aNode);
  1365   },
  1367   /**
  1368    * A function declaration.
  1370    * interface FunctionDeclaration <: Function, Declaration {
  1371    *   type: "FunctionDeclaration";
  1372    *   id: Identifier;
  1373    *   params: [ Pattern ];
  1374    *   defaults: [ Expression ];
  1375    *   rest: Identifier | null;
  1376    *   body: BlockStatement | Expression;
  1377    *   generator: boolean;
  1378    *   expression: boolean;
  1379    * }
  1380    */
  1381   FunctionDeclaration: function(aNode, aParent, aCallbacks) {
  1382     aNode._parent = aParent;
  1384     if (this.break) {
  1385       return;
  1387     if (aCallbacks.onNode) {
  1388       if (aCallbacks.onNode(aNode, aParent) === false) {
  1389         return;
  1392     if (aCallbacks.onFunctionDeclaration) {
  1393       aCallbacks.onFunctionDeclaration(aNode);
  1395     this[aNode.id.type](aNode.id, aNode, aCallbacks);
  1396     for (let param of aNode.params) {
  1397       this[param.type](param, aNode, aCallbacks);
  1399     for (let _default of aNode.defaults) {
  1400       this[_default.type](_default, aNode, aCallbacks);
  1402     if (aNode.rest) {
  1403       this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
  1405     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1406   },
  1408   /**
  1409    * A variable declaration, via one of var, let, or const.
  1411    * interface VariableDeclaration <: Declaration {
  1412    *   type: "VariableDeclaration";
  1413    *   declarations: [ VariableDeclarator ];
  1414    *   kind: "var" | "let" | "const";
  1415    * }
  1416    */
  1417   VariableDeclaration: function(aNode, aParent, aCallbacks) {
  1418     aNode._parent = aParent;
  1420     if (this.break) {
  1421       return;
  1423     if (aCallbacks.onNode) {
  1424       if (aCallbacks.onNode(aNode, aParent) === false) {
  1425         return;
  1428     if (aCallbacks.onVariableDeclaration) {
  1429       aCallbacks.onVariableDeclaration(aNode);
  1431     for (let declaration of aNode.declarations) {
  1432       this[declaration.type](declaration, aNode, aCallbacks);
  1434   },
  1436   /**
  1437    * A variable declarator.
  1439    * interface VariableDeclarator <: Node {
  1440    *   type: "VariableDeclarator";
  1441    *   id: Pattern;
  1442    *   init: Expression | null;
  1443    * }
  1444    */
  1445   VariableDeclarator: function(aNode, aParent, aCallbacks) {
  1446     aNode._parent = aParent;
  1448     if (this.break) {
  1449       return;
  1451     if (aCallbacks.onNode) {
  1452       if (aCallbacks.onNode(aNode, aParent) === false) {
  1453         return;
  1456     if (aCallbacks.onVariableDeclarator) {
  1457       aCallbacks.onVariableDeclarator(aNode);
  1459     this[aNode.id.type](aNode.id, aNode, aCallbacks);
  1460     if (aNode.init) {
  1461       this[aNode.init.type](aNode.init, aNode, aCallbacks);
  1463   },
  1465   /**
  1466    * Any expression node. Since the left-hand side of an assignment may be any
  1467    * expression in general, an expression can also be a pattern.
  1469    * interface Expression <: Node, Pattern { }
  1470    */
  1471   Expression: function(aNode, aParent, aCallbacks) {
  1472     aNode._parent = aParent;
  1474     if (this.break) {
  1475       return;
  1477     if (aCallbacks.onNode) {
  1478       if (aCallbacks.onNode(aNode, aParent) === false) {
  1479         return;
  1482     if (aCallbacks.onExpression) {
  1483       aCallbacks.onExpression(aNode);
  1485   },
  1487   /**
  1488    * A this expression.
  1490    * interface ThisExpression <: Expression {
  1491    *   type: "ThisExpression";
  1492    * }
  1493    */
  1494   ThisExpression: function(aNode, aParent, aCallbacks) {
  1495     aNode._parent = aParent;
  1497     if (this.break) {
  1498       return;
  1500     if (aCallbacks.onNode) {
  1501       if (aCallbacks.onNode(aNode, aParent) === false) {
  1502         return;
  1505     if (aCallbacks.onThisExpression) {
  1506       aCallbacks.onThisExpression(aNode);
  1508   },
  1510   /**
  1511    * An array expression.
  1513    * interface ArrayExpression <: Expression {
  1514    *   type: "ArrayExpression";
  1515    *   elements: [ Expression | null ];
  1516    * }
  1517    */
  1518   ArrayExpression: function(aNode, aParent, aCallbacks) {
  1519     aNode._parent = aParent;
  1521     if (this.break) {
  1522       return;
  1524     if (aCallbacks.onNode) {
  1525       if (aCallbacks.onNode(aNode, aParent) === false) {
  1526         return;
  1529     if (aCallbacks.onArrayExpression) {
  1530       aCallbacks.onArrayExpression(aNode);
  1532     for (let element of aNode.elements) {
  1533       // TODO: remove the typeof check when support for SpreadExpression is
  1534       // added (bug 890913).
  1535       if (element && typeof this[element.type] == "function") {
  1536         this[element.type](element, aNode, aCallbacks);
  1539   },
  1541   /**
  1542    * An object expression. A literal property in an object expression can have
  1543    * either a string or number as its value. Ordinary property initializers
  1544    * have a kind value "init"; getters and setters have the kind values "get"
  1545    * and "set", respectively.
  1547    * interface ObjectExpression <: Expression {
  1548    *   type: "ObjectExpression";
  1549    *   properties: [ { key: Literal | Identifier,
  1550    *                   value: Expression,
  1551    *                   kind: "init" | "get" | "set" } ];
  1552    * }
  1553    */
  1554   ObjectExpression: function(aNode, aParent, aCallbacks) {
  1555     aNode._parent = aParent;
  1557     if (this.break) {
  1558       return;
  1560     if (aCallbacks.onNode) {
  1561       if (aCallbacks.onNode(aNode, aParent) === false) {
  1562         return;
  1565     if (aCallbacks.onObjectExpression) {
  1566       aCallbacks.onObjectExpression(aNode);
  1568     for (let { key, value } of aNode.properties) {
  1569       this[key.type](key, aNode, aCallbacks);
  1570       this[value.type](value, aNode, aCallbacks);
  1572   },
  1574   /**
  1575    * A function expression.
  1577    * interface FunctionExpression <: Function, Expression {
  1578    *   type: "FunctionExpression";
  1579    *   id: Identifier | null;
  1580    *   params: [ Pattern ];
  1581    *   defaults: [ Expression ];
  1582    *   rest: Identifier | null;
  1583    *   body: BlockStatement | Expression;
  1584    *   generator: boolean;
  1585    *   expression: boolean;
  1586    * }
  1587    */
  1588   FunctionExpression: function(aNode, aParent, aCallbacks) {
  1589     aNode._parent = aParent;
  1591     if (this.break) {
  1592       return;
  1594     if (aCallbacks.onNode) {
  1595       if (aCallbacks.onNode(aNode, aParent) === false) {
  1596         return;
  1599     if (aCallbacks.onFunctionExpression) {
  1600       aCallbacks.onFunctionExpression(aNode);
  1602     if (aNode.id) {
  1603       this[aNode.id.type](aNode.id, aNode, aCallbacks);
  1605     for (let param of aNode.params) {
  1606       this[param.type](param, aNode, aCallbacks);
  1608     for (let _default of aNode.defaults) {
  1609       this[_default.type](_default, aNode, aCallbacks);
  1611     if (aNode.rest) {
  1612       this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
  1614     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1615   },
  1617   /**
  1618    * An arrow expression.
  1620    * interface ArrowExpression <: Function, Expression {
  1621    *   type: "ArrowExpression";
  1622    *   params: [ Pattern ];
  1623    *   defaults: [ Expression ];
  1624    *   rest: Identifier | null;
  1625    *   body: BlockStatement | Expression;
  1626    *   generator: boolean;
  1627    *   expression: boolean;
  1628    * }
  1629    */
  1630   ArrowExpression: function(aNode, aParent, aCallbacks) {
  1631     aNode._parent = aParent;
  1633     if (this.break) {
  1634       return;
  1636     if (aCallbacks.onNode) {
  1637       if (aCallbacks.onNode(aNode, aParent) === false) {
  1638         return;
  1641     if (aCallbacks.onArrowExpression) {
  1642       aCallbacks.onArrowExpression(aNode);
  1644     for (let param of aNode.params) {
  1645       this[param.type](param, aNode, aCallbacks);
  1647     for (let _default of aNode.defaults) {
  1648       this[_default.type](_default, aNode, aCallbacks);
  1650     if (aNode.rest) {
  1651       this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
  1653     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1654   },
  1656   /**
  1657    * A sequence expression, i.e., a comma-separated sequence of expressions.
  1659    * interface SequenceExpression <: Expression {
  1660    *   type: "SequenceExpression";
  1661    *   expressions: [ Expression ];
  1662    * }
  1663    */
  1664   SequenceExpression: function(aNode, aParent, aCallbacks) {
  1665     aNode._parent = aParent;
  1667     if (this.break) {
  1668       return;
  1670     if (aCallbacks.onNode) {
  1671       if (aCallbacks.onNode(aNode, aParent) === false) {
  1672         return;
  1675     if (aCallbacks.onSequenceExpression) {
  1676       aCallbacks.onSequenceExpression(aNode);
  1678     for (let expression of aNode.expressions) {
  1679       this[expression.type](expression, aNode, aCallbacks);
  1681   },
  1683   /**
  1684    * A unary operator expression.
  1686    * interface UnaryExpression <: Expression {
  1687    *   type: "UnaryExpression";
  1688    *   operator: UnaryOperator;
  1689    *   prefix: boolean;
  1690    *   argument: Expression;
  1691    * }
  1692    */
  1693   UnaryExpression: function(aNode, aParent, aCallbacks) {
  1694     aNode._parent = aParent;
  1696     if (this.break) {
  1697       return;
  1699     if (aCallbacks.onNode) {
  1700       if (aCallbacks.onNode(aNode, aParent) === false) {
  1701         return;
  1704     if (aCallbacks.onUnaryExpression) {
  1705       aCallbacks.onUnaryExpression(aNode);
  1707     this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1708   },
  1710   /**
  1711    * A binary operator expression.
  1713    * interface BinaryExpression <: Expression {
  1714    *   type: "BinaryExpression";
  1715    *   operator: BinaryOperator;
  1716    *   left: Expression;
  1717    *   right: Expression;
  1718    * }
  1719    */
  1720   BinaryExpression: function(aNode, aParent, aCallbacks) {
  1721     aNode._parent = aParent;
  1723     if (this.break) {
  1724       return;
  1726     if (aCallbacks.onNode) {
  1727       if (aCallbacks.onNode(aNode, aParent) === false) {
  1728         return;
  1731     if (aCallbacks.onBinaryExpression) {
  1732       aCallbacks.onBinaryExpression(aNode);
  1734     this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1735     this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1736   },
  1738   /**
  1739    * An assignment operator expression.
  1741    * interface AssignmentExpression <: Expression {
  1742    *   type: "AssignmentExpression";
  1743    *   operator: AssignmentOperator;
  1744    *   left: Expression;
  1745    *   right: Expression;
  1746    * }
  1747    */
  1748   AssignmentExpression: function(aNode, aParent, aCallbacks) {
  1749     aNode._parent = aParent;
  1751     if (this.break) {
  1752       return;
  1754     if (aCallbacks.onNode) {
  1755       if (aCallbacks.onNode(aNode, aParent) === false) {
  1756         return;
  1759     if (aCallbacks.onAssignmentExpression) {
  1760       aCallbacks.onAssignmentExpression(aNode);
  1762     this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1763     this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1764   },
  1766   /**
  1767    * An update (increment or decrement) operator expression.
  1769    * interface UpdateExpression <: Expression {
  1770    *   type: "UpdateExpression";
  1771    *   operator: UpdateOperator;
  1772    *   argument: Expression;
  1773    *   prefix: boolean;
  1774    * }
  1775    */
  1776   UpdateExpression: function(aNode, aParent, aCallbacks) {
  1777     aNode._parent = aParent;
  1779     if (this.break) {
  1780       return;
  1782     if (aCallbacks.onNode) {
  1783       if (aCallbacks.onNode(aNode, aParent) === false) {
  1784         return;
  1787     if (aCallbacks.onUpdateExpression) {
  1788       aCallbacks.onUpdateExpression(aNode);
  1790     this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1791   },
  1793   /**
  1794    * A logical operator expression.
  1796    * interface LogicalExpression <: Expression {
  1797    *   type: "LogicalExpression";
  1798    *   operator: LogicalOperator;
  1799    *   left: Expression;
  1800    *   right: Expression;
  1801    * }
  1802    */
  1803   LogicalExpression: function(aNode, aParent, aCallbacks) {
  1804     aNode._parent = aParent;
  1806     if (this.break) {
  1807       return;
  1809     if (aCallbacks.onNode) {
  1810       if (aCallbacks.onNode(aNode, aParent) === false) {
  1811         return;
  1814     if (aCallbacks.onLogicalExpression) {
  1815       aCallbacks.onLogicalExpression(aNode);
  1817     this[aNode.left.type](aNode.left, aNode, aCallbacks);
  1818     this[aNode.right.type](aNode.right, aNode, aCallbacks);
  1819   },
  1821   /**
  1822    * A conditional expression, i.e., a ternary ?/: expression.
  1824    * interface ConditionalExpression <: Expression {
  1825    *   type: "ConditionalExpression";
  1826    *   test: Expression;
  1827    *   alternate: Expression;
  1828    *   consequent: Expression;
  1829    * }
  1830    */
  1831   ConditionalExpression: function(aNode, aParent, aCallbacks) {
  1832     aNode._parent = aParent;
  1834     if (this.break) {
  1835       return;
  1837     if (aCallbacks.onNode) {
  1838       if (aCallbacks.onNode(aNode, aParent) === false) {
  1839         return;
  1842     if (aCallbacks.onConditionalExpression) {
  1843       aCallbacks.onConditionalExpression(aNode);
  1845     this[aNode.test.type](aNode.test, aNode, aCallbacks);
  1846     this[aNode.alternate.type](aNode.alternate, aNode, aCallbacks);
  1847     this[aNode.consequent.type](aNode.consequent, aNode, aCallbacks);
  1848   },
  1850   /**
  1851    * A new expression.
  1853    * interface NewExpression <: Expression {
  1854    *   type: "NewExpression";
  1855    *   callee: Expression;
  1856    *   arguments: [ Expression | null ];
  1857    * }
  1858    */
  1859   NewExpression: function(aNode, aParent, aCallbacks) {
  1860     aNode._parent = aParent;
  1862     if (this.break) {
  1863       return;
  1865     if (aCallbacks.onNode) {
  1866       if (aCallbacks.onNode(aNode, aParent) === false) {
  1867         return;
  1870     if (aCallbacks.onNewExpression) {
  1871       aCallbacks.onNewExpression(aNode);
  1873     this[aNode.callee.type](aNode.callee, aNode, aCallbacks);
  1874     for (let argument of aNode.arguments) {
  1875       if (argument) {
  1876         this[argument.type](argument, aNode, aCallbacks);
  1879   },
  1881   /**
  1882    * A function or method call expression.
  1884    * interface CallExpression <: Expression {
  1885    *   type: "CallExpression";
  1886    *   callee: Expression;
  1887    *   arguments: [ Expression | null ];
  1888    * }
  1889    */
  1890   CallExpression: function(aNode, aParent, aCallbacks) {
  1891     aNode._parent = aParent;
  1893     if (this.break) {
  1894       return;
  1896     if (aCallbacks.onNode) {
  1897       if (aCallbacks.onNode(aNode, aParent) === false) {
  1898         return;
  1901     if (aCallbacks.onCallExpression) {
  1902       aCallbacks.onCallExpression(aNode);
  1904     this[aNode.callee.type](aNode.callee, aNode, aCallbacks);
  1905     for (let argument of aNode.arguments) {
  1906       if (argument) {
  1907         this[argument.type](argument, aNode, aCallbacks);
  1910   },
  1912   /**
  1913    * A member expression. If computed is true, the node corresponds to a
  1914    * computed e1[e2] expression and property is an Expression. If computed is
  1915    * false, the node corresponds to a static e1.x expression and property is an
  1916    * Identifier.
  1918    * interface MemberExpression <: Expression {
  1919    *   type: "MemberExpression";
  1920    *   object: Expression;
  1921    *   property: Identifier | Expression;
  1922    *   computed: boolean;
  1923    * }
  1924    */
  1925   MemberExpression: function(aNode, aParent, aCallbacks) {
  1926     aNode._parent = aParent;
  1928     if (this.break) {
  1929       return;
  1931     if (aCallbacks.onNode) {
  1932       if (aCallbacks.onNode(aNode, aParent) === false) {
  1933         return;
  1936     if (aCallbacks.onMemberExpression) {
  1937       aCallbacks.onMemberExpression(aNode);
  1939     this[aNode.object.type](aNode.object, aNode, aCallbacks);
  1940     this[aNode.property.type](aNode.property, aNode, aCallbacks);
  1941   },
  1943   /**
  1944    * A yield expression.
  1946    * interface YieldExpression <: Expression {
  1947    *   argument: Expression | null;
  1948    * }
  1949    */
  1950   YieldExpression: function(aNode, aParent, aCallbacks) {
  1951     aNode._parent = aParent;
  1953     if (this.break) {
  1954       return;
  1956     if (aCallbacks.onNode) {
  1957       if (aCallbacks.onNode(aNode, aParent) === false) {
  1958         return;
  1961     if (aCallbacks.onYieldExpression) {
  1962       aCallbacks.onYieldExpression(aNode);
  1964     if (aNode.argument) {
  1965       this[aNode.argument.type](aNode.argument, aNode, aCallbacks);
  1967   },
  1969   /**
  1970    * An array comprehension. The blocks array corresponds to the sequence of
  1971    * for and for each blocks. The optional filter expression corresponds to the
  1972    * final if clause, if present.
  1974    * interface ComprehensionExpression <: Expression {
  1975    *   body: Expression;
  1976    *   blocks: [ ComprehensionBlock ];
  1977    *   filter: Expression | null;
  1978    * }
  1979    */
  1980   ComprehensionExpression: function(aNode, aParent, aCallbacks) {
  1981     aNode._parent = aParent;
  1983     if (this.break) {
  1984       return;
  1986     if (aCallbacks.onNode) {
  1987       if (aCallbacks.onNode(aNode, aParent) === false) {
  1988         return;
  1991     if (aCallbacks.onComprehensionExpression) {
  1992       aCallbacks.onComprehensionExpression(aNode);
  1994     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  1995     for (let block of aNode.blocks) {
  1996       this[block.type](block, aNode, aCallbacks);
  1998     if (aNode.filter) {
  1999       this[aNode.filter.type](aNode.filter, aNode, aCallbacks);
  2001   },
  2003   /**
  2004    * A generator expression. As with array comprehensions, the blocks array
  2005    * corresponds to the sequence of for and for each blocks, and the optional
  2006    * filter expression corresponds to the final if clause, if present.
  2008    * interface GeneratorExpression <: Expression {
  2009    *   body: Expression;
  2010    *   blocks: [ ComprehensionBlock ];
  2011    *   filter: Expression | null;
  2012    * }
  2013    */
  2014   GeneratorExpression: function(aNode, aParent, aCallbacks) {
  2015     aNode._parent = aParent;
  2017     if (this.break) {
  2018       return;
  2020     if (aCallbacks.onNode) {
  2021       if (aCallbacks.onNode(aNode, aParent) === false) {
  2022         return;
  2025     if (aCallbacks.onGeneratorExpression) {
  2026       aCallbacks.onGeneratorExpression(aNode);
  2028     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  2029     for (let block of aNode.blocks) {
  2030       this[block.type](block, aNode, aCallbacks);
  2032     if (aNode.filter) {
  2033       this[aNode.filter.type](aNode.filter, aNode, aCallbacks);
  2035   },
  2037   /**
  2038    * A graph expression, aka "sharp literal," such as #1={ self: #1# }.
  2040    * interface GraphExpression <: Expression {
  2041    *   index: uint32;
  2042    *   expression: Literal;
  2043    * }
  2044    */
  2045   GraphExpression: function(aNode, aParent, aCallbacks) {
  2046     aNode._parent = aParent;
  2048     if (this.break) {
  2049       return;
  2051     if (aCallbacks.onNode) {
  2052       if (aCallbacks.onNode(aNode, aParent) === false) {
  2053         return;
  2056     if (aCallbacks.onGraphExpression) {
  2057       aCallbacks.onGraphExpression(aNode);
  2059     this[aNode.expression.type](aNode.expression, aNode, aCallbacks);
  2060   },
  2062   /**
  2063    * A graph index expression, aka "sharp variable," such as #1#.
  2065    * interface GraphIndexExpression <: Expression {
  2066    *   index: uint32;
  2067    * }
  2068    */
  2069   GraphIndexExpression: function(aNode, aParent, aCallbacks) {
  2070     aNode._parent = aParent;
  2072     if (this.break) {
  2073       return;
  2075     if (aCallbacks.onNode) {
  2076       if (aCallbacks.onNode(aNode, aParent) === false) {
  2077         return;
  2080     if (aCallbacks.onGraphIndexExpression) {
  2081       aCallbacks.onGraphIndexExpression(aNode);
  2083   },
  2085   /**
  2086    * A let expression.
  2088    * interface LetExpression <: Expression {
  2089    *   type: "LetExpression";
  2090    *   head: [ { id: Pattern, init: Expression | null } ];
  2091    *   body: Expression;
  2092    * }
  2093    */
  2094   LetExpression: function(aNode, aParent, aCallbacks) {
  2095     aNode._parent = aParent;
  2097     if (this.break) {
  2098       return;
  2100     if (aCallbacks.onNode) {
  2101       if (aCallbacks.onNode(aNode, aParent) === false) {
  2102         return;
  2105     if (aCallbacks.onLetExpression) {
  2106       aCallbacks.onLetExpression(aNode);
  2108     for (let { id, init } of aNode.head) {
  2109       this[id.type](id, aNode, aCallbacks);
  2110       if (init) {
  2111         this[init.type](init, aNode, aCallbacks);
  2114     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  2115   },
  2117   /**
  2118    * Any pattern.
  2120    * interface Pattern <: Node { }
  2121    */
  2122   Pattern: function(aNode, aParent, aCallbacks) {
  2123     aNode._parent = aParent;
  2125     if (this.break) {
  2126       return;
  2128     if (aCallbacks.onNode) {
  2129       if (aCallbacks.onNode(aNode, aParent) === false) {
  2130         return;
  2133     if (aCallbacks.onPattern) {
  2134       aCallbacks.onPattern(aNode);
  2136   },
  2138   /**
  2139    * An object-destructuring pattern. A literal property in an object pattern
  2140    * can have either a string or number as its value.
  2142    * interface ObjectPattern <: Pattern {
  2143    *   type: "ObjectPattern";
  2144    *   properties: [ { key: Literal | Identifier, value: Pattern } ];
  2145    * }
  2146    */
  2147   ObjectPattern: function(aNode, aParent, aCallbacks) {
  2148     aNode._parent = aParent;
  2150     if (this.break) {
  2151       return;
  2153     if (aCallbacks.onNode) {
  2154       if (aCallbacks.onNode(aNode, aParent) === false) {
  2155         return;
  2158     if (aCallbacks.onObjectPattern) {
  2159       aCallbacks.onObjectPattern(aNode);
  2161     for (let { key, value } of aNode.properties) {
  2162       this[key.type](key, aNode, aCallbacks);
  2163       this[value.type](value, aNode, aCallbacks);
  2165   },
  2167   /**
  2168    * An array-destructuring pattern.
  2170    * interface ArrayPattern <: Pattern {
  2171    *   type: "ArrayPattern";
  2172    *   elements: [ Pattern | null ];
  2173    * }
  2174    */
  2175   ArrayPattern: function(aNode, aParent, aCallbacks) {
  2176     aNode._parent = aParent;
  2178     if (this.break) {
  2179       return;
  2181     if (aCallbacks.onNode) {
  2182       if (aCallbacks.onNode(aNode, aParent) === false) {
  2183         return;
  2186     if (aCallbacks.onArrayPattern) {
  2187       aCallbacks.onArrayPattern(aNode);
  2189     for (let element of aNode.elements) {
  2190       if (element) {
  2191         this[element.type](element, aNode, aCallbacks);
  2194   },
  2196   /**
  2197    * A case (if test is an Expression) or default (if test is null) clause in
  2198    * the body of a switch statement.
  2200    * interface SwitchCase <: Node {
  2201    *   type: "SwitchCase";
  2202    *   test: Expression | null;
  2203    *   consequent: [ Statement ];
  2204    * }
  2205    */
  2206   SwitchCase: function(aNode, aParent, aCallbacks) {
  2207     aNode._parent = aParent;
  2209     if (this.break) {
  2210       return;
  2212     if (aCallbacks.onNode) {
  2213       if (aCallbacks.onNode(aNode, aParent) === false) {
  2214         return;
  2217     if (aCallbacks.onSwitchCase) {
  2218       aCallbacks.onSwitchCase(aNode);
  2220     if (aNode.test) {
  2221       this[aNode.test.type](aNode.test, aNode, aCallbacks);
  2223     for (let consequent of aNode.consequent) {
  2224       this[consequent.type](consequent, aNode, aCallbacks);
  2226   },
  2228   /**
  2229    * A catch clause following a try block. The optional guard property
  2230    * corresponds to the optional expression guard on the bound variable.
  2232    * interface CatchClause <: Node {
  2233    *   type: "CatchClause";
  2234    *   param: Pattern;
  2235    *   guard: Expression | null;
  2236    *   body: BlockStatement;
  2237    * }
  2238    */
  2239   CatchClause: function(aNode, aParent, aCallbacks) {
  2240     aNode._parent = aParent;
  2242     if (this.break) {
  2243       return;
  2245     if (aCallbacks.onNode) {
  2246       if (aCallbacks.onNode(aNode, aParent) === false) {
  2247         return;
  2250     if (aCallbacks.onCatchClause) {
  2251       aCallbacks.onCatchClause(aNode);
  2253     this[aNode.param.type](aNode.param, aNode, aCallbacks);
  2254     if (aNode.guard) {
  2255       this[aNode.guard.type](aNode.guard, aNode, aCallbacks);
  2257     this[aNode.body.type](aNode.body, aNode, aCallbacks);
  2258   },
  2260   /**
  2261    * A for or for each block in an array comprehension or generator expression.
  2263    * interface ComprehensionBlock <: Node {
  2264    *   left: Pattern;
  2265    *   right: Expression;
  2266    *   each: boolean;
  2267    * }
  2268    */
  2269   ComprehensionBlock: function(aNode, aParent, aCallbacks) {
  2270     aNode._parent = aParent;
  2272     if (this.break) {
  2273       return;
  2275     if (aCallbacks.onNode) {
  2276       if (aCallbacks.onNode(aNode, aParent) === false) {
  2277         return;
  2280     if (aCallbacks.onComprehensionBlock) {
  2281       aCallbacks.onComprehensionBlock(aNode);
  2283     this[aNode.left.type](aNode.left, aNode, aCallbacks);
  2284     this[aNode.right.type](aNode.right, aNode, aCallbacks);
  2285   },
  2287   /**
  2288    * An identifier. Note that an identifier may be an expression or a
  2289    * destructuring pattern.
  2291    * interface Identifier <: Node, Expression, Pattern {
  2292    *   type: "Identifier";
  2293    *   name: string;
  2294    * }
  2295    */
  2296   Identifier: function(aNode, aParent, aCallbacks) {
  2297     aNode._parent = aParent;
  2299     if (this.break) {
  2300       return;
  2302     if (aCallbacks.onNode) {
  2303       if (aCallbacks.onNode(aNode, aParent) === false) {
  2304         return;
  2307     if (aCallbacks.onIdentifier) {
  2308       aCallbacks.onIdentifier(aNode);
  2310   },
  2312   /**
  2313    * A literal token. Note that a literal can be an expression.
  2315    * interface Literal <: Node, Expression {
  2316    *   type: "Literal";
  2317    *   value: string | boolean | null | number | RegExp;
  2318    * }
  2319    */
  2320   Literal: function(aNode, aParent, aCallbacks) {
  2321     aNode._parent = aParent;
  2323     if (this.break) {
  2324       return;
  2326     if (aCallbacks.onNode) {
  2327       if (aCallbacks.onNode(aNode, aParent) === false) {
  2328         return;
  2331     if (aCallbacks.onLiteral) {
  2332       aCallbacks.onLiteral(aNode);
  2335 };
  2337 XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect);

mercurial