ScopeAnalyzer.js 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Haijie Xie @hai-x
  4. */
  5. "use strict";
  6. /**
  7. * @import {
  8. * Function as ESTreeFunction,
  9. * Identifier,
  10. * Node,
  11. * Program
  12. * } from "estree"
  13. */
  14. /**
  15. * @typedef {"global" | "module" | "function" | "function-expression-name" | "block" | "switch" | "catch" | "with" | "for" | "class" | "class-field-initializer" | "class-static-block"} ScopeType
  16. */
  17. /**
  18. * Acorn records every node's offset as `start`, but the nodes are typed as
  19. * `estree`, which models the spec and has only the optional `range`/`loc`.
  20. * Intersect with this to read the offset the parser really wrote.
  21. * @typedef {{ start: number }} Offset
  22. */
  23. /**
  24. * A node's visitable slots, as `CHILD_KEYS` names them.
  25. * @typedef {Record<string, Node | Node[] | null | undefined>} NodeChildren
  26. */
  27. /**
  28. * Child properties to visit for node types with no special scoping or
  29. * referencing behaviour. Types handled by the walker's `switch` never reach
  30. * this table.
  31. * @type {Record<string, string[]>}
  32. */
  33. const CHILD_KEYS = {
  34. ArrayExpression: ["elements"],
  35. ArrayPattern: ["elements"],
  36. ArrowFunctionExpression: ["params", "body"],
  37. AssignmentExpression: ["left", "right"],
  38. AssignmentPattern: ["left", "right"],
  39. AwaitExpression: ["argument"],
  40. BinaryExpression: ["left", "right"],
  41. BlockStatement: ["body"],
  42. BreakStatement: ["label"],
  43. CallExpression: ["callee", "arguments"],
  44. CatchClause: ["param", "body"],
  45. ChainExpression: ["expression"],
  46. ClassBody: ["body"],
  47. ClassDeclaration: ["id", "superClass", "body"],
  48. ClassExpression: ["id", "superClass", "body"],
  49. ConditionalExpression: ["test", "consequent", "alternate"],
  50. ContinueStatement: ["label"],
  51. DoWhileStatement: ["body", "test"],
  52. EmptyStatement: [],
  53. ExportAllDeclaration: ["source"],
  54. ExportDefaultDeclaration: ["declaration"],
  55. ExportNamedDeclaration: ["declaration", "specifiers", "source"],
  56. ExportSpecifier: ["exported", "local"],
  57. ExpressionStatement: ["expression"],
  58. ForInStatement: ["left", "right", "body"],
  59. ForOfStatement: ["left", "right", "body"],
  60. ForStatement: ["init", "test", "update", "body"],
  61. FunctionDeclaration: ["id", "params", "body"],
  62. FunctionExpression: ["id", "params", "body"],
  63. Identifier: [],
  64. IfStatement: ["test", "consequent", "alternate"],
  65. ImportDeclaration: ["specifiers", "source"],
  66. ImportDefaultSpecifier: ["local"],
  67. ImportExpression: ["source", "options"],
  68. ImportNamespaceSpecifier: ["local"],
  69. ImportSpecifier: ["imported", "local"],
  70. LabeledStatement: ["label", "body"],
  71. Literal: [],
  72. LogicalExpression: ["left", "right"],
  73. MemberExpression: ["object", "property"],
  74. MetaProperty: ["meta", "property"],
  75. MethodDefinition: ["key", "value"],
  76. NewExpression: ["callee", "arguments"],
  77. ObjectExpression: ["properties"],
  78. ObjectPattern: ["properties"],
  79. PrivateIdentifier: [],
  80. Program: ["body"],
  81. Property: ["key", "value"],
  82. PropertyDefinition: ["key", "value"],
  83. RestElement: ["argument"],
  84. ReturnStatement: ["argument"],
  85. SequenceExpression: ["expressions"],
  86. SpreadElement: ["argument"],
  87. StaticBlock: ["body"],
  88. Super: [],
  89. SwitchCase: ["test", "consequent"],
  90. SwitchStatement: ["discriminant", "cases"],
  91. TaggedTemplateExpression: ["tag", "quasi"],
  92. TemplateElement: [],
  93. TemplateLiteral: ["quasis", "expressions"],
  94. ThisExpression: [],
  95. ThrowStatement: ["argument"],
  96. TryStatement: ["block", "handler", "finalizer"],
  97. UnaryExpression: ["argument"],
  98. UpdateExpression: ["argument"],
  99. VariableDeclaration: ["declarations"],
  100. VariableDeclarator: ["id", "init"],
  101. WhileStatement: ["test", "body"],
  102. WithStatement: ["object", "body"],
  103. YieldExpression: ["argument"]
  104. };
  105. /**
  106. * A lexical scope. One shape for every kind, so the property loads in the
  107. * resolution loop stay monomorphic.
  108. */
  109. class Scope {
  110. /**
  111. * @param {ScopeType} type what opened this scope
  112. * @param {Node} block the node that opened this scope
  113. * @param {Scope | null} upper the enclosing scope
  114. * @param {boolean} isVarScope whether `var` and function declarations hoist to here
  115. * @param {boolean} recordEveryReference whether every binding collects its references, not only the ones webpack reads
  116. */
  117. constructor(type, block, upper, isVarScope, recordEveryReference) {
  118. /** @type {ScopeType} */
  119. this.type = type;
  120. /** @type {Node} */
  121. this.block = block;
  122. /** @type {Scope | null} */
  123. this.upper = upper;
  124. /** @type {Scope[]} */
  125. this.childScopes = NO_CHILD_SCOPES;
  126. /** @type {Variable[]} */
  127. this.variables = NO_VARIABLE_LIST;
  128. /** @type {Map<string, Variable> | undefined} the index, once this scope outgrows a scan */
  129. this._index = undefined;
  130. /** @type {Scope} the nearest enclosing scope `var` hoists to */
  131. this.variableScope = isVarScope
  132. ? this
  133. : /** @type {Scope} */ (upper).variableScope;
  134. /**
  135. * For a function scope with parameters, the offset where its body
  136. * starts; `-1` for every other scope. Separates the two regions that
  137. * share this scope, so a reference in the parameter list can be kept
  138. * from resolving to a binding declared in the body — see
  139. * `isHiddenBodyBinding`.
  140. * @type {number}
  141. */
  142. this.paramBoundary = -1;
  143. /**
  144. * Whether this scope's bindings collect their references — only the
  145. * module scope and its direct children are ever asked. See `_resolve`.
  146. * @type {boolean}
  147. */
  148. this._recorded =
  149. recordEveryReference ||
  150. type === "module" ||
  151. (upper !== null && upper.type === "module");
  152. if (upper !== null) {
  153. if (upper.childScopes === NO_CHILD_SCOPES) upper.childScopes = [this];
  154. else upper.childScopes.push(this);
  155. }
  156. }
  157. /**
  158. * @param {string} name name to look up
  159. * @returns {Variable | undefined} the binding this scope declares under that name
  160. */
  161. getBinding(name) {
  162. const index = this._index;
  163. if (index !== undefined) return index.get(name);
  164. const variables = this.variables;
  165. for (let i = 0; i < variables.length; i++) {
  166. const variable = variables[i];
  167. if (variable.name === name) return variable;
  168. }
  169. return undefined;
  170. }
  171. }
  172. /** A binding: one name declared in one scope. */
  173. class Variable {
  174. /**
  175. * @param {string} name the declared name
  176. * @param {Scope} scope the declaring scope
  177. */
  178. constructor(name, scope) {
  179. /** @type {string} */
  180. this.name = name;
  181. /** @type {Identifier[]} declaring occurrences */
  182. this.identifiers = NO_IDENTIFIERS;
  183. /** @type {Reference[]} occurrences that resolved here, at any depth */
  184. this.references = NO_REFERENCES;
  185. /** @type {Scope} */
  186. this.scope = scope;
  187. }
  188. }
  189. /** One identifier occurrence that refers to a binding. */
  190. class Reference {
  191. /**
  192. * @param {Identifier} identifier the identifier node
  193. * @param {Scope} from the scope the identifier was seen in
  194. */
  195. constructor(identifier, from) {
  196. /** @type {Identifier} */
  197. this.identifier = identifier;
  198. /** @type {Scope} */
  199. this.from = from;
  200. /** @type {Variable | undefined} set during resolution, absent when free */
  201. this.resolved = undefined;
  202. }
  203. }
  204. /**
  205. * Returned by `_pattern` for a plain identifier. Never mutated by callers.
  206. * @type {Node[]}
  207. */
  208. const NO_RIGHT_HAND_NODES = [];
  209. /**
  210. * Stand-ins for the collections of a scope that declares nothing and encloses
  211. * nothing — over half of them are one or both. Each is replaced by a real
  212. * collection when the scope first needs it. None of them is ever mutated.
  213. * @type {Variable[]}
  214. */
  215. const NO_VARIABLE_LIST = [];
  216. /** @type {Scope[]} */
  217. const NO_CHILD_SCOPES = [];
  218. /** @type {Reference[]} */
  219. const NO_REFERENCES = [];
  220. /** @type {Identifier[]} */
  221. const NO_IDENTIFIERS = [];
  222. /**
  223. * Above this many bindings a scope indexes them in a `Map`; below it, scanning
  224. * the list is as fast and costs no second structure. ~96% of the scopes that
  225. * bind anything stay below.
  226. */
  227. const INDEX_THRESHOLD = 8;
  228. /**
  229. * What `_pattern` does with each name it binds. An integer rather than a
  230. * callback, so walking a pattern allocates no closure.
  231. */
  232. const PATTERN_DEFINE = 0;
  233. const PATTERN_DEFINE_INIT = 1;
  234. const PATTERN_REFERENCE = 2;
  235. const PATTERN_REFERENCE_PLAIN = 3;
  236. /**
  237. * Adds a binding to a scope, seeding its list on the first one and building an
  238. * index once the scope has outgrown a scan.
  239. * @param {Scope} scope the scope that just declared a name
  240. * @param {Variable} variable the binding it declared
  241. * @returns {void}
  242. */
  243. const addBinding = (scope, variable) => {
  244. const variables = scope.variables;
  245. if (variables === NO_VARIABLE_LIST) {
  246. scope.variables = [variable];
  247. return;
  248. }
  249. variables.push(variable);
  250. const index = scope._index;
  251. if (index !== undefined) {
  252. index.set(variable.name, variable);
  253. return;
  254. }
  255. if (variables.length <= INDEX_THRESHOLD) return;
  256. /** @type {Map<string, Variable>} */
  257. const built = new Map();
  258. for (let i = 0; i < variables.length; i++) {
  259. built.set(variables[i].name, variables[i]);
  260. }
  261. scope._index = built;
  262. };
  263. /**
  264. * Statement types that provably declare nothing in the block that holds them —
  265. * `var` hoists past it, and the rest bind nowhere. Anything else, a syntax
  266. * this list has not heard of included, is assumed to declare.
  267. * @type {Set<string>}
  268. */
  269. const STATEMENTS_WITHOUT_BINDINGS = new Set([
  270. "BlockStatement",
  271. "BreakStatement",
  272. "ContinueStatement",
  273. "DebuggerStatement",
  274. "DoWhileStatement",
  275. "EmptyStatement",
  276. "ExpressionStatement",
  277. "ForInStatement",
  278. "ForOfStatement",
  279. "ForStatement",
  280. "IfStatement",
  281. "ReturnStatement",
  282. "SwitchStatement",
  283. "ThrowStatement",
  284. "TryStatement",
  285. "WhileStatement",
  286. "WithStatement"
  287. ]);
  288. /**
  289. * Whether a statement list needs a scope of its own to hold what it declares.
  290. * @param {(Node | null | undefined)[]} body statement list
  291. * @returns {boolean} true when a scope has to be opened for it
  292. */
  293. const needsScope = (body) => {
  294. for (let i = 0; i < body.length; i++) {
  295. const statement = body[i];
  296. if (statement === null || statement === undefined) continue;
  297. const type = statement.type;
  298. if (type === "VariableDeclaration") {
  299. if (statement.kind !== "var") return true;
  300. } else if (type === "SwitchCase") {
  301. if (needsScope(statement.consequent)) return true;
  302. } else if (!STATEMENTS_WITHOUT_BINDINGS.has(type)) {
  303. return true;
  304. }
  305. }
  306. return false;
  307. };
  308. /**
  309. * Node types that may appear as an assignment or binding target.
  310. * @param {Node} node node to test
  311. * @returns {boolean} true when the node can hold bindings
  312. */
  313. const isPattern = (node) => {
  314. const type = node.type;
  315. return (
  316. type === "Identifier" ||
  317. type === "ObjectPattern" ||
  318. type === "ArrayPattern" ||
  319. type === "SpreadElement" ||
  320. type === "RestElement" ||
  321. type === "AssignmentPattern"
  322. );
  323. };
  324. /**
  325. * Whether a binding found in a function scope is invisible to a reference,
  326. * because the reference sits in the parameter list and the binding is declared
  327. * in the body.
  328. *
  329. * Parameters and body share one scope here, but the language gives them two:
  330. * a function with parameters evaluates them first, and only then creates the
  331. * environment its body declarations live in. So a parameter default reads the
  332. * enclosing scope, never the body.
  333. *
  334. * A name declared in *both* places is not hidden — that is what keeps the `x`
  335. * in `function f(x) { var x = 1; return x }` resolving to the parameter.
  336. * @param {Variable} variable a binding found in a function scope with parameters
  337. * @param {Identifier} identifier the identifier being resolved
  338. * @param {number} boundary source offset where that function's body starts
  339. * @returns {boolean} true when resolution must skip this binding and climb
  340. * @example
  341. * ```js
  342. * const x = 1;
  343. * function f(a = x) { const x = 2; }
  344. * // the default reads the outer `x`; the body's `x` does not exist yet
  345. * ```
  346. */
  347. const isHiddenBodyBinding = (variable, identifier, boundary) => {
  348. // a reference in the body sees everything the scope holds
  349. if (/** @type {Identifier & Offset} */ (identifier).start >= boundary) {
  350. return false;
  351. }
  352. const identifiers = variable.identifiers;
  353. for (let i = 0; i < identifiers.length; i++) {
  354. // declared in the parameter list too, so the parameters do see it
  355. if (/** @type {Identifier & Offset} */ (identifiers[i]).start < boundary) {
  356. return false;
  357. }
  358. }
  359. return true;
  360. };
  361. class ScopeAnalyzer {
  362. /**
  363. * @param {boolean} recordEveryReference whether every binding collects its references
  364. */
  365. constructor(recordEveryReference) {
  366. /** @type {Scope} the scope the walker is currently inside */
  367. this.scope = /** @type {Scope} */ (/** @type {unknown} */ (null));
  368. /** @type {Identifier[]} identifiers awaiting resolution */
  369. this.pendingIdentifiers = [];
  370. /** @type {Scope[]} the scope each pending identifier was seen in */
  371. this.pendingScopes = [];
  372. /** @type {boolean} */
  373. this.recordEveryReference = recordEveryReference;
  374. }
  375. /**
  376. * @param {ScopeType} type scope kind
  377. * @param {Node} block node opening the scope
  378. * @param {boolean} isVarScope whether `var` hoists to here
  379. * @returns {Scope} the new scope, now current
  380. */
  381. _push(type, block, isVarScope) {
  382. const scope = new Scope(
  383. type,
  384. block,
  385. this.scope,
  386. isVarScope,
  387. this.recordEveryReference
  388. );
  389. this.scope = scope;
  390. return scope;
  391. }
  392. /**
  393. * @returns {void}
  394. */
  395. _pop() {
  396. this.scope = /** @type {Scope} */ (this.scope.upper);
  397. }
  398. /**
  399. * Declares a name in a scope, reusing the binding when it already exists
  400. * (`var x; var x;`, a function and its hoisted declaration, and so on).
  401. * @param {Scope} scope scope to declare in
  402. * @param {Node | null} node the declaring identifier, absent for an anonymous `export default` declaration
  403. * @returns {void}
  404. */
  405. _define(scope, node) {
  406. if (node === null || node.type !== "Identifier") return;
  407. const name = node.name;
  408. let variable = scope.getBinding(name);
  409. if (variable === undefined) {
  410. variable = new Variable(name, scope);
  411. addBinding(scope, variable);
  412. }
  413. if (variable.identifiers === NO_IDENTIFIERS) {
  414. variable.identifiers = [/** @type {Identifier} */ (node)];
  415. } else {
  416. variable.identifiers.push(/** @type {Identifier} */ (node));
  417. }
  418. }
  419. /**
  420. * Records an identifier occurrence to be resolved once the walk is done.
  421. * @param {Node} node the identifier
  422. * @returns {void}
  423. */
  424. _reference(node) {
  425. this.pendingIdentifiers.push(/** @type {Identifier} */ (node));
  426. this.pendingScopes.push(this.scope);
  427. }
  428. /**
  429. * Binds or references one name of a pattern, as `mode` asks.
  430. * @param {Node} node the bound identifier
  431. * @param {number} defaults number of enclosing defaults
  432. * @param {number} mode one of the `PATTERN_*` constants
  433. * @param {Scope} target scope to declare in
  434. * @returns {void}
  435. */
  436. _bind(node, defaults, mode, target) {
  437. if (mode <= PATTERN_DEFINE_INIT) this._define(target, node);
  438. if (mode !== PATTERN_REFERENCE_PLAIN) {
  439. for (let d = 0; d < defaults; d++) this._reference(node);
  440. }
  441. if (mode >= PATTERN_DEFINE_INIT) this._reference(node);
  442. }
  443. /**
  444. * Walks a binding pattern, binding each name it holds. The expressions
  445. * inside it are returned rather than visited, so every name binds first.
  446. * @param {Node} root the pattern
  447. * @param {number} mode one of the `PATTERN_*` constants
  448. * @param {Scope} target scope to declare in
  449. * @returns {Node[]} expressions still to visit
  450. */
  451. _pattern(root, mode, target) {
  452. // a bare identifier is ~97% of calls and holds no expressions, so it
  453. // needs neither the result array nor a walk
  454. if (root.type === "Identifier") {
  455. this._bind(root, 0, mode, target);
  456. return NO_RIGHT_HAND_NODES;
  457. }
  458. /** @type {Node[]} */
  459. const rightHandNodes = [];
  460. this._patternWalk(root, mode, target, 0, rightHandNodes);
  461. return rightHandNodes;
  462. }
  463. /**
  464. * @param {Node | null} node current pattern node
  465. * @param {number} mode one of the `PATTERN_*` constants
  466. * @param {Scope} target scope to declare in
  467. * @param {number} defaults number of enclosing defaults
  468. * @param {Node[]} rightHandNodes collects the expressions still to visit
  469. * @returns {void}
  470. */
  471. _patternWalk(node, mode, target, defaults, rightHandNodes) {
  472. if (node === null || node === undefined) return;
  473. switch (node.type) {
  474. case "Identifier":
  475. this._bind(node, defaults, mode, target);
  476. return;
  477. case "ObjectPattern":
  478. for (const property of node.properties) {
  479. this._patternWalk(property, mode, target, defaults, rightHandNodes);
  480. }
  481. return;
  482. case "ArrayPattern":
  483. for (const element of node.elements) {
  484. this._patternWalk(element, mode, target, defaults, rightHandNodes);
  485. }
  486. return;
  487. case "Property":
  488. if (node.computed) rightHandNodes.push(node.key);
  489. this._patternWalk(node.value, mode, target, defaults, rightHandNodes);
  490. return;
  491. case "AssignmentPattern":
  492. this._patternWalk(
  493. node.left,
  494. mode,
  495. target,
  496. defaults + 1,
  497. rightHandNodes
  498. );
  499. rightHandNodes.push(node.right);
  500. return;
  501. case "RestElement":
  502. case "SpreadElement":
  503. this._patternWalk(
  504. node.argument,
  505. mode,
  506. target,
  507. defaults,
  508. rightHandNodes
  509. );
  510. return;
  511. case "MemberExpression":
  512. // the object is only read; the write lands on its property
  513. if (node.computed) rightHandNodes.push(node.property);
  514. rightHandNodes.push(node.object);
  515. return;
  516. // assignment targets the parser reports as expressions
  517. case "ArrayExpression":
  518. for (const element of node.elements) {
  519. this._patternWalk(element, mode, target, defaults, rightHandNodes);
  520. }
  521. return;
  522. case "ObjectExpression":
  523. for (const property of node.properties) {
  524. this._patternWalk(property, mode, target, defaults, rightHandNodes);
  525. }
  526. return;
  527. case "AssignmentExpression":
  528. this._patternWalk(
  529. node.left,
  530. mode,
  531. target,
  532. defaults + 1,
  533. rightHandNodes
  534. );
  535. rightHandNodes.push(node.right);
  536. return;
  537. default:
  538. rightHandNodes.push(node);
  539. }
  540. }
  541. /**
  542. * @param {(Node | null | undefined)[]} nodes statement or expression list, holes and all
  543. * @returns {void}
  544. */
  545. _visitAll(nodes) {
  546. for (let i = 0; i < nodes.length; i++) {
  547. const node = nodes[i];
  548. if (node !== null && node !== undefined) this._visit(node);
  549. }
  550. }
  551. /**
  552. * A function's parameters and body share one scope, so a named function
  553. * expression gets an extra scope above it holding only its own name.
  554. * @param {ESTreeFunction} node Function node
  555. * @returns {void}
  556. */
  557. _visitFunction(node) {
  558. if (node.type === "FunctionDeclaration") {
  559. // block scoped in ES6, so it lands in the enclosing scope
  560. this._define(this.scope, /** @type {Node} */ (node.id));
  561. }
  562. const named =
  563. node.type === "FunctionExpression" &&
  564. node.id !== null &&
  565. node.id !== undefined;
  566. if (named) {
  567. this._push("function-expression-name", node, false);
  568. this._define(this.scope, /** @type {Node} */ (node.id));
  569. }
  570. const scope = this._push("function", node, true);
  571. if (node.type !== "ArrowFunctionExpression") {
  572. // every non-arrow function has an implicit `arguments`
  573. addBinding(scope, new Variable("arguments", scope));
  574. }
  575. const params = node.params;
  576. if (params.length !== 0) {
  577. scope.paramBoundary = /** @type {Node & Offset} */ (node.body).start;
  578. for (let i = 0; i < params.length; i++) {
  579. const rightHandNodes = this._pattern(params[i], PATTERN_DEFINE, scope);
  580. this._visitAll(rightHandNodes);
  581. }
  582. }
  583. const body = node.body;
  584. if (body.type === "BlockStatement") {
  585. // the body block is the function scope; it gets no scope of its own
  586. this._visitAll(body.body);
  587. } else {
  588. this._visit(body);
  589. }
  590. this._pop();
  591. if (named) this._pop();
  592. }
  593. /**
  594. * The class name is bound twice: outside, so siblings can see the class,
  595. * and inside, so the body and the heritage clause see a binding that an
  596. * outer reassignment cannot change.
  597. * @param {import("estree").ClassDeclaration | import("estree").ClassExpression} node Class node
  598. * @returns {void}
  599. */
  600. _visitClass(node) {
  601. if (node.type === "ClassDeclaration") {
  602. this._define(this.scope, /** @type {Node} */ (node.id));
  603. }
  604. const scope = this._push("class", node, false);
  605. if (node.id !== null && node.id !== undefined) {
  606. this._define(scope, node.id);
  607. }
  608. // the heritage clause is evaluated inside the class scope
  609. if (node.superClass !== null && node.superClass !== undefined) {
  610. this._visit(node.superClass);
  611. }
  612. this._visit(node.body);
  613. this._pop();
  614. }
  615. /**
  616. * @param {import("estree").ForInStatement | import("estree").ForOfStatement} node the loop
  617. * @returns {void}
  618. */
  619. _visitForIn(node) {
  620. const left = node.left;
  621. const lexical = left.type === "VariableDeclaration" && left.kind !== "var";
  622. if (lexical) this._push("for", node, false);
  623. if (left.type === "VariableDeclaration") {
  624. this._visit(left);
  625. // the loop head writes each iteration; right-hand nodes were
  626. // already visited by the declaration above
  627. this._pattern(
  628. left.declarations[0].id,
  629. PATTERN_REFERENCE_PLAIN,
  630. this.scope
  631. );
  632. } else {
  633. const rightHandNodes = this._pattern(left, PATTERN_REFERENCE, this.scope);
  634. this._visitAll(rightHandNodes);
  635. }
  636. this._visit(node.right);
  637. this._visit(node.body);
  638. if (lexical) this._pop();
  639. }
  640. /**
  641. * Fallback for node types the key table does not know, so unfamiliar
  642. * syntax still contributes its references instead of silently vanishing.
  643. * @param {Node} node node of an unknown type
  644. * @returns {void}
  645. */
  646. _visitUnknown(node) {
  647. for (const key in node) {
  648. if (
  649. key === "type" ||
  650. key === "start" ||
  651. key === "end" ||
  652. key === "range" ||
  653. key === "loc" ||
  654. key === "parent" ||
  655. key === "leadingComments" ||
  656. key === "trailingComments"
  657. ) {
  658. continue;
  659. }
  660. if (key === "key" && "computed" in node && node.computed === false) {
  661. continue;
  662. }
  663. const child = /** @type {Record<string, unknown>} */ (
  664. /** @type {unknown} */ (node)
  665. )[key];
  666. if (child === null || typeof child !== "object") continue;
  667. if (Array.isArray(child)) {
  668. for (const item of child) {
  669. if (item !== null && typeof item === "object" && item.type) {
  670. this._visit(item);
  671. }
  672. }
  673. } else if (/** @type {Node} */ (child).type) {
  674. this._visit(/** @type {Node} */ (child));
  675. }
  676. }
  677. }
  678. /**
  679. * @param {Node} node node to visit
  680. * @returns {void}
  681. */
  682. _visit(node) {
  683. // cases are ordered by measured frequency: the chain is a sequence of
  684. // comparisons, so a type that falls through to `default` pays all of them
  685. switch (node.type) {
  686. case "Identifier":
  687. this._reference(node);
  688. return;
  689. // leaves too common to leave at the end of the chain: together they
  690. // are ~14% of all visits, and every case above them is a comparison
  691. case "Literal":
  692. case "ThisExpression":
  693. return;
  694. case "MemberExpression":
  695. this._visit(node.object);
  696. // `a.b` reads `a`; `b` is a property name, not a binding
  697. if (node.computed) this._visit(node.property);
  698. return;
  699. // the types the key table below would otherwise handle, and which
  700. // together are ~31% of all visits — a call alone is 10%
  701. case "CallExpression":
  702. case "NewExpression":
  703. this._visit(node.callee);
  704. this._visitAll(node.arguments);
  705. return;
  706. case "ExpressionStatement":
  707. this._visit(node.expression);
  708. return;
  709. case "BinaryExpression":
  710. case "LogicalExpression":
  711. this._visit(node.left);
  712. this._visit(node.right);
  713. return;
  714. case "ReturnStatement":
  715. case "ThrowStatement":
  716. case "UnaryExpression":
  717. case "AwaitExpression":
  718. case "SpreadElement":
  719. case "YieldExpression":
  720. if (node.argument !== null && node.argument !== undefined) {
  721. this._visit(node.argument);
  722. }
  723. return;
  724. case "IfStatement":
  725. case "ConditionalExpression":
  726. this._visit(node.test);
  727. this._visit(node.consequent);
  728. if (node.alternate !== null && node.alternate !== undefined) {
  729. this._visit(node.alternate);
  730. }
  731. return;
  732. case "SwitchCase":
  733. if (node.test !== null && node.test !== undefined) {
  734. this._visit(node.test);
  735. }
  736. this._visitAll(node.consequent);
  737. return;
  738. case "ArrayExpression":
  739. this._visitAll(node.elements);
  740. return;
  741. case "ObjectExpression":
  742. this._visitAll(node.properties);
  743. return;
  744. case "Property":
  745. case "MethodDefinition":
  746. if (node.computed) this._visit(node.key);
  747. this._visit(node.value);
  748. return;
  749. case "PropertyDefinition":
  750. if (node.computed) this._visit(node.key);
  751. if (node.value !== null && node.value !== undefined) {
  752. // each field initializer runs in its own scope
  753. this._push("class-field-initializer", node.value, true);
  754. this._visit(node.value);
  755. this._pop();
  756. }
  757. return;
  758. case "StaticBlock":
  759. this._push("class-static-block", node, true);
  760. this._visitAll(node.body);
  761. this._pop();
  762. return;
  763. case "BlockStatement": {
  764. const scoped = needsScope(node.body);
  765. if (scoped) this._push("block", node, false);
  766. this._visitAll(node.body);
  767. if (scoped) this._pop();
  768. return;
  769. }
  770. case "SwitchStatement": {
  771. this._visit(node.discriminant);
  772. const scoped = needsScope(node.cases);
  773. if (scoped) this._push("switch", node, false);
  774. this._visitAll(node.cases);
  775. if (scoped) this._pop();
  776. return;
  777. }
  778. case "ForStatement": {
  779. const init = node.init;
  780. const lexical =
  781. init !== null &&
  782. init !== undefined &&
  783. init.type === "VariableDeclaration" &&
  784. init.kind !== "var";
  785. if (lexical) this._push("for", node, false);
  786. if (init !== null && init !== undefined) this._visit(init);
  787. if (node.test !== null && node.test !== undefined) {
  788. this._visit(node.test);
  789. }
  790. if (node.update !== null && node.update !== undefined) {
  791. this._visit(node.update);
  792. }
  793. this._visit(node.body);
  794. if (lexical) this._pop();
  795. return;
  796. }
  797. case "ForInStatement":
  798. case "ForOfStatement":
  799. this._visitForIn(node);
  800. return;
  801. case "VariableDeclaration": {
  802. // `var` hoists to the nearest function-like scope, everything
  803. // else binds right here
  804. const target =
  805. node.kind === "var" ? this.scope.variableScope : this.scope;
  806. for (const declarator of node.declarations) {
  807. const init = declarator.init;
  808. const initialized = init !== null && init !== undefined;
  809. const rightHandNodes = this._pattern(
  810. declarator.id,
  811. initialized ? PATTERN_DEFINE_INIT : PATTERN_DEFINE,
  812. target
  813. );
  814. this._visitAll(rightHandNodes);
  815. if (initialized) this._visit(init);
  816. }
  817. return;
  818. }
  819. case "AssignmentExpression":
  820. if (isPattern(node.left)) {
  821. if (node.operator === "=") {
  822. const rightHandNodes = this._pattern(
  823. node.left,
  824. PATTERN_REFERENCE,
  825. this.scope
  826. );
  827. this._visitAll(rightHandNodes);
  828. } else if (node.left.type === "Identifier") {
  829. // `x += 1` reads and writes the same binding
  830. this._reference(node.left);
  831. } else {
  832. this._visit(node.left);
  833. }
  834. } else {
  835. this._visit(node.left);
  836. }
  837. this._visit(node.right);
  838. return;
  839. case "UpdateExpression":
  840. if (node.argument.type === "Identifier") {
  841. this._reference(node.argument);
  842. } else {
  843. this._visit(node.argument);
  844. }
  845. return;
  846. case "FunctionDeclaration":
  847. case "FunctionExpression":
  848. case "ArrowFunctionExpression":
  849. this._visitFunction(node);
  850. return;
  851. case "ClassDeclaration":
  852. case "ClassExpression":
  853. this._visitClass(node);
  854. return;
  855. case "CatchClause":
  856. this._push("catch", node, false);
  857. if (node.param !== null && node.param !== undefined) {
  858. const rightHandNodes = this._pattern(
  859. node.param,
  860. PATTERN_DEFINE,
  861. this.scope
  862. );
  863. this._visitAll(rightHandNodes);
  864. }
  865. this._visit(node.body);
  866. this._pop();
  867. return;
  868. case "WithStatement":
  869. this._visit(node.object);
  870. this._push("with", node, false);
  871. this._visit(node.body);
  872. this._pop();
  873. return;
  874. case "ImportDeclaration":
  875. // every specifier introduces a local binding; the source is a
  876. // literal and attributes hold no references
  877. for (const specifier of node.specifiers) {
  878. const local = specifier.local;
  879. if (local !== null && local !== undefined) {
  880. this._define(this.scope, local);
  881. }
  882. }
  883. return;
  884. case "ExportAllDeclaration":
  885. // always re-exports from a source, so nothing local is referenced
  886. return;
  887. case "ExportDefaultDeclaration":
  888. this._visit(/** @type {Node} */ (node.declaration));
  889. return;
  890. case "ExportNamedDeclaration":
  891. if (node.source !== null && node.source !== undefined) return;
  892. if (node.declaration !== null && node.declaration !== undefined) {
  893. this._visit(node.declaration);
  894. return;
  895. }
  896. this._visitAll(node.specifiers);
  897. return;
  898. case "ExportSpecifier":
  899. // `export { x }` reads `x`; the exported name is not a binding
  900. if (node.local.type === "Identifier") this._reference(node.local);
  901. return;
  902. case "LabeledStatement":
  903. // labels share the identifier node type but are not bindings
  904. this._visit(node.body);
  905. return;
  906. case "BreakStatement":
  907. case "ContinueStatement":
  908. case "MetaProperty":
  909. case "PrivateIdentifier":
  910. case "Super":
  911. case "EmptyStatement":
  912. case "DebuggerStatement":
  913. return;
  914. default: {
  915. const keys = CHILD_KEYS[node.type];
  916. if (keys === undefined) {
  917. this._visitUnknown(node);
  918. return;
  919. }
  920. for (let i = 0; i < keys.length; i++) {
  921. const child = /** @type {NodeChildren} */ (
  922. /** @type {unknown} */ (node)
  923. )[keys[i]];
  924. if (child === null || child === undefined) continue;
  925. if (Array.isArray(child)) {
  926. for (let j = 0; j < child.length; j++) {
  927. const item = child[j];
  928. if (item !== null && item !== undefined) this._visit(item);
  929. }
  930. } else {
  931. this._visit(child);
  932. }
  933. }
  934. }
  935. }
  936. }
  937. /**
  938. * Resolves every recorded reference by climbing the scope chain from where
  939. * it was seen. Runs once, after the whole tree has been walked, so a
  940. * reference to a binding declared later still finds it.
  941. * @returns {Reference[]} references that resolved to no binding
  942. */
  943. _resolve() {
  944. const identifiers = this.pendingIdentifiers;
  945. const scopes = this.pendingScopes;
  946. /** @type {Reference[]} */
  947. const unresolved = [];
  948. for (let i = 0; i < identifiers.length; i++) {
  949. const identifier = identifiers[i];
  950. const name = identifier.name;
  951. const from = scopes[i];
  952. /** @type {Scope | null} */
  953. let scope = from;
  954. let resolved = false;
  955. while (scope !== null) {
  956. const variable = scope.getBinding(name);
  957. if (variable !== undefined) {
  958. // `-1` is every scope but a function scope with parameters, so
  959. // the common case is one integer compare and no call
  960. const boundary = scope.paramBoundary;
  961. if (
  962. boundary === -1 ||
  963. !isHiddenBodyBinding(variable, identifier, boundary)
  964. ) {
  965. // most identifiers resolve into a scope nothing reads back,
  966. // so the `Reference` is built only where one is kept
  967. if (scope._recorded) {
  968. const reference = new Reference(identifier, from);
  969. reference.resolved = variable;
  970. if (variable.references === NO_REFERENCES) {
  971. variable.references = [reference];
  972. } else {
  973. variable.references.push(reference);
  974. }
  975. }
  976. resolved = true;
  977. break;
  978. }
  979. }
  980. scope = scope.upper;
  981. }
  982. if (!resolved) unresolved.push(new Reference(identifier, from));
  983. }
  984. return unresolved;
  985. }
  986. }
  987. /**
  988. * @typedef {object} ScopeAnalysis
  989. * @property {Scope} globalScope the outermost scope
  990. * @property {Scope} moduleScope the module body scope, where top-level declarations live
  991. * @property {Reference[]} unresolvedReferences every identifier that resolved to no binding — the module's free names
  992. */
  993. /**
  994. * Analyses a generated module source as a strict ES module. Only the module
  995. * scope and its direct children collect references; `recordEveryReference`
  996. * widens that to the whole tree, retaining one per identifier.
  997. * @param {Program} ast the program to analyse
  998. * @param {boolean=} recordEveryReference whether every binding collects its references
  999. * @returns {ScopeAnalysis} the scope tree and the module's free references
  1000. */
  1001. const analyzeScope = (ast, recordEveryReference = false) => {
  1002. const analyzer = new ScopeAnalyzer(recordEveryReference);
  1003. const globalScope = analyzer._push("global", ast, true);
  1004. const moduleScope = analyzer._push("module", ast, true);
  1005. analyzer._visitAll(ast.body);
  1006. const unresolvedReferences = analyzer._resolve();
  1007. return { globalScope, moduleScope, unresolvedReferences };
  1008. };
  1009. analyzeScope.Reference = Reference;
  1010. analyzeScope.Scope = Scope;
  1011. analyzeScope.Variable = Variable;
  1012. module.exports = analyzeScope;