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.

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

mercurial