123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451 |
- /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const Cu = Components.utils;
- const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
- const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
- const { console } = require("resource://gre/modules/Console.jsm");
- const DevToolsUtils = require("devtools/shared/DevToolsUtils");
- XPCOMUtils.defineLazyModuleGetter(this,
- "Reflect", "resource://gre/modules/reflect.jsm");
- this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
- /**
- * A JS parser using the reflection API.
- */
- this.Parser = function Parser() {
- this._cache = new Map();
- this.errors = [];
- this.logExceptions = true;
- };
- Parser.prototype = {
- /**
- * Gets a collection of parser methods for a specified source.
- *
- * @param string source
- * The source text content.
- * @param string url [optional]
- * The source url. The AST nodes will be cached, so you can use this
- * identifier to avoid parsing the whole source again.
- */
- get(source, url = "") {
- // Try to use the cached AST nodes, to avoid useless parsing operations.
- if (this._cache.has(url)) {
- return this._cache.get(url);
- }
- // The source may not necessarily be JS, in which case we need to extract
- // all the scripts. Fastest/easiest way is with a regular expression.
- // Don't worry, the rules of using a <script> tag are really strict,
- // this will work.
- let regexp = /<script[^>]*?(?:>([^]*?)<\/script\s*>|\/>)/gim;
- let syntaxTrees = [];
- let scriptMatches = [];
- let scriptMatch;
- if (source.match(/^\s*</)) {
- // First non whitespace character is <, so most definitely HTML.
- while ((scriptMatch = regexp.exec(source))) {
- // Contents are captured at index 1 or nothing: Self-closing scripts
- // won't capture code content
- scriptMatches.push(scriptMatch[1] || "");
- }
- }
- // If there are no script matches, send the whole source directly to the
- // reflection API to generate the AST nodes.
- if (!scriptMatches.length) {
- // Reflect.parse throws when encounters a syntax error.
- try {
- let nodes = Reflect.parse(source);
- let length = source.length;
- syntaxTrees.push(new SyntaxTree(nodes, url, length));
- } catch (e) {
- this.errors.push(e);
- if (this.logExceptions) {
- DevToolsUtils.reportException(url, e);
- }
- }
- } else {
- // Generate the AST nodes for each script.
- for (let script of scriptMatches) {
- // Reflect.parse throws when encounters a syntax error.
- try {
- let nodes = Reflect.parse(script);
- let offset = source.indexOf(script);
- let length = script.length;
- syntaxTrees.push(new SyntaxTree(nodes, url, length, offset));
- } catch (e) {
- this.errors.push(e);
- if (this.logExceptions) {
- DevToolsUtils.reportException(url, e);
- }
- }
- }
- }
- let pool = new SyntaxTreesPool(syntaxTrees, url);
- // Cache the syntax trees pool by the specified url. This is entirely
- // optional, but it's strongly encouraged to cache ASTs because
- // generating them can be costly with big/complex sources.
- if (url) {
- this._cache.set(url, pool);
- }
- return pool;
- },
- /**
- * Clears all the parsed sources from cache.
- */
- clearCache() {
- this._cache.clear();
- },
- /**
- * Clears the AST for a particular source.
- *
- * @param String url
- * The URL of the source that is being cleared.
- */
- clearSource(url) {
- this._cache.delete(url);
- },
- _cache: null,
- errors: null
- };
- /**
- * A pool handling a collection of AST nodes generated by the reflection API.
- *
- * @param object syntaxTrees
- * A collection of AST nodes generated for a source.
- * @param string url [optional]
- * The source url.
- */
- function SyntaxTreesPool(syntaxTrees, url = "<unknown>") {
- this._trees = syntaxTrees;
- this._url = url;
- this._cache = new Map();
- }
- SyntaxTreesPool.prototype = {
- /**
- * @see SyntaxTree.prototype.getIdentifierAt
- */
- getIdentifierAt({ line, column, scriptIndex, ignoreLiterals }) {
- return this._call("getIdentifierAt",
- scriptIndex, line, column, ignoreLiterals)[0];
- },
- /**
- * @see SyntaxTree.prototype.getNamedFunctionDefinitions
- */
- getNamedFunctionDefinitions(substring) {
- return this._call("getNamedFunctionDefinitions", -1, substring);
- },
- /**
- * @return SyntaxTree
- * The last tree in this._trees
- */
- getLastSyntaxTree() {
- return this._trees[this._trees.length - 1];
- },
- /**
- * Gets the total number of scripts in the parent source.
- * @return number
- */
- get scriptCount() {
- return this._trees.length;
- },
- /**
- * Finds the start and length of the script containing the specified offset
- * relative to its parent source.
- *
- * @param number atOffset
- * The offset relative to the parent source.
- * @return object
- * The offset and length relative to the enclosing script.
- */
- getScriptInfo(atOffset) {
- let info = { start: -1, length: -1, index: -1 };
- for (let { offset, length } of this._trees) {
- info.index++;
- if (offset <= atOffset && offset + length >= atOffset) {
- info.start = offset;
- info.length = length;
- return info;
- }
- }
- info.index = -1;
- return info;
- },
- /**
- * Handles a request for a specific or all known syntax trees.
- *
- * @param string functionName
- * The function name to call on the SyntaxTree instances.
- * @param number syntaxTreeIndex
- * The syntax tree for which to handle the request. If the tree at
- * the specified index isn't found, the accumulated results for all
- * syntax trees are returned.
- * @param any params
- * Any kind params to pass to the request function.
- * @return array
- * The results given by all known syntax trees.
- */
- _call(functionName, syntaxTreeIndex, ...params) {
- let results = [];
- let requestId = [functionName, syntaxTreeIndex, params].toSource();
- if (this._cache.has(requestId)) {
- return this._cache.get(requestId);
- }
- let requestedTree = this._trees[syntaxTreeIndex];
- let targettedTrees = requestedTree ? [requestedTree] : this._trees;
- for (let syntaxTree of targettedTrees) {
- try {
- let parseResults = syntaxTree[functionName].apply(syntaxTree, params);
- if (parseResults) {
- parseResults.sourceUrl = syntaxTree.url;
- parseResults.scriptLength = syntaxTree.length;
- parseResults.scriptOffset = syntaxTree.offset;
- results.push(parseResults);
- }
- } catch (e) {
- // Can't guarantee that the tree traversal logic is forever perfect :)
- // Language features may be added, in which case the recursive methods
- // need to be updated. If an exception is thrown here, file a bug.
- DevToolsUtils.reportException(
- `Syntax tree visitor for ${this._url}`, e);
- }
- }
- this._cache.set(requestId, results);
- return results;
- },
- _trees: null,
- _cache: null
- };
- /**
- * A collection of AST nodes generated by the reflection API.
- *
- * @param object nodes
- * The AST nodes.
- * @param string url
- * The source url.
- * @param number length
- * The total number of chars of the parsed script in the parent source.
- * @param number offset [optional]
- * The char offset of the parsed script in the parent source.
- */
- function SyntaxTree(nodes, url, length, offset = 0) {
- this.AST = nodes;
- this.url = url;
- this.length = length;
- this.offset = offset;
- }
- SyntaxTree.prototype = {
- /**
- * Gets the identifier at the specified location.
- *
- * @param number line
- * The line in the source.
- * @param number column
- * The column in the source.
- * @param boolean ignoreLiterals
- * Specifies if alone literals should be ignored.
- * @return object
- * An object containing identifier information as { name, location,
- * evalString } properties, or null if nothing is found.
- */
- getIdentifierAt(line, column, ignoreLiterals) {
- let info = null;
- SyntaxTreeVisitor.walk(this.AST, {
- /**
- * Callback invoked for each identifier node.
- * @param Node node
- */
- onIdentifier(node) {
- if (ParserHelpers.nodeContainsPoint(node, line, column)) {
- info = {
- name: node.name,
- location: ParserHelpers.getNodeLocation(node),
- evalString: ParserHelpers.getIdentifierEvalString(node)
- };
- // Abruptly halt walking the syntax tree.
- SyntaxTreeVisitor.break = true;
- }
- },
- /**
- * Callback invoked for each literal node.
- * @param Node node
- */
- onLiteral(node) {
- if (!ignoreLiterals) {
- this.onIdentifier(node);
- }
- },
- /**
- * Callback invoked for each 'this' node.
- * @param Node node
- */
- onThisExpression(node) {
- this.onIdentifier(node);
- }
- });
- return info;
- },
- /**
- * Searches for all function definitions (declarations and expressions)
- * whose names (or inferred names) contain a string.
- *
- * @param string substring
- * The string to be contained in the function name (or inferred name).
- * Can be an empty string to match all functions.
- * @return array
- * All the matching function declarations and expressions, as
- * { functionName, functionLocation ... } object hashes.
- */
- getNamedFunctionDefinitions(substring) {
- let lowerCaseToken = substring.toLowerCase();
- let store = [];
- function includesToken(name) {
- return name && name.toLowerCase().includes(lowerCaseToken);
- }
- SyntaxTreeVisitor.walk(this.AST, {
- /**
- * Callback invoked for each function declaration node.
- * @param Node node
- */
- onFunctionDeclaration(node) {
- let functionName = node.id.name;
- if (includesToken(functionName)) {
- store.push({
- functionName: functionName,
- functionLocation: ParserHelpers.getNodeLocation(node)
- });
- }
- },
- /**
- * Callback invoked for each function expression node.
- * @param Node node
- */
- onFunctionExpression(node) {
- // Function expressions don't necessarily have a name.
- let functionName = node.id ? node.id.name : "";
- let functionLocation = ParserHelpers.getNodeLocation(node);
- // Infer the function's name from an enclosing syntax tree node.
- let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(node);
- let inferredName = inferredInfo.name;
- let inferredChain = inferredInfo.chain;
- let inferredLocation = inferredInfo.loc;
- // Current node may be part of a larger assignment expression stack.
- if (node._parent.type == "AssignmentExpression") {
- this.onFunctionExpression(node._parent);
- }
- if (includesToken(functionName) || includesToken(inferredName)) {
- store.push({
- functionName: functionName,
- functionLocation: functionLocation,
- inferredName: inferredName,
- inferredChain: inferredChain,
- inferredLocation: inferredLocation
- });
- }
- },
- /**
- * Callback invoked for each arrow expression node.
- * @param Node node
- */
- onArrowFunctionExpression(node) {
- // Infer the function's name from an enclosing syntax tree node.
- let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(node);
- let inferredName = inferredInfo.name;
- let inferredChain = inferredInfo.chain;
- let inferredLocation = inferredInfo.loc;
- // Current node may be part of a larger assignment expression stack.
- if (node._parent.type == "AssignmentExpression") {
- this.onFunctionExpression(node._parent);
- }
- if (includesToken(inferredName)) {
- store.push({
- inferredName: inferredName,
- inferredChain: inferredChain,
- inferredLocation: inferredLocation
- });
- }
- }
- });
- return store;
- },
- AST: null,
- url: "",
- length: 0,
- offset: 0
- };
- /**
- * Parser utility methods.
- */
- var ParserHelpers = {
- /**
- * Gets the location information for a node. Not all nodes have a
- * location property directly attached, or the location information
- * is incorrect, in which cases it's accessible via the parent.
- *
- * @param Node node
- * The node who's location needs to be retrieved.
- * @return object
- * An object containing { line, column } information.
- */
- getNodeLocation(node) {
- if (node.type != "Identifier") {
- return node.loc;
- }
- // Work around the fact that some identifier nodes don't have the
- // correct location attached.
- let { loc: parentLocation, type: parentType } = node._parent;
- let { loc: nodeLocation } = node;
- if (!nodeLocation) {
- if (parentType == "FunctionDeclaration" ||
- parentType == "FunctionExpression") {
- // e.g. "function foo() {}" or "{ bar: function foo() {} }"
- // The location is unavailable for the identifier node "foo".
- let loc = Cu.cloneInto(parentLocation, {});
- loc.end.line = loc.start.line;
- loc.end.column = loc.start.column + node.name.length;
- return loc;
- }
- if (parentType == "MemberExpression") {
- // e.g. "foo.bar"
- // The location is unavailable for the identifier node "bar".
- let loc = Cu.cloneInto(parentLocation, {});
- loc.start.line = loc.end.line;
- loc.start.column = loc.end.column - node.name.length;
- return loc;
- }
- if (parentType == "LabeledStatement") {
- // e.g. label: ...
- // The location is unavailable for the identifier node "label".
- let loc = Cu.cloneInto(parentLocation, {});
- loc.end.line = loc.start.line;
- loc.end.column = loc.start.column + node.name.length;
- return loc;
- }
- if (parentType == "ContinueStatement" || parentType == "BreakStatement") {
- // e.g. continue label; or break label;
- // The location is unavailable for the identifier node "label".
- let loc = Cu.cloneInto(parentLocation, {});
- loc.start.line = loc.end.line;
- loc.start.column = loc.end.column - node.name.length;
- return loc;
- }
- } else if (parentType == "VariableDeclarator") {
- // e.g. "let foo = 42"
- // The location incorrectly spans across the whole variable declaration,
- // not just the identifier node "foo".
- let loc = Cu.cloneInto(nodeLocation, {});
- loc.end.line = loc.start.line;
- loc.end.column = loc.start.column + node.name.length;
- return loc;
- }
- return node.loc;
- },
- /**
- * Checks if a node's bounds contains a specified line.
- *
- * @param Node node
- * The node's bounds used as reference.
- * @param number line
- * The line number to check.
- * @return boolean
- * True if the line and column is contained in the node's bounds.
- */
- nodeContainsLine(node, line) {
- let { start: s, end: e } = this.getNodeLocation(node);
- return s.line <= line && e.line >= line;
- },
- /**
- * Checks if a node's bounds contains a specified line and column.
- *
- * @param Node node
- * The node's bounds used as reference.
- * @param number line
- * The line number to check.
- * @param number column
- * The column number to check.
- * @return boolean
- * True if the line and column is contained in the node's bounds.
- */
- nodeContainsPoint(node, line, column) {
- let { start: s, end: e } = this.getNodeLocation(node);
- return s.line == line && e.line == line &&
- s.column <= column && e.column >= column;
- },
- /**
- * Try to infer a function expression's name & other details based on the
- * enclosing VariableDeclarator, AssignmentExpression or ObjectExpression.
- *
- * @param Node node
- * The function expression node to get the name for.
- * @return object
- * The inferred function name, or empty string can't infer the name,
- * along with the chain (a generic "context", like a prototype chain)
- * and location if available.
- */
- inferFunctionExpressionInfo(node) {
- let parent = node._parent;
- // A function expression may be defined in a variable declarator,
- // e.g. var foo = function(){}, in which case it is possible to infer
- // the variable name.
- if (parent.type == "VariableDeclarator") {
- return {
- name: parent.id.name,
- chain: null,
- loc: this.getNodeLocation(parent.id)
- };
- }
- // Function expressions can also be defined in assignment expressions,
- // e.g. foo = function(){} or foo.bar = function(){}, in which case it is
- // possible to infer the assignee name ("foo" and "bar" respectively).
- if (parent.type == "AssignmentExpression") {
- let propertyChain = this._getMemberExpressionPropertyChain(parent.left);
- let propertyLeaf = propertyChain.pop();
- return {
- name: propertyLeaf,
- chain: propertyChain,
- loc: this.getNodeLocation(parent.left)
- };
- }
- // If a function expression is defined in an object expression,
- // e.g. { foo: function(){} }, then it is possible to infer the name
- // from the corresponding property.
- if (parent.type == "ObjectExpression") {
- let propertyKey = this._getObjectExpressionPropertyKeyForValue(node);
- let propertyChain = this._getObjectExpressionPropertyChain(parent);
- let propertyLeaf = propertyKey.name;
- return {
- name: propertyLeaf,
- chain: propertyChain,
- loc: this.getNodeLocation(propertyKey)
- };
- }
- // Can't infer the function expression's name.
- return {
- name: "",
- chain: null,
- loc: null
- };
- },
- /**
- * Gets the name of an object expression's property to which a specified
- * value is assigned.
- *
- * Used for inferring function expression information and retrieving
- * an identifier evaluation string.
- *
- * For example, if "node" represents the "bar" identifier in a hypothetical
- * "{ foo: bar }" object expression, the returned node is the "foo"
- * identifier.
- *
- * @param Node node
- * The value node in an object expression.
- * @return object
- * The key identifier node in the object expression.
- */
- _getObjectExpressionPropertyKeyForValue(node) {
- let parent = node._parent;
- if (parent.type != "ObjectExpression") {
- return null;
- }
- for (let property of parent.properties) {
- if (property.value == node) {
- return property.key;
- }
- }
- return null;
- },
- /**
- * Gets an object expression's property chain to its parent
- * variable declarator or assignment expression, if available.
- *
- * Used for inferring function expression information and retrieving
- * an identifier evaluation string.
- *
- * For example, if node represents the "baz: {}" object expression in a
- * hypothetical "foo = { bar: { baz: {} } }" assignment expression, the
- * returned chain is ["foo", "bar", "baz"].
- *
- * @param Node node
- * The object expression node to begin the scan from.
- * @param array aStore [optional]
- * The chain to store the nodes into.
- * @return array
- * The chain to the parent variable declarator, as strings.
- */
- _getObjectExpressionPropertyChain(node, aStore = []) {
- switch (node.type) {
- case "ObjectExpression":
- this._getObjectExpressionPropertyChain(node._parent, aStore);
- let propertyKey = this._getObjectExpressionPropertyKeyForValue(node);
- if (propertyKey) {
- aStore.push(propertyKey.name);
- }
- break;
- // Handle "var foo = { ... }" variable declarators.
- case "VariableDeclarator":
- aStore.push(node.id.name);
- break;
- // Handle "foo.bar = { ... }" assignment expressions, since they're
- // commonly used when defining an object's prototype methods; e.g:
- // "Foo.prototype = { ... }".
- case "AssignmentExpression":
- this._getMemberExpressionPropertyChain(node.left, aStore);
- break;
- // Additionally handle stuff like "foo = bar.baz({ ... })", because it's
- // commonly used in prototype-based inheritance in many libraries; e.g:
- // "Foo = Bar.extend({ ... })".
- case "NewExpression":
- case "CallExpression":
- this._getObjectExpressionPropertyChain(node._parent, aStore);
- break;
- }
- return aStore;
- },
- /**
- * Gets a member expression's property chain.
- *
- * Used for inferring function expression information and retrieving
- * an identifier evaluation string.
- *
- * For example, if node represents a hypothetical "foo.bar.baz"
- * member expression, the returned chain ["foo", "bar", "baz"].
- *
- * More complex expressions like foo.bar().baz are intentionally not handled.
- *
- * @param Node node
- * The member expression node to begin the scan from.
- * @param array store [optional]
- * The chain to store the nodes into.
- * @return array
- * The full member chain, as strings.
- */
- _getMemberExpressionPropertyChain(node, store = []) {
- switch (node.type) {
- case "MemberExpression":
- this._getMemberExpressionPropertyChain(node.object, store);
- this._getMemberExpressionPropertyChain(node.property, store);
- break;
- case "ThisExpression":
- store.push("this");
- break;
- case "Identifier":
- store.push(node.name);
- break;
- }
- return store;
- },
- /**
- * Returns an evaluation string which can be used to obtain the
- * current value for the respective identifier.
- *
- * @param Node node
- * The leaf node (e.g. Identifier, Literal) to begin the scan from.
- * @return string
- * The corresponding evaluation string, or empty string if
- * the specified leaf node can't be used.
- */
- getIdentifierEvalString(node) {
- switch (node._parent.type) {
- case "ObjectExpression":
- // If the identifier is the actual property value, it can be used
- // directly as an evaluation string. Otherwise, construct the property
- // access chain, since the value might have changed.
- if (!this._getObjectExpressionPropertyKeyForValue(node)) {
- let propertyChain =
- this._getObjectExpressionPropertyChain(node._parent);
- let propertyLeaf = node.name;
- return [...propertyChain, propertyLeaf].join(".");
- }
- break;
- case "MemberExpression":
- // Make sure this is a property identifier, not the parent object.
- if (node._parent.property == node) {
- return this._getMemberExpressionPropertyChain(node._parent).join(".");
- }
- break;
- }
- switch (node.type) {
- case "ThisExpression":
- return "this";
- case "Identifier":
- return node.name;
- case "Literal":
- return uneval(node.value);
- default:
- return "";
- }
- }
- };
- /**
- * A visitor for a syntax tree generated by the reflection API.
- * See https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API.
- *
- * All node types implement the following interface:
- * interface Node {
- * type: string;
- * loc: SourceLocation | null;
- * }
- */
- var SyntaxTreeVisitor = {
- /**
- * Walks a syntax tree.
- *
- * @param object tree
- * The AST nodes generated by the reflection API
- * @param object callbacks
- * A map of all the callbacks to invoke when passing through certain
- * types of noes (e.g: onFunctionDeclaration, onBlockStatement etc.).
- */
- walk(tree, callbacks) {
- this.break = false;
- this[tree.type](tree, callbacks);
- },
- /**
- * Filters all the nodes in this syntax tree based on a predicate.
- *
- * @param object tree
- * The AST nodes generated by the reflection API
- * @param function predicate
- * The predicate ran on each node.
- * @return array
- * An array of nodes validating the predicate.
- */
- filter(tree, predicate) {
- let store = [];
- this.walk(tree, {
- onNode: e => {
- if (predicate(e)) {
- store.push(e);
- }
- }
- });
- return store;
- },
- /**
- * A flag checked on each node in the syntax tree. If true, walking is
- * abruptly halted.
- */
- break: false,
- /**
- * A complete program source tree.
- *
- * interface Program <: Node {
- * type: "Program";
- * body: [ Statement ];
- * }
- */
- Program(node, callbacks) {
- if (callbacks.onProgram) {
- callbacks.onProgram(node);
- }
- for (let statement of node.body) {
- this[statement.type](statement, node, callbacks);
- }
- },
- /**
- * Any statement.
- *
- * interface Statement <: Node { }
- */
- Statement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onStatement) {
- callbacks.onStatement(node);
- }
- },
- /**
- * An empty statement, i.e., a solitary semicolon.
- *
- * interface EmptyStatement <: Statement {
- * type: "EmptyStatement";
- * }
- */
- EmptyStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onEmptyStatement) {
- callbacks.onEmptyStatement(node);
- }
- },
- /**
- * A block statement, i.e., a sequence of statements surrounded by braces.
- *
- * interface BlockStatement <: Statement {
- * type: "BlockStatement";
- * body: [ Statement ];
- * }
- */
- BlockStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onBlockStatement) {
- callbacks.onBlockStatement(node);
- }
- for (let statement of node.body) {
- this[statement.type](statement, node, callbacks);
- }
- },
- /**
- * An expression statement, i.e., a statement consisting of a single
- * expression.
- *
- * interface ExpressionStatement <: Statement {
- * type: "ExpressionStatement";
- * expression: Expression;
- * }
- */
- ExpressionStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onExpressionStatement) {
- callbacks.onExpressionStatement(node);
- }
- this[node.expression.type](node.expression, node, callbacks);
- },
- /**
- * An if statement.
- *
- * interface IfStatement <: Statement {
- * type: "IfStatement";
- * test: Expression;
- * consequent: Statement;
- * alternate: Statement | null;
- * }
- */
- IfStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onIfStatement) {
- callbacks.onIfStatement(node);
- }
- this[node.test.type](node.test, node, callbacks);
- this[node.consequent.type](node.consequent, node, callbacks);
- if (node.alternate) {
- this[node.alternate.type](node.alternate, node, callbacks);
- }
- },
- /**
- * A labeled statement, i.e., a statement prefixed by a break/continue label.
- *
- * interface LabeledStatement <: Statement {
- * type: "LabeledStatement";
- * label: Identifier;
- * body: Statement;
- * }
- */
- LabeledStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onLabeledStatement) {
- callbacks.onLabeledStatement(node);
- }
- this[node.label.type](node.label, node, callbacks);
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A break statement.
- *
- * interface BreakStatement <: Statement {
- * type: "BreakStatement";
- * label: Identifier | null;
- * }
- */
- BreakStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onBreakStatement) {
- callbacks.onBreakStatement(node);
- }
- if (node.label) {
- this[node.label.type](node.label, node, callbacks);
- }
- },
- /**
- * A continue statement.
- *
- * interface ContinueStatement <: Statement {
- * type: "ContinueStatement";
- * label: Identifier | null;
- * }
- */
- ContinueStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onContinueStatement) {
- callbacks.onContinueStatement(node);
- }
- if (node.label) {
- this[node.label.type](node.label, node, callbacks);
- }
- },
- /**
- * A with statement.
- *
- * interface WithStatement <: Statement {
- * type: "WithStatement";
- * object: Expression;
- * body: Statement;
- * }
- */
- WithStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onWithStatement) {
- callbacks.onWithStatement(node);
- }
- this[node.object.type](node.object, node, callbacks);
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A switch statement. The lexical flag is metadata indicating whether the
- * switch statement contains any unnested let declarations (and therefore
- * introduces a new lexical scope).
- *
- * interface SwitchStatement <: Statement {
- * type: "SwitchStatement";
- * discriminant: Expression;
- * cases: [ SwitchCase ];
- * lexical: boolean;
- * }
- */
- SwitchStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onSwitchStatement) {
- callbacks.onSwitchStatement(node);
- }
- this[node.discriminant.type](node.discriminant, node, callbacks);
- for (let _case of node.cases) {
- this[_case.type](_case, node, callbacks);
- }
- },
- /**
- * A return statement.
- *
- * interface ReturnStatement <: Statement {
- * type: "ReturnStatement";
- * argument: Expression | null;
- * }
- */
- ReturnStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onReturnStatement) {
- callbacks.onReturnStatement(node);
- }
- if (node.argument) {
- this[node.argument.type](node.argument, node, callbacks);
- }
- },
- /**
- * A throw statement.
- *
- * interface ThrowStatement <: Statement {
- * type: "ThrowStatement";
- * argument: Expression;
- * }
- */
- ThrowStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onThrowStatement) {
- callbacks.onThrowStatement(node);
- }
- this[node.argument.type](node.argument, node, callbacks);
- },
- /**
- * A try statement.
- *
- * interface TryStatement <: Statement {
- * type: "TryStatement";
- * block: BlockStatement;
- * handler: CatchClause | null;
- * guardedHandlers: [ CatchClause ];
- * finalizer: BlockStatement | null;
- * }
- */
- TryStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onTryStatement) {
- callbacks.onTryStatement(node);
- }
- this[node.block.type](node.block, node, callbacks);
- if (node.handler) {
- this[node.handler.type](node.handler, node, callbacks);
- }
- for (let guardedHandler of node.guardedHandlers) {
- this[guardedHandler.type](guardedHandler, node, callbacks);
- }
- if (node.finalizer) {
- this[node.finalizer.type](node.finalizer, node, callbacks);
- }
- },
- /**
- * A while statement.
- *
- * interface WhileStatement <: Statement {
- * type: "WhileStatement";
- * test: Expression;
- * body: Statement;
- * }
- */
- WhileStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onWhileStatement) {
- callbacks.onWhileStatement(node);
- }
- this[node.test.type](node.test, node, callbacks);
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A do/while statement.
- *
- * interface DoWhileStatement <: Statement {
- * type: "DoWhileStatement";
- * body: Statement;
- * test: Expression;
- * }
- */
- DoWhileStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onDoWhileStatement) {
- callbacks.onDoWhileStatement(node);
- }
- this[node.body.type](node.body, node, callbacks);
- this[node.test.type](node.test, node, callbacks);
- },
- /**
- * A for statement.
- *
- * interface ForStatement <: Statement {
- * type: "ForStatement";
- * init: VariableDeclaration | Expression | null;
- * test: Expression | null;
- * update: Expression | null;
- * body: Statement;
- * }
- */
- ForStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onForStatement) {
- callbacks.onForStatement(node);
- }
- if (node.init) {
- this[node.init.type](node.init, node, callbacks);
- }
- if (node.test) {
- this[node.test.type](node.test, node, callbacks);
- }
- if (node.update) {
- this[node.update.type](node.update, node, callbacks);
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A for/in statement, or, if each is true, a for each/in statement.
- *
- * interface ForInStatement <: Statement {
- * type: "ForInStatement";
- * left: VariableDeclaration | Expression;
- * right: Expression;
- * body: Statement;
- * each: boolean;
- * }
- */
- ForInStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onForInStatement) {
- callbacks.onForInStatement(node);
- }
- this[node.left.type](node.left, node, callbacks);
- this[node.right.type](node.right, node, callbacks);
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A for/of statement.
- *
- * interface ForOfStatement <: Statement {
- * type: "ForOfStatement";
- * left: VariableDeclaration | Expression;
- * right: Expression;
- * body: Statement;
- * }
- */
- ForOfStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onForOfStatement) {
- callbacks.onForOfStatement(node);
- }
- this[node.left.type](node.left, node, callbacks);
- this[node.right.type](node.right, node, callbacks);
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A let statement.
- *
- * interface LetStatement <: Statement {
- * type: "LetStatement";
- * head: [ { id: Pattern, init: Expression | null } ];
- * body: Statement;
- * }
- */
- LetStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onLetStatement) {
- callbacks.onLetStatement(node);
- }
- for (let { id, init } of node.head) {
- this[id.type](id, node, callbacks);
- if (init) {
- this[init.type](init, node, callbacks);
- }
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A debugger statement.
- *
- * interface DebuggerStatement <: Statement {
- * type: "DebuggerStatement";
- * }
- */
- DebuggerStatement(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onDebuggerStatement) {
- callbacks.onDebuggerStatement(node);
- }
- },
- /**
- * Any declaration node. Note that declarations are considered statements;
- * this is because declarations can appear in any statement context in the
- * language recognized by the SpiderMonkey parser.
- *
- * interface Declaration <: Statement { }
- */
- Declaration(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onDeclaration) {
- callbacks.onDeclaration(node);
- }
- },
- /**
- * A function declaration.
- *
- * interface FunctionDeclaration <: Function, Declaration {
- * type: "FunctionDeclaration";
- * id: Identifier;
- * params: [ Pattern ];
- * defaults: [ Expression ];
- * rest: Identifier | null;
- * body: BlockStatement | Expression;
- * generator: boolean;
- * expression: boolean;
- * }
- */
- FunctionDeclaration(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onFunctionDeclaration) {
- callbacks.onFunctionDeclaration(node);
- }
- this[node.id.type](node.id, node, callbacks);
- for (let param of node.params) {
- this[param.type](param, node, callbacks);
- }
- for (let _default of node.defaults) {
- if (_default) {
- this[_default.type](_default, node, callbacks);
- }
- }
- if (node.rest) {
- this[node.rest.type](node.rest, node, callbacks);
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A variable declaration, via one of var, let, or const.
- *
- * interface VariableDeclaration <: Declaration {
- * type: "VariableDeclaration";
- * declarations: [ VariableDeclarator ];
- * kind: "var" | "let" | "const";
- * }
- */
- VariableDeclaration(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onVariableDeclaration) {
- callbacks.onVariableDeclaration(node);
- }
- for (let declaration of node.declarations) {
- this[declaration.type](declaration, node, callbacks);
- }
- },
- /**
- * A variable declarator.
- *
- * interface VariableDeclarator <: Node {
- * type: "VariableDeclarator";
- * id: Pattern;
- * init: Expression | null;
- * }
- */
- VariableDeclarator(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onVariableDeclarator) {
- callbacks.onVariableDeclarator(node);
- }
- this[node.id.type](node.id, node, callbacks);
- if (node.init) {
- this[node.init.type](node.init, node, callbacks);
- }
- },
- /**
- * Any expression node. Since the left-hand side of an assignment may be any
- * expression in general, an expression can also be a pattern.
- *
- * interface Expression <: Node, Pattern { }
- */
- Expression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onExpression) {
- callbacks.onExpression(node);
- }
- },
- /**
- * A this expression.
- *
- * interface ThisExpression <: Expression {
- * type: "ThisExpression";
- * }
- */
- ThisExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onThisExpression) {
- callbacks.onThisExpression(node);
- }
- },
- /**
- * An array expression.
- *
- * interface ArrayExpression <: Expression {
- * type: "ArrayExpression";
- * elements: [ Expression | null ];
- * }
- */
- ArrayExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onArrayExpression) {
- callbacks.onArrayExpression(node);
- }
- for (let element of node.elements) {
- if (element) {
- this[element.type](element, node, callbacks);
- }
- }
- },
- /**
- * A spread expression.
- *
- * interface SpreadExpression <: Expression {
- * type: "SpreadExpression";
- * expression: Expression;
- * }
- */
- SpreadExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onSpreadExpression) {
- callbacks.onSpreadExpression(node);
- }
- this[node.expression.type](node.expression, node, callbacks);
- },
- /**
- * An object expression. A literal property in an object expression can have
- * either a string or number as its value. Ordinary property initializers
- * have a kind value "init"; getters and setters have the kind values "get"
- * and "set", respectively.
- *
- * interface ObjectExpression <: Expression {
- * type: "ObjectExpression";
- * properties: [ { key: Literal | Identifier | ComputedName,
- * value: Expression,
- * kind: "init" | "get" | "set" } ];
- * }
- */
- ObjectExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onObjectExpression) {
- callbacks.onObjectExpression(node);
- }
- for (let { key, value } of node.properties) {
- this[key.type](key, node, callbacks);
- this[value.type](value, node, callbacks);
- }
- },
- /**
- * A computed property name in object expression, like in { [a]: b }
- *
- * interface ComputedName <: Node {
- * type: "ComputedName";
- * name: Expression;
- * }
- */
- ComputedName(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onComputedName) {
- callbacks.onComputedName(node);
- }
- this[node.name.type](node.name, node, callbacks);
- },
- /**
- * A function expression.
- *
- * interface FunctionExpression <: Function, Expression {
- * type: "FunctionExpression";
- * id: Identifier | null;
- * params: [ Pattern ];
- * defaults: [ Expression ];
- * rest: Identifier | null;
- * body: BlockStatement | Expression;
- * generator: boolean;
- * expression: boolean;
- * }
- */
- FunctionExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onFunctionExpression) {
- callbacks.onFunctionExpression(node);
- }
- if (node.id) {
- this[node.id.type](node.id, node, callbacks);
- }
- for (let param of node.params) {
- this[param.type](param, node, callbacks);
- }
- for (let _default of node.defaults) {
- if (_default) {
- this[_default.type](_default, node, callbacks);
- }
- }
- if (node.rest) {
- this[node.rest.type](node.rest, node, callbacks);
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * An arrow expression.
- *
- * interface ArrowFunctionExpression <: Function, Expression {
- * type: "ArrowFunctionExpression";
- * params: [ Pattern ];
- * defaults: [ Expression ];
- * rest: Identifier | null;
- * body: BlockStatement | Expression;
- * generator: boolean;
- * expression: boolean;
- * }
- */
- ArrowFunctionExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onArrowFunctionExpression) {
- callbacks.onArrowFunctionExpression(node);
- }
- for (let param of node.params) {
- this[param.type](param, node, callbacks);
- }
- for (let _default of node.defaults) {
- if (_default) {
- this[_default.type](_default, node, callbacks);
- }
- }
- if (node.rest) {
- this[node.rest.type](node.rest, node, callbacks);
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A sequence expression, i.e., a comma-separated sequence of expressions.
- *
- * interface SequenceExpression <: Expression {
- * type: "SequenceExpression";
- * expressions: [ Expression ];
- * }
- */
- SequenceExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onSequenceExpression) {
- callbacks.onSequenceExpression(node);
- }
- for (let expression of node.expressions) {
- this[expression.type](expression, node, callbacks);
- }
- },
- /**
- * A unary operator expression.
- *
- * interface UnaryExpression <: Expression {
- * type: "UnaryExpression";
- * operator: UnaryOperator;
- * prefix: boolean;
- * argument: Expression;
- * }
- */
- UnaryExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onUnaryExpression) {
- callbacks.onUnaryExpression(node);
- }
- this[node.argument.type](node.argument, node, callbacks);
- },
- /**
- * A binary operator expression.
- *
- * interface BinaryExpression <: Expression {
- * type: "BinaryExpression";
- * operator: BinaryOperator;
- * left: Expression;
- * right: Expression;
- * }
- */
- BinaryExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onBinaryExpression) {
- callbacks.onBinaryExpression(node);
- }
- this[node.left.type](node.left, node, callbacks);
- this[node.right.type](node.right, node, callbacks);
- },
- /**
- * An assignment operator expression.
- *
- * interface AssignmentExpression <: Expression {
- * type: "AssignmentExpression";
- * operator: AssignmentOperator;
- * left: Expression;
- * right: Expression;
- * }
- */
- AssignmentExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onAssignmentExpression) {
- callbacks.onAssignmentExpression(node);
- }
- this[node.left.type](node.left, node, callbacks);
- this[node.right.type](node.right, node, callbacks);
- },
- /**
- * An update (increment or decrement) operator expression.
- *
- * interface UpdateExpression <: Expression {
- * type: "UpdateExpression";
- * operator: UpdateOperator;
- * argument: Expression;
- * prefix: boolean;
- * }
- */
- UpdateExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onUpdateExpression) {
- callbacks.onUpdateExpression(node);
- }
- this[node.argument.type](node.argument, node, callbacks);
- },
- /**
- * A logical operator expression.
- *
- * interface LogicalExpression <: Expression {
- * type: "LogicalExpression";
- * operator: LogicalOperator;
- * left: Expression;
- * right: Expression;
- * }
- */
- LogicalExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onLogicalExpression) {
- callbacks.onLogicalExpression(node);
- }
- this[node.left.type](node.left, node, callbacks);
- this[node.right.type](node.right, node, callbacks);
- },
- /**
- * A conditional expression, i.e., a ternary ?/: expression.
- *
- * interface ConditionalExpression <: Expression {
- * type: "ConditionalExpression";
- * test: Expression;
- * alternate: Expression;
- * consequent: Expression;
- * }
- */
- ConditionalExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onConditionalExpression) {
- callbacks.onConditionalExpression(node);
- }
- this[node.test.type](node.test, node, callbacks);
- this[node.alternate.type](node.alternate, node, callbacks);
- this[node.consequent.type](node.consequent, node, callbacks);
- },
- /**
- * A new expression.
- *
- * interface NewExpression <: Expression {
- * type: "NewExpression";
- * callee: Expression;
- * arguments: [ Expression | null ];
- * }
- */
- NewExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onNewExpression) {
- callbacks.onNewExpression(node);
- }
- this[node.callee.type](node.callee, node, callbacks);
- for (let argument of node.arguments) {
- if (argument) {
- this[argument.type](argument, node, callbacks);
- }
- }
- },
- /**
- * A function or method call expression.
- *
- * interface CallExpression <: Expression {
- * type: "CallExpression";
- * callee: Expression;
- * arguments: [ Expression | null ];
- * }
- */
- CallExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onCallExpression) {
- callbacks.onCallExpression(node);
- }
- this[node.callee.type](node.callee, node, callbacks);
- for (let argument of node.arguments) {
- if (argument) {
- if (!this[argument.type]) {
- console.error("Unknown parser object:", argument.type);
- }
- this[argument.type](argument, node, callbacks);
- }
- }
- },
- /**
- * A member expression. If computed is true, the node corresponds to a
- * computed e1[e2] expression and property is an Expression. If computed is
- * false, the node corresponds to a static e1.x expression and property is an
- * Identifier.
- *
- * interface MemberExpression <: Expression {
- * type: "MemberExpression";
- * object: Expression;
- * property: Identifier | Expression;
- * computed: boolean;
- * }
- */
- MemberExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onMemberExpression) {
- callbacks.onMemberExpression(node);
- }
- this[node.object.type](node.object, node, callbacks);
- this[node.property.type](node.property, node, callbacks);
- },
- /**
- * A yield expression.
- *
- * interface YieldExpression <: Expression {
- * argument: Expression | null;
- * }
- */
- YieldExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onYieldExpression) {
- callbacks.onYieldExpression(node);
- }
- if (node.argument) {
- this[node.argument.type](node.argument, node, callbacks);
- }
- },
- /**
- * An array comprehension. The blocks array corresponds to the sequence of
- * for and for each blocks. The optional filter expression corresponds to the
- * final if clause, if present.
- *
- * interface ComprehensionExpression <: Expression {
- * body: Expression;
- * blocks: [ ComprehensionBlock ];
- * filter: Expression | null;
- * }
- */
- ComprehensionExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onComprehensionExpression) {
- callbacks.onComprehensionExpression(node);
- }
- this[node.body.type](node.body, node, callbacks);
- for (let block of node.blocks) {
- this[block.type](block, node, callbacks);
- }
- if (node.filter) {
- this[node.filter.type](node.filter, node, callbacks);
- }
- },
- /**
- * A generator expression. As with array comprehensions, the blocks array
- * corresponds to the sequence of for and for each blocks, and the optional
- * filter expression corresponds to the final if clause, if present.
- *
- * interface GeneratorExpression <: Expression {
- * body: Expression;
- * blocks: [ ComprehensionBlock ];
- * filter: Expression | null;
- * }
- */
- GeneratorExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onGeneratorExpression) {
- callbacks.onGeneratorExpression(node);
- }
- this[node.body.type](node.body, node, callbacks);
- for (let block of node.blocks) {
- this[block.type](block, node, callbacks);
- }
- if (node.filter) {
- this[node.filter.type](node.filter, node, callbacks);
- }
- },
- /**
- * A graph expression, aka "sharp literal," such as #1={ self: #1# }.
- *
- * interface GraphExpression <: Expression {
- * index: uint32;
- * expression: Literal;
- * }
- */
- GraphExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onGraphExpression) {
- callbacks.onGraphExpression(node);
- }
- this[node.expression.type](node.expression, node, callbacks);
- },
- /**
- * A graph index expression, aka "sharp variable," such as #1#.
- *
- * interface GraphIndexExpression <: Expression {
- * index: uint32;
- * }
- */
- GraphIndexExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onGraphIndexExpression) {
- callbacks.onGraphIndexExpression(node);
- }
- },
- /**
- * A let expression.
- *
- * interface LetExpression <: Expression {
- * type: "LetExpression";
- * head: [ { id: Pattern, init: Expression | null } ];
- * body: Expression;
- * }
- */
- LetExpression(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onLetExpression) {
- callbacks.onLetExpression(node);
- }
- for (let { id, init } of node.head) {
- this[id.type](id, node, callbacks);
- if (init) {
- this[init.type](init, node, callbacks);
- }
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * Any pattern.
- *
- * interface Pattern <: Node { }
- */
- Pattern(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onPattern) {
- callbacks.onPattern(node);
- }
- },
- /**
- * An object-destructuring pattern. A literal property in an object pattern
- * can have either a string or number as its value.
- *
- * interface ObjectPattern <: Pattern {
- * type: "ObjectPattern";
- * properties: [ { key: Literal | Identifier, value: Pattern } ];
- * }
- */
- ObjectPattern(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onObjectPattern) {
- callbacks.onObjectPattern(node);
- }
- for (let { key, value } of node.properties) {
- this[key.type](key, node, callbacks);
- this[value.type](value, node, callbacks);
- }
- },
- /**
- * An array-destructuring pattern.
- *
- * interface ArrayPattern <: Pattern {
- * type: "ArrayPattern";
- * elements: [ Pattern | null ];
- * }
- */
- ArrayPattern(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onArrayPattern) {
- callbacks.onArrayPattern(node);
- }
- for (let element of node.elements) {
- if (element) {
- this[element.type](element, node, callbacks);
- }
- }
- },
- /**
- * A case (if test is an Expression) or default (if test is null) clause in
- * the body of a switch statement.
- *
- * interface SwitchCase <: Node {
- * type: "SwitchCase";
- * test: Expression | null;
- * consequent: [ Statement ];
- * }
- */
- SwitchCase(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onSwitchCase) {
- callbacks.onSwitchCase(node);
- }
- if (node.test) {
- this[node.test.type](node.test, node, callbacks);
- }
- for (let consequent of node.consequent) {
- this[consequent.type](consequent, node, callbacks);
- }
- },
- /**
- * A catch clause following a try block. The optional guard property
- * corresponds to the optional expression guard on the bound variable.
- *
- * interface CatchClause <: Node {
- * type: "CatchClause";
- * param: Pattern;
- * guard: Expression | null;
- * body: BlockStatement;
- * }
- */
- CatchClause(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onCatchClause) {
- callbacks.onCatchClause(node);
- }
- this[node.param.type](node.param, node, callbacks);
- if (node.guard) {
- this[node.guard.type](node.guard, node, callbacks);
- }
- this[node.body.type](node.body, node, callbacks);
- },
- /**
- * A for or for each block in an array comprehension or generator expression.
- *
- * interface ComprehensionBlock <: Node {
- * left: Pattern;
- * right: Expression;
- * each: boolean;
- * }
- */
- ComprehensionBlock(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onComprehensionBlock) {
- callbacks.onComprehensionBlock(node);
- }
- this[node.left.type](node.left, node, callbacks);
- this[node.right.type](node.right, node, callbacks);
- },
- /**
- * An identifier. Note that an identifier may be an expression or a
- * destructuring pattern.
- *
- * interface Identifier <: Node, Expression, Pattern {
- * type: "Identifier";
- * name: string;
- * }
- */
- Identifier(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onIdentifier) {
- callbacks.onIdentifier(node);
- }
- },
- /**
- * A literal token. Note that a literal can be an expression.
- *
- * interface Literal <: Node, Expression {
- * type: "Literal";
- * value: string | boolean | null | number | RegExp;
- * }
- */
- Literal(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onLiteral) {
- callbacks.onLiteral(node);
- }
- },
- /**
- * A template string literal.
- *
- * interface TemplateLiteral <: Node {
- * type: "TemplateLiteral";
- * elements: [ Expression ];
- * }
- */
- TemplateLiteral(node, parent, callbacks) {
- node._parent = parent;
- if (this.break) {
- return;
- }
- if (callbacks.onNode) {
- if (callbacks.onNode(node, parent) === false) {
- return;
- }
- }
- if (callbacks.onTemplateLiteral) {
- callbacks.onTemplateLiteral(node);
- }
- for (let element of node.elements) {
- if (element) {
- this[element.type](element, node, callbacks);
- }
- }
- }
- };
- XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect);
|