|
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"; |
|
7 |
|
8 const Ci = Components.interfaces; |
|
9 const Cu = Components.utils; |
|
10 |
|
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
12 const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {}); |
|
13 |
|
14 XPCOMUtils.defineLazyModuleGetter(this, |
|
15 "Reflect", "resource://gre/modules/reflect.jsm"); |
|
16 |
|
17 this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"]; |
|
18 |
|
19 /** |
|
20 * A JS parser using the reflection API. |
|
21 */ |
|
22 this.Parser = function Parser() { |
|
23 this._cache = new Map(); |
|
24 this.errors = []; |
|
25 }; |
|
26 |
|
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 } |
|
42 |
|
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; |
|
51 |
|
52 if (aSource.match(/^\s*</)) { |
|
53 // First non whitespace character is <, so most definitely HTML. |
|
54 while (scriptMatch = regexp.exec(aSource)) { |
|
55 scriptMatches.push(scriptMatch[1]); // Contents are captured at index 1. |
|
56 } |
|
57 } |
|
58 |
|
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 } |
|
87 |
|
88 let pool = new SyntaxTreesPool(syntaxTrees, aUrl); |
|
89 |
|
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 } |
|
96 |
|
97 return pool; |
|
98 }, |
|
99 |
|
100 /** |
|
101 * Clears all the parsed sources from cache. |
|
102 */ |
|
103 clearCache: function() { |
|
104 this._cache.clear(); |
|
105 }, |
|
106 |
|
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 }, |
|
116 |
|
117 _cache: null, |
|
118 errors: null |
|
119 }; |
|
120 |
|
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 } |
|
134 |
|
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 }, |
|
142 |
|
143 /** |
|
144 * @see SyntaxTree.prototype.getNamedFunctionDefinitions |
|
145 */ |
|
146 getNamedFunctionDefinitions: function(aSubstring) { |
|
147 return this._call("getNamedFunctionDefinitions", -1, aSubstring); |
|
148 }, |
|
149 |
|
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 }, |
|
157 |
|
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 }; |
|
169 |
|
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 } |
|
178 |
|
179 info.index = -1; |
|
180 return info; |
|
181 }, |
|
182 |
|
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(); |
|
200 |
|
201 if (this._cache.has(requestId)) { |
|
202 return this._cache.get(requestId); |
|
203 } |
|
204 |
|
205 let requestedTree = this._trees[aSyntaxTreeIndex]; |
|
206 let targettedTrees = requestedTree ? [requestedTree] : this._trees; |
|
207 |
|
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 }, |
|
227 |
|
228 _trees: null, |
|
229 _cache: null |
|
230 }; |
|
231 |
|
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 }; |
|
250 |
|
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; |
|
267 |
|
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 }; |
|
280 |
|
281 // Abruptly halt walking the syntax tree. |
|
282 SyntaxTreeVisitor.break = true; |
|
283 } |
|
284 }, |
|
285 |
|
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 }, |
|
295 |
|
296 /** |
|
297 * Callback invoked for each 'this' node. |
|
298 * @param Node aNode |
|
299 */ |
|
300 onThisExpression: function(aNode) { |
|
301 this.onIdentifier(aNode); |
|
302 } |
|
303 }); |
|
304 |
|
305 return info; |
|
306 }, |
|
307 |
|
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 = []; |
|
322 |
|
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 }, |
|
337 |
|
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); |
|
346 |
|
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; |
|
352 |
|
353 // Current node may be part of a larger assignment expression stack. |
|
354 if (aNode._parent.type == "AssignmentExpression") { |
|
355 this.onFunctionExpression(aNode._parent); |
|
356 } |
|
357 |
|
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 }, |
|
369 |
|
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; |
|
380 |
|
381 // Current node may be part of a larger assignment expression stack. |
|
382 if (aNode._parent.type == "AssignmentExpression") { |
|
383 this.onFunctionExpression(aNode._parent); |
|
384 } |
|
385 |
|
386 if (inferredName && inferredName.toLowerCase().contains(lowerCaseToken)) { |
|
387 store.push({ |
|
388 inferredName: inferredName, |
|
389 inferredChain: inferredChain, |
|
390 inferredLocation: inferredLocation |
|
391 }); |
|
392 } |
|
393 } |
|
394 }); |
|
395 |
|
396 return store; |
|
397 }, |
|
398 |
|
399 AST: null, |
|
400 url: "", |
|
401 length: 0, |
|
402 offset: 0 |
|
403 }; |
|
404 |
|
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 }, |
|
474 |
|
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 }, |
|
489 |
|
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 }, |
|
507 |
|
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; |
|
521 |
|
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 } |
|
532 |
|
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 } |
|
545 |
|
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 } |
|
559 |
|
560 // Can't infer the function expression's name. |
|
561 return { |
|
562 name: "", |
|
563 chain: null, |
|
564 loc: null |
|
565 }; |
|
566 }, |
|
567 |
|
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 }, |
|
594 |
|
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 }, |
|
642 |
|
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 }, |
|
676 |
|
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 }; |
|
718 |
|
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 }, |
|
743 |
|
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 }, |
|
759 |
|
760 /** |
|
761 * A flag checked on each node in the syntax tree. If true, walking is |
|
762 * abruptly halted. |
|
763 */ |
|
764 break: false, |
|
765 |
|
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 }, |
|
782 |
|
783 /** |
|
784 * Any statement. |
|
785 * |
|
786 * interface Statement <: Node { } |
|
787 */ |
|
788 Statement: function(aNode, aParent, aCallbacks) { |
|
789 aNode._parent = aParent; |
|
790 |
|
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 }, |
|
803 |
|
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; |
|
813 |
|
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 }, |
|
826 |
|
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; |
|
837 |
|
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 }, |
|
853 |
|
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; |
|
864 |
|
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 }, |
|
878 |
|
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; |
|
891 |
|
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 }, |
|
909 |
|
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; |
|
921 |
|
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 }, |
|
936 |
|
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; |
|
947 |
|
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 }, |
|
963 |
|
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; |
|
974 |
|
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 }, |
|
990 |
|
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; |
|
1002 |
|
1003 if (this.break) { |
|
1004 return; |
|
1005 } |
|
1006 if (aCallbacks.onNode) { |
|
1007 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1008 return; |
|
1009 } |
|
1010 } |
|
1011 if (aCallbacks.onWithStatement) { |
|
1012 aCallbacks.onWithStatement(aNode); |
|
1013 } |
|
1014 this[aNode.object.type](aNode.object, aNode, aCallbacks); |
|
1015 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1016 }, |
|
1017 |
|
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). |
|
1022 * |
|
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; |
|
1032 |
|
1033 if (this.break) { |
|
1034 return; |
|
1035 } |
|
1036 if (aCallbacks.onNode) { |
|
1037 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1038 return; |
|
1039 } |
|
1040 } |
|
1041 if (aCallbacks.onSwitchStatement) { |
|
1042 aCallbacks.onSwitchStatement(aNode); |
|
1043 } |
|
1044 this[aNode.discriminant.type](aNode.discriminant, aNode, aCallbacks); |
|
1045 for (let _case of aNode.cases) { |
|
1046 this[_case.type](_case, aNode, aCallbacks); |
|
1047 } |
|
1048 }, |
|
1049 |
|
1050 /** |
|
1051 * A return statement. |
|
1052 * |
|
1053 * interface ReturnStatement <: Statement { |
|
1054 * type: "ReturnStatement"; |
|
1055 * argument: Expression | null; |
|
1056 * } |
|
1057 */ |
|
1058 ReturnStatement: function(aNode, aParent, aCallbacks) { |
|
1059 aNode._parent = aParent; |
|
1060 |
|
1061 if (this.break) { |
|
1062 return; |
|
1063 } |
|
1064 if (aCallbacks.onNode) { |
|
1065 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1066 return; |
|
1067 } |
|
1068 } |
|
1069 if (aCallbacks.onReturnStatement) { |
|
1070 aCallbacks.onReturnStatement(aNode); |
|
1071 } |
|
1072 if (aNode.argument) { |
|
1073 this[aNode.argument.type](aNode.argument, aNode, aCallbacks); |
|
1074 } |
|
1075 }, |
|
1076 |
|
1077 /** |
|
1078 * A throw statement. |
|
1079 * |
|
1080 * interface ThrowStatement <: Statement { |
|
1081 * type: "ThrowStatement"; |
|
1082 * argument: Expression; |
|
1083 * } |
|
1084 */ |
|
1085 ThrowStatement: function(aNode, aParent, aCallbacks) { |
|
1086 aNode._parent = aParent; |
|
1087 |
|
1088 if (this.break) { |
|
1089 return; |
|
1090 } |
|
1091 if (aCallbacks.onNode) { |
|
1092 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1093 return; |
|
1094 } |
|
1095 } |
|
1096 if (aCallbacks.onThrowStatement) { |
|
1097 aCallbacks.onThrowStatement(aNode); |
|
1098 } |
|
1099 this[aNode.argument.type](aNode.argument, aNode, aCallbacks); |
|
1100 }, |
|
1101 |
|
1102 /** |
|
1103 * A try statement. |
|
1104 * |
|
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; |
|
1115 |
|
1116 if (this.break) { |
|
1117 return; |
|
1118 } |
|
1119 if (aCallbacks.onNode) { |
|
1120 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1121 return; |
|
1122 } |
|
1123 } |
|
1124 if (aCallbacks.onTryStatement) { |
|
1125 aCallbacks.onTryStatement(aNode); |
|
1126 } |
|
1127 this[aNode.block.type](aNode.block, aNode, aCallbacks); |
|
1128 if (aNode.handler) { |
|
1129 this[aNode.handler.type](aNode.handler, aNode, aCallbacks); |
|
1130 } |
|
1131 for (let guardedHandler of aNode.guardedHandlers) { |
|
1132 this[guardedHandler.type](guardedHandler, aNode, aCallbacks); |
|
1133 } |
|
1134 if (aNode.finalizer) { |
|
1135 this[aNode.finalizer.type](aNode.finalizer, aNode, aCallbacks); |
|
1136 } |
|
1137 }, |
|
1138 |
|
1139 /** |
|
1140 * A while statement. |
|
1141 * |
|
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; |
|
1150 |
|
1151 if (this.break) { |
|
1152 return; |
|
1153 } |
|
1154 if (aCallbacks.onNode) { |
|
1155 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1156 return; |
|
1157 } |
|
1158 } |
|
1159 if (aCallbacks.onWhileStatement) { |
|
1160 aCallbacks.onWhileStatement(aNode); |
|
1161 } |
|
1162 this[aNode.test.type](aNode.test, aNode, aCallbacks); |
|
1163 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1164 }, |
|
1165 |
|
1166 /** |
|
1167 * A do/while statement. |
|
1168 * |
|
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; |
|
1177 |
|
1178 if (this.break) { |
|
1179 return; |
|
1180 } |
|
1181 if (aCallbacks.onNode) { |
|
1182 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1183 return; |
|
1184 } |
|
1185 } |
|
1186 if (aCallbacks.onDoWhileStatement) { |
|
1187 aCallbacks.onDoWhileStatement(aNode); |
|
1188 } |
|
1189 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1190 this[aNode.test.type](aNode.test, aNode, aCallbacks); |
|
1191 }, |
|
1192 |
|
1193 /** |
|
1194 * A for statement. |
|
1195 * |
|
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; |
|
1206 |
|
1207 if (this.break) { |
|
1208 return; |
|
1209 } |
|
1210 if (aCallbacks.onNode) { |
|
1211 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1212 return; |
|
1213 } |
|
1214 } |
|
1215 if (aCallbacks.onForStatement) { |
|
1216 aCallbacks.onForStatement(aNode); |
|
1217 } |
|
1218 if (aNode.init) { |
|
1219 this[aNode.init.type](aNode.init, aNode, aCallbacks); |
|
1220 } |
|
1221 if (aNode.test) { |
|
1222 this[aNode.test.type](aNode.test, aNode, aCallbacks); |
|
1223 } |
|
1224 if (aNode.update) { |
|
1225 this[aNode.update.type](aNode.update, aNode, aCallbacks); |
|
1226 } |
|
1227 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1228 }, |
|
1229 |
|
1230 /** |
|
1231 * A for/in statement, or, if each is true, a for each/in statement. |
|
1232 * |
|
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; |
|
1243 |
|
1244 if (this.break) { |
|
1245 return; |
|
1246 } |
|
1247 if (aCallbacks.onNode) { |
|
1248 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1249 return; |
|
1250 } |
|
1251 } |
|
1252 if (aCallbacks.onForInStatement) { |
|
1253 aCallbacks.onForInStatement(aNode); |
|
1254 } |
|
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 }, |
|
1259 |
|
1260 /** |
|
1261 * A for/of statement. |
|
1262 * |
|
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; |
|
1272 |
|
1273 if (this.break) { |
|
1274 return; |
|
1275 } |
|
1276 if (aCallbacks.onNode) { |
|
1277 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1278 return; |
|
1279 } |
|
1280 } |
|
1281 if (aCallbacks.onForOfStatement) { |
|
1282 aCallbacks.onForOfStatement(aNode); |
|
1283 } |
|
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 }, |
|
1288 |
|
1289 /** |
|
1290 * A let statement. |
|
1291 * |
|
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; |
|
1300 |
|
1301 if (this.break) { |
|
1302 return; |
|
1303 } |
|
1304 if (aCallbacks.onNode) { |
|
1305 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1306 return; |
|
1307 } |
|
1308 } |
|
1309 if (aCallbacks.onLetStatement) { |
|
1310 aCallbacks.onLetStatement(aNode); |
|
1311 } |
|
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); |
|
1316 } |
|
1317 } |
|
1318 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1319 }, |
|
1320 |
|
1321 /** |
|
1322 * A debugger statement. |
|
1323 * |
|
1324 * interface DebuggerStatement <: Statement { |
|
1325 * type: "DebuggerStatement"; |
|
1326 * } |
|
1327 */ |
|
1328 DebuggerStatement: function(aNode, aParent, aCallbacks) { |
|
1329 aNode._parent = aParent; |
|
1330 |
|
1331 if (this.break) { |
|
1332 return; |
|
1333 } |
|
1334 if (aCallbacks.onNode) { |
|
1335 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1336 return; |
|
1337 } |
|
1338 } |
|
1339 if (aCallbacks.onDebuggerStatement) { |
|
1340 aCallbacks.onDebuggerStatement(aNode); |
|
1341 } |
|
1342 }, |
|
1343 |
|
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. |
|
1348 * |
|
1349 * interface Declaration <: Statement { } |
|
1350 */ |
|
1351 Declaration: function(aNode, aParent, aCallbacks) { |
|
1352 aNode._parent = aParent; |
|
1353 |
|
1354 if (this.break) { |
|
1355 return; |
|
1356 } |
|
1357 if (aCallbacks.onNode) { |
|
1358 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1359 return; |
|
1360 } |
|
1361 } |
|
1362 if (aCallbacks.onDeclaration) { |
|
1363 aCallbacks.onDeclaration(aNode); |
|
1364 } |
|
1365 }, |
|
1366 |
|
1367 /** |
|
1368 * A function declaration. |
|
1369 * |
|
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; |
|
1383 |
|
1384 if (this.break) { |
|
1385 return; |
|
1386 } |
|
1387 if (aCallbacks.onNode) { |
|
1388 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1389 return; |
|
1390 } |
|
1391 } |
|
1392 if (aCallbacks.onFunctionDeclaration) { |
|
1393 aCallbacks.onFunctionDeclaration(aNode); |
|
1394 } |
|
1395 this[aNode.id.type](aNode.id, aNode, aCallbacks); |
|
1396 for (let param of aNode.params) { |
|
1397 this[param.type](param, aNode, aCallbacks); |
|
1398 } |
|
1399 for (let _default of aNode.defaults) { |
|
1400 this[_default.type](_default, aNode, aCallbacks); |
|
1401 } |
|
1402 if (aNode.rest) { |
|
1403 this[aNode.rest.type](aNode.rest, aNode, aCallbacks); |
|
1404 } |
|
1405 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1406 }, |
|
1407 |
|
1408 /** |
|
1409 * A variable declaration, via one of var, let, or const. |
|
1410 * |
|
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; |
|
1419 |
|
1420 if (this.break) { |
|
1421 return; |
|
1422 } |
|
1423 if (aCallbacks.onNode) { |
|
1424 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1425 return; |
|
1426 } |
|
1427 } |
|
1428 if (aCallbacks.onVariableDeclaration) { |
|
1429 aCallbacks.onVariableDeclaration(aNode); |
|
1430 } |
|
1431 for (let declaration of aNode.declarations) { |
|
1432 this[declaration.type](declaration, aNode, aCallbacks); |
|
1433 } |
|
1434 }, |
|
1435 |
|
1436 /** |
|
1437 * A variable declarator. |
|
1438 * |
|
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; |
|
1447 |
|
1448 if (this.break) { |
|
1449 return; |
|
1450 } |
|
1451 if (aCallbacks.onNode) { |
|
1452 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1453 return; |
|
1454 } |
|
1455 } |
|
1456 if (aCallbacks.onVariableDeclarator) { |
|
1457 aCallbacks.onVariableDeclarator(aNode); |
|
1458 } |
|
1459 this[aNode.id.type](aNode.id, aNode, aCallbacks); |
|
1460 if (aNode.init) { |
|
1461 this[aNode.init.type](aNode.init, aNode, aCallbacks); |
|
1462 } |
|
1463 }, |
|
1464 |
|
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. |
|
1468 * |
|
1469 * interface Expression <: Node, Pattern { } |
|
1470 */ |
|
1471 Expression: function(aNode, aParent, aCallbacks) { |
|
1472 aNode._parent = aParent; |
|
1473 |
|
1474 if (this.break) { |
|
1475 return; |
|
1476 } |
|
1477 if (aCallbacks.onNode) { |
|
1478 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1479 return; |
|
1480 } |
|
1481 } |
|
1482 if (aCallbacks.onExpression) { |
|
1483 aCallbacks.onExpression(aNode); |
|
1484 } |
|
1485 }, |
|
1486 |
|
1487 /** |
|
1488 * A this expression. |
|
1489 * |
|
1490 * interface ThisExpression <: Expression { |
|
1491 * type: "ThisExpression"; |
|
1492 * } |
|
1493 */ |
|
1494 ThisExpression: function(aNode, aParent, aCallbacks) { |
|
1495 aNode._parent = aParent; |
|
1496 |
|
1497 if (this.break) { |
|
1498 return; |
|
1499 } |
|
1500 if (aCallbacks.onNode) { |
|
1501 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1502 return; |
|
1503 } |
|
1504 } |
|
1505 if (aCallbacks.onThisExpression) { |
|
1506 aCallbacks.onThisExpression(aNode); |
|
1507 } |
|
1508 }, |
|
1509 |
|
1510 /** |
|
1511 * An array expression. |
|
1512 * |
|
1513 * interface ArrayExpression <: Expression { |
|
1514 * type: "ArrayExpression"; |
|
1515 * elements: [ Expression | null ]; |
|
1516 * } |
|
1517 */ |
|
1518 ArrayExpression: function(aNode, aParent, aCallbacks) { |
|
1519 aNode._parent = aParent; |
|
1520 |
|
1521 if (this.break) { |
|
1522 return; |
|
1523 } |
|
1524 if (aCallbacks.onNode) { |
|
1525 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1526 return; |
|
1527 } |
|
1528 } |
|
1529 if (aCallbacks.onArrayExpression) { |
|
1530 aCallbacks.onArrayExpression(aNode); |
|
1531 } |
|
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); |
|
1537 } |
|
1538 } |
|
1539 }, |
|
1540 |
|
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. |
|
1546 * |
|
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; |
|
1556 |
|
1557 if (this.break) { |
|
1558 return; |
|
1559 } |
|
1560 if (aCallbacks.onNode) { |
|
1561 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1562 return; |
|
1563 } |
|
1564 } |
|
1565 if (aCallbacks.onObjectExpression) { |
|
1566 aCallbacks.onObjectExpression(aNode); |
|
1567 } |
|
1568 for (let { key, value } of aNode.properties) { |
|
1569 this[key.type](key, aNode, aCallbacks); |
|
1570 this[value.type](value, aNode, aCallbacks); |
|
1571 } |
|
1572 }, |
|
1573 |
|
1574 /** |
|
1575 * A function expression. |
|
1576 * |
|
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; |
|
1590 |
|
1591 if (this.break) { |
|
1592 return; |
|
1593 } |
|
1594 if (aCallbacks.onNode) { |
|
1595 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1596 return; |
|
1597 } |
|
1598 } |
|
1599 if (aCallbacks.onFunctionExpression) { |
|
1600 aCallbacks.onFunctionExpression(aNode); |
|
1601 } |
|
1602 if (aNode.id) { |
|
1603 this[aNode.id.type](aNode.id, aNode, aCallbacks); |
|
1604 } |
|
1605 for (let param of aNode.params) { |
|
1606 this[param.type](param, aNode, aCallbacks); |
|
1607 } |
|
1608 for (let _default of aNode.defaults) { |
|
1609 this[_default.type](_default, aNode, aCallbacks); |
|
1610 } |
|
1611 if (aNode.rest) { |
|
1612 this[aNode.rest.type](aNode.rest, aNode, aCallbacks); |
|
1613 } |
|
1614 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1615 }, |
|
1616 |
|
1617 /** |
|
1618 * An arrow expression. |
|
1619 * |
|
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; |
|
1632 |
|
1633 if (this.break) { |
|
1634 return; |
|
1635 } |
|
1636 if (aCallbacks.onNode) { |
|
1637 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1638 return; |
|
1639 } |
|
1640 } |
|
1641 if (aCallbacks.onArrowExpression) { |
|
1642 aCallbacks.onArrowExpression(aNode); |
|
1643 } |
|
1644 for (let param of aNode.params) { |
|
1645 this[param.type](param, aNode, aCallbacks); |
|
1646 } |
|
1647 for (let _default of aNode.defaults) { |
|
1648 this[_default.type](_default, aNode, aCallbacks); |
|
1649 } |
|
1650 if (aNode.rest) { |
|
1651 this[aNode.rest.type](aNode.rest, aNode, aCallbacks); |
|
1652 } |
|
1653 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1654 }, |
|
1655 |
|
1656 /** |
|
1657 * A sequence expression, i.e., a comma-separated sequence of expressions. |
|
1658 * |
|
1659 * interface SequenceExpression <: Expression { |
|
1660 * type: "SequenceExpression"; |
|
1661 * expressions: [ Expression ]; |
|
1662 * } |
|
1663 */ |
|
1664 SequenceExpression: function(aNode, aParent, aCallbacks) { |
|
1665 aNode._parent = aParent; |
|
1666 |
|
1667 if (this.break) { |
|
1668 return; |
|
1669 } |
|
1670 if (aCallbacks.onNode) { |
|
1671 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1672 return; |
|
1673 } |
|
1674 } |
|
1675 if (aCallbacks.onSequenceExpression) { |
|
1676 aCallbacks.onSequenceExpression(aNode); |
|
1677 } |
|
1678 for (let expression of aNode.expressions) { |
|
1679 this[expression.type](expression, aNode, aCallbacks); |
|
1680 } |
|
1681 }, |
|
1682 |
|
1683 /** |
|
1684 * A unary operator expression. |
|
1685 * |
|
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; |
|
1695 |
|
1696 if (this.break) { |
|
1697 return; |
|
1698 } |
|
1699 if (aCallbacks.onNode) { |
|
1700 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1701 return; |
|
1702 } |
|
1703 } |
|
1704 if (aCallbacks.onUnaryExpression) { |
|
1705 aCallbacks.onUnaryExpression(aNode); |
|
1706 } |
|
1707 this[aNode.argument.type](aNode.argument, aNode, aCallbacks); |
|
1708 }, |
|
1709 |
|
1710 /** |
|
1711 * A binary operator expression. |
|
1712 * |
|
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; |
|
1722 |
|
1723 if (this.break) { |
|
1724 return; |
|
1725 } |
|
1726 if (aCallbacks.onNode) { |
|
1727 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1728 return; |
|
1729 } |
|
1730 } |
|
1731 if (aCallbacks.onBinaryExpression) { |
|
1732 aCallbacks.onBinaryExpression(aNode); |
|
1733 } |
|
1734 this[aNode.left.type](aNode.left, aNode, aCallbacks); |
|
1735 this[aNode.right.type](aNode.right, aNode, aCallbacks); |
|
1736 }, |
|
1737 |
|
1738 /** |
|
1739 * An assignment operator expression. |
|
1740 * |
|
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; |
|
1750 |
|
1751 if (this.break) { |
|
1752 return; |
|
1753 } |
|
1754 if (aCallbacks.onNode) { |
|
1755 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1756 return; |
|
1757 } |
|
1758 } |
|
1759 if (aCallbacks.onAssignmentExpression) { |
|
1760 aCallbacks.onAssignmentExpression(aNode); |
|
1761 } |
|
1762 this[aNode.left.type](aNode.left, aNode, aCallbacks); |
|
1763 this[aNode.right.type](aNode.right, aNode, aCallbacks); |
|
1764 }, |
|
1765 |
|
1766 /** |
|
1767 * An update (increment or decrement) operator expression. |
|
1768 * |
|
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; |
|
1778 |
|
1779 if (this.break) { |
|
1780 return; |
|
1781 } |
|
1782 if (aCallbacks.onNode) { |
|
1783 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1784 return; |
|
1785 } |
|
1786 } |
|
1787 if (aCallbacks.onUpdateExpression) { |
|
1788 aCallbacks.onUpdateExpression(aNode); |
|
1789 } |
|
1790 this[aNode.argument.type](aNode.argument, aNode, aCallbacks); |
|
1791 }, |
|
1792 |
|
1793 /** |
|
1794 * A logical operator expression. |
|
1795 * |
|
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; |
|
1805 |
|
1806 if (this.break) { |
|
1807 return; |
|
1808 } |
|
1809 if (aCallbacks.onNode) { |
|
1810 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1811 return; |
|
1812 } |
|
1813 } |
|
1814 if (aCallbacks.onLogicalExpression) { |
|
1815 aCallbacks.onLogicalExpression(aNode); |
|
1816 } |
|
1817 this[aNode.left.type](aNode.left, aNode, aCallbacks); |
|
1818 this[aNode.right.type](aNode.right, aNode, aCallbacks); |
|
1819 }, |
|
1820 |
|
1821 /** |
|
1822 * A conditional expression, i.e., a ternary ?/: expression. |
|
1823 * |
|
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; |
|
1833 |
|
1834 if (this.break) { |
|
1835 return; |
|
1836 } |
|
1837 if (aCallbacks.onNode) { |
|
1838 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1839 return; |
|
1840 } |
|
1841 } |
|
1842 if (aCallbacks.onConditionalExpression) { |
|
1843 aCallbacks.onConditionalExpression(aNode); |
|
1844 } |
|
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 }, |
|
1849 |
|
1850 /** |
|
1851 * A new expression. |
|
1852 * |
|
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; |
|
1861 |
|
1862 if (this.break) { |
|
1863 return; |
|
1864 } |
|
1865 if (aCallbacks.onNode) { |
|
1866 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1867 return; |
|
1868 } |
|
1869 } |
|
1870 if (aCallbacks.onNewExpression) { |
|
1871 aCallbacks.onNewExpression(aNode); |
|
1872 } |
|
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); |
|
1877 } |
|
1878 } |
|
1879 }, |
|
1880 |
|
1881 /** |
|
1882 * A function or method call expression. |
|
1883 * |
|
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; |
|
1892 |
|
1893 if (this.break) { |
|
1894 return; |
|
1895 } |
|
1896 if (aCallbacks.onNode) { |
|
1897 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1898 return; |
|
1899 } |
|
1900 } |
|
1901 if (aCallbacks.onCallExpression) { |
|
1902 aCallbacks.onCallExpression(aNode); |
|
1903 } |
|
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); |
|
1908 } |
|
1909 } |
|
1910 }, |
|
1911 |
|
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. |
|
1917 * |
|
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; |
|
1927 |
|
1928 if (this.break) { |
|
1929 return; |
|
1930 } |
|
1931 if (aCallbacks.onNode) { |
|
1932 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1933 return; |
|
1934 } |
|
1935 } |
|
1936 if (aCallbacks.onMemberExpression) { |
|
1937 aCallbacks.onMemberExpression(aNode); |
|
1938 } |
|
1939 this[aNode.object.type](aNode.object, aNode, aCallbacks); |
|
1940 this[aNode.property.type](aNode.property, aNode, aCallbacks); |
|
1941 }, |
|
1942 |
|
1943 /** |
|
1944 * A yield expression. |
|
1945 * |
|
1946 * interface YieldExpression <: Expression { |
|
1947 * argument: Expression | null; |
|
1948 * } |
|
1949 */ |
|
1950 YieldExpression: function(aNode, aParent, aCallbacks) { |
|
1951 aNode._parent = aParent; |
|
1952 |
|
1953 if (this.break) { |
|
1954 return; |
|
1955 } |
|
1956 if (aCallbacks.onNode) { |
|
1957 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1958 return; |
|
1959 } |
|
1960 } |
|
1961 if (aCallbacks.onYieldExpression) { |
|
1962 aCallbacks.onYieldExpression(aNode); |
|
1963 } |
|
1964 if (aNode.argument) { |
|
1965 this[aNode.argument.type](aNode.argument, aNode, aCallbacks); |
|
1966 } |
|
1967 }, |
|
1968 |
|
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. |
|
1973 * |
|
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; |
|
1982 |
|
1983 if (this.break) { |
|
1984 return; |
|
1985 } |
|
1986 if (aCallbacks.onNode) { |
|
1987 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
1988 return; |
|
1989 } |
|
1990 } |
|
1991 if (aCallbacks.onComprehensionExpression) { |
|
1992 aCallbacks.onComprehensionExpression(aNode); |
|
1993 } |
|
1994 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
1995 for (let block of aNode.blocks) { |
|
1996 this[block.type](block, aNode, aCallbacks); |
|
1997 } |
|
1998 if (aNode.filter) { |
|
1999 this[aNode.filter.type](aNode.filter, aNode, aCallbacks); |
|
2000 } |
|
2001 }, |
|
2002 |
|
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. |
|
2007 * |
|
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; |
|
2016 |
|
2017 if (this.break) { |
|
2018 return; |
|
2019 } |
|
2020 if (aCallbacks.onNode) { |
|
2021 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2022 return; |
|
2023 } |
|
2024 } |
|
2025 if (aCallbacks.onGeneratorExpression) { |
|
2026 aCallbacks.onGeneratorExpression(aNode); |
|
2027 } |
|
2028 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
2029 for (let block of aNode.blocks) { |
|
2030 this[block.type](block, aNode, aCallbacks); |
|
2031 } |
|
2032 if (aNode.filter) { |
|
2033 this[aNode.filter.type](aNode.filter, aNode, aCallbacks); |
|
2034 } |
|
2035 }, |
|
2036 |
|
2037 /** |
|
2038 * A graph expression, aka "sharp literal," such as #1={ self: #1# }. |
|
2039 * |
|
2040 * interface GraphExpression <: Expression { |
|
2041 * index: uint32; |
|
2042 * expression: Literal; |
|
2043 * } |
|
2044 */ |
|
2045 GraphExpression: function(aNode, aParent, aCallbacks) { |
|
2046 aNode._parent = aParent; |
|
2047 |
|
2048 if (this.break) { |
|
2049 return; |
|
2050 } |
|
2051 if (aCallbacks.onNode) { |
|
2052 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2053 return; |
|
2054 } |
|
2055 } |
|
2056 if (aCallbacks.onGraphExpression) { |
|
2057 aCallbacks.onGraphExpression(aNode); |
|
2058 } |
|
2059 this[aNode.expression.type](aNode.expression, aNode, aCallbacks); |
|
2060 }, |
|
2061 |
|
2062 /** |
|
2063 * A graph index expression, aka "sharp variable," such as #1#. |
|
2064 * |
|
2065 * interface GraphIndexExpression <: Expression { |
|
2066 * index: uint32; |
|
2067 * } |
|
2068 */ |
|
2069 GraphIndexExpression: function(aNode, aParent, aCallbacks) { |
|
2070 aNode._parent = aParent; |
|
2071 |
|
2072 if (this.break) { |
|
2073 return; |
|
2074 } |
|
2075 if (aCallbacks.onNode) { |
|
2076 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2077 return; |
|
2078 } |
|
2079 } |
|
2080 if (aCallbacks.onGraphIndexExpression) { |
|
2081 aCallbacks.onGraphIndexExpression(aNode); |
|
2082 } |
|
2083 }, |
|
2084 |
|
2085 /** |
|
2086 * A let expression. |
|
2087 * |
|
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; |
|
2096 |
|
2097 if (this.break) { |
|
2098 return; |
|
2099 } |
|
2100 if (aCallbacks.onNode) { |
|
2101 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2102 return; |
|
2103 } |
|
2104 } |
|
2105 if (aCallbacks.onLetExpression) { |
|
2106 aCallbacks.onLetExpression(aNode); |
|
2107 } |
|
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); |
|
2112 } |
|
2113 } |
|
2114 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
2115 }, |
|
2116 |
|
2117 /** |
|
2118 * Any pattern. |
|
2119 * |
|
2120 * interface Pattern <: Node { } |
|
2121 */ |
|
2122 Pattern: function(aNode, aParent, aCallbacks) { |
|
2123 aNode._parent = aParent; |
|
2124 |
|
2125 if (this.break) { |
|
2126 return; |
|
2127 } |
|
2128 if (aCallbacks.onNode) { |
|
2129 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2130 return; |
|
2131 } |
|
2132 } |
|
2133 if (aCallbacks.onPattern) { |
|
2134 aCallbacks.onPattern(aNode); |
|
2135 } |
|
2136 }, |
|
2137 |
|
2138 /** |
|
2139 * An object-destructuring pattern. A literal property in an object pattern |
|
2140 * can have either a string or number as its value. |
|
2141 * |
|
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; |
|
2149 |
|
2150 if (this.break) { |
|
2151 return; |
|
2152 } |
|
2153 if (aCallbacks.onNode) { |
|
2154 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2155 return; |
|
2156 } |
|
2157 } |
|
2158 if (aCallbacks.onObjectPattern) { |
|
2159 aCallbacks.onObjectPattern(aNode); |
|
2160 } |
|
2161 for (let { key, value } of aNode.properties) { |
|
2162 this[key.type](key, aNode, aCallbacks); |
|
2163 this[value.type](value, aNode, aCallbacks); |
|
2164 } |
|
2165 }, |
|
2166 |
|
2167 /** |
|
2168 * An array-destructuring pattern. |
|
2169 * |
|
2170 * interface ArrayPattern <: Pattern { |
|
2171 * type: "ArrayPattern"; |
|
2172 * elements: [ Pattern | null ]; |
|
2173 * } |
|
2174 */ |
|
2175 ArrayPattern: function(aNode, aParent, aCallbacks) { |
|
2176 aNode._parent = aParent; |
|
2177 |
|
2178 if (this.break) { |
|
2179 return; |
|
2180 } |
|
2181 if (aCallbacks.onNode) { |
|
2182 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2183 return; |
|
2184 } |
|
2185 } |
|
2186 if (aCallbacks.onArrayPattern) { |
|
2187 aCallbacks.onArrayPattern(aNode); |
|
2188 } |
|
2189 for (let element of aNode.elements) { |
|
2190 if (element) { |
|
2191 this[element.type](element, aNode, aCallbacks); |
|
2192 } |
|
2193 } |
|
2194 }, |
|
2195 |
|
2196 /** |
|
2197 * A case (if test is an Expression) or default (if test is null) clause in |
|
2198 * the body of a switch statement. |
|
2199 * |
|
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; |
|
2208 |
|
2209 if (this.break) { |
|
2210 return; |
|
2211 } |
|
2212 if (aCallbacks.onNode) { |
|
2213 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2214 return; |
|
2215 } |
|
2216 } |
|
2217 if (aCallbacks.onSwitchCase) { |
|
2218 aCallbacks.onSwitchCase(aNode); |
|
2219 } |
|
2220 if (aNode.test) { |
|
2221 this[aNode.test.type](aNode.test, aNode, aCallbacks); |
|
2222 } |
|
2223 for (let consequent of aNode.consequent) { |
|
2224 this[consequent.type](consequent, aNode, aCallbacks); |
|
2225 } |
|
2226 }, |
|
2227 |
|
2228 /** |
|
2229 * A catch clause following a try block. The optional guard property |
|
2230 * corresponds to the optional expression guard on the bound variable. |
|
2231 * |
|
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; |
|
2241 |
|
2242 if (this.break) { |
|
2243 return; |
|
2244 } |
|
2245 if (aCallbacks.onNode) { |
|
2246 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2247 return; |
|
2248 } |
|
2249 } |
|
2250 if (aCallbacks.onCatchClause) { |
|
2251 aCallbacks.onCatchClause(aNode); |
|
2252 } |
|
2253 this[aNode.param.type](aNode.param, aNode, aCallbacks); |
|
2254 if (aNode.guard) { |
|
2255 this[aNode.guard.type](aNode.guard, aNode, aCallbacks); |
|
2256 } |
|
2257 this[aNode.body.type](aNode.body, aNode, aCallbacks); |
|
2258 }, |
|
2259 |
|
2260 /** |
|
2261 * A for or for each block in an array comprehension or generator expression. |
|
2262 * |
|
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; |
|
2271 |
|
2272 if (this.break) { |
|
2273 return; |
|
2274 } |
|
2275 if (aCallbacks.onNode) { |
|
2276 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2277 return; |
|
2278 } |
|
2279 } |
|
2280 if (aCallbacks.onComprehensionBlock) { |
|
2281 aCallbacks.onComprehensionBlock(aNode); |
|
2282 } |
|
2283 this[aNode.left.type](aNode.left, aNode, aCallbacks); |
|
2284 this[aNode.right.type](aNode.right, aNode, aCallbacks); |
|
2285 }, |
|
2286 |
|
2287 /** |
|
2288 * An identifier. Note that an identifier may be an expression or a |
|
2289 * destructuring pattern. |
|
2290 * |
|
2291 * interface Identifier <: Node, Expression, Pattern { |
|
2292 * type: "Identifier"; |
|
2293 * name: string; |
|
2294 * } |
|
2295 */ |
|
2296 Identifier: function(aNode, aParent, aCallbacks) { |
|
2297 aNode._parent = aParent; |
|
2298 |
|
2299 if (this.break) { |
|
2300 return; |
|
2301 } |
|
2302 if (aCallbacks.onNode) { |
|
2303 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2304 return; |
|
2305 } |
|
2306 } |
|
2307 if (aCallbacks.onIdentifier) { |
|
2308 aCallbacks.onIdentifier(aNode); |
|
2309 } |
|
2310 }, |
|
2311 |
|
2312 /** |
|
2313 * A literal token. Note that a literal can be an expression. |
|
2314 * |
|
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; |
|
2322 |
|
2323 if (this.break) { |
|
2324 return; |
|
2325 } |
|
2326 if (aCallbacks.onNode) { |
|
2327 if (aCallbacks.onNode(aNode, aParent) === false) { |
|
2328 return; |
|
2329 } |
|
2330 } |
|
2331 if (aCallbacks.onLiteral) { |
|
2332 aCallbacks.onLiteral(aNode); |
|
2333 } |
|
2334 } |
|
2335 }; |
|
2336 |
|
2337 XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect); |