JavascriptParser.js 116 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { Parser: AcornParser } = require("acorn");
  7. const { importAssertions } = require("acorn-import-assertions");
  8. const { SyncBailHook, HookMap } = require("tapable");
  9. const vm = require("vm");
  10. const Parser = require("../Parser");
  11. const StackedMap = require("../util/StackedMap");
  12. const binarySearchBounds = require("../util/binarySearchBounds");
  13. const memoize = require("../util/memoize");
  14. const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
  15. /** @typedef {import("acorn").Options} AcornOptions */
  16. /** @typedef {import("estree").ArrayExpression} ArrayExpressionNode */
  17. /** @typedef {import("estree").BinaryExpression} BinaryExpressionNode */
  18. /** @typedef {import("estree").BlockStatement} BlockStatementNode */
  19. /** @typedef {import("estree").SequenceExpression} SequenceExpressionNode */
  20. /** @typedef {import("estree").CallExpression} CallExpressionNode */
  21. /** @typedef {import("estree").ClassDeclaration} ClassDeclarationNode */
  22. /** @typedef {import("estree").ClassExpression} ClassExpressionNode */
  23. /** @typedef {import("estree").Comment} CommentNode */
  24. /** @typedef {import("estree").ConditionalExpression} ConditionalExpressionNode */
  25. /** @typedef {import("estree").Declaration} DeclarationNode */
  26. /** @typedef {import("estree").PrivateIdentifier} PrivateIdentifierNode */
  27. /** @typedef {import("estree").PropertyDefinition} PropertyDefinitionNode */
  28. /** @typedef {import("estree").Expression} ExpressionNode */
  29. /** @typedef {import("estree").Identifier} IdentifierNode */
  30. /** @typedef {import("estree").IfStatement} IfStatementNode */
  31. /** @typedef {import("estree").LabeledStatement} LabeledStatementNode */
  32. /** @typedef {import("estree").Literal} LiteralNode */
  33. /** @typedef {import("estree").LogicalExpression} LogicalExpressionNode */
  34. /** @typedef {import("estree").ChainExpression} ChainExpressionNode */
  35. /** @typedef {import("estree").MemberExpression} MemberExpressionNode */
  36. /** @typedef {import("estree").MetaProperty} MetaPropertyNode */
  37. /** @typedef {import("estree").MethodDefinition} MethodDefinitionNode */
  38. /** @typedef {import("estree").ModuleDeclaration} ModuleDeclarationNode */
  39. /** @typedef {import("estree").NewExpression} NewExpressionNode */
  40. /** @typedef {import("estree").Node} AnyNode */
  41. /** @typedef {import("estree").Program} ProgramNode */
  42. /** @typedef {import("estree").Statement} StatementNode */
  43. /** @typedef {import("estree").ImportDeclaration} ImportDeclarationNode */
  44. /** @typedef {import("estree").ExportNamedDeclaration} ExportNamedDeclarationNode */
  45. /** @typedef {import("estree").ExportDefaultDeclaration} ExportDefaultDeclarationNode */
  46. /** @typedef {import("estree").ExportAllDeclaration} ExportAllDeclarationNode */
  47. /** @typedef {import("estree").Super} SuperNode */
  48. /** @typedef {import("estree").TaggedTemplateExpression} TaggedTemplateExpressionNode */
  49. /** @typedef {import("estree").TemplateLiteral} TemplateLiteralNode */
  50. /** @typedef {import("estree").ThisExpression} ThisExpressionNode */
  51. /** @typedef {import("estree").UnaryExpression} UnaryExpressionNode */
  52. /** @typedef {import("estree").VariableDeclarator} VariableDeclaratorNode */
  53. /** @template T @typedef {import("tapable").AsArray<T>} AsArray<T> */
  54. /** @typedef {import("../Parser").ParserState} ParserState */
  55. /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
  56. /** @typedef {{declaredScope: ScopeInfo, freeName: string | true, tagInfo: TagInfo | undefined}} VariableInfoInterface */
  57. /** @typedef {{ name: string | VariableInfo, rootInfo: string | VariableInfo, getMembers: () => string[], getMembersOptionals: () => boolean[] }} GetInfoResult */
  58. const EMPTY_ARRAY = [];
  59. const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01;
  60. const ALLOWED_MEMBER_TYPES_EXPRESSION = 0b10;
  61. const ALLOWED_MEMBER_TYPES_ALL = 0b11;
  62. // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
  63. const parser = AcornParser.extend(importAssertions);
  64. class VariableInfo {
  65. /**
  66. * @param {ScopeInfo} declaredScope scope in which the variable is declared
  67. * @param {string | true} freeName which free name the variable aliases, or true when none
  68. * @param {TagInfo | undefined} tagInfo info about tags
  69. */
  70. constructor(declaredScope, freeName, tagInfo) {
  71. this.declaredScope = declaredScope;
  72. this.freeName = freeName;
  73. this.tagInfo = tagInfo;
  74. }
  75. }
  76. /** @typedef {string | ScopeInfo | VariableInfo} ExportedVariableInfo */
  77. /** @typedef {LiteralNode | string | null | undefined} ImportSource */
  78. /** @typedef {Omit<AcornOptions, "sourceType" | "ecmaVersion"> & { sourceType: "module" | "script" | "auto", ecmaVersion?: AcornOptions["ecmaVersion"] }} ParseOptions */
  79. /**
  80. * @typedef {Object} TagInfo
  81. * @property {any} tag
  82. * @property {any} data
  83. * @property {TagInfo | undefined} next
  84. */
  85. /**
  86. * @typedef {Object} ScopeInfo
  87. * @property {StackedMap<string, VariableInfo | ScopeInfo>} definitions
  88. * @property {boolean | "arrow"} topLevelScope
  89. * @property {boolean} inShorthand
  90. * @property {boolean} isStrict
  91. * @property {boolean} isAsmJs
  92. * @property {boolean} inTry
  93. */
  94. const joinRanges = (startRange, endRange) => {
  95. if (!endRange) return startRange;
  96. if (!startRange) return endRange;
  97. return [startRange[0], endRange[1]];
  98. };
  99. const objectAndMembersToName = (object, membersReversed) => {
  100. let name = object;
  101. for (let i = membersReversed.length - 1; i >= 0; i--) {
  102. name = name + "." + membersReversed[i];
  103. }
  104. return name;
  105. };
  106. const getRootName = expression => {
  107. switch (expression.type) {
  108. case "Identifier":
  109. return expression.name;
  110. case "ThisExpression":
  111. return "this";
  112. case "MetaProperty":
  113. return `${expression.meta.name}.${expression.property.name}`;
  114. default:
  115. return undefined;
  116. }
  117. };
  118. /** @type {AcornOptions} */
  119. const defaultParserOptions = {
  120. ranges: true,
  121. locations: true,
  122. ecmaVersion: "latest",
  123. sourceType: "module",
  124. // https://github.com/tc39/proposal-hashbang
  125. allowHashBang: true,
  126. onComment: null
  127. };
  128. // regexp to match at least one "magic comment"
  129. const webpackCommentRegExp = new RegExp(/(^|\W)webpack[A-Z]{1,}[A-Za-z]{1,}:/);
  130. const EMPTY_COMMENT_OPTIONS = {
  131. options: null,
  132. errors: null
  133. };
  134. class JavascriptParser extends Parser {
  135. /**
  136. * @param {"module" | "script" | "auto"} sourceType default source type
  137. */
  138. constructor(sourceType = "auto") {
  139. super();
  140. this.hooks = Object.freeze({
  141. /** @type {HookMap<SyncBailHook<[UnaryExpressionNode], BasicEvaluatedExpression | undefined | null>>} */
  142. evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
  143. /** @type {HookMap<SyncBailHook<[ExpressionNode], BasicEvaluatedExpression | undefined | null>>} */
  144. evaluate: new HookMap(() => new SyncBailHook(["expression"])),
  145. /** @type {HookMap<SyncBailHook<[IdentifierNode | ThisExpressionNode | MemberExpressionNode | MetaPropertyNode], BasicEvaluatedExpression | undefined | null>>} */
  146. evaluateIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
  147. /** @type {HookMap<SyncBailHook<[IdentifierNode | ThisExpressionNode | MemberExpressionNode], BasicEvaluatedExpression | undefined | null>>} */
  148. evaluateDefinedIdentifier: new HookMap(
  149. () => new SyncBailHook(["expression"])
  150. ),
  151. /** @type {HookMap<SyncBailHook<[NewExpressionNode], BasicEvaluatedExpression | undefined | null>>} */
  152. evaluateNewExpression: new HookMap(
  153. () => new SyncBailHook(["expression"])
  154. ),
  155. /** @type {HookMap<SyncBailHook<[CallExpressionNode], BasicEvaluatedExpression | undefined | null>>} */
  156. evaluateCallExpression: new HookMap(
  157. () => new SyncBailHook(["expression"])
  158. ),
  159. /** @type {HookMap<SyncBailHook<[CallExpressionNode, BasicEvaluatedExpression | undefined], BasicEvaluatedExpression | undefined | null>>} */
  160. evaluateCallExpressionMember: new HookMap(
  161. () => new SyncBailHook(["expression", "param"])
  162. ),
  163. /** @type {HookMap<SyncBailHook<[ExpressionNode | DeclarationNode | PrivateIdentifierNode, number], boolean | void>>} */
  164. isPure: new HookMap(
  165. () => new SyncBailHook(["expression", "commentsStartPosition"])
  166. ),
  167. /** @type {SyncBailHook<[StatementNode | ModuleDeclarationNode], boolean | void>} */
  168. preStatement: new SyncBailHook(["statement"]),
  169. /** @type {SyncBailHook<[StatementNode | ModuleDeclarationNode], boolean | void>} */
  170. blockPreStatement: new SyncBailHook(["declaration"]),
  171. /** @type {SyncBailHook<[StatementNode | ModuleDeclarationNode], boolean | void>} */
  172. statement: new SyncBailHook(["statement"]),
  173. /** @type {SyncBailHook<[IfStatementNode], boolean | void>} */
  174. statementIf: new SyncBailHook(["statement"]),
  175. /** @type {SyncBailHook<[ExpressionNode, ClassExpressionNode | ClassDeclarationNode], boolean | void>} */
  176. classExtendsExpression: new SyncBailHook([
  177. "expression",
  178. "classDefinition"
  179. ]),
  180. /** @type {SyncBailHook<[MethodDefinitionNode | PropertyDefinitionNode, ClassExpressionNode | ClassDeclarationNode], boolean | void>} */
  181. classBodyElement: new SyncBailHook(["element", "classDefinition"]),
  182. /** @type {SyncBailHook<[ExpressionNode, MethodDefinitionNode | PropertyDefinitionNode, ClassExpressionNode | ClassDeclarationNode], boolean | void>} */
  183. classBodyValue: new SyncBailHook([
  184. "expression",
  185. "element",
  186. "classDefinition"
  187. ]),
  188. /** @type {HookMap<SyncBailHook<[LabeledStatementNode], boolean | void>>} */
  189. label: new HookMap(() => new SyncBailHook(["statement"])),
  190. /** @type {SyncBailHook<[ImportDeclarationNode, ImportSource], boolean | void>} */
  191. import: new SyncBailHook(["statement", "source"]),
  192. /** @type {SyncBailHook<[ImportDeclarationNode, ImportSource, string, string], boolean | void>} */
  193. importSpecifier: new SyncBailHook([
  194. "statement",
  195. "source",
  196. "exportName",
  197. "identifierName"
  198. ]),
  199. /** @type {SyncBailHook<[ExportNamedDeclarationNode | ExportAllDeclarationNode], boolean | void>} */
  200. export: new SyncBailHook(["statement"]),
  201. /** @type {SyncBailHook<[ExportNamedDeclarationNode | ExportAllDeclarationNode, ImportSource], boolean | void>} */
  202. exportImport: new SyncBailHook(["statement", "source"]),
  203. /** @type {SyncBailHook<[ExportNamedDeclarationNode | ExportAllDeclarationNode, DeclarationNode], boolean | void>} */
  204. exportDeclaration: new SyncBailHook(["statement", "declaration"]),
  205. /** @type {SyncBailHook<[ExportDefaultDeclarationNode, DeclarationNode], boolean | void>} */
  206. exportExpression: new SyncBailHook(["statement", "declaration"]),
  207. /** @type {SyncBailHook<[ExportNamedDeclarationNode | ExportAllDeclarationNode, string, string, number | undefined], boolean | void>} */
  208. exportSpecifier: new SyncBailHook([
  209. "statement",
  210. "identifierName",
  211. "exportName",
  212. "index"
  213. ]),
  214. /** @type {SyncBailHook<[ExportNamedDeclarationNode | ExportAllDeclarationNode, ImportSource, string, string, number | undefined], boolean | void>} */
  215. exportImportSpecifier: new SyncBailHook([
  216. "statement",
  217. "source",
  218. "identifierName",
  219. "exportName",
  220. "index"
  221. ]),
  222. /** @type {SyncBailHook<[VariableDeclaratorNode, StatementNode], boolean | void>} */
  223. preDeclarator: new SyncBailHook(["declarator", "statement"]),
  224. /** @type {SyncBailHook<[VariableDeclaratorNode, StatementNode], boolean | void>} */
  225. declarator: new SyncBailHook(["declarator", "statement"]),
  226. /** @type {HookMap<SyncBailHook<[DeclarationNode], boolean | void>>} */
  227. varDeclaration: new HookMap(() => new SyncBailHook(["declaration"])),
  228. /** @type {HookMap<SyncBailHook<[DeclarationNode], boolean | void>>} */
  229. varDeclarationLet: new HookMap(() => new SyncBailHook(["declaration"])),
  230. /** @type {HookMap<SyncBailHook<[DeclarationNode], boolean | void>>} */
  231. varDeclarationConst: new HookMap(() => new SyncBailHook(["declaration"])),
  232. /** @type {HookMap<SyncBailHook<[DeclarationNode], boolean | void>>} */
  233. varDeclarationVar: new HookMap(() => new SyncBailHook(["declaration"])),
  234. /** @type {HookMap<SyncBailHook<[IdentifierNode], boolean | void>>} */
  235. pattern: new HookMap(() => new SyncBailHook(["pattern"])),
  236. /** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
  237. canRename: new HookMap(() => new SyncBailHook(["initExpression"])),
  238. /** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
  239. rename: new HookMap(() => new SyncBailHook(["initExpression"])),
  240. /** @type {HookMap<SyncBailHook<[import("estree").AssignmentExpression], boolean | void>>} */
  241. assign: new HookMap(() => new SyncBailHook(["expression"])),
  242. /** @type {HookMap<SyncBailHook<[import("estree").AssignmentExpression, string[]], boolean | void>>} */
  243. assignMemberChain: new HookMap(
  244. () => new SyncBailHook(["expression", "members"])
  245. ),
  246. /** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
  247. typeof: new HookMap(() => new SyncBailHook(["expression"])),
  248. /** @type {SyncBailHook<[ExpressionNode], boolean | void>} */
  249. importCall: new SyncBailHook(["expression"]),
  250. /** @type {SyncBailHook<[ExpressionNode], boolean | void>} */
  251. topLevelAwait: new SyncBailHook(["expression"]),
  252. /** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
  253. call: new HookMap(() => new SyncBailHook(["expression"])),
  254. /** Something like "a.b()" */
  255. /** @type {HookMap<SyncBailHook<[CallExpressionNode, string[], boolean[]], boolean | void>>} */
  256. callMemberChain: new HookMap(
  257. () => new SyncBailHook(["expression", "members", "membersOptionals"])
  258. ),
  259. /** Something like "a.b().c.d" */
  260. /** @type {HookMap<SyncBailHook<[ExpressionNode, string[], CallExpressionNode, string[]], boolean | void>>} */
  261. memberChainOfCallMemberChain: new HookMap(
  262. () =>
  263. new SyncBailHook([
  264. "expression",
  265. "calleeMembers",
  266. "callExpression",
  267. "members"
  268. ])
  269. ),
  270. /** Something like "a.b().c.d()"" */
  271. /** @type {HookMap<SyncBailHook<[ExpressionNode, string[], CallExpressionNode, string[]], boolean | void>>} */
  272. callMemberChainOfCallMemberChain: new HookMap(
  273. () =>
  274. new SyncBailHook([
  275. "expression",
  276. "calleeMembers",
  277. "innerCallExpression",
  278. "members"
  279. ])
  280. ),
  281. /** @type {SyncBailHook<[ChainExpressionNode], boolean | void>} */
  282. optionalChaining: new SyncBailHook(["optionalChaining"]),
  283. /** @type {HookMap<SyncBailHook<[NewExpressionNode], boolean | void>>} */
  284. new: new HookMap(() => new SyncBailHook(["expression"])),
  285. /** @type {SyncBailHook<[BinaryExpressionNode], boolean | void>} */
  286. binaryExpression: new SyncBailHook(["binaryExpression"]),
  287. /** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
  288. expression: new HookMap(() => new SyncBailHook(["expression"])),
  289. /** @type {HookMap<SyncBailHook<[ExpressionNode, string[], boolean[]], boolean | void>>} */
  290. expressionMemberChain: new HookMap(
  291. () => new SyncBailHook(["expression", "members", "membersOptionals"])
  292. ),
  293. /** @type {HookMap<SyncBailHook<[ExpressionNode, string[]], boolean | void>>} */
  294. unhandledExpressionMemberChain: new HookMap(
  295. () => new SyncBailHook(["expression", "members"])
  296. ),
  297. /** @type {SyncBailHook<[ExpressionNode], boolean | void>} */
  298. expressionConditionalOperator: new SyncBailHook(["expression"]),
  299. /** @type {SyncBailHook<[ExpressionNode], boolean | void>} */
  300. expressionLogicalOperator: new SyncBailHook(["expression"]),
  301. /** @type {SyncBailHook<[ProgramNode, CommentNode[]], boolean | void>} */
  302. program: new SyncBailHook(["ast", "comments"]),
  303. /** @type {SyncBailHook<[ProgramNode, CommentNode[]], boolean | void>} */
  304. finish: new SyncBailHook(["ast", "comments"])
  305. });
  306. this.sourceType = sourceType;
  307. /** @type {ScopeInfo} */
  308. this.scope = undefined;
  309. /** @type {ParserState} */
  310. this.state = undefined;
  311. this.comments = undefined;
  312. this.semicolons = undefined;
  313. /** @type {(StatementNode|ExpressionNode)[]} */
  314. this.statementPath = undefined;
  315. this.prevStatement = undefined;
  316. /** @type {WeakMap<ExpressionNode, Set<string>>} */
  317. this.destructuringAssignmentProperties = undefined;
  318. this.currentTagData = undefined;
  319. this._initializeEvaluating();
  320. }
  321. _initializeEvaluating() {
  322. this.hooks.evaluate.for("Literal").tap("JavascriptParser", _expr => {
  323. const expr = /** @type {LiteralNode} */ (_expr);
  324. switch (typeof expr.value) {
  325. case "number":
  326. return new BasicEvaluatedExpression()
  327. .setNumber(expr.value)
  328. .setRange(expr.range);
  329. case "bigint":
  330. return new BasicEvaluatedExpression()
  331. .setBigInt(expr.value)
  332. .setRange(expr.range);
  333. case "string":
  334. return new BasicEvaluatedExpression()
  335. .setString(expr.value)
  336. .setRange(expr.range);
  337. case "boolean":
  338. return new BasicEvaluatedExpression()
  339. .setBoolean(expr.value)
  340. .setRange(expr.range);
  341. }
  342. if (expr.value === null) {
  343. return new BasicEvaluatedExpression().setNull().setRange(expr.range);
  344. }
  345. if (expr.value instanceof RegExp) {
  346. return new BasicEvaluatedExpression()
  347. .setRegExp(expr.value)
  348. .setRange(expr.range);
  349. }
  350. });
  351. this.hooks.evaluate.for("NewExpression").tap("JavascriptParser", _expr => {
  352. const expr = /** @type {NewExpressionNode} */ (_expr);
  353. const callee = expr.callee;
  354. if (callee.type !== "Identifier") return;
  355. if (callee.name !== "RegExp") {
  356. return this.callHooksForName(
  357. this.hooks.evaluateNewExpression,
  358. callee.name,
  359. expr
  360. );
  361. } else if (
  362. expr.arguments.length > 2 ||
  363. this.getVariableInfo("RegExp") !== "RegExp"
  364. )
  365. return;
  366. let regExp, flags;
  367. const arg1 = expr.arguments[0];
  368. if (arg1) {
  369. if (arg1.type === "SpreadElement") return;
  370. const evaluatedRegExp = this.evaluateExpression(arg1);
  371. if (!evaluatedRegExp) return;
  372. regExp = evaluatedRegExp.asString();
  373. if (!regExp) return;
  374. } else {
  375. return new BasicEvaluatedExpression()
  376. .setRegExp(new RegExp(""))
  377. .setRange(expr.range);
  378. }
  379. const arg2 = expr.arguments[1];
  380. if (arg2) {
  381. if (arg2.type === "SpreadElement") return;
  382. const evaluatedFlags = this.evaluateExpression(arg2);
  383. if (!evaluatedFlags) return;
  384. if (!evaluatedFlags.isUndefined()) {
  385. flags = evaluatedFlags.asString();
  386. if (
  387. flags === undefined ||
  388. !BasicEvaluatedExpression.isValidRegExpFlags(flags)
  389. )
  390. return;
  391. }
  392. }
  393. return new BasicEvaluatedExpression()
  394. .setRegExp(flags ? new RegExp(regExp, flags) : new RegExp(regExp))
  395. .setRange(expr.range);
  396. });
  397. this.hooks.evaluate
  398. .for("LogicalExpression")
  399. .tap("JavascriptParser", _expr => {
  400. const expr = /** @type {LogicalExpressionNode} */ (_expr);
  401. const left = this.evaluateExpression(expr.left);
  402. let returnRight = false;
  403. /** @type {boolean|undefined} */
  404. let allowedRight;
  405. if (expr.operator === "&&") {
  406. const leftAsBool = left.asBool();
  407. if (leftAsBool === false) return left.setRange(expr.range);
  408. returnRight = leftAsBool === true;
  409. allowedRight = false;
  410. } else if (expr.operator === "||") {
  411. const leftAsBool = left.asBool();
  412. if (leftAsBool === true) return left.setRange(expr.range);
  413. returnRight = leftAsBool === false;
  414. allowedRight = true;
  415. } else if (expr.operator === "??") {
  416. const leftAsNullish = left.asNullish();
  417. if (leftAsNullish === false) return left.setRange(expr.range);
  418. if (leftAsNullish !== true) return;
  419. returnRight = true;
  420. } else return;
  421. const right = this.evaluateExpression(expr.right);
  422. if (returnRight) {
  423. if (left.couldHaveSideEffects()) right.setSideEffects();
  424. return right.setRange(expr.range);
  425. }
  426. const asBool = right.asBool();
  427. if (allowedRight === true && asBool === true) {
  428. return new BasicEvaluatedExpression()
  429. .setRange(expr.range)
  430. .setTruthy();
  431. } else if (allowedRight === false && asBool === false) {
  432. return new BasicEvaluatedExpression().setRange(expr.range).setFalsy();
  433. }
  434. });
  435. const valueAsExpression = (value, expr, sideEffects) => {
  436. switch (typeof value) {
  437. case "boolean":
  438. return new BasicEvaluatedExpression()
  439. .setBoolean(value)
  440. .setSideEffects(sideEffects)
  441. .setRange(expr.range);
  442. case "number":
  443. return new BasicEvaluatedExpression()
  444. .setNumber(value)
  445. .setSideEffects(sideEffects)
  446. .setRange(expr.range);
  447. case "bigint":
  448. return new BasicEvaluatedExpression()
  449. .setBigInt(value)
  450. .setSideEffects(sideEffects)
  451. .setRange(expr.range);
  452. case "string":
  453. return new BasicEvaluatedExpression()
  454. .setString(value)
  455. .setSideEffects(sideEffects)
  456. .setRange(expr.range);
  457. }
  458. };
  459. this.hooks.evaluate
  460. .for("BinaryExpression")
  461. .tap("JavascriptParser", _expr => {
  462. const expr = /** @type {BinaryExpressionNode} */ (_expr);
  463. const handleConstOperation = fn => {
  464. const left = this.evaluateExpression(expr.left);
  465. if (!left.isCompileTimeValue()) return;
  466. const right = this.evaluateExpression(expr.right);
  467. if (!right.isCompileTimeValue()) return;
  468. const result = fn(
  469. left.asCompileTimeValue(),
  470. right.asCompileTimeValue()
  471. );
  472. return valueAsExpression(
  473. result,
  474. expr,
  475. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  476. );
  477. };
  478. const isAlwaysDifferent = (a, b) =>
  479. (a === true && b === false) || (a === false && b === true);
  480. const handleTemplateStringCompare = (left, right, res, eql) => {
  481. const getPrefix = parts => {
  482. let value = "";
  483. for (const p of parts) {
  484. const v = p.asString();
  485. if (v !== undefined) value += v;
  486. else break;
  487. }
  488. return value;
  489. };
  490. const getSuffix = parts => {
  491. let value = "";
  492. for (let i = parts.length - 1; i >= 0; i--) {
  493. const v = parts[i].asString();
  494. if (v !== undefined) value = v + value;
  495. else break;
  496. }
  497. return value;
  498. };
  499. const leftPrefix = getPrefix(left.parts);
  500. const rightPrefix = getPrefix(right.parts);
  501. const leftSuffix = getSuffix(left.parts);
  502. const rightSuffix = getSuffix(right.parts);
  503. const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
  504. const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
  505. if (
  506. leftPrefix.slice(0, lenPrefix) !==
  507. rightPrefix.slice(0, lenPrefix) ||
  508. leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix)
  509. ) {
  510. return res
  511. .setBoolean(!eql)
  512. .setSideEffects(
  513. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  514. );
  515. }
  516. };
  517. const handleStrictEqualityComparison = eql => {
  518. const left = this.evaluateExpression(expr.left);
  519. const right = this.evaluateExpression(expr.right);
  520. const res = new BasicEvaluatedExpression();
  521. res.setRange(expr.range);
  522. const leftConst = left.isCompileTimeValue();
  523. const rightConst = right.isCompileTimeValue();
  524. if (leftConst && rightConst) {
  525. return res
  526. .setBoolean(
  527. eql ===
  528. (left.asCompileTimeValue() === right.asCompileTimeValue())
  529. )
  530. .setSideEffects(
  531. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  532. );
  533. }
  534. if (left.isArray() && right.isArray()) {
  535. return res
  536. .setBoolean(!eql)
  537. .setSideEffects(
  538. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  539. );
  540. }
  541. if (left.isTemplateString() && right.isTemplateString()) {
  542. return handleTemplateStringCompare(left, right, res, eql);
  543. }
  544. const leftPrimitive = left.isPrimitiveType();
  545. const rightPrimitive = right.isPrimitiveType();
  546. if (
  547. // Primitive !== Object or
  548. // compile-time object types are never equal to something at runtime
  549. (leftPrimitive === false &&
  550. (leftConst || rightPrimitive === true)) ||
  551. (rightPrimitive === false &&
  552. (rightConst || leftPrimitive === true)) ||
  553. // Different nullish or boolish status also means not equal
  554. isAlwaysDifferent(left.asBool(), right.asBool()) ||
  555. isAlwaysDifferent(left.asNullish(), right.asNullish())
  556. ) {
  557. return res
  558. .setBoolean(!eql)
  559. .setSideEffects(
  560. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  561. );
  562. }
  563. };
  564. const handleAbstractEqualityComparison = eql => {
  565. const left = this.evaluateExpression(expr.left);
  566. const right = this.evaluateExpression(expr.right);
  567. const res = new BasicEvaluatedExpression();
  568. res.setRange(expr.range);
  569. const leftConst = left.isCompileTimeValue();
  570. const rightConst = right.isCompileTimeValue();
  571. if (leftConst && rightConst) {
  572. return res
  573. .setBoolean(
  574. eql ===
  575. // eslint-disable-next-line eqeqeq
  576. (left.asCompileTimeValue() == right.asCompileTimeValue())
  577. )
  578. .setSideEffects(
  579. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  580. );
  581. }
  582. if (left.isArray() && right.isArray()) {
  583. return res
  584. .setBoolean(!eql)
  585. .setSideEffects(
  586. left.couldHaveSideEffects() || right.couldHaveSideEffects()
  587. );
  588. }
  589. if (left.isTemplateString() && right.isTemplateString()) {
  590. return handleTemplateStringCompare(left, right, res, eql);
  591. }
  592. };
  593. if (expr.operator === "+") {
  594. const left = this.evaluateExpression(expr.left);
  595. const right = this.evaluateExpression(expr.right);
  596. const res = new BasicEvaluatedExpression();
  597. if (left.isString()) {
  598. if (right.isString()) {
  599. res.setString(left.string + right.string);
  600. } else if (right.isNumber()) {
  601. res.setString(left.string + right.number);
  602. } else if (
  603. right.isWrapped() &&
  604. right.prefix &&
  605. right.prefix.isString()
  606. ) {
  607. // "left" + ("prefix" + inner + "postfix")
  608. // => ("leftPrefix" + inner + "postfix")
  609. res.setWrapped(
  610. new BasicEvaluatedExpression()
  611. .setString(left.string + right.prefix.string)
  612. .setRange(joinRanges(left.range, right.prefix.range)),
  613. right.postfix,
  614. right.wrappedInnerExpressions
  615. );
  616. } else if (right.isWrapped()) {
  617. // "left" + ([null] + inner + "postfix")
  618. // => ("left" + inner + "postfix")
  619. res.setWrapped(
  620. left,
  621. right.postfix,
  622. right.wrappedInnerExpressions
  623. );
  624. } else {
  625. // "left" + expr
  626. // => ("left" + expr + "")
  627. res.setWrapped(left, null, [right]);
  628. }
  629. } else if (left.isNumber()) {
  630. if (right.isString()) {
  631. res.setString(left.number + right.string);
  632. } else if (right.isNumber()) {
  633. res.setNumber(left.number + right.number);
  634. } else {
  635. return;
  636. }
  637. } else if (left.isBigInt()) {
  638. if (right.isBigInt()) {
  639. res.setBigInt(left.bigint + right.bigint);
  640. }
  641. } else if (left.isWrapped()) {
  642. if (left.postfix && left.postfix.isString() && right.isString()) {
  643. // ("prefix" + inner + "postfix") + "right"
  644. // => ("prefix" + inner + "postfixRight")
  645. res.setWrapped(
  646. left.prefix,
  647. new BasicEvaluatedExpression()
  648. .setString(left.postfix.string + right.string)
  649. .setRange(joinRanges(left.postfix.range, right.range)),
  650. left.wrappedInnerExpressions
  651. );
  652. } else if (
  653. left.postfix &&
  654. left.postfix.isString() &&
  655. right.isNumber()
  656. ) {
  657. // ("prefix" + inner + "postfix") + 123
  658. // => ("prefix" + inner + "postfix123")
  659. res.setWrapped(
  660. left.prefix,
  661. new BasicEvaluatedExpression()
  662. .setString(left.postfix.string + right.number)
  663. .setRange(joinRanges(left.postfix.range, right.range)),
  664. left.wrappedInnerExpressions
  665. );
  666. } else if (right.isString()) {
  667. // ("prefix" + inner + [null]) + "right"
  668. // => ("prefix" + inner + "right")
  669. res.setWrapped(left.prefix, right, left.wrappedInnerExpressions);
  670. } else if (right.isNumber()) {
  671. // ("prefix" + inner + [null]) + 123
  672. // => ("prefix" + inner + "123")
  673. res.setWrapped(
  674. left.prefix,
  675. new BasicEvaluatedExpression()
  676. .setString(right.number + "")
  677. .setRange(right.range),
  678. left.wrappedInnerExpressions
  679. );
  680. } else if (right.isWrapped()) {
  681. // ("prefix1" + inner1 + "postfix1") + ("prefix2" + inner2 + "postfix2")
  682. // ("prefix1" + inner1 + "postfix1" + "prefix2" + inner2 + "postfix2")
  683. res.setWrapped(
  684. left.prefix,
  685. right.postfix,
  686. left.wrappedInnerExpressions &&
  687. right.wrappedInnerExpressions &&
  688. left.wrappedInnerExpressions
  689. .concat(left.postfix ? [left.postfix] : [])
  690. .concat(right.prefix ? [right.prefix] : [])
  691. .concat(right.wrappedInnerExpressions)
  692. );
  693. } else {
  694. // ("prefix" + inner + postfix) + expr
  695. // => ("prefix" + inner + postfix + expr + [null])
  696. res.setWrapped(
  697. left.prefix,
  698. null,
  699. left.wrappedInnerExpressions &&
  700. left.wrappedInnerExpressions.concat(
  701. left.postfix ? [left.postfix, right] : [right]
  702. )
  703. );
  704. }
  705. } else {
  706. if (right.isString()) {
  707. // left + "right"
  708. // => ([null] + left + "right")
  709. res.setWrapped(null, right, [left]);
  710. } else if (right.isWrapped()) {
  711. // left + (prefix + inner + "postfix")
  712. // => ([null] + left + prefix + inner + "postfix")
  713. res.setWrapped(
  714. null,
  715. right.postfix,
  716. right.wrappedInnerExpressions &&
  717. (right.prefix ? [left, right.prefix] : [left]).concat(
  718. right.wrappedInnerExpressions
  719. )
  720. );
  721. } else {
  722. return;
  723. }
  724. }
  725. if (left.couldHaveSideEffects() || right.couldHaveSideEffects())
  726. res.setSideEffects();
  727. res.setRange(expr.range);
  728. return res;
  729. } else if (expr.operator === "-") {
  730. return handleConstOperation((l, r) => l - r);
  731. } else if (expr.operator === "*") {
  732. return handleConstOperation((l, r) => l * r);
  733. } else if (expr.operator === "/") {
  734. return handleConstOperation((l, r) => l / r);
  735. } else if (expr.operator === "**") {
  736. return handleConstOperation((l, r) => l ** r);
  737. } else if (expr.operator === "===") {
  738. return handleStrictEqualityComparison(true);
  739. } else if (expr.operator === "==") {
  740. return handleAbstractEqualityComparison(true);
  741. } else if (expr.operator === "!==") {
  742. return handleStrictEqualityComparison(false);
  743. } else if (expr.operator === "!=") {
  744. return handleAbstractEqualityComparison(false);
  745. } else if (expr.operator === "&") {
  746. return handleConstOperation((l, r) => l & r);
  747. } else if (expr.operator === "|") {
  748. return handleConstOperation((l, r) => l | r);
  749. } else if (expr.operator === "^") {
  750. return handleConstOperation((l, r) => l ^ r);
  751. } else if (expr.operator === ">>>") {
  752. return handleConstOperation((l, r) => l >>> r);
  753. } else if (expr.operator === ">>") {
  754. return handleConstOperation((l, r) => l >> r);
  755. } else if (expr.operator === "<<") {
  756. return handleConstOperation((l, r) => l << r);
  757. } else if (expr.operator === "<") {
  758. return handleConstOperation((l, r) => l < r);
  759. } else if (expr.operator === ">") {
  760. return handleConstOperation((l, r) => l > r);
  761. } else if (expr.operator === "<=") {
  762. return handleConstOperation((l, r) => l <= r);
  763. } else if (expr.operator === ">=") {
  764. return handleConstOperation((l, r) => l >= r);
  765. }
  766. });
  767. this.hooks.evaluate
  768. .for("UnaryExpression")
  769. .tap("JavascriptParser", _expr => {
  770. const expr = /** @type {UnaryExpressionNode} */ (_expr);
  771. const handleConstOperation = fn => {
  772. const argument = this.evaluateExpression(expr.argument);
  773. if (!argument.isCompileTimeValue()) return;
  774. const result = fn(argument.asCompileTimeValue());
  775. return valueAsExpression(
  776. result,
  777. expr,
  778. argument.couldHaveSideEffects()
  779. );
  780. };
  781. if (expr.operator === "typeof") {
  782. switch (expr.argument.type) {
  783. case "Identifier": {
  784. const res = this.callHooksForName(
  785. this.hooks.evaluateTypeof,
  786. expr.argument.name,
  787. expr
  788. );
  789. if (res !== undefined) return res;
  790. break;
  791. }
  792. case "MetaProperty": {
  793. const res = this.callHooksForName(
  794. this.hooks.evaluateTypeof,
  795. getRootName(expr.argument),
  796. expr
  797. );
  798. if (res !== undefined) return res;
  799. break;
  800. }
  801. case "MemberExpression": {
  802. const res = this.callHooksForExpression(
  803. this.hooks.evaluateTypeof,
  804. expr.argument,
  805. expr
  806. );
  807. if (res !== undefined) return res;
  808. break;
  809. }
  810. case "ChainExpression": {
  811. const res = this.callHooksForExpression(
  812. this.hooks.evaluateTypeof,
  813. expr.argument.expression,
  814. expr
  815. );
  816. if (res !== undefined) return res;
  817. break;
  818. }
  819. case "FunctionExpression": {
  820. return new BasicEvaluatedExpression()
  821. .setString("function")
  822. .setRange(expr.range);
  823. }
  824. }
  825. const arg = this.evaluateExpression(expr.argument);
  826. if (arg.isUnknown()) return;
  827. if (arg.isString()) {
  828. return new BasicEvaluatedExpression()
  829. .setString("string")
  830. .setRange(expr.range);
  831. }
  832. if (arg.isWrapped()) {
  833. return new BasicEvaluatedExpression()
  834. .setString("string")
  835. .setSideEffects()
  836. .setRange(expr.range);
  837. }
  838. if (arg.isUndefined()) {
  839. return new BasicEvaluatedExpression()
  840. .setString("undefined")
  841. .setRange(expr.range);
  842. }
  843. if (arg.isNumber()) {
  844. return new BasicEvaluatedExpression()
  845. .setString("number")
  846. .setRange(expr.range);
  847. }
  848. if (arg.isBigInt()) {
  849. return new BasicEvaluatedExpression()
  850. .setString("bigint")
  851. .setRange(expr.range);
  852. }
  853. if (arg.isBoolean()) {
  854. return new BasicEvaluatedExpression()
  855. .setString("boolean")
  856. .setRange(expr.range);
  857. }
  858. if (arg.isConstArray() || arg.isRegExp() || arg.isNull()) {
  859. return new BasicEvaluatedExpression()
  860. .setString("object")
  861. .setRange(expr.range);
  862. }
  863. if (arg.isArray()) {
  864. return new BasicEvaluatedExpression()
  865. .setString("object")
  866. .setSideEffects(arg.couldHaveSideEffects())
  867. .setRange(expr.range);
  868. }
  869. } else if (expr.operator === "!") {
  870. const argument = this.evaluateExpression(expr.argument);
  871. const bool = argument.asBool();
  872. if (typeof bool !== "boolean") return;
  873. return new BasicEvaluatedExpression()
  874. .setBoolean(!bool)
  875. .setSideEffects(argument.couldHaveSideEffects())
  876. .setRange(expr.range);
  877. } else if (expr.operator === "~") {
  878. return handleConstOperation(v => ~v);
  879. } else if (expr.operator === "+") {
  880. return handleConstOperation(v => +v);
  881. } else if (expr.operator === "-") {
  882. return handleConstOperation(v => -v);
  883. }
  884. });
  885. this.hooks.evaluateTypeof.for("undefined").tap("JavascriptParser", expr => {
  886. return new BasicEvaluatedExpression()
  887. .setString("undefined")
  888. .setRange(expr.range);
  889. });
  890. this.hooks.evaluate.for("Identifier").tap("JavascriptParser", expr => {
  891. if (/** @type {IdentifierNode} */ (expr).name === "undefined") {
  892. return new BasicEvaluatedExpression()
  893. .setUndefined()
  894. .setRange(expr.range);
  895. }
  896. });
  897. /**
  898. * @param {string} exprType expression type name
  899. * @param {function(ExpressionNode): GetInfoResult | undefined} getInfo get info
  900. * @returns {void}
  901. */
  902. const tapEvaluateWithVariableInfo = (exprType, getInfo) => {
  903. /** @type {ExpressionNode | undefined} */
  904. let cachedExpression = undefined;
  905. /** @type {GetInfoResult | undefined} */
  906. let cachedInfo = undefined;
  907. this.hooks.evaluate.for(exprType).tap("JavascriptParser", expr => {
  908. const expression = /** @type {MemberExpressionNode} */ (expr);
  909. const info = getInfo(expr);
  910. if (info !== undefined) {
  911. return this.callHooksForInfoWithFallback(
  912. this.hooks.evaluateIdentifier,
  913. info.name,
  914. name => {
  915. cachedExpression = expression;
  916. cachedInfo = info;
  917. },
  918. name => {
  919. const hook = this.hooks.evaluateDefinedIdentifier.get(name);
  920. if (hook !== undefined) {
  921. return hook.call(expression);
  922. }
  923. },
  924. expression
  925. );
  926. }
  927. });
  928. this.hooks.evaluate
  929. .for(exprType)
  930. .tap({ name: "JavascriptParser", stage: 100 }, expr => {
  931. const info = cachedExpression === expr ? cachedInfo : getInfo(expr);
  932. if (info !== undefined) {
  933. return new BasicEvaluatedExpression()
  934. .setIdentifier(
  935. info.name,
  936. info.rootInfo,
  937. info.getMembers,
  938. info.getMembersOptionals
  939. )
  940. .setRange(expr.range);
  941. }
  942. });
  943. this.hooks.finish.tap("JavascriptParser", () => {
  944. // Cleanup for GC
  945. cachedExpression = cachedInfo = undefined;
  946. });
  947. };
  948. tapEvaluateWithVariableInfo("Identifier", expr => {
  949. const info = this.getVariableInfo(
  950. /** @type {IdentifierNode} */ (expr).name
  951. );
  952. if (
  953. typeof info === "string" ||
  954. (info instanceof VariableInfo && typeof info.freeName === "string")
  955. ) {
  956. return {
  957. name: info,
  958. rootInfo: info,
  959. getMembers: () => [],
  960. getMembersOptionals: () => []
  961. };
  962. }
  963. });
  964. tapEvaluateWithVariableInfo("ThisExpression", expr => {
  965. const info = this.getVariableInfo("this");
  966. if (
  967. typeof info === "string" ||
  968. (info instanceof VariableInfo && typeof info.freeName === "string")
  969. ) {
  970. return {
  971. name: info,
  972. rootInfo: info,
  973. getMembers: () => [],
  974. getMembersOptionals: () => []
  975. };
  976. }
  977. });
  978. this.hooks.evaluate.for("MetaProperty").tap("JavascriptParser", expr => {
  979. const metaProperty = /** @type {MetaPropertyNode} */ (expr);
  980. return this.callHooksForName(
  981. this.hooks.evaluateIdentifier,
  982. getRootName(expr),
  983. metaProperty
  984. );
  985. });
  986. tapEvaluateWithVariableInfo("MemberExpression", expr =>
  987. this.getMemberExpressionInfo(
  988. /** @type {MemberExpressionNode} */ (expr),
  989. ALLOWED_MEMBER_TYPES_EXPRESSION
  990. )
  991. );
  992. this.hooks.evaluate.for("CallExpression").tap("JavascriptParser", _expr => {
  993. const expr = /** @type {CallExpressionNode} */ (_expr);
  994. if (
  995. expr.callee.type === "MemberExpression" &&
  996. expr.callee.property.type ===
  997. (expr.callee.computed ? "Literal" : "Identifier")
  998. ) {
  999. // type Super also possible here
  1000. const param = this.evaluateExpression(
  1001. /** @type {ExpressionNode} */ (expr.callee.object)
  1002. );
  1003. const property =
  1004. expr.callee.property.type === "Literal"
  1005. ? `${expr.callee.property.value}`
  1006. : expr.callee.property.name;
  1007. const hook = this.hooks.evaluateCallExpressionMember.get(property);
  1008. if (hook !== undefined) {
  1009. return hook.call(expr, param);
  1010. }
  1011. } else if (expr.callee.type === "Identifier") {
  1012. return this.callHooksForName(
  1013. this.hooks.evaluateCallExpression,
  1014. expr.callee.name,
  1015. expr
  1016. );
  1017. }
  1018. });
  1019. this.hooks.evaluateCallExpressionMember
  1020. .for("indexOf")
  1021. .tap("JavascriptParser", (expr, param) => {
  1022. if (!param.isString()) return;
  1023. if (expr.arguments.length === 0) return;
  1024. const [arg1, arg2] = expr.arguments;
  1025. if (arg1.type === "SpreadElement") return;
  1026. const arg1Eval = this.evaluateExpression(arg1);
  1027. if (!arg1Eval.isString()) return;
  1028. const arg1Value = arg1Eval.string;
  1029. let result;
  1030. if (arg2) {
  1031. if (arg2.type === "SpreadElement") return;
  1032. const arg2Eval = this.evaluateExpression(arg2);
  1033. if (!arg2Eval.isNumber()) return;
  1034. result = param.string.indexOf(arg1Value, arg2Eval.number);
  1035. } else {
  1036. result = param.string.indexOf(arg1Value);
  1037. }
  1038. return new BasicEvaluatedExpression()
  1039. .setNumber(result)
  1040. .setSideEffects(param.couldHaveSideEffects())
  1041. .setRange(expr.range);
  1042. });
  1043. this.hooks.evaluateCallExpressionMember
  1044. .for("replace")
  1045. .tap("JavascriptParser", (expr, param) => {
  1046. if (!param.isString()) return;
  1047. if (expr.arguments.length !== 2) return;
  1048. if (expr.arguments[0].type === "SpreadElement") return;
  1049. if (expr.arguments[1].type === "SpreadElement") return;
  1050. let arg1 = this.evaluateExpression(expr.arguments[0]);
  1051. let arg2 = this.evaluateExpression(expr.arguments[1]);
  1052. if (!arg1.isString() && !arg1.isRegExp()) return;
  1053. const arg1Value = arg1.regExp || arg1.string;
  1054. if (!arg2.isString()) return;
  1055. const arg2Value = arg2.string;
  1056. return new BasicEvaluatedExpression()
  1057. .setString(param.string.replace(arg1Value, arg2Value))
  1058. .setSideEffects(param.couldHaveSideEffects())
  1059. .setRange(expr.range);
  1060. });
  1061. ["substr", "substring", "slice"].forEach(fn => {
  1062. this.hooks.evaluateCallExpressionMember
  1063. .for(fn)
  1064. .tap("JavascriptParser", (expr, param) => {
  1065. if (!param.isString()) return;
  1066. let arg1;
  1067. let result,
  1068. str = param.string;
  1069. switch (expr.arguments.length) {
  1070. case 1:
  1071. if (expr.arguments[0].type === "SpreadElement") return;
  1072. arg1 = this.evaluateExpression(expr.arguments[0]);
  1073. if (!arg1.isNumber()) return;
  1074. result = str[fn](arg1.number);
  1075. break;
  1076. case 2: {
  1077. if (expr.arguments[0].type === "SpreadElement") return;
  1078. if (expr.arguments[1].type === "SpreadElement") return;
  1079. arg1 = this.evaluateExpression(expr.arguments[0]);
  1080. const arg2 = this.evaluateExpression(expr.arguments[1]);
  1081. if (!arg1.isNumber()) return;
  1082. if (!arg2.isNumber()) return;
  1083. result = str[fn](arg1.number, arg2.number);
  1084. break;
  1085. }
  1086. default:
  1087. return;
  1088. }
  1089. return new BasicEvaluatedExpression()
  1090. .setString(result)
  1091. .setSideEffects(param.couldHaveSideEffects())
  1092. .setRange(expr.range);
  1093. });
  1094. });
  1095. /**
  1096. * @param {"cooked" | "raw"} kind kind of values to get
  1097. * @param {TemplateLiteralNode} templateLiteralExpr TemplateLiteral expr
  1098. * @returns {{quasis: BasicEvaluatedExpression[], parts: BasicEvaluatedExpression[]}} Simplified template
  1099. */
  1100. const getSimplifiedTemplateResult = (kind, templateLiteralExpr) => {
  1101. /** @type {BasicEvaluatedExpression[]} */
  1102. const quasis = [];
  1103. /** @type {BasicEvaluatedExpression[]} */
  1104. const parts = [];
  1105. for (let i = 0; i < templateLiteralExpr.quasis.length; i++) {
  1106. const quasiExpr = templateLiteralExpr.quasis[i];
  1107. const quasi = quasiExpr.value[kind];
  1108. if (i > 0) {
  1109. const prevExpr = parts[parts.length - 1];
  1110. const expr = this.evaluateExpression(
  1111. templateLiteralExpr.expressions[i - 1]
  1112. );
  1113. const exprAsString = expr.asString();
  1114. if (
  1115. typeof exprAsString === "string" &&
  1116. !expr.couldHaveSideEffects()
  1117. ) {
  1118. // We can merge quasi + expr + quasi when expr
  1119. // is a const string
  1120. prevExpr.setString(prevExpr.string + exprAsString + quasi);
  1121. prevExpr.setRange([prevExpr.range[0], quasiExpr.range[1]]);
  1122. // We unset the expression as it doesn't match to a single expression
  1123. prevExpr.setExpression(undefined);
  1124. continue;
  1125. }
  1126. parts.push(expr);
  1127. }
  1128. const part = new BasicEvaluatedExpression()
  1129. .setString(quasi)
  1130. .setRange(quasiExpr.range)
  1131. .setExpression(quasiExpr);
  1132. quasis.push(part);
  1133. parts.push(part);
  1134. }
  1135. return {
  1136. quasis,
  1137. parts
  1138. };
  1139. };
  1140. this.hooks.evaluate
  1141. .for("TemplateLiteral")
  1142. .tap("JavascriptParser", _node => {
  1143. const node = /** @type {TemplateLiteralNode} */ (_node);
  1144. const { quasis, parts } = getSimplifiedTemplateResult("cooked", node);
  1145. if (parts.length === 1) {
  1146. return parts[0].setRange(node.range);
  1147. }
  1148. return new BasicEvaluatedExpression()
  1149. .setTemplateString(quasis, parts, "cooked")
  1150. .setRange(node.range);
  1151. });
  1152. this.hooks.evaluate
  1153. .for("TaggedTemplateExpression")
  1154. .tap("JavascriptParser", _node => {
  1155. const node = /** @type {TaggedTemplateExpressionNode} */ (_node);
  1156. const tag = this.evaluateExpression(node.tag);
  1157. if (tag.isIdentifier() && tag.identifier === "String.raw") {
  1158. const { quasis, parts } = getSimplifiedTemplateResult(
  1159. "raw",
  1160. node.quasi
  1161. );
  1162. return new BasicEvaluatedExpression()
  1163. .setTemplateString(quasis, parts, "raw")
  1164. .setRange(node.range);
  1165. }
  1166. });
  1167. this.hooks.evaluateCallExpressionMember
  1168. .for("concat")
  1169. .tap("JavascriptParser", (expr, param) => {
  1170. if (!param.isString() && !param.isWrapped()) return;
  1171. let stringSuffix = null;
  1172. let hasUnknownParams = false;
  1173. const innerExpressions = [];
  1174. for (let i = expr.arguments.length - 1; i >= 0; i--) {
  1175. const arg = expr.arguments[i];
  1176. if (arg.type === "SpreadElement") return;
  1177. const argExpr = this.evaluateExpression(arg);
  1178. if (
  1179. hasUnknownParams ||
  1180. (!argExpr.isString() && !argExpr.isNumber())
  1181. ) {
  1182. hasUnknownParams = true;
  1183. innerExpressions.push(argExpr);
  1184. continue;
  1185. }
  1186. const value = argExpr.isString()
  1187. ? argExpr.string
  1188. : "" + argExpr.number;
  1189. const newString = value + (stringSuffix ? stringSuffix.string : "");
  1190. const newRange = [
  1191. argExpr.range[0],
  1192. (stringSuffix || argExpr).range[1]
  1193. ];
  1194. stringSuffix = new BasicEvaluatedExpression()
  1195. .setString(newString)
  1196. .setSideEffects(
  1197. (stringSuffix && stringSuffix.couldHaveSideEffects()) ||
  1198. argExpr.couldHaveSideEffects()
  1199. )
  1200. .setRange(newRange);
  1201. }
  1202. if (hasUnknownParams) {
  1203. const prefix = param.isString() ? param : param.prefix;
  1204. const inner =
  1205. param.isWrapped() && param.wrappedInnerExpressions
  1206. ? param.wrappedInnerExpressions.concat(innerExpressions.reverse())
  1207. : innerExpressions.reverse();
  1208. return new BasicEvaluatedExpression()
  1209. .setWrapped(prefix, stringSuffix, inner)
  1210. .setRange(expr.range);
  1211. } else if (param.isWrapped()) {
  1212. const postfix = stringSuffix || param.postfix;
  1213. const inner = param.wrappedInnerExpressions
  1214. ? param.wrappedInnerExpressions.concat(innerExpressions.reverse())
  1215. : innerExpressions.reverse();
  1216. return new BasicEvaluatedExpression()
  1217. .setWrapped(param.prefix, postfix, inner)
  1218. .setRange(expr.range);
  1219. } else {
  1220. const newString =
  1221. param.string + (stringSuffix ? stringSuffix.string : "");
  1222. return new BasicEvaluatedExpression()
  1223. .setString(newString)
  1224. .setSideEffects(
  1225. (stringSuffix && stringSuffix.couldHaveSideEffects()) ||
  1226. param.couldHaveSideEffects()
  1227. )
  1228. .setRange(expr.range);
  1229. }
  1230. });
  1231. this.hooks.evaluateCallExpressionMember
  1232. .for("split")
  1233. .tap("JavascriptParser", (expr, param) => {
  1234. if (!param.isString()) return;
  1235. if (expr.arguments.length !== 1) return;
  1236. if (expr.arguments[0].type === "SpreadElement") return;
  1237. let result;
  1238. const arg = this.evaluateExpression(expr.arguments[0]);
  1239. if (arg.isString()) {
  1240. result = param.string.split(arg.string);
  1241. } else if (arg.isRegExp()) {
  1242. result = param.string.split(arg.regExp);
  1243. } else {
  1244. return;
  1245. }
  1246. return new BasicEvaluatedExpression()
  1247. .setArray(result)
  1248. .setSideEffects(param.couldHaveSideEffects())
  1249. .setRange(expr.range);
  1250. });
  1251. this.hooks.evaluate
  1252. .for("ConditionalExpression")
  1253. .tap("JavascriptParser", _expr => {
  1254. const expr = /** @type {ConditionalExpressionNode} */ (_expr);
  1255. const condition = this.evaluateExpression(expr.test);
  1256. const conditionValue = condition.asBool();
  1257. let res;
  1258. if (conditionValue === undefined) {
  1259. const consequent = this.evaluateExpression(expr.consequent);
  1260. const alternate = this.evaluateExpression(expr.alternate);
  1261. res = new BasicEvaluatedExpression();
  1262. if (consequent.isConditional()) {
  1263. res.setOptions(consequent.options);
  1264. } else {
  1265. res.setOptions([consequent]);
  1266. }
  1267. if (alternate.isConditional()) {
  1268. res.addOptions(alternate.options);
  1269. } else {
  1270. res.addOptions([alternate]);
  1271. }
  1272. } else {
  1273. res = this.evaluateExpression(
  1274. conditionValue ? expr.consequent : expr.alternate
  1275. );
  1276. if (condition.couldHaveSideEffects()) res.setSideEffects();
  1277. }
  1278. res.setRange(expr.range);
  1279. return res;
  1280. });
  1281. this.hooks.evaluate
  1282. .for("ArrayExpression")
  1283. .tap("JavascriptParser", _expr => {
  1284. const expr = /** @type {ArrayExpressionNode} */ (_expr);
  1285. const items = expr.elements.map(element => {
  1286. return (
  1287. element !== null &&
  1288. element.type !== "SpreadElement" &&
  1289. this.evaluateExpression(element)
  1290. );
  1291. });
  1292. if (!items.every(Boolean)) return;
  1293. return new BasicEvaluatedExpression()
  1294. .setItems(items)
  1295. .setRange(expr.range);
  1296. });
  1297. this.hooks.evaluate
  1298. .for("ChainExpression")
  1299. .tap("JavascriptParser", _expr => {
  1300. const expr = /** @type {ChainExpressionNode} */ (_expr);
  1301. /** @type {ExpressionNode[]} */
  1302. const optionalExpressionsStack = [];
  1303. /** @type {ExpressionNode|SuperNode} */
  1304. let next = expr.expression;
  1305. while (
  1306. next.type === "MemberExpression" ||
  1307. next.type === "CallExpression"
  1308. ) {
  1309. if (next.type === "MemberExpression") {
  1310. if (next.optional) {
  1311. // SuperNode can not be optional
  1312. optionalExpressionsStack.push(
  1313. /** @type {ExpressionNode} */ (next.object)
  1314. );
  1315. }
  1316. next = next.object;
  1317. } else {
  1318. if (next.optional) {
  1319. // SuperNode can not be optional
  1320. optionalExpressionsStack.push(
  1321. /** @type {ExpressionNode} */ (next.callee)
  1322. );
  1323. }
  1324. next = next.callee;
  1325. }
  1326. }
  1327. while (optionalExpressionsStack.length > 0) {
  1328. const expression = optionalExpressionsStack.pop();
  1329. const evaluated = this.evaluateExpression(expression);
  1330. if (evaluated.asNullish()) {
  1331. return evaluated.setRange(_expr.range);
  1332. }
  1333. }
  1334. return this.evaluateExpression(expr.expression);
  1335. });
  1336. }
  1337. /**
  1338. * @param {ExpressionNode} node node
  1339. * @returns {Set<string>|undefined} destructured identifiers
  1340. */
  1341. destructuringAssignmentPropertiesFor(node) {
  1342. if (!this.destructuringAssignmentProperties) return undefined;
  1343. return this.destructuringAssignmentProperties.get(node);
  1344. }
  1345. getRenameIdentifier(expr) {
  1346. const result = this.evaluateExpression(expr);
  1347. if (result.isIdentifier()) {
  1348. return result.identifier;
  1349. }
  1350. }
  1351. /**
  1352. * @param {ClassExpressionNode | ClassDeclarationNode} classy a class node
  1353. * @returns {void}
  1354. */
  1355. walkClass(classy) {
  1356. if (classy.superClass) {
  1357. if (!this.hooks.classExtendsExpression.call(classy.superClass, classy)) {
  1358. this.walkExpression(classy.superClass);
  1359. }
  1360. }
  1361. if (classy.body && classy.body.type === "ClassBody") {
  1362. for (const classElement of /** @type {TODO} */ (classy.body.body)) {
  1363. if (!this.hooks.classBodyElement.call(classElement, classy)) {
  1364. if (classElement.computed && classElement.key) {
  1365. this.walkExpression(classElement.key);
  1366. }
  1367. if (classElement.value) {
  1368. if (
  1369. !this.hooks.classBodyValue.call(
  1370. classElement.value,
  1371. classElement,
  1372. classy
  1373. )
  1374. ) {
  1375. const wasTopLevel = this.scope.topLevelScope;
  1376. this.scope.topLevelScope = false;
  1377. this.walkExpression(classElement.value);
  1378. this.scope.topLevelScope = wasTopLevel;
  1379. }
  1380. } else if (classElement.type === "StaticBlock") {
  1381. const wasTopLevel = this.scope.topLevelScope;
  1382. this.scope.topLevelScope = false;
  1383. this.walkBlockStatement(classElement);
  1384. this.scope.topLevelScope = wasTopLevel;
  1385. }
  1386. }
  1387. }
  1388. }
  1389. }
  1390. // Pre walking iterates the scope for variable declarations
  1391. preWalkStatements(statements) {
  1392. for (let index = 0, len = statements.length; index < len; index++) {
  1393. const statement = statements[index];
  1394. this.preWalkStatement(statement);
  1395. }
  1396. }
  1397. // Block pre walking iterates the scope for block variable declarations
  1398. blockPreWalkStatements(statements) {
  1399. for (let index = 0, len = statements.length; index < len; index++) {
  1400. const statement = statements[index];
  1401. this.blockPreWalkStatement(statement);
  1402. }
  1403. }
  1404. // Walking iterates the statements and expressions and processes them
  1405. walkStatements(statements) {
  1406. for (let index = 0, len = statements.length; index < len; index++) {
  1407. const statement = statements[index];
  1408. this.walkStatement(statement);
  1409. }
  1410. }
  1411. preWalkStatement(statement) {
  1412. this.statementPath.push(statement);
  1413. if (this.hooks.preStatement.call(statement)) {
  1414. this.prevStatement = this.statementPath.pop();
  1415. return;
  1416. }
  1417. switch (statement.type) {
  1418. case "BlockStatement":
  1419. this.preWalkBlockStatement(statement);
  1420. break;
  1421. case "DoWhileStatement":
  1422. this.preWalkDoWhileStatement(statement);
  1423. break;
  1424. case "ForInStatement":
  1425. this.preWalkForInStatement(statement);
  1426. break;
  1427. case "ForOfStatement":
  1428. this.preWalkForOfStatement(statement);
  1429. break;
  1430. case "ForStatement":
  1431. this.preWalkForStatement(statement);
  1432. break;
  1433. case "FunctionDeclaration":
  1434. this.preWalkFunctionDeclaration(statement);
  1435. break;
  1436. case "IfStatement":
  1437. this.preWalkIfStatement(statement);
  1438. break;
  1439. case "LabeledStatement":
  1440. this.preWalkLabeledStatement(statement);
  1441. break;
  1442. case "SwitchStatement":
  1443. this.preWalkSwitchStatement(statement);
  1444. break;
  1445. case "TryStatement":
  1446. this.preWalkTryStatement(statement);
  1447. break;
  1448. case "VariableDeclaration":
  1449. this.preWalkVariableDeclaration(statement);
  1450. break;
  1451. case "WhileStatement":
  1452. this.preWalkWhileStatement(statement);
  1453. break;
  1454. case "WithStatement":
  1455. this.preWalkWithStatement(statement);
  1456. break;
  1457. }
  1458. this.prevStatement = this.statementPath.pop();
  1459. }
  1460. blockPreWalkStatement(statement) {
  1461. this.statementPath.push(statement);
  1462. if (this.hooks.blockPreStatement.call(statement)) {
  1463. this.prevStatement = this.statementPath.pop();
  1464. return;
  1465. }
  1466. switch (statement.type) {
  1467. case "ImportDeclaration":
  1468. this.blockPreWalkImportDeclaration(statement);
  1469. break;
  1470. case "ExportAllDeclaration":
  1471. this.blockPreWalkExportAllDeclaration(statement);
  1472. break;
  1473. case "ExportDefaultDeclaration":
  1474. this.blockPreWalkExportDefaultDeclaration(statement);
  1475. break;
  1476. case "ExportNamedDeclaration":
  1477. this.blockPreWalkExportNamedDeclaration(statement);
  1478. break;
  1479. case "VariableDeclaration":
  1480. this.blockPreWalkVariableDeclaration(statement);
  1481. break;
  1482. case "ClassDeclaration":
  1483. this.blockPreWalkClassDeclaration(statement);
  1484. break;
  1485. case "ExpressionStatement":
  1486. this.blockPreWalkExpressionStatement(statement);
  1487. }
  1488. this.prevStatement = this.statementPath.pop();
  1489. }
  1490. walkStatement(statement) {
  1491. this.statementPath.push(statement);
  1492. if (this.hooks.statement.call(statement) !== undefined) {
  1493. this.prevStatement = this.statementPath.pop();
  1494. return;
  1495. }
  1496. switch (statement.type) {
  1497. case "BlockStatement":
  1498. this.walkBlockStatement(statement);
  1499. break;
  1500. case "ClassDeclaration":
  1501. this.walkClassDeclaration(statement);
  1502. break;
  1503. case "DoWhileStatement":
  1504. this.walkDoWhileStatement(statement);
  1505. break;
  1506. case "ExportDefaultDeclaration":
  1507. this.walkExportDefaultDeclaration(statement);
  1508. break;
  1509. case "ExportNamedDeclaration":
  1510. this.walkExportNamedDeclaration(statement);
  1511. break;
  1512. case "ExpressionStatement":
  1513. this.walkExpressionStatement(statement);
  1514. break;
  1515. case "ForInStatement":
  1516. this.walkForInStatement(statement);
  1517. break;
  1518. case "ForOfStatement":
  1519. this.walkForOfStatement(statement);
  1520. break;
  1521. case "ForStatement":
  1522. this.walkForStatement(statement);
  1523. break;
  1524. case "FunctionDeclaration":
  1525. this.walkFunctionDeclaration(statement);
  1526. break;
  1527. case "IfStatement":
  1528. this.walkIfStatement(statement);
  1529. break;
  1530. case "LabeledStatement":
  1531. this.walkLabeledStatement(statement);
  1532. break;
  1533. case "ReturnStatement":
  1534. this.walkReturnStatement(statement);
  1535. break;
  1536. case "SwitchStatement":
  1537. this.walkSwitchStatement(statement);
  1538. break;
  1539. case "ThrowStatement":
  1540. this.walkThrowStatement(statement);
  1541. break;
  1542. case "TryStatement":
  1543. this.walkTryStatement(statement);
  1544. break;
  1545. case "VariableDeclaration":
  1546. this.walkVariableDeclaration(statement);
  1547. break;
  1548. case "WhileStatement":
  1549. this.walkWhileStatement(statement);
  1550. break;
  1551. case "WithStatement":
  1552. this.walkWithStatement(statement);
  1553. break;
  1554. }
  1555. this.prevStatement = this.statementPath.pop();
  1556. }
  1557. /**
  1558. * Walks a statements that is nested within a parent statement
  1559. * and can potentially be a non-block statement.
  1560. * This enforces the nested statement to never be in ASI position.
  1561. * @param {StatementNode} statement the nested statement
  1562. * @returns {void}
  1563. */
  1564. walkNestedStatement(statement) {
  1565. this.prevStatement = undefined;
  1566. this.walkStatement(statement);
  1567. }
  1568. // Real Statements
  1569. preWalkBlockStatement(statement) {
  1570. this.preWalkStatements(statement.body);
  1571. }
  1572. walkBlockStatement(statement) {
  1573. this.inBlockScope(() => {
  1574. const body = statement.body;
  1575. const prev = this.prevStatement;
  1576. this.blockPreWalkStatements(body);
  1577. this.prevStatement = prev;
  1578. this.walkStatements(body);
  1579. });
  1580. }
  1581. walkExpressionStatement(statement) {
  1582. this.walkExpression(statement.expression);
  1583. }
  1584. preWalkIfStatement(statement) {
  1585. this.preWalkStatement(statement.consequent);
  1586. if (statement.alternate) {
  1587. this.preWalkStatement(statement.alternate);
  1588. }
  1589. }
  1590. walkIfStatement(statement) {
  1591. const result = this.hooks.statementIf.call(statement);
  1592. if (result === undefined) {
  1593. this.walkExpression(statement.test);
  1594. this.walkNestedStatement(statement.consequent);
  1595. if (statement.alternate) {
  1596. this.walkNestedStatement(statement.alternate);
  1597. }
  1598. } else {
  1599. if (result) {
  1600. this.walkNestedStatement(statement.consequent);
  1601. } else if (statement.alternate) {
  1602. this.walkNestedStatement(statement.alternate);
  1603. }
  1604. }
  1605. }
  1606. preWalkLabeledStatement(statement) {
  1607. this.preWalkStatement(statement.body);
  1608. }
  1609. walkLabeledStatement(statement) {
  1610. const hook = this.hooks.label.get(statement.label.name);
  1611. if (hook !== undefined) {
  1612. const result = hook.call(statement);
  1613. if (result === true) return;
  1614. }
  1615. this.walkNestedStatement(statement.body);
  1616. }
  1617. preWalkWithStatement(statement) {
  1618. this.preWalkStatement(statement.body);
  1619. }
  1620. walkWithStatement(statement) {
  1621. this.walkExpression(statement.object);
  1622. this.walkNestedStatement(statement.body);
  1623. }
  1624. preWalkSwitchStatement(statement) {
  1625. this.preWalkSwitchCases(statement.cases);
  1626. }
  1627. walkSwitchStatement(statement) {
  1628. this.walkExpression(statement.discriminant);
  1629. this.walkSwitchCases(statement.cases);
  1630. }
  1631. walkTerminatingStatement(statement) {
  1632. if (statement.argument) this.walkExpression(statement.argument);
  1633. }
  1634. walkReturnStatement(statement) {
  1635. this.walkTerminatingStatement(statement);
  1636. }
  1637. walkThrowStatement(statement) {
  1638. this.walkTerminatingStatement(statement);
  1639. }
  1640. preWalkTryStatement(statement) {
  1641. this.preWalkStatement(statement.block);
  1642. if (statement.handler) this.preWalkCatchClause(statement.handler);
  1643. if (statement.finalizer) this.preWalkStatement(statement.finalizer);
  1644. }
  1645. walkTryStatement(statement) {
  1646. if (this.scope.inTry) {
  1647. this.walkStatement(statement.block);
  1648. } else {
  1649. this.scope.inTry = true;
  1650. this.walkStatement(statement.block);
  1651. this.scope.inTry = false;
  1652. }
  1653. if (statement.handler) this.walkCatchClause(statement.handler);
  1654. if (statement.finalizer) this.walkStatement(statement.finalizer);
  1655. }
  1656. preWalkWhileStatement(statement) {
  1657. this.preWalkStatement(statement.body);
  1658. }
  1659. walkWhileStatement(statement) {
  1660. this.walkExpression(statement.test);
  1661. this.walkNestedStatement(statement.body);
  1662. }
  1663. preWalkDoWhileStatement(statement) {
  1664. this.preWalkStatement(statement.body);
  1665. }
  1666. walkDoWhileStatement(statement) {
  1667. this.walkNestedStatement(statement.body);
  1668. this.walkExpression(statement.test);
  1669. }
  1670. preWalkForStatement(statement) {
  1671. if (statement.init) {
  1672. if (statement.init.type === "VariableDeclaration") {
  1673. this.preWalkStatement(statement.init);
  1674. }
  1675. }
  1676. this.preWalkStatement(statement.body);
  1677. }
  1678. walkForStatement(statement) {
  1679. this.inBlockScope(() => {
  1680. if (statement.init) {
  1681. if (statement.init.type === "VariableDeclaration") {
  1682. this.blockPreWalkVariableDeclaration(statement.init);
  1683. this.prevStatement = undefined;
  1684. this.walkStatement(statement.init);
  1685. } else {
  1686. this.walkExpression(statement.init);
  1687. }
  1688. }
  1689. if (statement.test) {
  1690. this.walkExpression(statement.test);
  1691. }
  1692. if (statement.update) {
  1693. this.walkExpression(statement.update);
  1694. }
  1695. const body = statement.body;
  1696. if (body.type === "BlockStatement") {
  1697. // no need to add additional scope
  1698. const prev = this.prevStatement;
  1699. this.blockPreWalkStatements(body.body);
  1700. this.prevStatement = prev;
  1701. this.walkStatements(body.body);
  1702. } else {
  1703. this.walkNestedStatement(body);
  1704. }
  1705. });
  1706. }
  1707. preWalkForInStatement(statement) {
  1708. if (statement.left.type === "VariableDeclaration") {
  1709. this.preWalkVariableDeclaration(statement.left);
  1710. }
  1711. this.preWalkStatement(statement.body);
  1712. }
  1713. walkForInStatement(statement) {
  1714. this.inBlockScope(() => {
  1715. if (statement.left.type === "VariableDeclaration") {
  1716. this.blockPreWalkVariableDeclaration(statement.left);
  1717. this.walkVariableDeclaration(statement.left);
  1718. } else {
  1719. this.walkPattern(statement.left);
  1720. }
  1721. this.walkExpression(statement.right);
  1722. const body = statement.body;
  1723. if (body.type === "BlockStatement") {
  1724. // no need to add additional scope
  1725. const prev = this.prevStatement;
  1726. this.blockPreWalkStatements(body.body);
  1727. this.prevStatement = prev;
  1728. this.walkStatements(body.body);
  1729. } else {
  1730. this.walkNestedStatement(body);
  1731. }
  1732. });
  1733. }
  1734. preWalkForOfStatement(statement) {
  1735. if (statement.await && this.scope.topLevelScope === true) {
  1736. this.hooks.topLevelAwait.call(statement);
  1737. }
  1738. if (statement.left.type === "VariableDeclaration") {
  1739. this.preWalkVariableDeclaration(statement.left);
  1740. }
  1741. this.preWalkStatement(statement.body);
  1742. }
  1743. walkForOfStatement(statement) {
  1744. this.inBlockScope(() => {
  1745. if (statement.left.type === "VariableDeclaration") {
  1746. this.blockPreWalkVariableDeclaration(statement.left);
  1747. this.walkVariableDeclaration(statement.left);
  1748. } else {
  1749. this.walkPattern(statement.left);
  1750. }
  1751. this.walkExpression(statement.right);
  1752. const body = statement.body;
  1753. if (body.type === "BlockStatement") {
  1754. // no need to add additional scope
  1755. const prev = this.prevStatement;
  1756. this.blockPreWalkStatements(body.body);
  1757. this.prevStatement = prev;
  1758. this.walkStatements(body.body);
  1759. } else {
  1760. this.walkNestedStatement(body);
  1761. }
  1762. });
  1763. }
  1764. // Declarations
  1765. preWalkFunctionDeclaration(statement) {
  1766. if (statement.id) {
  1767. this.defineVariable(statement.id.name);
  1768. }
  1769. }
  1770. walkFunctionDeclaration(statement) {
  1771. const wasTopLevel = this.scope.topLevelScope;
  1772. this.scope.topLevelScope = false;
  1773. this.inFunctionScope(true, statement.params, () => {
  1774. for (const param of statement.params) {
  1775. this.walkPattern(param);
  1776. }
  1777. if (statement.body.type === "BlockStatement") {
  1778. this.detectMode(statement.body.body);
  1779. const prev = this.prevStatement;
  1780. this.preWalkStatement(statement.body);
  1781. this.prevStatement = prev;
  1782. this.walkStatement(statement.body);
  1783. } else {
  1784. this.walkExpression(statement.body);
  1785. }
  1786. });
  1787. this.scope.topLevelScope = wasTopLevel;
  1788. }
  1789. blockPreWalkExpressionStatement(statement) {
  1790. const expression = statement.expression;
  1791. switch (expression.type) {
  1792. case "AssignmentExpression":
  1793. this.preWalkAssignmentExpression(expression);
  1794. }
  1795. }
  1796. preWalkAssignmentExpression(expression) {
  1797. if (
  1798. expression.left.type !== "ObjectPattern" ||
  1799. !this.destructuringAssignmentProperties
  1800. )
  1801. return;
  1802. const keys = this._preWalkObjectPattern(expression.left);
  1803. if (!keys) return;
  1804. // check multiple assignments
  1805. if (this.destructuringAssignmentProperties.has(expression)) {
  1806. const set = this.destructuringAssignmentProperties.get(expression);
  1807. this.destructuringAssignmentProperties.delete(expression);
  1808. for (const id of set) keys.add(id);
  1809. }
  1810. this.destructuringAssignmentProperties.set(
  1811. expression.right.type === "AwaitExpression"
  1812. ? expression.right.argument
  1813. : expression.right,
  1814. keys
  1815. );
  1816. if (expression.right.type === "AssignmentExpression") {
  1817. this.preWalkAssignmentExpression(expression.right);
  1818. }
  1819. }
  1820. blockPreWalkImportDeclaration(statement) {
  1821. const source = statement.source.value;
  1822. this.hooks.import.call(statement, source);
  1823. for (const specifier of statement.specifiers) {
  1824. const name = specifier.local.name;
  1825. switch (specifier.type) {
  1826. case "ImportDefaultSpecifier":
  1827. if (
  1828. !this.hooks.importSpecifier.call(statement, source, "default", name)
  1829. ) {
  1830. this.defineVariable(name);
  1831. }
  1832. break;
  1833. case "ImportSpecifier":
  1834. if (
  1835. !this.hooks.importSpecifier.call(
  1836. statement,
  1837. source,
  1838. specifier.imported.name || specifier.imported.value,
  1839. name
  1840. )
  1841. ) {
  1842. this.defineVariable(name);
  1843. }
  1844. break;
  1845. case "ImportNamespaceSpecifier":
  1846. if (!this.hooks.importSpecifier.call(statement, source, null, name)) {
  1847. this.defineVariable(name);
  1848. }
  1849. break;
  1850. default:
  1851. this.defineVariable(name);
  1852. }
  1853. }
  1854. }
  1855. enterDeclaration(declaration, onIdent) {
  1856. switch (declaration.type) {
  1857. case "VariableDeclaration":
  1858. for (const declarator of declaration.declarations) {
  1859. switch (declarator.type) {
  1860. case "VariableDeclarator": {
  1861. this.enterPattern(declarator.id, onIdent);
  1862. break;
  1863. }
  1864. }
  1865. }
  1866. break;
  1867. case "FunctionDeclaration":
  1868. this.enterPattern(declaration.id, onIdent);
  1869. break;
  1870. case "ClassDeclaration":
  1871. this.enterPattern(declaration.id, onIdent);
  1872. break;
  1873. }
  1874. }
  1875. blockPreWalkExportNamedDeclaration(statement) {
  1876. let source;
  1877. if (statement.source) {
  1878. source = statement.source.value;
  1879. this.hooks.exportImport.call(statement, source);
  1880. } else {
  1881. this.hooks.export.call(statement);
  1882. }
  1883. if (statement.declaration) {
  1884. if (
  1885. !this.hooks.exportDeclaration.call(statement, statement.declaration)
  1886. ) {
  1887. const prev = this.prevStatement;
  1888. this.preWalkStatement(statement.declaration);
  1889. this.prevStatement = prev;
  1890. this.blockPreWalkStatement(statement.declaration);
  1891. let index = 0;
  1892. this.enterDeclaration(statement.declaration, def => {
  1893. this.hooks.exportSpecifier.call(statement, def, def, index++);
  1894. });
  1895. }
  1896. }
  1897. if (statement.specifiers) {
  1898. for (
  1899. let specifierIndex = 0;
  1900. specifierIndex < statement.specifiers.length;
  1901. specifierIndex++
  1902. ) {
  1903. const specifier = statement.specifiers[specifierIndex];
  1904. switch (specifier.type) {
  1905. case "ExportSpecifier": {
  1906. const name = specifier.exported.name || specifier.exported.value;
  1907. if (source) {
  1908. this.hooks.exportImportSpecifier.call(
  1909. statement,
  1910. source,
  1911. specifier.local.name,
  1912. name,
  1913. specifierIndex
  1914. );
  1915. } else {
  1916. this.hooks.exportSpecifier.call(
  1917. statement,
  1918. specifier.local.name,
  1919. name,
  1920. specifierIndex
  1921. );
  1922. }
  1923. break;
  1924. }
  1925. }
  1926. }
  1927. }
  1928. }
  1929. walkExportNamedDeclaration(statement) {
  1930. if (statement.declaration) {
  1931. this.walkStatement(statement.declaration);
  1932. }
  1933. }
  1934. blockPreWalkExportDefaultDeclaration(statement) {
  1935. const prev = this.prevStatement;
  1936. this.preWalkStatement(statement.declaration);
  1937. this.prevStatement = prev;
  1938. this.blockPreWalkStatement(statement.declaration);
  1939. if (
  1940. statement.declaration.id &&
  1941. statement.declaration.type !== "FunctionExpression" &&
  1942. statement.declaration.type !== "ClassExpression"
  1943. ) {
  1944. this.hooks.exportSpecifier.call(
  1945. statement,
  1946. statement.declaration.id.name,
  1947. "default",
  1948. undefined
  1949. );
  1950. }
  1951. }
  1952. walkExportDefaultDeclaration(statement) {
  1953. this.hooks.export.call(statement);
  1954. if (
  1955. statement.declaration.id &&
  1956. statement.declaration.type !== "FunctionExpression" &&
  1957. statement.declaration.type !== "ClassExpression"
  1958. ) {
  1959. if (
  1960. !this.hooks.exportDeclaration.call(statement, statement.declaration)
  1961. ) {
  1962. this.walkStatement(statement.declaration);
  1963. }
  1964. } else {
  1965. // Acorn parses `export default function() {}` as `FunctionDeclaration` and
  1966. // `export default class {}` as `ClassDeclaration`, both with `id = null`.
  1967. // These nodes must be treated as expressions.
  1968. if (
  1969. statement.declaration.type === "FunctionDeclaration" ||
  1970. statement.declaration.type === "ClassDeclaration"
  1971. ) {
  1972. this.walkStatement(statement.declaration);
  1973. } else {
  1974. this.walkExpression(statement.declaration);
  1975. }
  1976. if (!this.hooks.exportExpression.call(statement, statement.declaration)) {
  1977. this.hooks.exportSpecifier.call(
  1978. statement,
  1979. statement.declaration,
  1980. "default",
  1981. undefined
  1982. );
  1983. }
  1984. }
  1985. }
  1986. blockPreWalkExportAllDeclaration(statement) {
  1987. const source = statement.source.value;
  1988. const name = statement.exported ? statement.exported.name : null;
  1989. this.hooks.exportImport.call(statement, source);
  1990. this.hooks.exportImportSpecifier.call(statement, source, null, name, 0);
  1991. }
  1992. preWalkVariableDeclaration(statement) {
  1993. if (statement.kind !== "var") return;
  1994. this._preWalkVariableDeclaration(statement, this.hooks.varDeclarationVar);
  1995. }
  1996. blockPreWalkVariableDeclaration(statement) {
  1997. if (statement.kind === "var") return;
  1998. const hookMap =
  1999. statement.kind === "const"
  2000. ? this.hooks.varDeclarationConst
  2001. : this.hooks.varDeclarationLet;
  2002. this._preWalkVariableDeclaration(statement, hookMap);
  2003. }
  2004. _preWalkVariableDeclaration(statement, hookMap) {
  2005. for (const declarator of statement.declarations) {
  2006. switch (declarator.type) {
  2007. case "VariableDeclarator": {
  2008. this.preWalkVariableDeclarator(declarator);
  2009. if (!this.hooks.preDeclarator.call(declarator, statement)) {
  2010. this.enterPattern(declarator.id, (name, decl) => {
  2011. let hook = hookMap.get(name);
  2012. if (hook === undefined || !hook.call(decl)) {
  2013. hook = this.hooks.varDeclaration.get(name);
  2014. if (hook === undefined || !hook.call(decl)) {
  2015. this.defineVariable(name);
  2016. }
  2017. }
  2018. });
  2019. }
  2020. break;
  2021. }
  2022. }
  2023. }
  2024. }
  2025. _preWalkObjectPattern(objectPattern) {
  2026. const ids = new Set();
  2027. const properties = objectPattern.properties;
  2028. for (let i = 0; i < properties.length; i++) {
  2029. const property = properties[i];
  2030. if (property.type !== "Property") return;
  2031. const key = property.key;
  2032. if (key.type === "Identifier") {
  2033. ids.add(key.name);
  2034. } else {
  2035. const id = this.evaluateExpression(key);
  2036. const str = id.asString();
  2037. if (str) {
  2038. ids.add(str);
  2039. } else {
  2040. // could not evaluate key
  2041. return;
  2042. }
  2043. }
  2044. }
  2045. return ids;
  2046. }
  2047. preWalkVariableDeclarator(declarator) {
  2048. if (
  2049. !declarator.init ||
  2050. declarator.id.type !== "ObjectPattern" ||
  2051. !this.destructuringAssignmentProperties
  2052. )
  2053. return;
  2054. const keys = this._preWalkObjectPattern(declarator.id);
  2055. if (!keys) return;
  2056. this.destructuringAssignmentProperties.set(
  2057. declarator.init.type === "AwaitExpression"
  2058. ? declarator.init.argument
  2059. : declarator.init,
  2060. keys
  2061. );
  2062. if (declarator.init.type === "AssignmentExpression") {
  2063. this.preWalkAssignmentExpression(declarator.init);
  2064. }
  2065. }
  2066. walkVariableDeclaration(statement) {
  2067. for (const declarator of statement.declarations) {
  2068. switch (declarator.type) {
  2069. case "VariableDeclarator": {
  2070. const renameIdentifier =
  2071. declarator.init && this.getRenameIdentifier(declarator.init);
  2072. if (renameIdentifier && declarator.id.type === "Identifier") {
  2073. const hook = this.hooks.canRename.get(renameIdentifier);
  2074. if (hook !== undefined && hook.call(declarator.init)) {
  2075. // renaming with "var a = b;"
  2076. const hook = this.hooks.rename.get(renameIdentifier);
  2077. if (hook === undefined || !hook.call(declarator.init)) {
  2078. this.setVariable(declarator.id.name, renameIdentifier);
  2079. }
  2080. break;
  2081. }
  2082. }
  2083. if (!this.hooks.declarator.call(declarator, statement)) {
  2084. this.walkPattern(declarator.id);
  2085. if (declarator.init) this.walkExpression(declarator.init);
  2086. }
  2087. break;
  2088. }
  2089. }
  2090. }
  2091. }
  2092. blockPreWalkClassDeclaration(statement) {
  2093. if (statement.id) {
  2094. this.defineVariable(statement.id.name);
  2095. }
  2096. }
  2097. walkClassDeclaration(statement) {
  2098. this.walkClass(statement);
  2099. }
  2100. preWalkSwitchCases(switchCases) {
  2101. for (let index = 0, len = switchCases.length; index < len; index++) {
  2102. const switchCase = switchCases[index];
  2103. this.preWalkStatements(switchCase.consequent);
  2104. }
  2105. }
  2106. walkSwitchCases(switchCases) {
  2107. this.inBlockScope(() => {
  2108. const len = switchCases.length;
  2109. // we need to pre walk all statements first since we can have invalid code
  2110. // import A from "module";
  2111. // switch(1) {
  2112. // case 1:
  2113. // console.log(A); // should fail at runtime
  2114. // case 2:
  2115. // const A = 1;
  2116. // }
  2117. for (let index = 0; index < len; index++) {
  2118. const switchCase = switchCases[index];
  2119. if (switchCase.consequent.length > 0) {
  2120. const prev = this.prevStatement;
  2121. this.blockPreWalkStatements(switchCase.consequent);
  2122. this.prevStatement = prev;
  2123. }
  2124. }
  2125. for (let index = 0; index < len; index++) {
  2126. const switchCase = switchCases[index];
  2127. if (switchCase.test) {
  2128. this.walkExpression(switchCase.test);
  2129. }
  2130. if (switchCase.consequent.length > 0) {
  2131. this.walkStatements(switchCase.consequent);
  2132. }
  2133. }
  2134. });
  2135. }
  2136. preWalkCatchClause(catchClause) {
  2137. this.preWalkStatement(catchClause.body);
  2138. }
  2139. walkCatchClause(catchClause) {
  2140. this.inBlockScope(() => {
  2141. // Error binding is optional in catch clause since ECMAScript 2019
  2142. if (catchClause.param !== null) {
  2143. this.enterPattern(catchClause.param, ident => {
  2144. this.defineVariable(ident);
  2145. });
  2146. this.walkPattern(catchClause.param);
  2147. }
  2148. const prev = this.prevStatement;
  2149. this.blockPreWalkStatement(catchClause.body);
  2150. this.prevStatement = prev;
  2151. this.walkStatement(catchClause.body);
  2152. });
  2153. }
  2154. walkPattern(pattern) {
  2155. switch (pattern.type) {
  2156. case "ArrayPattern":
  2157. this.walkArrayPattern(pattern);
  2158. break;
  2159. case "AssignmentPattern":
  2160. this.walkAssignmentPattern(pattern);
  2161. break;
  2162. case "MemberExpression":
  2163. this.walkMemberExpression(pattern);
  2164. break;
  2165. case "ObjectPattern":
  2166. this.walkObjectPattern(pattern);
  2167. break;
  2168. case "RestElement":
  2169. this.walkRestElement(pattern);
  2170. break;
  2171. }
  2172. }
  2173. walkAssignmentPattern(pattern) {
  2174. this.walkExpression(pattern.right);
  2175. this.walkPattern(pattern.left);
  2176. }
  2177. walkObjectPattern(pattern) {
  2178. for (let i = 0, len = pattern.properties.length; i < len; i++) {
  2179. const prop = pattern.properties[i];
  2180. if (prop) {
  2181. if (prop.computed) this.walkExpression(prop.key);
  2182. if (prop.value) this.walkPattern(prop.value);
  2183. }
  2184. }
  2185. }
  2186. walkArrayPattern(pattern) {
  2187. for (let i = 0, len = pattern.elements.length; i < len; i++) {
  2188. const element = pattern.elements[i];
  2189. if (element) this.walkPattern(element);
  2190. }
  2191. }
  2192. walkRestElement(pattern) {
  2193. this.walkPattern(pattern.argument);
  2194. }
  2195. walkExpressions(expressions) {
  2196. for (const expression of expressions) {
  2197. if (expression) {
  2198. this.walkExpression(expression);
  2199. }
  2200. }
  2201. }
  2202. walkExpression(expression) {
  2203. switch (expression.type) {
  2204. case "ArrayExpression":
  2205. this.walkArrayExpression(expression);
  2206. break;
  2207. case "ArrowFunctionExpression":
  2208. this.walkArrowFunctionExpression(expression);
  2209. break;
  2210. case "AssignmentExpression":
  2211. this.walkAssignmentExpression(expression);
  2212. break;
  2213. case "AwaitExpression":
  2214. this.walkAwaitExpression(expression);
  2215. break;
  2216. case "BinaryExpression":
  2217. this.walkBinaryExpression(expression);
  2218. break;
  2219. case "CallExpression":
  2220. this.walkCallExpression(expression);
  2221. break;
  2222. case "ChainExpression":
  2223. this.walkChainExpression(expression);
  2224. break;
  2225. case "ClassExpression":
  2226. this.walkClassExpression(expression);
  2227. break;
  2228. case "ConditionalExpression":
  2229. this.walkConditionalExpression(expression);
  2230. break;
  2231. case "FunctionExpression":
  2232. this.walkFunctionExpression(expression);
  2233. break;
  2234. case "Identifier":
  2235. this.walkIdentifier(expression);
  2236. break;
  2237. case "ImportExpression":
  2238. this.walkImportExpression(expression);
  2239. break;
  2240. case "LogicalExpression":
  2241. this.walkLogicalExpression(expression);
  2242. break;
  2243. case "MetaProperty":
  2244. this.walkMetaProperty(expression);
  2245. break;
  2246. case "MemberExpression":
  2247. this.walkMemberExpression(expression);
  2248. break;
  2249. case "NewExpression":
  2250. this.walkNewExpression(expression);
  2251. break;
  2252. case "ObjectExpression":
  2253. this.walkObjectExpression(expression);
  2254. break;
  2255. case "SequenceExpression":
  2256. this.walkSequenceExpression(expression);
  2257. break;
  2258. case "SpreadElement":
  2259. this.walkSpreadElement(expression);
  2260. break;
  2261. case "TaggedTemplateExpression":
  2262. this.walkTaggedTemplateExpression(expression);
  2263. break;
  2264. case "TemplateLiteral":
  2265. this.walkTemplateLiteral(expression);
  2266. break;
  2267. case "ThisExpression":
  2268. this.walkThisExpression(expression);
  2269. break;
  2270. case "UnaryExpression":
  2271. this.walkUnaryExpression(expression);
  2272. break;
  2273. case "UpdateExpression":
  2274. this.walkUpdateExpression(expression);
  2275. break;
  2276. case "YieldExpression":
  2277. this.walkYieldExpression(expression);
  2278. break;
  2279. }
  2280. }
  2281. walkAwaitExpression(expression) {
  2282. if (this.scope.topLevelScope === true)
  2283. this.hooks.topLevelAwait.call(expression);
  2284. this.walkExpression(expression.argument);
  2285. }
  2286. walkArrayExpression(expression) {
  2287. if (expression.elements) {
  2288. this.walkExpressions(expression.elements);
  2289. }
  2290. }
  2291. walkSpreadElement(expression) {
  2292. if (expression.argument) {
  2293. this.walkExpression(expression.argument);
  2294. }
  2295. }
  2296. walkObjectExpression(expression) {
  2297. for (
  2298. let propIndex = 0, len = expression.properties.length;
  2299. propIndex < len;
  2300. propIndex++
  2301. ) {
  2302. const prop = expression.properties[propIndex];
  2303. this.walkProperty(prop);
  2304. }
  2305. }
  2306. walkProperty(prop) {
  2307. if (prop.type === "SpreadElement") {
  2308. this.walkExpression(prop.argument);
  2309. return;
  2310. }
  2311. if (prop.computed) {
  2312. this.walkExpression(prop.key);
  2313. }
  2314. if (prop.shorthand && prop.value && prop.value.type === "Identifier") {
  2315. this.scope.inShorthand = prop.value.name;
  2316. this.walkIdentifier(prop.value);
  2317. this.scope.inShorthand = false;
  2318. } else {
  2319. this.walkExpression(prop.value);
  2320. }
  2321. }
  2322. walkFunctionExpression(expression) {
  2323. const wasTopLevel = this.scope.topLevelScope;
  2324. this.scope.topLevelScope = false;
  2325. const scopeParams = expression.params;
  2326. // Add function name in scope for recursive calls
  2327. if (expression.id) {
  2328. scopeParams.push(expression.id.name);
  2329. }
  2330. this.inFunctionScope(true, scopeParams, () => {
  2331. for (const param of expression.params) {
  2332. this.walkPattern(param);
  2333. }
  2334. if (expression.body.type === "BlockStatement") {
  2335. this.detectMode(expression.body.body);
  2336. const prev = this.prevStatement;
  2337. this.preWalkStatement(expression.body);
  2338. this.prevStatement = prev;
  2339. this.walkStatement(expression.body);
  2340. } else {
  2341. this.walkExpression(expression.body);
  2342. }
  2343. });
  2344. this.scope.topLevelScope = wasTopLevel;
  2345. }
  2346. walkArrowFunctionExpression(expression) {
  2347. const wasTopLevel = this.scope.topLevelScope;
  2348. this.scope.topLevelScope = wasTopLevel ? "arrow" : false;
  2349. this.inFunctionScope(false, expression.params, () => {
  2350. for (const param of expression.params) {
  2351. this.walkPattern(param);
  2352. }
  2353. if (expression.body.type === "BlockStatement") {
  2354. this.detectMode(expression.body.body);
  2355. const prev = this.prevStatement;
  2356. this.preWalkStatement(expression.body);
  2357. this.prevStatement = prev;
  2358. this.walkStatement(expression.body);
  2359. } else {
  2360. this.walkExpression(expression.body);
  2361. }
  2362. });
  2363. this.scope.topLevelScope = wasTopLevel;
  2364. }
  2365. /**
  2366. * @param {SequenceExpressionNode} expression the sequence
  2367. */
  2368. walkSequenceExpression(expression) {
  2369. if (!expression.expressions) return;
  2370. // We treat sequence expressions like statements when they are one statement level
  2371. // This has some benefits for optimizations that only work on statement level
  2372. const currentStatement = this.statementPath[this.statementPath.length - 1];
  2373. if (
  2374. currentStatement === expression ||
  2375. (currentStatement.type === "ExpressionStatement" &&
  2376. currentStatement.expression === expression)
  2377. ) {
  2378. const old = this.statementPath.pop();
  2379. for (const expr of expression.expressions) {
  2380. this.statementPath.push(expr);
  2381. this.walkExpression(expr);
  2382. this.statementPath.pop();
  2383. }
  2384. this.statementPath.push(old);
  2385. } else {
  2386. this.walkExpressions(expression.expressions);
  2387. }
  2388. }
  2389. walkUpdateExpression(expression) {
  2390. this.walkExpression(expression.argument);
  2391. }
  2392. walkUnaryExpression(expression) {
  2393. if (expression.operator === "typeof") {
  2394. const result = this.callHooksForExpression(
  2395. this.hooks.typeof,
  2396. expression.argument,
  2397. expression
  2398. );
  2399. if (result === true) return;
  2400. if (expression.argument.type === "ChainExpression") {
  2401. const result = this.callHooksForExpression(
  2402. this.hooks.typeof,
  2403. expression.argument.expression,
  2404. expression
  2405. );
  2406. if (result === true) return;
  2407. }
  2408. }
  2409. this.walkExpression(expression.argument);
  2410. }
  2411. walkLeftRightExpression(expression) {
  2412. this.walkExpression(expression.left);
  2413. this.walkExpression(expression.right);
  2414. }
  2415. walkBinaryExpression(expression) {
  2416. if (this.hooks.binaryExpression.call(expression) === undefined) {
  2417. this.walkLeftRightExpression(expression);
  2418. }
  2419. }
  2420. walkLogicalExpression(expression) {
  2421. const result = this.hooks.expressionLogicalOperator.call(expression);
  2422. if (result === undefined) {
  2423. this.walkLeftRightExpression(expression);
  2424. } else {
  2425. if (result) {
  2426. this.walkExpression(expression.right);
  2427. }
  2428. }
  2429. }
  2430. walkAssignmentExpression(expression) {
  2431. if (expression.left.type === "Identifier") {
  2432. const renameIdentifier = this.getRenameIdentifier(expression.right);
  2433. if (renameIdentifier) {
  2434. if (
  2435. this.callHooksForInfo(
  2436. this.hooks.canRename,
  2437. renameIdentifier,
  2438. expression.right
  2439. )
  2440. ) {
  2441. // renaming "a = b;"
  2442. if (
  2443. !this.callHooksForInfo(
  2444. this.hooks.rename,
  2445. renameIdentifier,
  2446. expression.right
  2447. )
  2448. ) {
  2449. this.setVariable(
  2450. expression.left.name,
  2451. typeof renameIdentifier === "string"
  2452. ? this.getVariableInfo(renameIdentifier)
  2453. : renameIdentifier
  2454. );
  2455. }
  2456. return;
  2457. }
  2458. }
  2459. this.walkExpression(expression.right);
  2460. this.enterPattern(expression.left, (name, decl) => {
  2461. if (!this.callHooksForName(this.hooks.assign, name, expression)) {
  2462. this.walkExpression(expression.left);
  2463. }
  2464. });
  2465. return;
  2466. }
  2467. if (expression.left.type.endsWith("Pattern")) {
  2468. this.walkExpression(expression.right);
  2469. this.enterPattern(expression.left, (name, decl) => {
  2470. if (!this.callHooksForName(this.hooks.assign, name, expression)) {
  2471. this.defineVariable(name);
  2472. }
  2473. });
  2474. this.walkPattern(expression.left);
  2475. } else if (expression.left.type === "MemberExpression") {
  2476. const exprName = this.getMemberExpressionInfo(
  2477. expression.left,
  2478. ALLOWED_MEMBER_TYPES_EXPRESSION
  2479. );
  2480. if (exprName) {
  2481. if (
  2482. this.callHooksForInfo(
  2483. this.hooks.assignMemberChain,
  2484. exprName.rootInfo,
  2485. expression,
  2486. exprName.getMembers()
  2487. )
  2488. ) {
  2489. return;
  2490. }
  2491. }
  2492. this.walkExpression(expression.right);
  2493. this.walkExpression(expression.left);
  2494. } else {
  2495. this.walkExpression(expression.right);
  2496. this.walkExpression(expression.left);
  2497. }
  2498. }
  2499. walkConditionalExpression(expression) {
  2500. const result = this.hooks.expressionConditionalOperator.call(expression);
  2501. if (result === undefined) {
  2502. this.walkExpression(expression.test);
  2503. this.walkExpression(expression.consequent);
  2504. if (expression.alternate) {
  2505. this.walkExpression(expression.alternate);
  2506. }
  2507. } else {
  2508. if (result) {
  2509. this.walkExpression(expression.consequent);
  2510. } else if (expression.alternate) {
  2511. this.walkExpression(expression.alternate);
  2512. }
  2513. }
  2514. }
  2515. walkNewExpression(expression) {
  2516. const result = this.callHooksForExpression(
  2517. this.hooks.new,
  2518. expression.callee,
  2519. expression
  2520. );
  2521. if (result === true) return;
  2522. this.walkExpression(expression.callee);
  2523. if (expression.arguments) {
  2524. this.walkExpressions(expression.arguments);
  2525. }
  2526. }
  2527. walkYieldExpression(expression) {
  2528. if (expression.argument) {
  2529. this.walkExpression(expression.argument);
  2530. }
  2531. }
  2532. walkTemplateLiteral(expression) {
  2533. if (expression.expressions) {
  2534. this.walkExpressions(expression.expressions);
  2535. }
  2536. }
  2537. walkTaggedTemplateExpression(expression) {
  2538. if (expression.tag) {
  2539. this.walkExpression(expression.tag);
  2540. }
  2541. if (expression.quasi && expression.quasi.expressions) {
  2542. this.walkExpressions(expression.quasi.expressions);
  2543. }
  2544. }
  2545. walkClassExpression(expression) {
  2546. this.walkClass(expression);
  2547. }
  2548. /**
  2549. * @param {ChainExpressionNode} expression expression
  2550. */
  2551. walkChainExpression(expression) {
  2552. const result = this.hooks.optionalChaining.call(expression);
  2553. if (result === undefined) {
  2554. if (expression.expression.type === "CallExpression") {
  2555. this.walkCallExpression(expression.expression);
  2556. } else {
  2557. this.walkMemberExpression(expression.expression);
  2558. }
  2559. }
  2560. }
  2561. _walkIIFE(functionExpression, options, currentThis) {
  2562. const getVarInfo = argOrThis => {
  2563. const renameIdentifier = this.getRenameIdentifier(argOrThis);
  2564. if (renameIdentifier) {
  2565. if (
  2566. this.callHooksForInfo(
  2567. this.hooks.canRename,
  2568. renameIdentifier,
  2569. argOrThis
  2570. )
  2571. ) {
  2572. if (
  2573. !this.callHooksForInfo(
  2574. this.hooks.rename,
  2575. renameIdentifier,
  2576. argOrThis
  2577. )
  2578. ) {
  2579. return typeof renameIdentifier === "string"
  2580. ? this.getVariableInfo(renameIdentifier)
  2581. : renameIdentifier;
  2582. }
  2583. }
  2584. }
  2585. this.walkExpression(argOrThis);
  2586. };
  2587. const { params, type } = functionExpression;
  2588. const arrow = type === "ArrowFunctionExpression";
  2589. const renameThis = currentThis ? getVarInfo(currentThis) : null;
  2590. const varInfoForArgs = options.map(getVarInfo);
  2591. const wasTopLevel = this.scope.topLevelScope;
  2592. this.scope.topLevelScope = wasTopLevel && arrow ? "arrow" : false;
  2593. const scopeParams = params.filter(
  2594. (identifier, idx) => !varInfoForArgs[idx]
  2595. );
  2596. // Add function name in scope for recursive calls
  2597. if (functionExpression.id) {
  2598. scopeParams.push(functionExpression.id.name);
  2599. }
  2600. this.inFunctionScope(true, scopeParams, () => {
  2601. if (renameThis && !arrow) {
  2602. this.setVariable("this", renameThis);
  2603. }
  2604. for (let i = 0; i < varInfoForArgs.length; i++) {
  2605. const varInfo = varInfoForArgs[i];
  2606. if (!varInfo) continue;
  2607. if (!params[i] || params[i].type !== "Identifier") continue;
  2608. this.setVariable(params[i].name, varInfo);
  2609. }
  2610. if (functionExpression.body.type === "BlockStatement") {
  2611. this.detectMode(functionExpression.body.body);
  2612. const prev = this.prevStatement;
  2613. this.preWalkStatement(functionExpression.body);
  2614. this.prevStatement = prev;
  2615. this.walkStatement(functionExpression.body);
  2616. } else {
  2617. this.walkExpression(functionExpression.body);
  2618. }
  2619. });
  2620. this.scope.topLevelScope = wasTopLevel;
  2621. }
  2622. walkImportExpression(expression) {
  2623. let result = this.hooks.importCall.call(expression);
  2624. if (result === true) return;
  2625. this.walkExpression(expression.source);
  2626. }
  2627. walkCallExpression(expression) {
  2628. const isSimpleFunction = fn => {
  2629. return fn.params.every(p => p.type === "Identifier");
  2630. };
  2631. if (
  2632. expression.callee.type === "MemberExpression" &&
  2633. expression.callee.object.type.endsWith("FunctionExpression") &&
  2634. !expression.callee.computed &&
  2635. (expression.callee.property.name === "call" ||
  2636. expression.callee.property.name === "bind") &&
  2637. expression.arguments.length > 0 &&
  2638. isSimpleFunction(expression.callee.object)
  2639. ) {
  2640. // (function(…) { }.call/bind(?, …))
  2641. this._walkIIFE(
  2642. expression.callee.object,
  2643. expression.arguments.slice(1),
  2644. expression.arguments[0]
  2645. );
  2646. } else if (
  2647. expression.callee.type.endsWith("FunctionExpression") &&
  2648. isSimpleFunction(expression.callee)
  2649. ) {
  2650. // (function(…) { }(…))
  2651. this._walkIIFE(expression.callee, expression.arguments, null);
  2652. } else {
  2653. if (expression.callee.type === "MemberExpression") {
  2654. const exprInfo = this.getMemberExpressionInfo(
  2655. expression.callee,
  2656. ALLOWED_MEMBER_TYPES_CALL_EXPRESSION
  2657. );
  2658. if (exprInfo && exprInfo.type === "call") {
  2659. const result = this.callHooksForInfo(
  2660. this.hooks.callMemberChainOfCallMemberChain,
  2661. exprInfo.rootInfo,
  2662. expression,
  2663. exprInfo.getCalleeMembers(),
  2664. exprInfo.call,
  2665. exprInfo.getMembers()
  2666. );
  2667. if (result === true) return;
  2668. }
  2669. }
  2670. const callee = this.evaluateExpression(expression.callee);
  2671. if (callee.isIdentifier()) {
  2672. const result1 = this.callHooksForInfo(
  2673. this.hooks.callMemberChain,
  2674. callee.rootInfo,
  2675. expression,
  2676. callee.getMembers(),
  2677. callee.getMembersOptionals
  2678. ? callee.getMembersOptionals()
  2679. : callee.getMembers().map(() => false)
  2680. );
  2681. if (result1 === true) return;
  2682. const result2 = this.callHooksForInfo(
  2683. this.hooks.call,
  2684. callee.identifier,
  2685. expression
  2686. );
  2687. if (result2 === true) return;
  2688. }
  2689. if (expression.callee) {
  2690. if (expression.callee.type === "MemberExpression") {
  2691. // because of call context we need to walk the call context as expression
  2692. this.walkExpression(expression.callee.object);
  2693. if (expression.callee.computed === true)
  2694. this.walkExpression(expression.callee.property);
  2695. } else {
  2696. this.walkExpression(expression.callee);
  2697. }
  2698. }
  2699. if (expression.arguments) this.walkExpressions(expression.arguments);
  2700. }
  2701. }
  2702. walkMemberExpression(expression) {
  2703. const exprInfo = this.getMemberExpressionInfo(
  2704. expression,
  2705. ALLOWED_MEMBER_TYPES_ALL
  2706. );
  2707. if (exprInfo) {
  2708. switch (exprInfo.type) {
  2709. case "expression": {
  2710. const result1 = this.callHooksForInfo(
  2711. this.hooks.expression,
  2712. exprInfo.name,
  2713. expression
  2714. );
  2715. if (result1 === true) return;
  2716. const members = exprInfo.getMembers();
  2717. const membersOptionals = exprInfo.getMembersOptionals();
  2718. const result2 = this.callHooksForInfo(
  2719. this.hooks.expressionMemberChain,
  2720. exprInfo.rootInfo,
  2721. expression,
  2722. members,
  2723. membersOptionals
  2724. );
  2725. if (result2 === true) return;
  2726. this.walkMemberExpressionWithExpressionName(
  2727. expression,
  2728. exprInfo.name,
  2729. exprInfo.rootInfo,
  2730. members.slice(),
  2731. () =>
  2732. this.callHooksForInfo(
  2733. this.hooks.unhandledExpressionMemberChain,
  2734. exprInfo.rootInfo,
  2735. expression,
  2736. members
  2737. )
  2738. );
  2739. return;
  2740. }
  2741. case "call": {
  2742. const result = this.callHooksForInfo(
  2743. this.hooks.memberChainOfCallMemberChain,
  2744. exprInfo.rootInfo,
  2745. expression,
  2746. exprInfo.getCalleeMembers(),
  2747. exprInfo.call,
  2748. exprInfo.getMembers()
  2749. );
  2750. if (result === true) return;
  2751. // Fast skip over the member chain as we already called memberChainOfCallMemberChain
  2752. // and call computed property are literals anyway
  2753. this.walkExpression(exprInfo.call);
  2754. return;
  2755. }
  2756. }
  2757. }
  2758. this.walkExpression(expression.object);
  2759. if (expression.computed === true) this.walkExpression(expression.property);
  2760. }
  2761. walkMemberExpressionWithExpressionName(
  2762. expression,
  2763. name,
  2764. rootInfo,
  2765. members,
  2766. onUnhandled
  2767. ) {
  2768. if (expression.object.type === "MemberExpression") {
  2769. // optimize the case where expression.object is a MemberExpression too.
  2770. // we can keep info here when calling walkMemberExpression directly
  2771. const property =
  2772. expression.property.name || `${expression.property.value}`;
  2773. name = name.slice(0, -property.length - 1);
  2774. members.pop();
  2775. const result = this.callHooksForInfo(
  2776. this.hooks.expression,
  2777. name,
  2778. expression.object
  2779. );
  2780. if (result === true) return;
  2781. this.walkMemberExpressionWithExpressionName(
  2782. expression.object,
  2783. name,
  2784. rootInfo,
  2785. members,
  2786. onUnhandled
  2787. );
  2788. } else if (!onUnhandled || !onUnhandled()) {
  2789. this.walkExpression(expression.object);
  2790. }
  2791. if (expression.computed === true) this.walkExpression(expression.property);
  2792. }
  2793. walkThisExpression(expression) {
  2794. this.callHooksForName(this.hooks.expression, "this", expression);
  2795. }
  2796. walkIdentifier(expression) {
  2797. this.callHooksForName(this.hooks.expression, expression.name, expression);
  2798. }
  2799. /**
  2800. * @param {MetaPropertyNode} metaProperty meta property
  2801. */
  2802. walkMetaProperty(metaProperty) {
  2803. this.hooks.expression.for(getRootName(metaProperty)).call(metaProperty);
  2804. }
  2805. callHooksForExpression(hookMap, expr, ...args) {
  2806. return this.callHooksForExpressionWithFallback(
  2807. hookMap,
  2808. expr,
  2809. undefined,
  2810. undefined,
  2811. ...args
  2812. );
  2813. }
  2814. /**
  2815. * @template T
  2816. * @template R
  2817. * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
  2818. * @param {MemberExpressionNode} expr expression info
  2819. * @param {function(string, string | ScopeInfo | VariableInfo, function(): string[]): any} fallback callback when variable in not handled by hooks
  2820. * @param {function(string): any} defined callback when variable is defined
  2821. * @param {AsArray<T>} args args for the hook
  2822. * @returns {R} result of hook
  2823. */
  2824. callHooksForExpressionWithFallback(
  2825. hookMap,
  2826. expr,
  2827. fallback,
  2828. defined,
  2829. ...args
  2830. ) {
  2831. const exprName = this.getMemberExpressionInfo(
  2832. expr,
  2833. ALLOWED_MEMBER_TYPES_EXPRESSION
  2834. );
  2835. if (exprName !== undefined) {
  2836. const members = exprName.getMembers();
  2837. return this.callHooksForInfoWithFallback(
  2838. hookMap,
  2839. members.length === 0 ? exprName.rootInfo : exprName.name,
  2840. fallback &&
  2841. (name => fallback(name, exprName.rootInfo, exprName.getMembers)),
  2842. defined && (() => defined(exprName.name)),
  2843. ...args
  2844. );
  2845. }
  2846. }
  2847. /**
  2848. * @template T
  2849. * @template R
  2850. * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
  2851. * @param {string} name key in map
  2852. * @param {AsArray<T>} args args for the hook
  2853. * @returns {R} result of hook
  2854. */
  2855. callHooksForName(hookMap, name, ...args) {
  2856. return this.callHooksForNameWithFallback(
  2857. hookMap,
  2858. name,
  2859. undefined,
  2860. undefined,
  2861. ...args
  2862. );
  2863. }
  2864. /**
  2865. * @template T
  2866. * @template R
  2867. * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that should be called
  2868. * @param {ExportedVariableInfo} info variable info
  2869. * @param {AsArray<T>} args args for the hook
  2870. * @returns {R} result of hook
  2871. */
  2872. callHooksForInfo(hookMap, info, ...args) {
  2873. return this.callHooksForInfoWithFallback(
  2874. hookMap,
  2875. info,
  2876. undefined,
  2877. undefined,
  2878. ...args
  2879. );
  2880. }
  2881. /**
  2882. * @template T
  2883. * @template R
  2884. * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
  2885. * @param {ExportedVariableInfo} info variable info
  2886. * @param {function(string): any} fallback callback when variable in not handled by hooks
  2887. * @param {function(): any} defined callback when variable is defined
  2888. * @param {AsArray<T>} args args for the hook
  2889. * @returns {R} result of hook
  2890. */
  2891. callHooksForInfoWithFallback(hookMap, info, fallback, defined, ...args) {
  2892. let name;
  2893. if (typeof info === "string") {
  2894. name = info;
  2895. } else {
  2896. if (!(info instanceof VariableInfo)) {
  2897. if (defined !== undefined) {
  2898. return defined();
  2899. }
  2900. return;
  2901. }
  2902. let tagInfo = info.tagInfo;
  2903. while (tagInfo !== undefined) {
  2904. const hook = hookMap.get(tagInfo.tag);
  2905. if (hook !== undefined) {
  2906. this.currentTagData = tagInfo.data;
  2907. const result = hook.call(...args);
  2908. this.currentTagData = undefined;
  2909. if (result !== undefined) return result;
  2910. }
  2911. tagInfo = tagInfo.next;
  2912. }
  2913. if (info.freeName === true) {
  2914. if (defined !== undefined) {
  2915. return defined();
  2916. }
  2917. return;
  2918. }
  2919. name = info.freeName;
  2920. }
  2921. const hook = hookMap.get(name);
  2922. if (hook !== undefined) {
  2923. const result = hook.call(...args);
  2924. if (result !== undefined) return result;
  2925. }
  2926. if (fallback !== undefined) {
  2927. return fallback(name);
  2928. }
  2929. }
  2930. /**
  2931. * @template T
  2932. * @template R
  2933. * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
  2934. * @param {string} name key in map
  2935. * @param {function(string): any} fallback callback when variable in not handled by hooks
  2936. * @param {function(): any} defined callback when variable is defined
  2937. * @param {AsArray<T>} args args for the hook
  2938. * @returns {R} result of hook
  2939. */
  2940. callHooksForNameWithFallback(hookMap, name, fallback, defined, ...args) {
  2941. return this.callHooksForInfoWithFallback(
  2942. hookMap,
  2943. this.getVariableInfo(name),
  2944. fallback,
  2945. defined,
  2946. ...args
  2947. );
  2948. }
  2949. /**
  2950. * @deprecated
  2951. * @param {any} params scope params
  2952. * @param {function(): void} fn inner function
  2953. * @returns {void}
  2954. */
  2955. inScope(params, fn) {
  2956. const oldScope = this.scope;
  2957. this.scope = {
  2958. topLevelScope: oldScope.topLevelScope,
  2959. inTry: false,
  2960. inShorthand: false,
  2961. isStrict: oldScope.isStrict,
  2962. isAsmJs: oldScope.isAsmJs,
  2963. definitions: oldScope.definitions.createChild()
  2964. };
  2965. this.undefineVariable("this");
  2966. this.enterPatterns(params, (ident, pattern) => {
  2967. this.defineVariable(ident);
  2968. });
  2969. fn();
  2970. this.scope = oldScope;
  2971. }
  2972. inFunctionScope(hasThis, params, fn) {
  2973. const oldScope = this.scope;
  2974. this.scope = {
  2975. topLevelScope: oldScope.topLevelScope,
  2976. inTry: false,
  2977. inShorthand: false,
  2978. isStrict: oldScope.isStrict,
  2979. isAsmJs: oldScope.isAsmJs,
  2980. definitions: oldScope.definitions.createChild()
  2981. };
  2982. if (hasThis) {
  2983. this.undefineVariable("this");
  2984. }
  2985. this.enterPatterns(params, (ident, pattern) => {
  2986. this.defineVariable(ident);
  2987. });
  2988. fn();
  2989. this.scope = oldScope;
  2990. }
  2991. inBlockScope(fn) {
  2992. const oldScope = this.scope;
  2993. this.scope = {
  2994. topLevelScope: oldScope.topLevelScope,
  2995. inTry: oldScope.inTry,
  2996. inShorthand: false,
  2997. isStrict: oldScope.isStrict,
  2998. isAsmJs: oldScope.isAsmJs,
  2999. definitions: oldScope.definitions.createChild()
  3000. };
  3001. fn();
  3002. this.scope = oldScope;
  3003. }
  3004. detectMode(statements) {
  3005. const isLiteral =
  3006. statements.length >= 1 &&
  3007. statements[0].type === "ExpressionStatement" &&
  3008. statements[0].expression.type === "Literal";
  3009. if (isLiteral && statements[0].expression.value === "use strict") {
  3010. this.scope.isStrict = true;
  3011. }
  3012. if (isLiteral && statements[0].expression.value === "use asm") {
  3013. this.scope.isAsmJs = true;
  3014. }
  3015. }
  3016. enterPatterns(patterns, onIdent) {
  3017. for (const pattern of patterns) {
  3018. if (typeof pattern !== "string") {
  3019. this.enterPattern(pattern, onIdent);
  3020. } else if (pattern) {
  3021. onIdent(pattern);
  3022. }
  3023. }
  3024. }
  3025. enterPattern(pattern, onIdent) {
  3026. if (!pattern) return;
  3027. switch (pattern.type) {
  3028. case "ArrayPattern":
  3029. this.enterArrayPattern(pattern, onIdent);
  3030. break;
  3031. case "AssignmentPattern":
  3032. this.enterAssignmentPattern(pattern, onIdent);
  3033. break;
  3034. case "Identifier":
  3035. this.enterIdentifier(pattern, onIdent);
  3036. break;
  3037. case "ObjectPattern":
  3038. this.enterObjectPattern(pattern, onIdent);
  3039. break;
  3040. case "RestElement":
  3041. this.enterRestElement(pattern, onIdent);
  3042. break;
  3043. case "Property":
  3044. if (pattern.shorthand && pattern.value.type === "Identifier") {
  3045. this.scope.inShorthand = pattern.value.name;
  3046. this.enterIdentifier(pattern.value, onIdent);
  3047. this.scope.inShorthand = false;
  3048. } else {
  3049. this.enterPattern(pattern.value, onIdent);
  3050. }
  3051. break;
  3052. }
  3053. }
  3054. enterIdentifier(pattern, onIdent) {
  3055. if (!this.callHooksForName(this.hooks.pattern, pattern.name, pattern)) {
  3056. onIdent(pattern.name, pattern);
  3057. }
  3058. }
  3059. enterObjectPattern(pattern, onIdent) {
  3060. for (
  3061. let propIndex = 0, len = pattern.properties.length;
  3062. propIndex < len;
  3063. propIndex++
  3064. ) {
  3065. const prop = pattern.properties[propIndex];
  3066. this.enterPattern(prop, onIdent);
  3067. }
  3068. }
  3069. enterArrayPattern(pattern, onIdent) {
  3070. for (
  3071. let elementIndex = 0, len = pattern.elements.length;
  3072. elementIndex < len;
  3073. elementIndex++
  3074. ) {
  3075. const element = pattern.elements[elementIndex];
  3076. this.enterPattern(element, onIdent);
  3077. }
  3078. }
  3079. enterRestElement(pattern, onIdent) {
  3080. this.enterPattern(pattern.argument, onIdent);
  3081. }
  3082. enterAssignmentPattern(pattern, onIdent) {
  3083. this.enterPattern(pattern.left, onIdent);
  3084. }
  3085. /**
  3086. * @param {ExpressionNode} expression expression node
  3087. * @returns {BasicEvaluatedExpression} evaluation result
  3088. */
  3089. evaluateExpression(expression) {
  3090. try {
  3091. const hook = this.hooks.evaluate.get(expression.type);
  3092. if (hook !== undefined) {
  3093. const result = hook.call(expression);
  3094. if (result !== undefined && result !== null) {
  3095. result.setExpression(expression);
  3096. return result;
  3097. }
  3098. }
  3099. } catch (e) {
  3100. console.warn(e);
  3101. // ignore error
  3102. }
  3103. return new BasicEvaluatedExpression()
  3104. .setRange(expression.range)
  3105. .setExpression(expression);
  3106. }
  3107. parseString(expression) {
  3108. switch (expression.type) {
  3109. case "BinaryExpression":
  3110. if (expression.operator === "+") {
  3111. return (
  3112. this.parseString(expression.left) +
  3113. this.parseString(expression.right)
  3114. );
  3115. }
  3116. break;
  3117. case "Literal":
  3118. return expression.value + "";
  3119. }
  3120. throw new Error(
  3121. expression.type + " is not supported as parameter for require"
  3122. );
  3123. }
  3124. parseCalculatedString(expression) {
  3125. switch (expression.type) {
  3126. case "BinaryExpression":
  3127. if (expression.operator === "+") {
  3128. const left = this.parseCalculatedString(expression.left);
  3129. const right = this.parseCalculatedString(expression.right);
  3130. if (left.code) {
  3131. return {
  3132. range: left.range,
  3133. value: left.value,
  3134. code: true,
  3135. conditional: false
  3136. };
  3137. } else if (right.code) {
  3138. return {
  3139. range: [
  3140. left.range[0],
  3141. right.range ? right.range[1] : left.range[1]
  3142. ],
  3143. value: left.value + right.value,
  3144. code: true,
  3145. conditional: false
  3146. };
  3147. } else {
  3148. return {
  3149. range: [left.range[0], right.range[1]],
  3150. value: left.value + right.value,
  3151. code: false,
  3152. conditional: false
  3153. };
  3154. }
  3155. }
  3156. break;
  3157. case "ConditionalExpression": {
  3158. const consequent = this.parseCalculatedString(expression.consequent);
  3159. const alternate = this.parseCalculatedString(expression.alternate);
  3160. const items = [];
  3161. if (consequent.conditional) {
  3162. items.push(...consequent.conditional);
  3163. } else if (!consequent.code) {
  3164. items.push(consequent);
  3165. } else {
  3166. break;
  3167. }
  3168. if (alternate.conditional) {
  3169. items.push(...alternate.conditional);
  3170. } else if (!alternate.code) {
  3171. items.push(alternate);
  3172. } else {
  3173. break;
  3174. }
  3175. return {
  3176. range: undefined,
  3177. value: "",
  3178. code: true,
  3179. conditional: items
  3180. };
  3181. }
  3182. case "Literal":
  3183. return {
  3184. range: expression.range,
  3185. value: expression.value + "",
  3186. code: false,
  3187. conditional: false
  3188. };
  3189. }
  3190. return {
  3191. range: undefined,
  3192. value: "",
  3193. code: true,
  3194. conditional: false
  3195. };
  3196. }
  3197. /**
  3198. * @param {string | Buffer | PreparsedAst} source the source to parse
  3199. * @param {ParserState} state the parser state
  3200. * @returns {ParserState} the parser state
  3201. */
  3202. parse(source, state) {
  3203. let ast;
  3204. let comments;
  3205. const semicolons = new Set();
  3206. if (source === null) {
  3207. throw new Error("source must not be null");
  3208. }
  3209. if (Buffer.isBuffer(source)) {
  3210. source = source.toString("utf-8");
  3211. }
  3212. if (typeof source === "object") {
  3213. ast = /** @type {ProgramNode} */ (source);
  3214. comments = source.comments;
  3215. } else {
  3216. comments = [];
  3217. ast = JavascriptParser._parse(source, {
  3218. sourceType: this.sourceType,
  3219. onComment: comments,
  3220. onInsertedSemicolon: pos => semicolons.add(pos)
  3221. });
  3222. }
  3223. const oldScope = this.scope;
  3224. const oldState = this.state;
  3225. const oldComments = this.comments;
  3226. const oldSemicolons = this.semicolons;
  3227. const oldStatementPath = this.statementPath;
  3228. const oldPrevStatement = this.prevStatement;
  3229. this.scope = {
  3230. topLevelScope: true,
  3231. inTry: false,
  3232. inShorthand: false,
  3233. isStrict: false,
  3234. isAsmJs: false,
  3235. definitions: new StackedMap()
  3236. };
  3237. /** @type {ParserState} */
  3238. this.state = state;
  3239. this.comments = comments;
  3240. this.semicolons = semicolons;
  3241. this.statementPath = [];
  3242. this.prevStatement = undefined;
  3243. if (this.hooks.program.call(ast, comments) === undefined) {
  3244. this.destructuringAssignmentProperties = new WeakMap();
  3245. this.detectMode(ast.body);
  3246. this.preWalkStatements(ast.body);
  3247. this.prevStatement = undefined;
  3248. this.blockPreWalkStatements(ast.body);
  3249. this.prevStatement = undefined;
  3250. this.walkStatements(ast.body);
  3251. this.destructuringAssignmentProperties = undefined;
  3252. }
  3253. this.hooks.finish.call(ast, comments);
  3254. this.scope = oldScope;
  3255. /** @type {ParserState} */
  3256. this.state = oldState;
  3257. this.comments = oldComments;
  3258. this.semicolons = oldSemicolons;
  3259. this.statementPath = oldStatementPath;
  3260. this.prevStatement = oldPrevStatement;
  3261. return state;
  3262. }
  3263. /**
  3264. * @param {string} source source code
  3265. * @returns {BasicEvaluatedExpression} evaluation result
  3266. */
  3267. evaluate(source) {
  3268. const ast = JavascriptParser._parse("(" + source + ")", {
  3269. sourceType: this.sourceType,
  3270. locations: false
  3271. });
  3272. if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
  3273. throw new Error("evaluate: Source is not a expression");
  3274. }
  3275. return this.evaluateExpression(ast.body[0].expression);
  3276. }
  3277. /**
  3278. * @param {ExpressionNode | DeclarationNode | PrivateIdentifierNode | null | undefined} expr an expression
  3279. * @param {number} commentsStartPos source position from which annotation comments are checked
  3280. * @returns {boolean} true, when the expression is pure
  3281. */
  3282. isPure(expr, commentsStartPos) {
  3283. if (!expr) return true;
  3284. const result = this.hooks.isPure
  3285. .for(expr.type)
  3286. .call(expr, commentsStartPos);
  3287. if (typeof result === "boolean") return result;
  3288. switch (expr.type) {
  3289. case "ClassDeclaration":
  3290. case "ClassExpression": {
  3291. if (expr.body.type !== "ClassBody") return false;
  3292. if (expr.superClass && !this.isPure(expr.superClass, expr.range[0])) {
  3293. return false;
  3294. }
  3295. const items =
  3296. /** @type {(MethodDefinitionNode | PropertyDefinitionNode)[]} */ (
  3297. expr.body.body
  3298. );
  3299. return items.every(
  3300. item =>
  3301. (!item.computed ||
  3302. !item.key ||
  3303. this.isPure(item.key, item.range[0])) &&
  3304. (!item.static ||
  3305. !item.value ||
  3306. this.isPure(
  3307. item.value,
  3308. item.key ? item.key.range[1] : item.range[0]
  3309. ))
  3310. );
  3311. }
  3312. case "FunctionDeclaration":
  3313. case "FunctionExpression":
  3314. case "ArrowFunctionExpression":
  3315. case "Literal":
  3316. case "PrivateIdentifier":
  3317. return true;
  3318. case "VariableDeclaration":
  3319. return expr.declarations.every(decl =>
  3320. this.isPure(decl.init, decl.range[0])
  3321. );
  3322. case "ConditionalExpression":
  3323. return (
  3324. this.isPure(expr.test, commentsStartPos) &&
  3325. this.isPure(expr.consequent, expr.test.range[1]) &&
  3326. this.isPure(expr.alternate, expr.consequent.range[1])
  3327. );
  3328. case "SequenceExpression":
  3329. return expr.expressions.every(expr => {
  3330. const pureFlag = this.isPure(expr, commentsStartPos);
  3331. commentsStartPos = expr.range[1];
  3332. return pureFlag;
  3333. });
  3334. case "CallExpression": {
  3335. const pureFlag =
  3336. expr.range[0] - commentsStartPos > 12 &&
  3337. this.getComments([commentsStartPos, expr.range[0]]).some(
  3338. comment =>
  3339. comment.type === "Block" &&
  3340. /^\s*(#|@)__PURE__\s*$/.test(comment.value)
  3341. );
  3342. if (!pureFlag) return false;
  3343. commentsStartPos = expr.callee.range[1];
  3344. return expr.arguments.every(arg => {
  3345. if (arg.type === "SpreadElement") return false;
  3346. const pureFlag = this.isPure(arg, commentsStartPos);
  3347. commentsStartPos = arg.range[1];
  3348. return pureFlag;
  3349. });
  3350. }
  3351. }
  3352. const evaluated = this.evaluateExpression(expr);
  3353. return !evaluated.couldHaveSideEffects();
  3354. }
  3355. getComments(range) {
  3356. const [rangeStart, rangeEnd] = range;
  3357. const compare = (comment, needle) => comment.range[0] - needle;
  3358. let idx = binarySearchBounds.ge(this.comments, rangeStart, compare);
  3359. let commentsInRange = [];
  3360. while (this.comments[idx] && this.comments[idx].range[1] <= rangeEnd) {
  3361. commentsInRange.push(this.comments[idx]);
  3362. idx++;
  3363. }
  3364. return commentsInRange;
  3365. }
  3366. /**
  3367. * @param {number} pos source code position
  3368. * @returns {boolean} true when a semicolon has been inserted before this position, false if not
  3369. */
  3370. isAsiPosition(pos) {
  3371. const currentStatement = this.statementPath[this.statementPath.length - 1];
  3372. if (currentStatement === undefined) throw new Error("Not in statement");
  3373. return (
  3374. // Either asking directly for the end position of the current statement
  3375. (currentStatement.range[1] === pos && this.semicolons.has(pos)) ||
  3376. // Or asking for the start position of the current statement,
  3377. // here we have to check multiple things
  3378. (currentStatement.range[0] === pos &&
  3379. // is there a previous statement which might be relevant?
  3380. this.prevStatement !== undefined &&
  3381. // is the end position of the previous statement an ASI position?
  3382. this.semicolons.has(this.prevStatement.range[1]))
  3383. );
  3384. }
  3385. /**
  3386. * @param {number} pos source code position
  3387. * @returns {void}
  3388. */
  3389. unsetAsiPosition(pos) {
  3390. this.semicolons.delete(pos);
  3391. }
  3392. isStatementLevelExpression(expr) {
  3393. const currentStatement = this.statementPath[this.statementPath.length - 1];
  3394. return (
  3395. expr === currentStatement ||
  3396. (currentStatement.type === "ExpressionStatement" &&
  3397. currentStatement.expression === expr)
  3398. );
  3399. }
  3400. getTagData(name, tag) {
  3401. const info = this.scope.definitions.get(name);
  3402. if (info instanceof VariableInfo) {
  3403. let tagInfo = info.tagInfo;
  3404. while (tagInfo !== undefined) {
  3405. if (tagInfo.tag === tag) return tagInfo.data;
  3406. tagInfo = tagInfo.next;
  3407. }
  3408. }
  3409. }
  3410. tagVariable(name, tag, data) {
  3411. const oldInfo = this.scope.definitions.get(name);
  3412. /** @type {VariableInfo} */
  3413. let newInfo;
  3414. if (oldInfo === undefined) {
  3415. newInfo = new VariableInfo(this.scope, name, {
  3416. tag,
  3417. data,
  3418. next: undefined
  3419. });
  3420. } else if (oldInfo instanceof VariableInfo) {
  3421. newInfo = new VariableInfo(oldInfo.declaredScope, oldInfo.freeName, {
  3422. tag,
  3423. data,
  3424. next: oldInfo.tagInfo
  3425. });
  3426. } else {
  3427. newInfo = new VariableInfo(oldInfo, true, {
  3428. tag,
  3429. data,
  3430. next: undefined
  3431. });
  3432. }
  3433. this.scope.definitions.set(name, newInfo);
  3434. }
  3435. defineVariable(name) {
  3436. const oldInfo = this.scope.definitions.get(name);
  3437. // Don't redefine variable in same scope to keep existing tags
  3438. if (oldInfo instanceof VariableInfo && oldInfo.declaredScope === this.scope)
  3439. return;
  3440. this.scope.definitions.set(name, this.scope);
  3441. }
  3442. undefineVariable(name) {
  3443. this.scope.definitions.delete(name);
  3444. }
  3445. isVariableDefined(name) {
  3446. const info = this.scope.definitions.get(name);
  3447. if (info === undefined) return false;
  3448. if (info instanceof VariableInfo) {
  3449. return info.freeName === true;
  3450. }
  3451. return true;
  3452. }
  3453. /**
  3454. * @param {string} name variable name
  3455. * @returns {ExportedVariableInfo} info for this variable
  3456. */
  3457. getVariableInfo(name) {
  3458. const value = this.scope.definitions.get(name);
  3459. if (value === undefined) {
  3460. return name;
  3461. } else {
  3462. return value;
  3463. }
  3464. }
  3465. /**
  3466. * @param {string} name variable name
  3467. * @param {ExportedVariableInfo} variableInfo new info for this variable
  3468. * @returns {void}
  3469. */
  3470. setVariable(name, variableInfo) {
  3471. if (typeof variableInfo === "string") {
  3472. if (variableInfo === name) {
  3473. this.scope.definitions.delete(name);
  3474. } else {
  3475. this.scope.definitions.set(
  3476. name,
  3477. new VariableInfo(this.scope, variableInfo, undefined)
  3478. );
  3479. }
  3480. } else {
  3481. this.scope.definitions.set(name, variableInfo);
  3482. }
  3483. }
  3484. evaluatedVariable(tagInfo) {
  3485. return new VariableInfo(this.scope, undefined, tagInfo);
  3486. }
  3487. parseCommentOptions(range) {
  3488. const comments = this.getComments(range);
  3489. if (comments.length === 0) {
  3490. return EMPTY_COMMENT_OPTIONS;
  3491. }
  3492. let options = {};
  3493. /** @type {unknown[]} */
  3494. let errors = [];
  3495. for (const comment of comments) {
  3496. const { value } = comment;
  3497. if (value && webpackCommentRegExp.test(value)) {
  3498. // try compile only if webpack options comment is present
  3499. try {
  3500. for (let [key, val] of Object.entries(
  3501. vm.runInNewContext(`(function(){return {${value}};})()`)
  3502. )) {
  3503. if (typeof val === "object" && val !== null) {
  3504. if (val.constructor.name === "RegExp") val = new RegExp(val);
  3505. else val = JSON.parse(JSON.stringify(val));
  3506. }
  3507. options[key] = val;
  3508. }
  3509. } catch (e) {
  3510. const newErr = new Error(String(e.message));
  3511. newErr.stack = String(e.stack);
  3512. Object.assign(newErr, { comment });
  3513. errors.push(newErr);
  3514. }
  3515. }
  3516. }
  3517. return { options, errors };
  3518. }
  3519. /**
  3520. * @param {MemberExpressionNode} expression a member expression
  3521. * @returns {{ members: string[], object: ExpressionNode | SuperNode, membersOptionals: boolean[] }} member names (reverse order) and remaining object
  3522. */
  3523. extractMemberExpressionChain(expression) {
  3524. /** @type {AnyNode} */
  3525. let expr = expression;
  3526. const members = [];
  3527. const membersOptionals = [];
  3528. while (expr.type === "MemberExpression") {
  3529. if (expr.computed) {
  3530. if (expr.property.type !== "Literal") break;
  3531. members.push(`${expr.property.value}`);
  3532. } else {
  3533. if (expr.property.type !== "Identifier") break;
  3534. members.push(expr.property.name);
  3535. }
  3536. membersOptionals.push(expr.optional);
  3537. expr = expr.object;
  3538. }
  3539. return {
  3540. members,
  3541. membersOptionals,
  3542. object: expr
  3543. };
  3544. }
  3545. /**
  3546. * @param {string} varName variable name
  3547. * @returns {{name: string, info: VariableInfo | string}} name of the free variable and variable info for that
  3548. */
  3549. getFreeInfoFromVariable(varName) {
  3550. const info = this.getVariableInfo(varName);
  3551. let name;
  3552. if (info instanceof VariableInfo) {
  3553. name = info.freeName;
  3554. if (typeof name !== "string") return undefined;
  3555. } else if (typeof info !== "string") {
  3556. return undefined;
  3557. } else {
  3558. name = info;
  3559. }
  3560. return { info, name };
  3561. }
  3562. /** @typedef {{ type: "call", call: CallExpressionNode, calleeName: string, rootInfo: string | VariableInfo, getCalleeMembers: () => string[], name: string, getMembers: () => string[], getMembersOptionals: () => boolean[]}} CallExpressionInfo */
  3563. /** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => string[], getMembersOptionals: () => boolean[]}} ExpressionExpressionInfo */
  3564. /**
  3565. * @param {MemberExpressionNode} expression a member expression
  3566. * @param {number} allowedTypes which types should be returned, presented in bit mask
  3567. * @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info
  3568. */
  3569. getMemberExpressionInfo(expression, allowedTypes) {
  3570. const { object, members, membersOptionals } =
  3571. this.extractMemberExpressionChain(expression);
  3572. switch (object.type) {
  3573. case "CallExpression": {
  3574. if ((allowedTypes & ALLOWED_MEMBER_TYPES_CALL_EXPRESSION) === 0)
  3575. return undefined;
  3576. let callee = object.callee;
  3577. let rootMembers = EMPTY_ARRAY;
  3578. if (callee.type === "MemberExpression") {
  3579. ({ object: callee, members: rootMembers } =
  3580. this.extractMemberExpressionChain(callee));
  3581. }
  3582. const rootName = getRootName(callee);
  3583. if (!rootName) return undefined;
  3584. const result = this.getFreeInfoFromVariable(rootName);
  3585. if (!result) return undefined;
  3586. const { info: rootInfo, name: resolvedRoot } = result;
  3587. const calleeName = objectAndMembersToName(resolvedRoot, rootMembers);
  3588. return {
  3589. type: "call",
  3590. call: object,
  3591. calleeName,
  3592. rootInfo,
  3593. getCalleeMembers: memoize(() => rootMembers.reverse()),
  3594. name: objectAndMembersToName(`${calleeName}()`, members),
  3595. getMembers: memoize(() => members.reverse()),
  3596. getMembersOptionals: memoize(() => membersOptionals.reverse())
  3597. };
  3598. }
  3599. case "Identifier":
  3600. case "MetaProperty":
  3601. case "ThisExpression": {
  3602. if ((allowedTypes & ALLOWED_MEMBER_TYPES_EXPRESSION) === 0)
  3603. return undefined;
  3604. const rootName = getRootName(object);
  3605. if (!rootName) return undefined;
  3606. const result = this.getFreeInfoFromVariable(rootName);
  3607. if (!result) return undefined;
  3608. const { info: rootInfo, name: resolvedRoot } = result;
  3609. return {
  3610. type: "expression",
  3611. name: objectAndMembersToName(resolvedRoot, members),
  3612. rootInfo,
  3613. getMembers: memoize(() => members.reverse()),
  3614. getMembersOptionals: memoize(() => membersOptionals.reverse())
  3615. };
  3616. }
  3617. }
  3618. }
  3619. /**
  3620. * @param {MemberExpressionNode} expression an expression
  3621. * @returns {{ name: string, rootInfo: ExportedVariableInfo, getMembers: () => string[]}} name info
  3622. */
  3623. getNameForExpression(expression) {
  3624. return this.getMemberExpressionInfo(
  3625. expression,
  3626. ALLOWED_MEMBER_TYPES_EXPRESSION
  3627. );
  3628. }
  3629. /**
  3630. * @param {string} code source code
  3631. * @param {ParseOptions} options parsing options
  3632. * @returns {ProgramNode} parsed ast
  3633. */
  3634. static _parse(code, options) {
  3635. const type = options ? options.sourceType : "module";
  3636. /** @type {AcornOptions} */
  3637. const parserOptions = {
  3638. ...defaultParserOptions,
  3639. allowReturnOutsideFunction: type === "script",
  3640. ...options,
  3641. sourceType: type === "auto" ? "module" : type
  3642. };
  3643. /** @type {AnyNode} */
  3644. let ast;
  3645. let error;
  3646. let threw = false;
  3647. try {
  3648. ast = /** @type {AnyNode} */ (parser.parse(code, parserOptions));
  3649. } catch (e) {
  3650. error = e;
  3651. threw = true;
  3652. }
  3653. if (threw && type === "auto") {
  3654. parserOptions.sourceType = "script";
  3655. if (!("allowReturnOutsideFunction" in options)) {
  3656. parserOptions.allowReturnOutsideFunction = true;
  3657. }
  3658. if (Array.isArray(parserOptions.onComment)) {
  3659. parserOptions.onComment.length = 0;
  3660. }
  3661. try {
  3662. ast = /** @type {AnyNode} */ (parser.parse(code, parserOptions));
  3663. threw = false;
  3664. } catch (e) {
  3665. // we use the error from first parse try
  3666. // so nothing to do here
  3667. }
  3668. }
  3669. if (threw) {
  3670. throw error;
  3671. }
  3672. return /** @type {ProgramNode} */ (ast);
  3673. }
  3674. }
  3675. module.exports = JavascriptParser;
  3676. module.exports.ALLOWED_MEMBER_TYPES_ALL = ALLOWED_MEMBER_TYPES_ALL;
  3677. module.exports.ALLOWED_MEMBER_TYPES_EXPRESSION =
  3678. ALLOWED_MEMBER_TYPES_EXPRESSION;
  3679. module.exports.ALLOWED_MEMBER_TYPES_CALL_EXPRESSION =
  3680. ALLOWED_MEMBER_TYPES_CALL_EXPRESSION;