Parser.js 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
  7. const acorn = require("acorn-dynamic-import").default;
  8. const { Tapable, SyncBailHook, HookMap } = require("tapable");
  9. const util = require("util");
  10. const vm = require("vm");
  11. const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
  12. const StackedSetMap = require("./util/StackedSetMap");
  13. const TrackingSet = require("./util/TrackingSet");
  14. const joinRanges = (startRange, endRange) => {
  15. if (!endRange) return startRange;
  16. if (!startRange) return endRange;
  17. return [startRange[0], endRange[1]];
  18. };
  19. const defaultParserOptions = {
  20. ranges: true,
  21. locations: true,
  22. ecmaVersion: 2019,
  23. sourceType: "module",
  24. onComment: null,
  25. plugins: {
  26. dynamicImport: true
  27. }
  28. };
  29. // regexp to match at lease one "magic comment"
  30. const webpackCommentRegExp = new RegExp(/(^|\W)webpack[A-Z]{1,}[A-Za-z]{1,}:/);
  31. const EMPTY_ARRAY = [];
  32. const EMPTY_COMMENT_OPTIONS = {
  33. options: null,
  34. errors: null
  35. };
  36. class Parser extends Tapable {
  37. constructor(options, sourceType = "auto") {
  38. super();
  39. this.hooks = {
  40. evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
  41. evaluate: new HookMap(() => new SyncBailHook(["expression"])),
  42. evaluateIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
  43. evaluateDefinedIdentifier: new HookMap(
  44. () => new SyncBailHook(["expression"])
  45. ),
  46. evaluateCallExpressionMember: new HookMap(
  47. () => new SyncBailHook(["expression", "param"])
  48. ),
  49. statement: new SyncBailHook(["statement"]),
  50. statementIf: new SyncBailHook(["statement"]),
  51. label: new HookMap(() => new SyncBailHook(["statement"])),
  52. import: new SyncBailHook(["statement", "source"]),
  53. importSpecifier: new SyncBailHook([
  54. "statement",
  55. "source",
  56. "exportName",
  57. "identifierName"
  58. ]),
  59. export: new SyncBailHook(["statement"]),
  60. exportImport: new SyncBailHook(["statement", "source"]),
  61. exportDeclaration: new SyncBailHook(["statement", "declaration"]),
  62. exportExpression: new SyncBailHook(["statement", "declaration"]),
  63. exportSpecifier: new SyncBailHook([
  64. "statement",
  65. "identifierName",
  66. "exportName",
  67. "index"
  68. ]),
  69. exportImportSpecifier: new SyncBailHook([
  70. "statement",
  71. "source",
  72. "identifierName",
  73. "exportName",
  74. "index"
  75. ]),
  76. varDeclaration: new HookMap(() => new SyncBailHook(["declaration"])),
  77. varDeclarationLet: new HookMap(() => new SyncBailHook(["declaration"])),
  78. varDeclarationConst: new HookMap(() => new SyncBailHook(["declaration"])),
  79. varDeclarationVar: new HookMap(() => new SyncBailHook(["declaration"])),
  80. canRename: new HookMap(() => new SyncBailHook(["initExpression"])),
  81. rename: new HookMap(() => new SyncBailHook(["initExpression"])),
  82. assigned: new HookMap(() => new SyncBailHook(["expression"])),
  83. assign: new HookMap(() => new SyncBailHook(["expression"])),
  84. typeof: new HookMap(() => new SyncBailHook(["expression"])),
  85. importCall: new SyncBailHook(["expression"]),
  86. call: new HookMap(() => new SyncBailHook(["expression"])),
  87. callAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
  88. new: new HookMap(() => new SyncBailHook(["expression"])),
  89. expression: new HookMap(() => new SyncBailHook(["expression"])),
  90. expressionAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
  91. expressionConditionalOperator: new SyncBailHook(["expression"]),
  92. program: new SyncBailHook(["ast", "comments"])
  93. };
  94. const HOOK_MAP_COMPAT_CONFIG = {
  95. evaluateTypeof: /^evaluate typeof (.+)$/,
  96. evaluateIdentifier: /^evaluate Identifier (.+)$/,
  97. evaluateDefinedIdentifier: /^evaluate defined Identifier (.+)$/,
  98. evaluateCallExpressionMember: /^evaluate CallExpression .(.+)$/,
  99. evaluate: /^evaluate (.+)$/,
  100. label: /^label (.+)$/,
  101. varDeclarationLet: /^var-let (.+)$/,
  102. varDeclarationConst: /^var-const (.+)$/,
  103. varDeclarationVar: /^var-var (.+)$/,
  104. varDeclaration: /^var (.+)$/,
  105. canRename: /^can-rename (.+)$/,
  106. rename: /^rename (.+)$/,
  107. typeof: /^typeof (.+)$/,
  108. assigned: /^assigned (.+)$/,
  109. assign: /^assign (.+)$/,
  110. callAnyMember: /^call (.+)\.\*$/,
  111. call: /^call (.+)$/,
  112. new: /^new (.+)$/,
  113. expressionConditionalOperator: /^expression \?:$/,
  114. expressionAnyMember: /^expression (.+)\.\*$/,
  115. expression: /^expression (.+)$/
  116. };
  117. this._pluginCompat.tap("Parser", options => {
  118. for (const name of Object.keys(HOOK_MAP_COMPAT_CONFIG)) {
  119. const regexp = HOOK_MAP_COMPAT_CONFIG[name];
  120. const match = regexp.exec(options.name);
  121. if (match) {
  122. if (match[1]) {
  123. this.hooks[name].tap(
  124. match[1],
  125. options.fn.name || "unnamed compat plugin",
  126. options.fn.bind(this)
  127. );
  128. } else {
  129. this.hooks[name].tap(
  130. options.fn.name || "unnamed compat plugin",
  131. options.fn.bind(this)
  132. );
  133. }
  134. return true;
  135. }
  136. }
  137. });
  138. this.options = options;
  139. this.sourceType = sourceType;
  140. this.scope = undefined;
  141. this.state = undefined;
  142. this.comments = undefined;
  143. this.initializeEvaluating();
  144. }
  145. initializeEvaluating() {
  146. this.hooks.evaluate.for("Literal").tap("Parser", expr => {
  147. switch (typeof expr.value) {
  148. case "number":
  149. return new BasicEvaluatedExpression()
  150. .setNumber(expr.value)
  151. .setRange(expr.range);
  152. case "string":
  153. return new BasicEvaluatedExpression()
  154. .setString(expr.value)
  155. .setRange(expr.range);
  156. case "boolean":
  157. return new BasicEvaluatedExpression()
  158. .setBoolean(expr.value)
  159. .setRange(expr.range);
  160. }
  161. if (expr.value === null) {
  162. return new BasicEvaluatedExpression().setNull().setRange(expr.range);
  163. }
  164. if (expr.value instanceof RegExp) {
  165. return new BasicEvaluatedExpression()
  166. .setRegExp(expr.value)
  167. .setRange(expr.range);
  168. }
  169. });
  170. this.hooks.evaluate.for("LogicalExpression").tap("Parser", expr => {
  171. let left;
  172. let leftAsBool;
  173. let right;
  174. if (expr.operator === "&&") {
  175. left = this.evaluateExpression(expr.left);
  176. leftAsBool = left && left.asBool();
  177. if (leftAsBool === false) return left.setRange(expr.range);
  178. if (leftAsBool !== true) return;
  179. right = this.evaluateExpression(expr.right);
  180. return right.setRange(expr.range);
  181. } else if (expr.operator === "||") {
  182. left = this.evaluateExpression(expr.left);
  183. leftAsBool = left && left.asBool();
  184. if (leftAsBool === true) return left.setRange(expr.range);
  185. if (leftAsBool !== false) return;
  186. right = this.evaluateExpression(expr.right);
  187. return right.setRange(expr.range);
  188. }
  189. });
  190. this.hooks.evaluate.for("BinaryExpression").tap("Parser", expr => {
  191. let left;
  192. let right;
  193. let res;
  194. if (expr.operator === "+") {
  195. left = this.evaluateExpression(expr.left);
  196. right = this.evaluateExpression(expr.right);
  197. if (!left || !right) return;
  198. res = new BasicEvaluatedExpression();
  199. if (left.isString()) {
  200. if (right.isString()) {
  201. res.setString(left.string + right.string);
  202. } else if (right.isNumber()) {
  203. res.setString(left.string + right.number);
  204. } else if (
  205. right.isWrapped() &&
  206. right.prefix &&
  207. right.prefix.isString()
  208. ) {
  209. res.setWrapped(
  210. new BasicEvaluatedExpression()
  211. .setString(left.string + right.prefix.string)
  212. .setRange(joinRanges(left.range, right.prefix.range)),
  213. right.postfix
  214. );
  215. } else if (right.isWrapped()) {
  216. res.setWrapped(
  217. new BasicEvaluatedExpression()
  218. .setString(left.string)
  219. .setRange(left.range),
  220. right.postfix
  221. );
  222. } else {
  223. res.setWrapped(left, null);
  224. }
  225. } else if (left.isNumber()) {
  226. if (right.isString()) {
  227. res.setString(left.number + right.string);
  228. } else if (right.isNumber()) {
  229. res.setNumber(left.number + right.number);
  230. }
  231. } else if (left.isWrapped()) {
  232. if (left.postfix && left.postfix.isString() && right.isString()) {
  233. res.setWrapped(
  234. left.prefix,
  235. new BasicEvaluatedExpression()
  236. .setString(left.postfix.string + right.string)
  237. .setRange(joinRanges(left.postfix.range, right.range))
  238. );
  239. } else if (
  240. left.postfix &&
  241. left.postfix.isString() &&
  242. right.isNumber()
  243. ) {
  244. res.setWrapped(
  245. left.prefix,
  246. new BasicEvaluatedExpression()
  247. .setString(left.postfix.string + right.number)
  248. .setRange(joinRanges(left.postfix.range, right.range))
  249. );
  250. } else if (right.isString()) {
  251. res.setWrapped(left.prefix, right);
  252. } else if (right.isNumber()) {
  253. res.setWrapped(
  254. left.prefix,
  255. new BasicEvaluatedExpression()
  256. .setString(right.number + "")
  257. .setRange(right.range)
  258. );
  259. } else {
  260. res.setWrapped(left.prefix, new BasicEvaluatedExpression());
  261. }
  262. } else {
  263. if (right.isString()) {
  264. res.setWrapped(null, right);
  265. }
  266. }
  267. res.setRange(expr.range);
  268. return res;
  269. } else if (expr.operator === "-") {
  270. left = this.evaluateExpression(expr.left);
  271. right = this.evaluateExpression(expr.right);
  272. if (!left || !right) return;
  273. if (!left.isNumber() || !right.isNumber()) return;
  274. res = new BasicEvaluatedExpression();
  275. res.setNumber(left.number - right.number);
  276. res.setRange(expr.range);
  277. return res;
  278. } else if (expr.operator === "*") {
  279. left = this.evaluateExpression(expr.left);
  280. right = this.evaluateExpression(expr.right);
  281. if (!left || !right) return;
  282. if (!left.isNumber() || !right.isNumber()) return;
  283. res = new BasicEvaluatedExpression();
  284. res.setNumber(left.number * right.number);
  285. res.setRange(expr.range);
  286. return res;
  287. } else if (expr.operator === "/") {
  288. left = this.evaluateExpression(expr.left);
  289. right = this.evaluateExpression(expr.right);
  290. if (!left || !right) return;
  291. if (!left.isNumber() || !right.isNumber()) return;
  292. res = new BasicEvaluatedExpression();
  293. res.setNumber(left.number / right.number);
  294. res.setRange(expr.range);
  295. return res;
  296. } else if (expr.operator === "**") {
  297. left = this.evaluateExpression(expr.left);
  298. right = this.evaluateExpression(expr.right);
  299. if (!left || !right) return;
  300. if (!left.isNumber() || !right.isNumber()) return;
  301. res = new BasicEvaluatedExpression();
  302. res.setNumber(Math.pow(left.number, right.number));
  303. res.setRange(expr.range);
  304. return res;
  305. } else if (expr.operator === "==" || expr.operator === "===") {
  306. left = this.evaluateExpression(expr.left);
  307. right = this.evaluateExpression(expr.right);
  308. if (!left || !right) return;
  309. res = new BasicEvaluatedExpression();
  310. res.setRange(expr.range);
  311. if (left.isString() && right.isString()) {
  312. return res.setBoolean(left.string === right.string);
  313. } else if (left.isNumber() && right.isNumber()) {
  314. return res.setBoolean(left.number === right.number);
  315. } else if (left.isBoolean() && right.isBoolean()) {
  316. return res.setBoolean(left.bool === right.bool);
  317. }
  318. } else if (expr.operator === "!=" || expr.operator === "!==") {
  319. left = this.evaluateExpression(expr.left);
  320. right = this.evaluateExpression(expr.right);
  321. if (!left || !right) return;
  322. res = new BasicEvaluatedExpression();
  323. res.setRange(expr.range);
  324. if (left.isString() && right.isString()) {
  325. return res.setBoolean(left.string !== right.string);
  326. } else if (left.isNumber() && right.isNumber()) {
  327. return res.setBoolean(left.number !== right.number);
  328. } else if (left.isBoolean() && right.isBoolean()) {
  329. return res.setBoolean(left.bool !== right.bool);
  330. }
  331. } else if (expr.operator === "&") {
  332. left = this.evaluateExpression(expr.left);
  333. right = this.evaluateExpression(expr.right);
  334. if (!left || !right) return;
  335. if (!left.isNumber() || !right.isNumber()) return;
  336. res = new BasicEvaluatedExpression();
  337. res.setNumber(left.number & right.number);
  338. res.setRange(expr.range);
  339. return res;
  340. } else if (expr.operator === "|") {
  341. left = this.evaluateExpression(expr.left);
  342. right = this.evaluateExpression(expr.right);
  343. if (!left || !right) return;
  344. if (!left.isNumber() || !right.isNumber()) return;
  345. res = new BasicEvaluatedExpression();
  346. res.setNumber(left.number | right.number);
  347. res.setRange(expr.range);
  348. return res;
  349. } else if (expr.operator === "^") {
  350. left = this.evaluateExpression(expr.left);
  351. right = this.evaluateExpression(expr.right);
  352. if (!left || !right) return;
  353. if (!left.isNumber() || !right.isNumber()) return;
  354. res = new BasicEvaluatedExpression();
  355. res.setNumber(left.number ^ right.number);
  356. res.setRange(expr.range);
  357. return res;
  358. } else if (expr.operator === ">>>") {
  359. left = this.evaluateExpression(expr.left);
  360. right = this.evaluateExpression(expr.right);
  361. if (!left || !right) return;
  362. if (!left.isNumber() || !right.isNumber()) return;
  363. res = new BasicEvaluatedExpression();
  364. res.setNumber(left.number >>> right.number);
  365. res.setRange(expr.range);
  366. return res;
  367. } else if (expr.operator === ">>") {
  368. left = this.evaluateExpression(expr.left);
  369. right = this.evaluateExpression(expr.right);
  370. if (!left || !right) return;
  371. if (!left.isNumber() || !right.isNumber()) return;
  372. res = new BasicEvaluatedExpression();
  373. res.setNumber(left.number >> right.number);
  374. res.setRange(expr.range);
  375. return res;
  376. } else if (expr.operator === "<<") {
  377. left = this.evaluateExpression(expr.left);
  378. right = this.evaluateExpression(expr.right);
  379. if (!left || !right) return;
  380. if (!left.isNumber() || !right.isNumber()) return;
  381. res = new BasicEvaluatedExpression();
  382. res.setNumber(left.number << right.number);
  383. res.setRange(expr.range);
  384. return res;
  385. }
  386. });
  387. this.hooks.evaluate.for("UnaryExpression").tap("Parser", expr => {
  388. if (expr.operator === "typeof") {
  389. let res;
  390. let name;
  391. if (expr.argument.type === "Identifier") {
  392. name =
  393. this.scope.renames.get(expr.argument.name) || expr.argument.name;
  394. if (!this.scope.definitions.has(name)) {
  395. const hook = this.hooks.evaluateTypeof.get(name);
  396. if (hook !== undefined) {
  397. res = hook.call(expr);
  398. if (res !== undefined) return res;
  399. }
  400. }
  401. }
  402. if (expr.argument.type === "MemberExpression") {
  403. const exprName = this.getNameForExpression(expr.argument);
  404. if (exprName && exprName.free) {
  405. const hook = this.hooks.evaluateTypeof.get(exprName.name);
  406. if (hook !== undefined) {
  407. res = hook.call(expr);
  408. if (res !== undefined) return res;
  409. }
  410. }
  411. }
  412. if (expr.argument.type === "FunctionExpression") {
  413. return new BasicEvaluatedExpression()
  414. .setString("function")
  415. .setRange(expr.range);
  416. }
  417. const arg = this.evaluateExpression(expr.argument);
  418. if (arg.isString() || arg.isWrapped()) {
  419. return new BasicEvaluatedExpression()
  420. .setString("string")
  421. .setRange(expr.range);
  422. }
  423. if (arg.isNumber()) {
  424. return new BasicEvaluatedExpression()
  425. .setString("number")
  426. .setRange(expr.range);
  427. }
  428. if (arg.isBoolean()) {
  429. return new BasicEvaluatedExpression()
  430. .setString("boolean")
  431. .setRange(expr.range);
  432. }
  433. if (arg.isArray() || arg.isConstArray() || arg.isRegExp()) {
  434. return new BasicEvaluatedExpression()
  435. .setString("object")
  436. .setRange(expr.range);
  437. }
  438. } else if (expr.operator === "!") {
  439. const argument = this.evaluateExpression(expr.argument);
  440. if (!argument) return;
  441. if (argument.isBoolean()) {
  442. return new BasicEvaluatedExpression()
  443. .setBoolean(!argument.bool)
  444. .setRange(expr.range);
  445. }
  446. if (argument.isTruthy()) {
  447. return new BasicEvaluatedExpression()
  448. .setBoolean(false)
  449. .setRange(expr.range);
  450. }
  451. if (argument.isFalsy()) {
  452. return new BasicEvaluatedExpression()
  453. .setBoolean(true)
  454. .setRange(expr.range);
  455. }
  456. if (argument.isString()) {
  457. return new BasicEvaluatedExpression()
  458. .setBoolean(!argument.string)
  459. .setRange(expr.range);
  460. }
  461. if (argument.isNumber()) {
  462. return new BasicEvaluatedExpression()
  463. .setBoolean(!argument.number)
  464. .setRange(expr.range);
  465. }
  466. } else if (expr.operator === "~") {
  467. const argument = this.evaluateExpression(expr.argument);
  468. if (!argument) return;
  469. if (!argument.isNumber()) return;
  470. const res = new BasicEvaluatedExpression();
  471. res.setNumber(~argument.number);
  472. res.setRange(expr.range);
  473. return res;
  474. }
  475. });
  476. this.hooks.evaluateTypeof.for("undefined").tap("Parser", expr => {
  477. return new BasicEvaluatedExpression()
  478. .setString("undefined")
  479. .setRange(expr.range);
  480. });
  481. this.hooks.evaluate.for("Identifier").tap("Parser", expr => {
  482. const name = this.scope.renames.get(expr.name) || expr.name;
  483. if (!this.scope.definitions.has(expr.name)) {
  484. const hook = this.hooks.evaluateIdentifier.get(name);
  485. if (hook !== undefined) {
  486. const result = hook.call(expr);
  487. if (result) return result;
  488. }
  489. return new BasicEvaluatedExpression()
  490. .setIdentifier(name)
  491. .setRange(expr.range);
  492. } else {
  493. const hook = this.hooks.evaluateDefinedIdentifier.get(name);
  494. if (hook !== undefined) {
  495. return hook.call(expr);
  496. }
  497. }
  498. });
  499. this.hooks.evaluate.for("ThisExpression").tap("Parser", expr => {
  500. const name = this.scope.renames.get("this");
  501. if (name) {
  502. const hook = this.hooks.evaluateIdentifier.get(name);
  503. if (hook !== undefined) {
  504. const result = hook.call(expr);
  505. if (result) return result;
  506. }
  507. return new BasicEvaluatedExpression()
  508. .setIdentifier(name)
  509. .setRange(expr.range);
  510. }
  511. });
  512. this.hooks.evaluate.for("MemberExpression").tap("Parser", expression => {
  513. let exprName = this.getNameForExpression(expression);
  514. if (exprName) {
  515. if (exprName.free) {
  516. const hook = this.hooks.evaluateIdentifier.get(exprName.name);
  517. if (hook !== undefined) {
  518. const result = hook.call(expression);
  519. if (result) return result;
  520. }
  521. return new BasicEvaluatedExpression()
  522. .setIdentifier(exprName.name)
  523. .setRange(expression.range);
  524. } else {
  525. const hook = this.hooks.evaluateDefinedIdentifier.get(exprName.name);
  526. if (hook !== undefined) {
  527. return hook.call(expression);
  528. }
  529. }
  530. }
  531. });
  532. this.hooks.evaluate.for("CallExpression").tap("Parser", expr => {
  533. if (expr.callee.type !== "MemberExpression") return;
  534. if (
  535. expr.callee.property.type !==
  536. (expr.callee.computed ? "Literal" : "Identifier")
  537. )
  538. return;
  539. const param = this.evaluateExpression(expr.callee.object);
  540. if (!param) return;
  541. const property = expr.callee.property.name || expr.callee.property.value;
  542. const hook = this.hooks.evaluateCallExpressionMember.get(property);
  543. if (hook !== undefined) {
  544. return hook.call(expr, param);
  545. }
  546. });
  547. this.hooks.evaluateCallExpressionMember
  548. .for("replace")
  549. .tap("Parser", (expr, param) => {
  550. if (!param.isString()) return;
  551. if (expr.arguments.length !== 2) return;
  552. let arg1 = this.evaluateExpression(expr.arguments[0]);
  553. let arg2 = this.evaluateExpression(expr.arguments[1]);
  554. if (!arg1.isString() && !arg1.isRegExp()) return;
  555. arg1 = arg1.regExp || arg1.string;
  556. if (!arg2.isString()) return;
  557. arg2 = arg2.string;
  558. return new BasicEvaluatedExpression()
  559. .setString(param.string.replace(arg1, arg2))
  560. .setRange(expr.range);
  561. });
  562. ["substr", "substring"].forEach(fn => {
  563. this.hooks.evaluateCallExpressionMember
  564. .for(fn)
  565. .tap("Parser", (expr, param) => {
  566. if (!param.isString()) return;
  567. let arg1;
  568. let result,
  569. str = param.string;
  570. switch (expr.arguments.length) {
  571. case 1:
  572. arg1 = this.evaluateExpression(expr.arguments[0]);
  573. if (!arg1.isNumber()) return;
  574. result = str[fn](arg1.number);
  575. break;
  576. case 2: {
  577. arg1 = this.evaluateExpression(expr.arguments[0]);
  578. const arg2 = this.evaluateExpression(expr.arguments[1]);
  579. if (!arg1.isNumber()) return;
  580. if (!arg2.isNumber()) return;
  581. result = str[fn](arg1.number, arg2.number);
  582. break;
  583. }
  584. default:
  585. return;
  586. }
  587. return new BasicEvaluatedExpression()
  588. .setString(result)
  589. .setRange(expr.range);
  590. });
  591. });
  592. /**
  593. * @param {string} kind "cooked" | "raw"
  594. * @param {TODO[]} quasis quasis
  595. * @param {TODO[]} expressions expressions
  596. * @returns {BasicEvaluatedExpression[]} Simplified template
  597. */
  598. const getSimplifiedTemplateResult = (kind, quasis, expressions) => {
  599. const parts = [];
  600. for (let i = 0; i < quasis.length; i++) {
  601. parts.push(
  602. new BasicEvaluatedExpression()
  603. .setString(quasis[i].value[kind])
  604. .setRange(quasis[i].range)
  605. );
  606. if (i > 0) {
  607. const prevExpr = parts[parts.length - 2],
  608. lastExpr = parts[parts.length - 1];
  609. const expr = this.evaluateExpression(expressions[i - 1]);
  610. if (!(expr.isString() || expr.isNumber())) continue;
  611. prevExpr.setString(
  612. prevExpr.string +
  613. (expr.isString() ? expr.string : expr.number) +
  614. lastExpr.string
  615. );
  616. prevExpr.setRange([prevExpr.range[0], lastExpr.range[1]]);
  617. parts.pop();
  618. }
  619. }
  620. return parts;
  621. };
  622. this.hooks.evaluate.for("TemplateLiteral").tap("Parser", node => {
  623. const parts = getSimplifiedTemplateResult.call(
  624. this,
  625. "cooked",
  626. node.quasis,
  627. node.expressions
  628. );
  629. if (parts.length === 1) {
  630. return parts[0].setRange(node.range);
  631. }
  632. return new BasicEvaluatedExpression()
  633. .setTemplateString(parts)
  634. .setRange(node.range);
  635. });
  636. this.hooks.evaluate.for("TaggedTemplateExpression").tap("Parser", node => {
  637. if (this.evaluateExpression(node.tag).identifier !== "String.raw") return;
  638. const parts = getSimplifiedTemplateResult.call(
  639. this,
  640. "raw",
  641. node.quasi.quasis,
  642. node.quasi.expressions
  643. );
  644. return new BasicEvaluatedExpression()
  645. .setTemplateString(parts)
  646. .setRange(node.range);
  647. });
  648. this.hooks.evaluateCallExpressionMember
  649. .for("concat")
  650. .tap("Parser", (expr, param) => {
  651. if (!param.isString() && !param.isWrapped()) return;
  652. let stringSuffix = null;
  653. let hasUnknownParams = false;
  654. for (let i = expr.arguments.length - 1; i >= 0; i--) {
  655. const argExpr = this.evaluateExpression(expr.arguments[i]);
  656. if (!argExpr.isString() && !argExpr.isNumber()) {
  657. hasUnknownParams = true;
  658. break;
  659. }
  660. const value = argExpr.isString()
  661. ? argExpr.string
  662. : "" + argExpr.number;
  663. const newString = value + (stringSuffix ? stringSuffix.string : "");
  664. const newRange = [
  665. argExpr.range[0],
  666. (stringSuffix || argExpr).range[1]
  667. ];
  668. stringSuffix = new BasicEvaluatedExpression()
  669. .setString(newString)
  670. .setRange(newRange);
  671. }
  672. if (hasUnknownParams) {
  673. const prefix = param.isString() ? param : param.prefix;
  674. return new BasicEvaluatedExpression()
  675. .setWrapped(prefix, stringSuffix)
  676. .setRange(expr.range);
  677. } else if (param.isWrapped()) {
  678. const postfix = stringSuffix || param.postfix;
  679. return new BasicEvaluatedExpression()
  680. .setWrapped(param.prefix, postfix)
  681. .setRange(expr.range);
  682. } else {
  683. const newString =
  684. param.string + (stringSuffix ? stringSuffix.string : "");
  685. return new BasicEvaluatedExpression()
  686. .setString(newString)
  687. .setRange(expr.range);
  688. }
  689. });
  690. this.hooks.evaluateCallExpressionMember
  691. .for("split")
  692. .tap("Parser", (expr, param) => {
  693. if (!param.isString()) return;
  694. if (expr.arguments.length !== 1) return;
  695. let result;
  696. const arg = this.evaluateExpression(expr.arguments[0]);
  697. if (arg.isString()) {
  698. result = param.string.split(arg.string);
  699. } else if (arg.isRegExp()) {
  700. result = param.string.split(arg.regExp);
  701. } else {
  702. return;
  703. }
  704. return new BasicEvaluatedExpression()
  705. .setArray(result)
  706. .setRange(expr.range);
  707. });
  708. this.hooks.evaluate.for("ConditionalExpression").tap("Parser", expr => {
  709. const condition = this.evaluateExpression(expr.test);
  710. const conditionValue = condition.asBool();
  711. let res;
  712. if (conditionValue === undefined) {
  713. const consequent = this.evaluateExpression(expr.consequent);
  714. const alternate = this.evaluateExpression(expr.alternate);
  715. if (!consequent || !alternate) return;
  716. res = new BasicEvaluatedExpression();
  717. if (consequent.isConditional()) {
  718. res.setOptions(consequent.options);
  719. } else {
  720. res.setOptions([consequent]);
  721. }
  722. if (alternate.isConditional()) {
  723. res.addOptions(alternate.options);
  724. } else {
  725. res.addOptions([alternate]);
  726. }
  727. } else {
  728. res = this.evaluateExpression(
  729. conditionValue ? expr.consequent : expr.alternate
  730. );
  731. }
  732. res.setRange(expr.range);
  733. return res;
  734. });
  735. this.hooks.evaluate.for("ArrayExpression").tap("Parser", expr => {
  736. const items = expr.elements.map(element => {
  737. return element !== null && this.evaluateExpression(element);
  738. });
  739. if (!items.every(Boolean)) return;
  740. return new BasicEvaluatedExpression()
  741. .setItems(items)
  742. .setRange(expr.range);
  743. });
  744. }
  745. getRenameIdentifier(expr) {
  746. const result = this.evaluateExpression(expr);
  747. if (result && result.isIdentifier()) {
  748. return result.identifier;
  749. }
  750. }
  751. walkClass(classy) {
  752. if (classy.superClass) this.walkExpression(classy.superClass);
  753. if (classy.body && classy.body.type === "ClassBody") {
  754. const wasTopLevel = this.scope.topLevelScope;
  755. this.scope.topLevelScope = false;
  756. for (const methodDefinition of classy.body.body) {
  757. if (methodDefinition.type === "MethodDefinition") {
  758. this.walkMethodDefinition(methodDefinition);
  759. }
  760. }
  761. this.scope.topLevelScope = wasTopLevel;
  762. }
  763. }
  764. walkMethodDefinition(methodDefinition) {
  765. if (methodDefinition.computed && methodDefinition.key) {
  766. this.walkExpression(methodDefinition.key);
  767. }
  768. if (methodDefinition.value) {
  769. this.walkExpression(methodDefinition.value);
  770. }
  771. }
  772. // Prewalking iterates the scope for variable declarations
  773. prewalkStatements(statements) {
  774. for (let index = 0, len = statements.length; index < len; index++) {
  775. const statement = statements[index];
  776. this.prewalkStatement(statement);
  777. }
  778. }
  779. // Walking iterates the statements and expressions and processes them
  780. walkStatements(statements) {
  781. for (let index = 0, len = statements.length; index < len; index++) {
  782. const statement = statements[index];
  783. this.walkStatement(statement);
  784. }
  785. }
  786. prewalkStatement(statement) {
  787. switch (statement.type) {
  788. case "BlockStatement":
  789. this.prewalkBlockStatement(statement);
  790. break;
  791. case "ClassDeclaration":
  792. this.prewalkClassDeclaration(statement);
  793. break;
  794. case "DoWhileStatement":
  795. this.prewalkDoWhileStatement(statement);
  796. break;
  797. case "ExportAllDeclaration":
  798. this.prewalkExportAllDeclaration(statement);
  799. break;
  800. case "ExportDefaultDeclaration":
  801. this.prewalkExportDefaultDeclaration(statement);
  802. break;
  803. case "ExportNamedDeclaration":
  804. this.prewalkExportNamedDeclaration(statement);
  805. break;
  806. case "ForInStatement":
  807. this.prewalkForInStatement(statement);
  808. break;
  809. case "ForOfStatement":
  810. this.prewalkForOfStatement(statement);
  811. break;
  812. case "ForStatement":
  813. this.prewalkForStatement(statement);
  814. break;
  815. case "FunctionDeclaration":
  816. this.prewalkFunctionDeclaration(statement);
  817. break;
  818. case "IfStatement":
  819. this.prewalkIfStatement(statement);
  820. break;
  821. case "ImportDeclaration":
  822. this.prewalkImportDeclaration(statement);
  823. break;
  824. case "LabeledStatement":
  825. this.prewalkLabeledStatement(statement);
  826. break;
  827. case "SwitchStatement":
  828. this.prewalkSwitchStatement(statement);
  829. break;
  830. case "TryStatement":
  831. this.prewalkTryStatement(statement);
  832. break;
  833. case "VariableDeclaration":
  834. this.prewalkVariableDeclaration(statement);
  835. break;
  836. case "WhileStatement":
  837. this.prewalkWhileStatement(statement);
  838. break;
  839. case "WithStatement":
  840. this.prewalkWithStatement(statement);
  841. break;
  842. }
  843. }
  844. walkStatement(statement) {
  845. if (this.hooks.statement.call(statement) !== undefined) return;
  846. switch (statement.type) {
  847. case "BlockStatement":
  848. this.walkBlockStatement(statement);
  849. break;
  850. case "ClassDeclaration":
  851. this.walkClassDeclaration(statement);
  852. break;
  853. case "DoWhileStatement":
  854. this.walkDoWhileStatement(statement);
  855. break;
  856. case "ExportDefaultDeclaration":
  857. this.walkExportDefaultDeclaration(statement);
  858. break;
  859. case "ExportNamedDeclaration":
  860. this.walkExportNamedDeclaration(statement);
  861. break;
  862. case "ExpressionStatement":
  863. this.walkExpressionStatement(statement);
  864. break;
  865. case "ForInStatement":
  866. this.walkForInStatement(statement);
  867. break;
  868. case "ForOfStatement":
  869. this.walkForOfStatement(statement);
  870. break;
  871. case "ForStatement":
  872. this.walkForStatement(statement);
  873. break;
  874. case "FunctionDeclaration":
  875. this.walkFunctionDeclaration(statement);
  876. break;
  877. case "IfStatement":
  878. this.walkIfStatement(statement);
  879. break;
  880. case "LabeledStatement":
  881. this.walkLabeledStatement(statement);
  882. break;
  883. case "ReturnStatement":
  884. this.walkReturnStatement(statement);
  885. break;
  886. case "SwitchStatement":
  887. this.walkSwitchStatement(statement);
  888. break;
  889. case "ThrowStatement":
  890. this.walkThrowStatement(statement);
  891. break;
  892. case "TryStatement":
  893. this.walkTryStatement(statement);
  894. break;
  895. case "VariableDeclaration":
  896. this.walkVariableDeclaration(statement);
  897. break;
  898. case "WhileStatement":
  899. this.walkWhileStatement(statement);
  900. break;
  901. case "WithStatement":
  902. this.walkWithStatement(statement);
  903. break;
  904. }
  905. }
  906. // Real Statements
  907. prewalkBlockStatement(statement) {
  908. this.prewalkStatements(statement.body);
  909. }
  910. walkBlockStatement(statement) {
  911. this.walkStatements(statement.body);
  912. }
  913. walkExpressionStatement(statement) {
  914. this.walkExpression(statement.expression);
  915. }
  916. prewalkIfStatement(statement) {
  917. this.prewalkStatement(statement.consequent);
  918. if (statement.alternate) {
  919. this.prewalkStatement(statement.alternate);
  920. }
  921. }
  922. walkIfStatement(statement) {
  923. const result = this.hooks.statementIf.call(statement);
  924. if (result === undefined) {
  925. this.walkExpression(statement.test);
  926. this.walkStatement(statement.consequent);
  927. if (statement.alternate) {
  928. this.walkStatement(statement.alternate);
  929. }
  930. } else {
  931. if (result) {
  932. this.walkStatement(statement.consequent);
  933. } else if (statement.alternate) {
  934. this.walkStatement(statement.alternate);
  935. }
  936. }
  937. }
  938. prewalkLabeledStatement(statement) {
  939. this.prewalkStatement(statement.body);
  940. }
  941. walkLabeledStatement(statement) {
  942. const hook = this.hooks.label.get(statement.label.name);
  943. if (hook !== undefined) {
  944. const result = hook.call(statement);
  945. if (result === true) return;
  946. }
  947. this.walkStatement(statement.body);
  948. }
  949. prewalkWithStatement(statement) {
  950. this.prewalkStatement(statement.body);
  951. }
  952. walkWithStatement(statement) {
  953. this.walkExpression(statement.object);
  954. this.walkStatement(statement.body);
  955. }
  956. prewalkSwitchStatement(statement) {
  957. this.prewalkSwitchCases(statement.cases);
  958. }
  959. walkSwitchStatement(statement) {
  960. this.walkExpression(statement.discriminant);
  961. this.walkSwitchCases(statement.cases);
  962. }
  963. walkTerminatingStatement(statement) {
  964. if (statement.argument) this.walkExpression(statement.argument);
  965. }
  966. walkReturnStatement(statement) {
  967. this.walkTerminatingStatement(statement);
  968. }
  969. walkThrowStatement(statement) {
  970. this.walkTerminatingStatement(statement);
  971. }
  972. prewalkTryStatement(statement) {
  973. this.prewalkStatement(statement.block);
  974. }
  975. walkTryStatement(statement) {
  976. if (this.scope.inTry) {
  977. this.walkStatement(statement.block);
  978. } else {
  979. this.scope.inTry = true;
  980. this.walkStatement(statement.block);
  981. this.scope.inTry = false;
  982. }
  983. if (statement.handler) this.walkCatchClause(statement.handler);
  984. if (statement.finalizer) this.walkStatement(statement.finalizer);
  985. }
  986. prewalkWhileStatement(statement) {
  987. this.prewalkStatement(statement.body);
  988. }
  989. walkWhileStatement(statement) {
  990. this.walkExpression(statement.test);
  991. this.walkStatement(statement.body);
  992. }
  993. prewalkDoWhileStatement(statement) {
  994. this.prewalkStatement(statement.body);
  995. }
  996. walkDoWhileStatement(statement) {
  997. this.walkStatement(statement.body);
  998. this.walkExpression(statement.test);
  999. }
  1000. prewalkForStatement(statement) {
  1001. if (statement.init) {
  1002. if (statement.init.type === "VariableDeclaration") {
  1003. this.prewalkStatement(statement.init);
  1004. }
  1005. }
  1006. this.prewalkStatement(statement.body);
  1007. }
  1008. walkForStatement(statement) {
  1009. if (statement.init) {
  1010. if (statement.init.type === "VariableDeclaration") {
  1011. this.walkStatement(statement.init);
  1012. } else {
  1013. this.walkExpression(statement.init);
  1014. }
  1015. }
  1016. if (statement.test) {
  1017. this.walkExpression(statement.test);
  1018. }
  1019. if (statement.update) {
  1020. this.walkExpression(statement.update);
  1021. }
  1022. this.walkStatement(statement.body);
  1023. }
  1024. prewalkForInStatement(statement) {
  1025. if (statement.left.type === "VariableDeclaration") {
  1026. this.prewalkVariableDeclaration(statement.left);
  1027. }
  1028. this.prewalkStatement(statement.body);
  1029. }
  1030. walkForInStatement(statement) {
  1031. if (statement.left.type === "VariableDeclaration") {
  1032. this.walkVariableDeclaration(statement.left);
  1033. } else {
  1034. this.walkPattern(statement.left);
  1035. }
  1036. this.walkExpression(statement.right);
  1037. this.walkStatement(statement.body);
  1038. }
  1039. prewalkForOfStatement(statement) {
  1040. if (statement.left.type === "VariableDeclaration") {
  1041. this.prewalkVariableDeclaration(statement.left);
  1042. }
  1043. this.prewalkStatement(statement.body);
  1044. }
  1045. walkForOfStatement(statement) {
  1046. if (statement.left.type === "VariableDeclaration") {
  1047. this.walkVariableDeclaration(statement.left);
  1048. } else {
  1049. this.walkPattern(statement.left);
  1050. }
  1051. this.walkExpression(statement.right);
  1052. this.walkStatement(statement.body);
  1053. }
  1054. // Declarations
  1055. prewalkFunctionDeclaration(statement) {
  1056. if (statement.id) {
  1057. this.scope.renames.set(statement.id.name, null);
  1058. this.scope.definitions.add(statement.id.name);
  1059. }
  1060. }
  1061. walkFunctionDeclaration(statement) {
  1062. const wasTopLevel = this.scope.topLevelScope;
  1063. this.scope.topLevelScope = false;
  1064. this.inScope(statement.params, () => {
  1065. for (const param of statement.params) {
  1066. this.walkPattern(param);
  1067. }
  1068. if (statement.body.type === "BlockStatement") {
  1069. this.detectStrictMode(statement.body.body);
  1070. this.prewalkStatement(statement.body);
  1071. this.walkStatement(statement.body);
  1072. } else {
  1073. this.walkExpression(statement.body);
  1074. }
  1075. });
  1076. this.scope.topLevelScope = wasTopLevel;
  1077. }
  1078. prewalkImportDeclaration(statement) {
  1079. const source = statement.source.value;
  1080. this.hooks.import.call(statement, source);
  1081. for (const specifier of statement.specifiers) {
  1082. const name = specifier.local.name;
  1083. this.scope.renames.set(name, null);
  1084. this.scope.definitions.add(name);
  1085. switch (specifier.type) {
  1086. case "ImportDefaultSpecifier":
  1087. this.hooks.importSpecifier.call(statement, source, "default", name);
  1088. break;
  1089. case "ImportSpecifier":
  1090. this.hooks.importSpecifier.call(
  1091. statement,
  1092. source,
  1093. specifier.imported.name,
  1094. name
  1095. );
  1096. break;
  1097. case "ImportNamespaceSpecifier":
  1098. this.hooks.importSpecifier.call(statement, source, null, name);
  1099. break;
  1100. }
  1101. }
  1102. }
  1103. prewalkExportNamedDeclaration(statement) {
  1104. let source;
  1105. if (statement.source) {
  1106. source = statement.source.value;
  1107. this.hooks.exportImport.call(statement, source);
  1108. } else {
  1109. this.hooks.export.call(statement);
  1110. }
  1111. if (statement.declaration) {
  1112. if (
  1113. !this.hooks.exportDeclaration.call(statement, statement.declaration)
  1114. ) {
  1115. const originalDefinitions = this.scope.definitions;
  1116. const tracker = new TrackingSet(this.scope.definitions);
  1117. this.scope.definitions = tracker;
  1118. this.prewalkStatement(statement.declaration);
  1119. const newDefs = Array.from(tracker.getAddedItems());
  1120. this.scope.definitions = originalDefinitions;
  1121. for (let index = newDefs.length - 1; index >= 0; index--) {
  1122. const def = newDefs[index];
  1123. this.hooks.exportSpecifier.call(statement, def, def, index);
  1124. }
  1125. }
  1126. }
  1127. if (statement.specifiers) {
  1128. for (
  1129. let specifierIndex = 0;
  1130. specifierIndex < statement.specifiers.length;
  1131. specifierIndex++
  1132. ) {
  1133. const specifier = statement.specifiers[specifierIndex];
  1134. switch (specifier.type) {
  1135. case "ExportSpecifier": {
  1136. const name = specifier.exported.name;
  1137. if (source) {
  1138. this.hooks.exportImportSpecifier.call(
  1139. statement,
  1140. source,
  1141. specifier.local.name,
  1142. name,
  1143. specifierIndex
  1144. );
  1145. } else {
  1146. this.hooks.exportSpecifier.call(
  1147. statement,
  1148. specifier.local.name,
  1149. name,
  1150. specifierIndex
  1151. );
  1152. }
  1153. break;
  1154. }
  1155. }
  1156. }
  1157. }
  1158. }
  1159. walkExportNamedDeclaration(statement) {
  1160. if (statement.declaration) {
  1161. this.walkStatement(statement.declaration);
  1162. }
  1163. }
  1164. prewalkExportDefaultDeclaration(statement) {
  1165. if (statement.declaration.id) {
  1166. const originalDefinitions = this.scope.definitions;
  1167. const tracker = new TrackingSet(this.scope.definitions);
  1168. this.scope.definitions = tracker;
  1169. this.prewalkStatement(statement.declaration);
  1170. const newDefs = Array.from(tracker.getAddedItems());
  1171. this.scope.definitions = originalDefinitions;
  1172. for (let index = 0, len = newDefs.length; index < len; index++) {
  1173. const def = newDefs[index];
  1174. this.hooks.exportSpecifier.call(statement, def, "default");
  1175. }
  1176. }
  1177. }
  1178. walkExportDefaultDeclaration(statement) {
  1179. this.hooks.export.call(statement);
  1180. if (
  1181. statement.declaration.id &&
  1182. statement.declaration.type !== "FunctionExpression" &&
  1183. statement.declaration.type !== "ClassExpression"
  1184. ) {
  1185. if (
  1186. !this.hooks.exportDeclaration.call(statement, statement.declaration)
  1187. ) {
  1188. this.walkStatement(statement.declaration);
  1189. }
  1190. } else {
  1191. // Acorn parses `export default function() {}` as `FunctionDeclaration` and
  1192. // `export default class {}` as `ClassDeclaration`, both with `id = null`.
  1193. // These nodes must be treated as expressions.
  1194. if (statement.declaration.type === "FunctionDeclaration") {
  1195. this.walkFunctionDeclaration(statement.declaration);
  1196. } else if (statement.declaration.type === "ClassDeclaration") {
  1197. this.walkClassDeclaration(statement.declaration);
  1198. } else {
  1199. this.walkExpression(statement.declaration);
  1200. }
  1201. if (!this.hooks.exportExpression.call(statement, statement.declaration)) {
  1202. this.hooks.exportSpecifier.call(
  1203. statement,
  1204. statement.declaration,
  1205. "default"
  1206. );
  1207. }
  1208. }
  1209. }
  1210. prewalkExportAllDeclaration(statement) {
  1211. const source = statement.source.value;
  1212. this.hooks.exportImport.call(statement, source);
  1213. this.hooks.exportImportSpecifier.call(statement, source, null, null, 0);
  1214. }
  1215. prewalkVariableDeclaration(statement) {
  1216. const hookMap =
  1217. statement.kind === "const"
  1218. ? this.hooks.varDeclarationConst
  1219. : statement.kind === "let"
  1220. ? this.hooks.varDeclarationLet
  1221. : this.hooks.varDeclarationVar;
  1222. for (const declarator of statement.declarations) {
  1223. switch (declarator.type) {
  1224. case "VariableDeclarator": {
  1225. this.enterPattern(declarator.id, (name, decl) => {
  1226. let hook = hookMap.get(name);
  1227. if (hook === undefined || !hook.call(decl)) {
  1228. hook = this.hooks.varDeclaration.get(name);
  1229. if (hook === undefined || !hook.call(decl)) {
  1230. this.scope.renames.set(name, null);
  1231. this.scope.definitions.add(name);
  1232. }
  1233. }
  1234. });
  1235. break;
  1236. }
  1237. }
  1238. }
  1239. }
  1240. walkVariableDeclaration(statement) {
  1241. for (const declarator of statement.declarations) {
  1242. switch (declarator.type) {
  1243. case "VariableDeclarator": {
  1244. const renameIdentifier =
  1245. declarator.init && this.getRenameIdentifier(declarator.init);
  1246. if (renameIdentifier && declarator.id.type === "Identifier") {
  1247. const hook = this.hooks.canRename.get(renameIdentifier);
  1248. if (hook !== undefined && hook.call(declarator.init)) {
  1249. // renaming with "var a = b;"
  1250. const hook = this.hooks.rename.get(renameIdentifier);
  1251. if (hook === undefined || !hook.call(declarator.init)) {
  1252. this.scope.renames.set(
  1253. declarator.id.name,
  1254. this.scope.renames.get(renameIdentifier) || renameIdentifier
  1255. );
  1256. this.scope.definitions.delete(declarator.id.name);
  1257. }
  1258. break;
  1259. }
  1260. }
  1261. this.walkPattern(declarator.id);
  1262. if (declarator.init) this.walkExpression(declarator.init);
  1263. break;
  1264. }
  1265. }
  1266. }
  1267. }
  1268. prewalkClassDeclaration(statement) {
  1269. if (statement.id) {
  1270. this.scope.renames.set(statement.id.name, null);
  1271. this.scope.definitions.add(statement.id.name);
  1272. }
  1273. }
  1274. walkClassDeclaration(statement) {
  1275. this.walkClass(statement);
  1276. }
  1277. prewalkSwitchCases(switchCases) {
  1278. for (let index = 0, len = switchCases.length; index < len; index++) {
  1279. const switchCase = switchCases[index];
  1280. this.prewalkStatements(switchCase.consequent);
  1281. }
  1282. }
  1283. walkSwitchCases(switchCases) {
  1284. for (let index = 0, len = switchCases.length; index < len; index++) {
  1285. const switchCase = switchCases[index];
  1286. if (switchCase.test) {
  1287. this.walkExpression(switchCase.test);
  1288. }
  1289. this.walkStatements(switchCase.consequent);
  1290. }
  1291. }
  1292. walkCatchClause(catchClause) {
  1293. // Error binding is optional in catch clause since ECMAScript 2019
  1294. const errorBinding =
  1295. catchClause.param === null ? EMPTY_ARRAY : [catchClause.param];
  1296. this.inScope(errorBinding, () => {
  1297. this.prewalkStatement(catchClause.body);
  1298. this.walkStatement(catchClause.body);
  1299. });
  1300. }
  1301. walkPattern(pattern) {
  1302. switch (pattern.type) {
  1303. case "ArrayPattern":
  1304. this.walkArrayPattern(pattern);
  1305. break;
  1306. case "AssignmentPattern":
  1307. this.walkAssignmentPattern(pattern);
  1308. break;
  1309. case "MemberExpression":
  1310. this.walkMemberExpression(pattern);
  1311. break;
  1312. case "ObjectPattern":
  1313. this.walkObjectPattern(pattern);
  1314. break;
  1315. case "RestElement":
  1316. this.walkRestElement(pattern);
  1317. break;
  1318. }
  1319. }
  1320. walkAssignmentPattern(pattern) {
  1321. this.walkExpression(pattern.right);
  1322. this.walkPattern(pattern.left);
  1323. }
  1324. walkObjectPattern(pattern) {
  1325. for (let i = 0, len = pattern.properties.length; i < len; i++) {
  1326. const prop = pattern.properties[i];
  1327. if (prop) {
  1328. if (prop.computed) this.walkExpression(prop.key);
  1329. if (prop.value) this.walkPattern(prop.value);
  1330. }
  1331. }
  1332. }
  1333. walkArrayPattern(pattern) {
  1334. for (let i = 0, len = pattern.elements.length; i < len; i++) {
  1335. const element = pattern.elements[i];
  1336. if (element) this.walkPattern(element);
  1337. }
  1338. }
  1339. walkRestElement(pattern) {
  1340. this.walkPattern(pattern.argument);
  1341. }
  1342. walkExpressions(expressions) {
  1343. for (const expression of expressions) {
  1344. if (expression) {
  1345. this.walkExpression(expression);
  1346. }
  1347. }
  1348. }
  1349. walkExpression(expression) {
  1350. switch (expression.type) {
  1351. case "ArrayExpression":
  1352. this.walkArrayExpression(expression);
  1353. break;
  1354. case "ArrowFunctionExpression":
  1355. this.walkArrowFunctionExpression(expression);
  1356. break;
  1357. case "AssignmentExpression":
  1358. this.walkAssignmentExpression(expression);
  1359. break;
  1360. case "AwaitExpression":
  1361. this.walkAwaitExpression(expression);
  1362. break;
  1363. case "BinaryExpression":
  1364. this.walkBinaryExpression(expression);
  1365. break;
  1366. case "CallExpression":
  1367. this.walkCallExpression(expression);
  1368. break;
  1369. case "ClassExpression":
  1370. this.walkClassExpression(expression);
  1371. break;
  1372. case "ConditionalExpression":
  1373. this.walkConditionalExpression(expression);
  1374. break;
  1375. case "FunctionExpression":
  1376. this.walkFunctionExpression(expression);
  1377. break;
  1378. case "Identifier":
  1379. this.walkIdentifier(expression);
  1380. break;
  1381. case "LogicalExpression":
  1382. this.walkLogicalExpression(expression);
  1383. break;
  1384. case "MemberExpression":
  1385. this.walkMemberExpression(expression);
  1386. break;
  1387. case "NewExpression":
  1388. this.walkNewExpression(expression);
  1389. break;
  1390. case "ObjectExpression":
  1391. this.walkObjectExpression(expression);
  1392. break;
  1393. case "SequenceExpression":
  1394. this.walkSequenceExpression(expression);
  1395. break;
  1396. case "SpreadElement":
  1397. this.walkSpreadElement(expression);
  1398. break;
  1399. case "TaggedTemplateExpression":
  1400. this.walkTaggedTemplateExpression(expression);
  1401. break;
  1402. case "TemplateLiteral":
  1403. this.walkTemplateLiteral(expression);
  1404. break;
  1405. case "ThisExpression":
  1406. this.walkThisExpression(expression);
  1407. break;
  1408. case "UnaryExpression":
  1409. this.walkUnaryExpression(expression);
  1410. break;
  1411. case "UpdateExpression":
  1412. this.walkUpdateExpression(expression);
  1413. break;
  1414. case "YieldExpression":
  1415. this.walkYieldExpression(expression);
  1416. break;
  1417. }
  1418. }
  1419. walkAwaitExpression(expression) {
  1420. this.walkExpression(expression.argument);
  1421. }
  1422. walkArrayExpression(expression) {
  1423. if (expression.elements) {
  1424. this.walkExpressions(expression.elements);
  1425. }
  1426. }
  1427. walkSpreadElement(expression) {
  1428. if (expression.argument) {
  1429. this.walkExpression(expression.argument);
  1430. }
  1431. }
  1432. walkObjectExpression(expression) {
  1433. for (
  1434. let propIndex = 0, len = expression.properties.length;
  1435. propIndex < len;
  1436. propIndex++
  1437. ) {
  1438. const prop = expression.properties[propIndex];
  1439. if (prop.type === "SpreadElement") {
  1440. this.walkExpression(prop.argument);
  1441. continue;
  1442. }
  1443. if (prop.computed) {
  1444. this.walkExpression(prop.key);
  1445. }
  1446. if (prop.shorthand) {
  1447. this.scope.inShorthand = true;
  1448. }
  1449. this.walkExpression(prop.value);
  1450. if (prop.shorthand) {
  1451. this.scope.inShorthand = false;
  1452. }
  1453. }
  1454. }
  1455. walkFunctionExpression(expression) {
  1456. const wasTopLevel = this.scope.topLevelScope;
  1457. this.scope.topLevelScope = false;
  1458. this.inScope(expression.params, () => {
  1459. for (const param of expression.params) {
  1460. this.walkPattern(param);
  1461. }
  1462. if (expression.body.type === "BlockStatement") {
  1463. this.detectStrictMode(expression.body.body);
  1464. this.prewalkStatement(expression.body);
  1465. this.walkStatement(expression.body);
  1466. } else {
  1467. this.walkExpression(expression.body);
  1468. }
  1469. });
  1470. this.scope.topLevelScope = wasTopLevel;
  1471. }
  1472. walkArrowFunctionExpression(expression) {
  1473. this.inScope(expression.params, () => {
  1474. for (const param of expression.params) {
  1475. this.walkPattern(param);
  1476. }
  1477. if (expression.body.type === "BlockStatement") {
  1478. this.detectStrictMode(expression.body.body);
  1479. this.prewalkStatement(expression.body);
  1480. this.walkStatement(expression.body);
  1481. } else {
  1482. this.walkExpression(expression.body);
  1483. }
  1484. });
  1485. }
  1486. walkSequenceExpression(expression) {
  1487. if (expression.expressions) this.walkExpressions(expression.expressions);
  1488. }
  1489. walkUpdateExpression(expression) {
  1490. this.walkExpression(expression.argument);
  1491. }
  1492. walkUnaryExpression(expression) {
  1493. if (expression.operator === "typeof") {
  1494. const exprName = this.getNameForExpression(expression.argument);
  1495. if (exprName && exprName.free) {
  1496. const hook = this.hooks.typeof.get(exprName.name);
  1497. if (hook !== undefined) {
  1498. const result = hook.call(expression);
  1499. if (result === true) return;
  1500. }
  1501. }
  1502. }
  1503. this.walkExpression(expression.argument);
  1504. }
  1505. walkLeftRightExpression(expression) {
  1506. this.walkExpression(expression.left);
  1507. this.walkExpression(expression.right);
  1508. }
  1509. walkBinaryExpression(expression) {
  1510. this.walkLeftRightExpression(expression);
  1511. }
  1512. walkLogicalExpression(expression) {
  1513. this.walkLeftRightExpression(expression);
  1514. }
  1515. walkAssignmentExpression(expression) {
  1516. const renameIdentifier = this.getRenameIdentifier(expression.right);
  1517. if (expression.left.type === "Identifier" && renameIdentifier) {
  1518. const hook = this.hooks.canRename.get(renameIdentifier);
  1519. if (hook !== undefined && hook.call(expression.right)) {
  1520. // renaming "a = b;"
  1521. const hook = this.hooks.rename.get(renameIdentifier);
  1522. if (hook === undefined || !hook.call(expression.right)) {
  1523. this.scope.renames.set(expression.left.name, renameIdentifier);
  1524. this.scope.definitions.delete(expression.left.name);
  1525. }
  1526. return;
  1527. }
  1528. }
  1529. if (expression.left.type === "Identifier") {
  1530. const assignedHook = this.hooks.assigned.get(expression.left.name);
  1531. if (assignedHook === undefined || !assignedHook.call(expression)) {
  1532. this.walkExpression(expression.right);
  1533. }
  1534. this.scope.renames.set(expression.left.name, null);
  1535. const assignHook = this.hooks.assign.get(expression.left.name);
  1536. if (assignHook === undefined || !assignHook.call(expression)) {
  1537. this.walkExpression(expression.left);
  1538. }
  1539. return;
  1540. }
  1541. this.walkExpression(expression.right);
  1542. this.walkPattern(expression.left);
  1543. this.enterPattern(expression.left, (name, decl) => {
  1544. this.scope.renames.set(name, null);
  1545. });
  1546. }
  1547. walkConditionalExpression(expression) {
  1548. const result = this.hooks.expressionConditionalOperator.call(expression);
  1549. if (result === undefined) {
  1550. this.walkExpression(expression.test);
  1551. this.walkExpression(expression.consequent);
  1552. if (expression.alternate) {
  1553. this.walkExpression(expression.alternate);
  1554. }
  1555. } else {
  1556. if (result) {
  1557. this.walkExpression(expression.consequent);
  1558. } else if (expression.alternate) {
  1559. this.walkExpression(expression.alternate);
  1560. }
  1561. }
  1562. }
  1563. walkNewExpression(expression) {
  1564. const callee = this.evaluateExpression(expression.callee);
  1565. if (callee.isIdentifier()) {
  1566. const hook = this.hooks.new.get(callee.identifier);
  1567. if (hook !== undefined) {
  1568. const result = hook.call(expression);
  1569. if (result === true) {
  1570. return;
  1571. }
  1572. }
  1573. }
  1574. this.walkExpression(expression.callee);
  1575. if (expression.arguments) {
  1576. this.walkExpressions(expression.arguments);
  1577. }
  1578. }
  1579. walkYieldExpression(expression) {
  1580. if (expression.argument) {
  1581. this.walkExpression(expression.argument);
  1582. }
  1583. }
  1584. walkTemplateLiteral(expression) {
  1585. if (expression.expressions) {
  1586. this.walkExpressions(expression.expressions);
  1587. }
  1588. }
  1589. walkTaggedTemplateExpression(expression) {
  1590. if (expression.tag) {
  1591. this.walkExpression(expression.tag);
  1592. }
  1593. if (expression.quasi && expression.quasi.expressions) {
  1594. this.walkExpressions(expression.quasi.expressions);
  1595. }
  1596. }
  1597. walkClassExpression(expression) {
  1598. this.walkClass(expression);
  1599. }
  1600. _walkIIFE(functionExpression, options, currentThis) {
  1601. const renameArgOrThis = argOrThis => {
  1602. const renameIdentifier = this.getRenameIdentifier(argOrThis);
  1603. if (renameIdentifier) {
  1604. const hook = this.hooks.canRename.get(renameIdentifier);
  1605. if (hook !== undefined && hook.call(argOrThis)) {
  1606. const hook = this.hooks.rename.get(renameIdentifier);
  1607. if (hook === undefined || !hook.call(argOrThis)) {
  1608. return renameIdentifier;
  1609. }
  1610. }
  1611. }
  1612. this.walkExpression(argOrThis);
  1613. };
  1614. const params = functionExpression.params;
  1615. const renameThis = currentThis ? renameArgOrThis(currentThis) : null;
  1616. const args = options.map(renameArgOrThis);
  1617. const wasTopLevel = this.scope.topLevelScope;
  1618. this.scope.topLevelScope = false;
  1619. this.inScope(params.filter((identifier, idx) => !args[idx]), () => {
  1620. if (renameThis) {
  1621. this.scope.renames.set("this", renameThis);
  1622. }
  1623. for (let i = 0; i < args.length; i++) {
  1624. const param = args[i];
  1625. if (!param) continue;
  1626. if (!params[i] || params[i].type !== "Identifier") continue;
  1627. this.scope.renames.set(params[i].name, param);
  1628. }
  1629. if (functionExpression.body.type === "BlockStatement") {
  1630. this.prewalkStatement(functionExpression.body);
  1631. this.walkStatement(functionExpression.body);
  1632. } else {
  1633. this.walkExpression(functionExpression.body);
  1634. }
  1635. });
  1636. this.scope.topLevelScope = wasTopLevel;
  1637. }
  1638. walkCallExpression(expression) {
  1639. if (
  1640. expression.callee.type === "MemberExpression" &&
  1641. expression.callee.object.type === "FunctionExpression" &&
  1642. !expression.callee.computed &&
  1643. (expression.callee.property.name === "call" ||
  1644. expression.callee.property.name === "bind") &&
  1645. expression.arguments.length > 0
  1646. ) {
  1647. // (function(…) { }.call/bind(?, …))
  1648. this._walkIIFE(
  1649. expression.callee.object,
  1650. expression.arguments.slice(1),
  1651. expression.arguments[0]
  1652. );
  1653. } else if (expression.callee.type === "FunctionExpression") {
  1654. // (function(…) { }(…))
  1655. this._walkIIFE(expression.callee, expression.arguments, null);
  1656. } else if (expression.callee.type === "Import") {
  1657. let result = this.hooks.importCall.call(expression);
  1658. if (result === true) return;
  1659. if (expression.arguments) this.walkExpressions(expression.arguments);
  1660. } else {
  1661. const callee = this.evaluateExpression(expression.callee);
  1662. if (callee.isIdentifier()) {
  1663. const callHook = this.hooks.call.get(callee.identifier);
  1664. if (callHook !== undefined) {
  1665. let result = callHook.call(expression);
  1666. if (result === true) return;
  1667. }
  1668. let identifier = callee.identifier.replace(/\.[^.]+$/, "");
  1669. if (identifier !== callee.identifier) {
  1670. const callAnyHook = this.hooks.callAnyMember.get(identifier);
  1671. if (callAnyHook !== undefined) {
  1672. let result = callAnyHook.call(expression);
  1673. if (result === true) return;
  1674. }
  1675. }
  1676. }
  1677. if (expression.callee) this.walkExpression(expression.callee);
  1678. if (expression.arguments) this.walkExpressions(expression.arguments);
  1679. }
  1680. }
  1681. walkMemberExpression(expression) {
  1682. const exprName = this.getNameForExpression(expression);
  1683. if (exprName && exprName.free) {
  1684. const expressionHook = this.hooks.expression.get(exprName.name);
  1685. if (expressionHook !== undefined) {
  1686. const result = expressionHook.call(expression);
  1687. if (result === true) return;
  1688. }
  1689. const expressionAnyMemberHook = this.hooks.expressionAnyMember.get(
  1690. exprName.nameGeneral
  1691. );
  1692. if (expressionAnyMemberHook !== undefined) {
  1693. const result = expressionAnyMemberHook.call(expression);
  1694. if (result === true) return;
  1695. }
  1696. }
  1697. this.walkExpression(expression.object);
  1698. if (expression.computed === true) this.walkExpression(expression.property);
  1699. }
  1700. walkThisExpression(expression) {
  1701. const expressionHook = this.hooks.expression.get("this");
  1702. if (expressionHook !== undefined) {
  1703. expressionHook.call(expression);
  1704. }
  1705. }
  1706. walkIdentifier(expression) {
  1707. if (!this.scope.definitions.has(expression.name)) {
  1708. const hook = this.hooks.expression.get(
  1709. this.scope.renames.get(expression.name) || expression.name
  1710. );
  1711. if (hook !== undefined) {
  1712. const result = hook.call(expression);
  1713. if (result === true) return;
  1714. }
  1715. }
  1716. }
  1717. inScope(params, fn) {
  1718. const oldScope = this.scope;
  1719. this.scope = {
  1720. topLevelScope: oldScope.topLevelScope,
  1721. inTry: false,
  1722. inShorthand: false,
  1723. isStrict: oldScope.isStrict,
  1724. definitions: oldScope.definitions.createChild(),
  1725. renames: oldScope.renames.createChild()
  1726. };
  1727. this.scope.renames.set("this", null);
  1728. for (const param of params) {
  1729. if (typeof param !== "string") {
  1730. this.enterPattern(param, param => {
  1731. this.scope.renames.set(param, null);
  1732. this.scope.definitions.add(param);
  1733. });
  1734. } else if (param) {
  1735. this.scope.renames.set(param, null);
  1736. this.scope.definitions.add(param);
  1737. }
  1738. }
  1739. fn();
  1740. this.scope = oldScope;
  1741. }
  1742. detectStrictMode(statements) {
  1743. const isStrict =
  1744. statements.length >= 1 &&
  1745. statements[0].type === "ExpressionStatement" &&
  1746. statements[0].expression.type === "Literal" &&
  1747. statements[0].expression.value === "use strict";
  1748. if (isStrict) {
  1749. this.scope.isStrict = true;
  1750. }
  1751. }
  1752. enterPattern(pattern, onIdent) {
  1753. if (!pattern) return;
  1754. switch (pattern.type) {
  1755. case "ArrayPattern":
  1756. this.enterArrayPattern(pattern, onIdent);
  1757. break;
  1758. case "AssignmentPattern":
  1759. this.enterAssignmentPattern(pattern, onIdent);
  1760. break;
  1761. case "Identifier":
  1762. this.enterIdentifier(pattern, onIdent);
  1763. break;
  1764. case "ObjectPattern":
  1765. this.enterObjectPattern(pattern, onIdent);
  1766. break;
  1767. case "RestElement":
  1768. this.enterRestElement(pattern, onIdent);
  1769. break;
  1770. }
  1771. }
  1772. enterIdentifier(pattern, onIdent) {
  1773. onIdent(pattern.name, pattern);
  1774. }
  1775. enterObjectPattern(pattern, onIdent) {
  1776. for (
  1777. let propIndex = 0, len = pattern.properties.length;
  1778. propIndex < len;
  1779. propIndex++
  1780. ) {
  1781. const prop = pattern.properties[propIndex];
  1782. this.enterPattern(prop.value, onIdent);
  1783. }
  1784. }
  1785. enterArrayPattern(pattern, onIdent) {
  1786. for (
  1787. let elementIndex = 0, len = pattern.elements.length;
  1788. elementIndex < len;
  1789. elementIndex++
  1790. ) {
  1791. const element = pattern.elements[elementIndex];
  1792. this.enterPattern(element, onIdent);
  1793. }
  1794. }
  1795. enterRestElement(pattern, onIdent) {
  1796. this.enterPattern(pattern.argument, onIdent);
  1797. }
  1798. enterAssignmentPattern(pattern, onIdent) {
  1799. this.enterPattern(pattern.left, onIdent);
  1800. }
  1801. evaluateExpression(expression) {
  1802. try {
  1803. const hook = this.hooks.evaluate.get(expression.type);
  1804. if (hook !== undefined) {
  1805. const result = hook.call(expression);
  1806. if (result !== undefined) return result;
  1807. }
  1808. } catch (e) {
  1809. console.warn(e);
  1810. // ignore error
  1811. }
  1812. return new BasicEvaluatedExpression().setRange(expression.range);
  1813. }
  1814. parseString(expression) {
  1815. switch (expression.type) {
  1816. case "BinaryExpression":
  1817. if (expression.operator === "+") {
  1818. return (
  1819. this.parseString(expression.left) +
  1820. this.parseString(expression.right)
  1821. );
  1822. }
  1823. break;
  1824. case "Literal":
  1825. return expression.value + "";
  1826. }
  1827. throw new Error(
  1828. expression.type + " is not supported as parameter for require"
  1829. );
  1830. }
  1831. parseCalculatedString(expression) {
  1832. switch (expression.type) {
  1833. case "BinaryExpression":
  1834. if (expression.operator === "+") {
  1835. const left = this.parseCalculatedString(expression.left);
  1836. const right = this.parseCalculatedString(expression.right);
  1837. if (left.code) {
  1838. return {
  1839. range: left.range,
  1840. value: left.value,
  1841. code: true,
  1842. conditional: false
  1843. };
  1844. } else if (right.code) {
  1845. return {
  1846. range: [
  1847. left.range[0],
  1848. right.range ? right.range[1] : left.range[1]
  1849. ],
  1850. value: left.value + right.value,
  1851. code: true,
  1852. conditional: false
  1853. };
  1854. } else {
  1855. return {
  1856. range: [left.range[0], right.range[1]],
  1857. value: left.value + right.value,
  1858. code: false,
  1859. conditional: false
  1860. };
  1861. }
  1862. }
  1863. break;
  1864. case "ConditionalExpression": {
  1865. const consequent = this.parseCalculatedString(expression.consequent);
  1866. const alternate = this.parseCalculatedString(expression.alternate);
  1867. const items = [];
  1868. if (consequent.conditional) {
  1869. items.push(...consequent.conditional);
  1870. } else if (!consequent.code) {
  1871. items.push(consequent);
  1872. } else {
  1873. break;
  1874. }
  1875. if (alternate.conditional) {
  1876. items.push(...alternate.conditional);
  1877. } else if (!alternate.code) {
  1878. items.push(alternate);
  1879. } else {
  1880. break;
  1881. }
  1882. return {
  1883. range: undefined,
  1884. value: "",
  1885. code: true,
  1886. conditional: items
  1887. };
  1888. }
  1889. case "Literal":
  1890. return {
  1891. range: expression.range,
  1892. value: expression.value + "",
  1893. code: false,
  1894. conditional: false
  1895. };
  1896. }
  1897. return {
  1898. range: undefined,
  1899. value: "",
  1900. code: true,
  1901. conditional: false
  1902. };
  1903. }
  1904. parse(source, initialState) {
  1905. let ast;
  1906. let comments;
  1907. if (typeof source === "object" && source !== null) {
  1908. ast = source;
  1909. comments = source.comments;
  1910. } else {
  1911. comments = [];
  1912. ast = Parser.parse(source, {
  1913. sourceType: this.sourceType,
  1914. onComment: comments
  1915. });
  1916. }
  1917. const oldScope = this.scope;
  1918. const oldState = this.state;
  1919. const oldComments = this.comments;
  1920. this.scope = {
  1921. topLevelScope: true,
  1922. inTry: false,
  1923. inShorthand: false,
  1924. isStrict: false,
  1925. definitions: new StackedSetMap(),
  1926. renames: new StackedSetMap()
  1927. };
  1928. const state = (this.state = initialState || {});
  1929. this.comments = comments;
  1930. if (this.hooks.program.call(ast, comments) === undefined) {
  1931. this.detectStrictMode(ast.body);
  1932. this.prewalkStatements(ast.body);
  1933. this.walkStatements(ast.body);
  1934. }
  1935. this.scope = oldScope;
  1936. this.state = oldState;
  1937. this.comments = oldComments;
  1938. return state;
  1939. }
  1940. evaluate(source) {
  1941. const ast = Parser.parse("(" + source + ")", {
  1942. sourceType: this.sourceType,
  1943. locations: false
  1944. });
  1945. if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
  1946. throw new Error("evaluate: Source is not a expression");
  1947. }
  1948. return this.evaluateExpression(ast.body[0].expression);
  1949. }
  1950. getComments(range) {
  1951. return this.comments.filter(
  1952. comment => comment.range[0] >= range[0] && comment.range[1] <= range[1]
  1953. );
  1954. }
  1955. parseCommentOptions(range) {
  1956. const comments = this.getComments(range);
  1957. if (comments.length === 0) {
  1958. return EMPTY_COMMENT_OPTIONS;
  1959. }
  1960. let options = {};
  1961. let errors = [];
  1962. for (const comment of comments) {
  1963. const { value } = comment;
  1964. if (value && webpackCommentRegExp.test(value)) {
  1965. // try compile only if webpack options comment is present
  1966. try {
  1967. const val = vm.runInNewContext(`(function(){return {${value}};})()`);
  1968. Object.assign(options, val);
  1969. } catch (e) {
  1970. e.comment = comment;
  1971. errors.push(e);
  1972. }
  1973. }
  1974. }
  1975. return { options, errors };
  1976. }
  1977. getNameForExpression(expression) {
  1978. let expr = expression;
  1979. const exprName = [];
  1980. while (
  1981. expr.type === "MemberExpression" &&
  1982. expr.property.type === (expr.computed ? "Literal" : "Identifier")
  1983. ) {
  1984. exprName.push(expr.computed ? expr.property.value : expr.property.name);
  1985. expr = expr.object;
  1986. }
  1987. let free;
  1988. if (expr.type === "Identifier") {
  1989. free = !this.scope.definitions.has(expr.name);
  1990. exprName.push(this.scope.renames.get(expr.name) || expr.name);
  1991. } else if (
  1992. expr.type === "ThisExpression" &&
  1993. this.scope.renames.get("this")
  1994. ) {
  1995. free = true;
  1996. exprName.push(this.scope.renames.get("this"));
  1997. } else if (expr.type === "ThisExpression") {
  1998. free = this.scope.topLevelScope;
  1999. exprName.push("this");
  2000. } else {
  2001. return null;
  2002. }
  2003. let prefix = "";
  2004. for (let i = exprName.length - 1; i >= 2; i--) {
  2005. prefix += exprName[i] + ".";
  2006. }
  2007. if (exprName.length > 1) {
  2008. prefix += exprName[1];
  2009. }
  2010. const name = prefix ? prefix + "." + exprName[0] : exprName[0];
  2011. const nameGeneral = prefix;
  2012. return {
  2013. name,
  2014. nameGeneral,
  2015. free
  2016. };
  2017. }
  2018. static parse(code, options) {
  2019. const type = options ? options.sourceType : "module";
  2020. const parserOptions = Object.assign(
  2021. Object.create(null),
  2022. defaultParserOptions,
  2023. options
  2024. );
  2025. if (type === "auto") {
  2026. parserOptions.sourceType = "module";
  2027. }
  2028. let ast;
  2029. let error;
  2030. let threw = false;
  2031. try {
  2032. ast = acorn.parse(code, parserOptions);
  2033. } catch (e) {
  2034. error = e;
  2035. threw = true;
  2036. }
  2037. if (threw && type === "auto") {
  2038. parserOptions.sourceType = "script";
  2039. if (Array.isArray(parserOptions.onComment)) {
  2040. parserOptions.onComment.length = 0;
  2041. }
  2042. try {
  2043. ast = acorn.parse(code, parserOptions);
  2044. threw = false;
  2045. } catch (e) {
  2046. threw = true;
  2047. }
  2048. }
  2049. if (threw) {
  2050. throw error;
  2051. }
  2052. return ast;
  2053. }
  2054. }
  2055. // TODO remove in webpack 5
  2056. Object.defineProperty(Parser.prototype, "getCommentOptions", {
  2057. configurable: false,
  2058. value: util.deprecate(
  2059. /**
  2060. * @deprecated
  2061. * @param {TODO} range Range
  2062. * @returns {void}
  2063. * @this {Parser}
  2064. */
  2065. function(range) {
  2066. return this.parseCommentOptions(range).options;
  2067. },
  2068. "Parser.getCommentOptions: Use Parser.parseCommentOptions(range) instead"
  2069. )
  2070. });
  2071. module.exports = Parser;