monkeypatchlist 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +61 -0
  3. package/README.md +183 -2
  4. package/catalog/catalog.json +552211 -0
  5. package/catalog/catalog.schema.json +787 -0
  6. package/catalog/changelog.json +3 -0
  7. package/dist/bin/monkeypatchlist.d.ts +2 -0
  8. package/dist/bin/monkeypatchlist.js +3 -0
  9. package/dist/catalog.d.ts +561 -0
  10. package/dist/catalog.js +58 -0
  11. package/dist/cli.d.ts +84 -0
  12. package/dist/cli.js +470 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +1 -0
  15. package/dist/lockfile-input.d.ts +9 -0
  16. package/dist/lockfile-input.js +91 -0
  17. package/dist/packages/analysis/binding.d.ts +2 -0
  18. package/dist/packages/analysis/binding.js +5 -0
  19. package/dist/packages/analysis/index.d.ts +10 -0
  20. package/dist/packages/analysis/index.js +13 -0
  21. package/dist/packages/analysis/job.d.ts +33 -0
  22. package/dist/packages/analysis/job.js +85 -0
  23. package/dist/packages/analysis/lib/absence-tags.d.ts +34 -0
  24. package/dist/packages/analysis/lib/absence-tags.js +340 -0
  25. package/dist/packages/analysis/lib/ast.d.ts +28 -0
  26. package/dist/packages/analysis/lib/ast.js +64 -0
  27. package/dist/packages/analysis/lib/binding.d.ts +36 -0
  28. package/dist/packages/analysis/lib/binding.js +66 -0
  29. package/dist/packages/analysis/lib/budget.d.ts +80 -0
  30. package/dist/packages/analysis/lib/budget.js +31 -0
  31. package/dist/packages/analysis/lib/catch-arms.d.ts +19 -0
  32. package/dist/packages/analysis/lib/catch-arms.js +351 -0
  33. package/dist/packages/analysis/lib/chunk-loads.d.ts +160 -0
  34. package/dist/packages/analysis/lib/chunk-loads.js +882 -0
  35. package/dist/packages/analysis/lib/chunk-registry.d.ts +45 -0
  36. package/dist/packages/analysis/lib/chunk-registry.js +334 -0
  37. package/dist/packages/analysis/lib/code-routes.d.ts +52 -0
  38. package/dist/packages/analysis/lib/code-routes.js +921 -0
  39. package/dist/packages/analysis/lib/collisions.d.ts +22 -0
  40. package/dist/packages/analysis/lib/collisions.js +103 -0
  41. package/dist/packages/analysis/lib/definitions.d.ts +76 -0
  42. package/dist/packages/analysis/lib/definitions.js +782 -0
  43. package/dist/packages/analysis/lib/early-exits.d.ts +39 -0
  44. package/dist/packages/analysis/lib/early-exits.js +161 -0
  45. package/dist/packages/analysis/lib/effect.d.ts +138 -0
  46. package/dist/packages/analysis/lib/effect.js +800 -0
  47. package/dist/packages/analysis/lib/global-object.d.ts +581 -0
  48. package/dist/packages/analysis/lib/global-object.js +8240 -0
  49. package/dist/packages/analysis/lib/keys.d.ts +33 -0
  50. package/dist/packages/analysis/lib/keys.js +82 -0
  51. package/dist/packages/analysis/lib/line-starts.d.ts +41 -0
  52. package/dist/packages/analysis/lib/line-starts.js +79 -0
  53. package/dist/packages/analysis/lib/link-group.d.ts +51 -0
  54. package/dist/packages/analysis/lib/link-group.js +487 -0
  55. package/dist/packages/analysis/lib/module-links.d.ts +117 -0
  56. package/dist/packages/analysis/lib/module-links.js +1060 -0
  57. package/dist/packages/analysis/lib/nested-code.d.ts +38 -0
  58. package/dist/packages/analysis/lib/nested-code.js +200 -0
  59. package/dist/packages/analysis/lib/numeric.d.ts +16 -0
  60. package/dist/packages/analysis/lib/numeric.js +216 -0
  61. package/dist/packages/analysis/lib/once-flags.d.ts +12 -0
  62. package/dist/packages/analysis/lib/once-flags.js +190 -0
  63. package/dist/packages/analysis/lib/parameter-arguments.d.ts +12 -0
  64. package/dist/packages/analysis/lib/parameter-arguments.js +34 -0
  65. package/dist/packages/analysis/lib/parse.d.ts +10 -0
  66. package/dist/packages/analysis/lib/parse.js +39 -0
  67. package/dist/packages/analysis/lib/partial.d.ts +60 -0
  68. package/dist/packages/analysis/lib/partial.js +518 -0
  69. package/dist/packages/analysis/lib/print.d.ts +3 -0
  70. package/dist/packages/analysis/lib/print.js +150 -0
  71. package/dist/packages/analysis/lib/reachability.d.ts +137 -0
  72. package/dist/packages/analysis/lib/reachability.js +1295 -0
  73. package/dist/packages/analysis/lib/resolver.d.ts +13 -0
  74. package/dist/packages/analysis/lib/resolver.js +2886 -0
  75. package/dist/packages/analysis/lib/restores.d.ts +42 -0
  76. package/dist/packages/analysis/lib/restores.js +482 -0
  77. package/dist/packages/analysis/lib/rewritten-deletes.d.ts +6 -0
  78. package/dist/packages/analysis/lib/rewritten-deletes.js +142 -0
  79. package/dist/packages/analysis/lib/scope.d.ts +110 -0
  80. package/dist/packages/analysis/lib/scope.js +443 -0
  81. package/dist/packages/analysis/lib/script-loads.d.ts +56 -0
  82. package/dist/packages/analysis/lib/script-loads.js +359 -0
  83. package/dist/packages/analysis/lib/summaries.d.ts +177 -0
  84. package/dist/packages/analysis/lib/summaries.js +498 -0
  85. package/dist/packages/analysis/lib/types.d.ts +632 -0
  86. package/dist/packages/analysis/lib/types.js +128 -0
  87. package/dist/packages/analysis/lib/wrap.d.ts +34 -0
  88. package/dist/packages/analysis/lib/wrap.js +407 -0
  89. package/dist/packages/analysis/lib/writes.d.ts +358 -0
  90. package/dist/packages/analysis/lib/writes.js +1612 -0
  91. package/dist/packages/analysis/visit-order.d.ts +3 -0
  92. package/dist/packages/analysis/visit-order.js +23 -0
  93. package/dist/packages/attribution/index.d.ts +233 -0
  94. package/dist/packages/attribution/index.js +444 -0
  95. package/dist/packages/attribution/lib/containing-package.d.ts +76 -0
  96. package/dist/packages/attribution/lib/containing-package.js +304 -0
  97. package/dist/packages/attribution/lib/first-party.d.ts +37 -0
  98. package/dist/packages/attribution/lib/first-party.js +153 -0
  99. package/dist/packages/attribution/lib/lockfile-graph.d.ts +99 -0
  100. package/dist/packages/attribution/lib/lockfile-graph.js +158 -0
  101. package/dist/packages/attribution/lib/lockfile.d.ts +33 -0
  102. package/dist/packages/attribution/lib/lockfile.js +330 -0
  103. package/dist/packages/attribution/lib/path-rules.d.ts +29 -0
  104. package/dist/packages/attribution/lib/path-rules.js +84 -0
  105. package/dist/packages/attribution/lib/pnpm.d.ts +4 -0
  106. package/dist/packages/attribution/lib/pnpm.js +133 -0
  107. package/dist/packages/attribution/lib/remote-url.d.ts +7 -0
  108. package/dist/packages/attribution/lib/remote-url.js +24 -0
  109. package/dist/packages/attribution/lib/source-map.d.ts +51 -0
  110. package/dist/packages/attribution/lib/source-map.js +125 -0
  111. package/dist/packages/attribution/lib/yaml.d.ts +9 -0
  112. package/dist/packages/attribution/lib/yaml.js +277 -0
  113. package/dist/packages/attribution/lib/yarn.d.ts +7 -0
  114. package/dist/packages/attribution/lib/yarn.js +227 -0
  115. package/dist/packages/baseline/index.d.ts +45 -0
  116. package/dist/packages/baseline/index.js +41 -0
  117. package/dist/packages/baseline/lib/error.d.ts +15 -0
  118. package/dist/packages/baseline/lib/error.js +25 -0
  119. package/dist/packages/baseline/lib/format.d.ts +49 -0
  120. package/dist/packages/baseline/lib/format.js +22 -0
  121. package/dist/packages/baseline/lib/migrations.d.ts +19 -0
  122. package/dist/packages/baseline/lib/migrations.js +7 -0
  123. package/dist/packages/baseline/lib/previous.d.ts +23 -0
  124. package/dist/packages/baseline/lib/previous.js +64 -0
  125. package/dist/packages/baseline/lib/read.d.ts +39 -0
  126. package/dist/packages/baseline/lib/read.js +219 -0
  127. package/dist/packages/baseline/lib/report.d.ts +25 -0
  128. package/dist/packages/baseline/lib/report.js +104 -0
  129. package/dist/packages/baseline/lib/skew.d.ts +51 -0
  130. package/dist/packages/baseline/lib/skew.js +75 -0
  131. package/dist/packages/compare/index.d.ts +120 -0
  132. package/dist/packages/compare/index.js +274 -0
  133. package/dist/packages/compare/lib/catalog-notes.d.ts +73 -0
  134. package/dist/packages/compare/lib/catalog-notes.js +224 -0
  135. package/dist/packages/completeness/index.d.ts +246 -0
  136. package/dist/packages/completeness/index.js +249 -0
  137. package/dist/packages/confidence/index.d.ts +54 -0
  138. package/dist/packages/confidence/index.js +71 -0
  139. package/dist/packages/config/index.d.ts +107 -0
  140. package/dist/packages/config/index.js +95 -0
  141. package/dist/packages/config/lib/command-line.d.ts +23 -0
  142. package/dist/packages/config/lib/command-line.js +239 -0
  143. package/dist/packages/config/lib/config-file.d.ts +11 -0
  144. package/dist/packages/config/lib/config-file.js +119 -0
  145. package/dist/packages/config/lib/scan-config-id.d.ts +28 -0
  146. package/dist/packages/config/lib/scan-config-id.js +54 -0
  147. package/dist/packages/config/lib/schema.d.ts +2 -0
  148. package/dist/packages/config/lib/schema.js +31 -0
  149. package/dist/packages/config/lib/types.d.ts +64 -0
  150. package/dist/packages/config/lib/types.js +6 -0
  151. package/dist/packages/config/lib/urls.d.ts +6 -0
  152. package/dist/packages/config/lib/urls.js +9 -0
  153. package/dist/packages/discovery/index.d.ts +277 -0
  154. package/dist/packages/discovery/index.js +1950 -0
  155. package/dist/packages/discovery/lib/import-map.d.ts +68 -0
  156. package/dist/packages/discovery/lib/import-map.js +180 -0
  157. package/dist/packages/discovery/lib/requests.d.ts +58 -0
  158. package/dist/packages/discovery/lib/requests.js +591 -0
  159. package/dist/packages/discovery/lib/scopes.d.ts +6 -0
  160. package/dist/packages/discovery/lib/scopes.js +286 -0
  161. package/dist/packages/discovery/lib/source-maps.d.ts +56 -0
  162. package/dist/packages/discovery/lib/source-maps.js +209 -0
  163. package/dist/packages/discovery/lib/swept-code.d.ts +27 -0
  164. package/dist/packages/discovery/lib/swept-code.js +107 -0
  165. package/dist/packages/discovery/lib/syntax.d.ts +17 -0
  166. package/dist/packages/discovery/lib/syntax.js +39 -0
  167. package/dist/packages/findings/index.d.ts +106 -0
  168. package/dist/packages/findings/index.js +254 -0
  169. package/dist/packages/findings/lib/scope.d.ts +38 -0
  170. package/dist/packages/findings/lib/scope.js +110 -0
  171. package/dist/packages/parallel/index.d.ts +2 -0
  172. package/dist/packages/parallel/index.js +5 -0
  173. package/dist/packages/parallel/lib/protocol.d.ts +15 -0
  174. package/dist/packages/parallel/lib/protocol.js +2 -0
  175. package/dist/packages/parallel/lib/runner.d.ts +36 -0
  176. package/dist/packages/parallel/lib/runner.js +96 -0
  177. package/dist/packages/parallel/lib/schedule.d.ts +16 -0
  178. package/dist/packages/parallel/lib/schedule.js +52 -0
  179. package/dist/packages/parallel/lib/worker.d.ts +1 -0
  180. package/dist/packages/parallel/lib/worker.js +18 -0
  181. package/dist/packages/policy/index.d.ts +36 -0
  182. package/dist/packages/policy/index.js +22 -0
  183. package/dist/packages/report/explain.d.ts +26 -0
  184. package/dist/packages/report/explain.js +99 -0
  185. package/dist/packages/report/index.d.ts +210 -0
  186. package/dist/packages/report/index.js +388 -0
  187. package/dist/packages/report/terminal.d.ts +11 -0
  188. package/dist/packages/report/terminal.js +331 -0
  189. package/dist/packages/source-store/fixture.d.ts +22 -0
  190. package/dist/packages/source-store/fixture.js +58 -0
  191. package/dist/packages/source-store/index.d.ts +278 -0
  192. package/dist/packages/source-store/index.js +322 -0
  193. package/dist/packages/source-store/lib/addresses.d.ts +6 -0
  194. package/dist/packages/source-store/lib/addresses.js +91 -0
  195. package/dist/packages/source-store/lib/connection.d.ts +15 -0
  196. package/dist/packages/source-store/lib/connection.js +135 -0
  197. package/dist/packages/source-store/lib/content-types.d.ts +16 -0
  198. package/dist/packages/source-store/lib/content-types.js +75 -0
  199. package/dist/packages/source-store/lib/data-urls.d.ts +10 -0
  200. package/dist/packages/source-store/lib/data-urls.js +85 -0
  201. package/dist/packages/source-store/lib/deadline.d.ts +18 -0
  202. package/dist/packages/source-store/lib/deadline.js +44 -0
  203. package/dist/packages/source-store/lib/exchange.d.ts +29 -0
  204. package/dist/packages/source-store/lib/exchange.js +112 -0
  205. package/dist/packages/source-store/lib/integrity.d.ts +19 -0
  206. package/dist/packages/source-store/lib/integrity.js +86 -0
  207. package/dist/packages/source-store/lib/outcomes.d.ts +12 -0
  208. package/dist/packages/source-store/lib/outcomes.js +22 -0
  209. package/dist/packages/source-store/lib/proxy.d.ts +11 -0
  210. package/dist/packages/source-store/lib/proxy.js +53 -0
  211. package/dist/packages/source-store/lib/session.d.ts +12 -0
  212. package/dist/packages/source-store/lib/session.js +139 -0
  213. package/dist/packages/source-store/lib/urls.d.ts +6 -0
  214. package/dist/packages/source-store/lib/urls.js +15 -0
  215. package/dist/packages/source-store/resolve.d.ts +59 -0
  216. package/dist/packages/source-store/resolve.js +45 -0
  217. package/dist/scan.d.ts +65 -0
  218. package/dist/scan.js +592 -0
  219. package/package.json +95 -4
  220. package/schema/config.schema.json +133 -0
  221. package/schema/report.schema.json +1905 -0
@@ -0,0 +1,921 @@
1
+ // Indirect code routes (§2 Content that isn't JavaScript): the calls that reach a native that compiles code from strings
2
+ // by any route other than the direct forms whose literal code is analyzed as nested code. A native `eval`, `Function`,
3
+ // string timer, `%AsyncFunction%`, `%GeneratorFunction%` or `%AsyncGeneratorFunction%` reached through `.call`,
4
+ // `.apply`, `.bind`, `Reflect.apply`, `Reflect.construct`, a computed key (`window["eval"]`), a catalog reader
5
+ // (`Reflect.get(window, "eval")`, `Object.getOwnPropertyDescriptor(window, "eval").value`), `.constructor` of a known
6
+ // function or object (`(function(){}).constructor`, `[]["filter"]["constructor"]`, `alert.constructor`) or a tagged
7
+ // template (``Function`…` ``) compiles code the analyzer doesn't read, so the resolver raises `dynamic-code` for it
8
+ // (§13).
9
+ //
10
+ // A route is followed only as far as the analyzer can show where it leads. A value here is a function or object whose
11
+ // members the unmutated catalog gives (§4), as prototype walks are simulated elsewhere: a native holder, a native
12
+ // function the catalog lists, `Function.prototype`'s `call`, `apply` and `bind`, `Reflect.apply`, `Reflect.construct`,
13
+ // `Reflect.get`, the two `getPrototypeOf`s and `getOwnPropertyDescriptor`s, a bound function, a property descriptor, or
14
+ // an object the code makes (a function, class, array, object or primitive literal, or a function or class declaration
15
+ // or a named function expression read by its own name, never written), whose prototype its syntax shows. These values
16
+ // only tell which native a call reaches. They never make a holder for a write: native holders are reached only from the
17
+ // global object (§5 Global resolution).
18
+ //
19
+ // Any alternative counts (Principle 3): an expression with several possible values (`c ? eval : f`, a computed key
20
+ // that folds to several strings) is each of them, and a call reaches every native any of them reaches.
21
+ import { calleeExpression, walkTree } from "./ast.js";
22
+ import { opaque, truthiness } from "./global-object.js";
23
+ import { literalKey, memberName, toPropertyKey } from "./keys.js";
24
+ /** The functions the global object holds that compile code from strings, which are no holders. */
25
+ export const isGlobalCompiler = (name) => name === 'eval' || name === 'setTimeout' || name === 'setInterval';
26
+ /** The native holders that compile code from strings: `Function` and the constructors of its other kinds. */
27
+ const functionConstructors = new Set([
28
+ 'Function',
29
+ '%AsyncFunction%',
30
+ '%GeneratorFunction%',
31
+ '%AsyncGeneratorFunction%',
32
+ ]);
33
+ const none = () => [];
34
+ const nothing = { compiles: [], returns: [] };
35
+ /**
36
+ * The keys a callee's route can end with. Other member calls are not evaluated. `getPrototypeOf`, `get` and
37
+ * `getOwnPropertyDescriptor` end a route whose call returns a function (`Object.getPrototypeOf(AsyncFunction)` is
38
+ * `Function`), and `value` one that reads a descriptor.
39
+ */
40
+ const routeKeys = new Set([
41
+ 'call',
42
+ 'apply',
43
+ 'bind',
44
+ 'construct',
45
+ 'getPrototypeOf',
46
+ 'get',
47
+ 'getOwnPropertyDescriptor',
48
+ 'value',
49
+ 'constructor',
50
+ 'eval',
51
+ 'setTimeout',
52
+ 'setInterval',
53
+ 'Function',
54
+ ]);
55
+ export function codeRoutes(context) {
56
+ const { catalog, realm, global, program, scope, resolution } = context;
57
+ const targets = catalog.targets[realm];
58
+ const holders = catalog.holders[realm];
59
+ /**
60
+ * What each expression is, for the call being followed: a route can reach an expression more than once (as a callee,
61
+ * then as the `this` it passes), and each time it is the same value.
62
+ */
63
+ let values = new Map();
64
+ function reached(node, at) {
65
+ // A tagged template calls its tag with the template's strings array, then its substitutions.
66
+ const tagged = node.type === 'TaggedTemplateExpression';
67
+ const callee = (tagged ? node['tag'] : node['callee']);
68
+ if (!tagged && !mayReach(callee))
69
+ return [];
70
+ values = new Map();
71
+ const reach = known(callee, at);
72
+ if (reach.length === 0)
73
+ return [];
74
+ const args = tagged ? templateArgs(node['quasi']) : argsOf(node['arguments']);
75
+ const construct = node.type === 'NewExpression';
76
+ const outcome = merged(reach.map((target) => invoke(target, () => thisOf(callee, at), args, construct, at)));
77
+ const { compiles } = tagged ? routed(outcome, 'a tagged template') : outcome;
78
+ // One for each native reached, by the first route that reaches it.
79
+ const first = new Map();
80
+ for (const call of compiles)
81
+ if (!first.has(call.compiler))
82
+ first.set(call.compiler, { ...call, via: [...new Set(call.via)] });
83
+ return [...first.values()];
84
+ }
85
+ /** What calling `target` with a `this` and `args` does, or constructing it with `new`. */
86
+ function invoke(target, self, args, construct, at) {
87
+ switch (target.kind) {
88
+ case 'compiler':
89
+ // `eval` and the timers are no constructors, so `new` throws.
90
+ return construct ? nothing : { compiles: [{ compiler: target.name, args, via: target.via }], returns: [] };
91
+ case 'holder': {
92
+ if (!functionConstructors.has(target.holder))
93
+ return nothing;
94
+ const compiler = target.holder;
95
+ // It returns a function of its kind, whose prototype is the constructor's `prototype`.
96
+ return {
97
+ compiles: [{ compiler, args, via: target.via }],
98
+ returns: [{ kind: 'object', proto: `${target.holder}.prototype`, via: [] }],
99
+ };
100
+ }
101
+ case 'call':
102
+ case 'apply': {
103
+ // `f.call(t, ...args)` and `f.apply(t, args)` call `f`, their own `this`.
104
+ if (construct)
105
+ return nothing;
106
+ const [thisArg] = args.items;
107
+ const passed = target.kind === 'call' ? after(args, 1) : spread(args.items[1], args.rest, 'empty');
108
+ if (passed === undefined)
109
+ return nothing;
110
+ return merged(self().map((called) => routed(invoke(called, () => knownArg(thisArg, at), passed, false, at), `.${target.kind}`)));
111
+ }
112
+ case 'bind': {
113
+ // `f.bind(t, ...args)` returns `f` bound to `t` and those arguments.
114
+ if (construct)
115
+ return nothing;
116
+ const [thisArg] = args.items;
117
+ const bound = after(args, 1);
118
+ return {
119
+ compiles: [],
120
+ returns: self().map((of) => ({ kind: 'bound', target: of, thisArg, args: bound, via: ['.bind'] })),
121
+ };
122
+ }
123
+ case 'reflect-apply': {
124
+ if (construct)
125
+ return nothing;
126
+ const [called, thisArg, list] = args.items;
127
+ const passed = spread(list, args.rest, 'throws');
128
+ if (passed === undefined)
129
+ return nothing;
130
+ return merged(knownArg(called, at).map((reach) => routed(invoke(reach, () => knownArg(thisArg, at), passed, false, at), 'Reflect.apply')));
131
+ }
132
+ case 'reflect-construct': {
133
+ if (construct)
134
+ return nothing;
135
+ const [constructed, list] = args.items;
136
+ const passed = spread(list, args.rest, 'throws');
137
+ if (passed === undefined)
138
+ return nothing;
139
+ return merged(knownArg(constructed, at).map((reach) => routed(invoke(reach, none, passed, true, at), 'Reflect.construct')));
140
+ }
141
+ case 'get-prototype-of': {
142
+ // `Object.getPrototypeOf(o)` and `Reflect.getPrototypeOf(o)` of an object whose prototype is known.
143
+ if (construct)
144
+ return nothing;
145
+ const returns = knownArg(args.items[0], at).flatMap((of) => {
146
+ const proto = prototypeOf(of);
147
+ return proto === undefined ? [] : [{ kind: 'holder', holder: proto, via: of.via }];
148
+ });
149
+ return { compiles: [], returns };
150
+ }
151
+ case 'reflect-get': {
152
+ // `Reflect.get(o, key)` reads the member `key` of an object, as `o[key]` does. A primitive makes it throw.
153
+ if (construct)
154
+ return nothing;
155
+ const [object, key] = args.items;
156
+ const keys = constantKeys(key, at);
157
+ const returns = knownArg(object, at)
158
+ .filter((of) => !(of.kind === 'object' && of.primitive === true))
159
+ .flatMap((of) => keys.flatMap((name) => stepped(memberOrValue(of, name), [...of.via, target.name, ...keyStep(name)])));
160
+ return { compiles: [], returns };
161
+ }
162
+ case 'get-own-property-descriptor': {
163
+ // The descriptor of a native holder's own data member, whose `value` is that member.
164
+ if (construct)
165
+ return nothing;
166
+ const [object, key] = args.items;
167
+ const keys = constantKeys(key, at);
168
+ const returns = knownArg(object, at).flatMap((of) => {
169
+ if (of.kind !== 'holder')
170
+ return [];
171
+ const value = keys.flatMap((name) => stepped(optional(ownMember(of.holder, name)), [...of.via, target.name, ...keyStep(name)]));
172
+ return value.length === 0 ? [] : [{ kind: 'descriptor', value, via: [] }];
173
+ });
174
+ return { compiles: [], returns };
175
+ }
176
+ case 'bound': {
177
+ // A bound function calls its target with its bound `this`, and its bound arguments first (ECMA-262
178
+ // BoundFunctionCall). `new` constructs its target, with no `this` (BoundFunctionConstruct).
179
+ const passed = joined(target.args, args);
180
+ const boundThis = construct ? none : () => knownArg(target.thisArg, at);
181
+ return routed(invoke(target.target, boundThis, passed, construct, at), ...target.via);
182
+ }
183
+ case 'object':
184
+ case 'descriptor':
185
+ return nothing;
186
+ }
187
+ }
188
+ /** Each function or object an expression may be, when the analyzer knows its members. */
189
+ function known(expression, at) {
190
+ const node = unwrapped(expression);
191
+ const seen = values.get(node);
192
+ if (seen !== undefined)
193
+ return seen;
194
+ // A route that comes back to an expression it is still evaluating shows nothing more there.
195
+ values.set(node, []);
196
+ const value = distinct(valueOf(node, at));
197
+ values.set(node, value);
198
+ return value;
199
+ }
200
+ function valueOf(node, at) {
201
+ switch (node.type) {
202
+ case 'Identifier':
203
+ return identifier(node, at);
204
+ case 'FunctionExpression':
205
+ case 'ArrowFunctionExpression':
206
+ return [{ kind: 'object', proto: functionPrototype(node), via: [] }];
207
+ case 'ClassExpression':
208
+ return shownClass(node, at) ? [{ kind: 'object', proto: 'Function.prototype', via: [] }] : [];
209
+ case 'ArrayExpression':
210
+ return [{ kind: 'object', proto: 'Array.prototype', via: [] }];
211
+ case 'ObjectExpression':
212
+ return [{ kind: 'object', proto: 'Object.prototype', own: node, via: [] }];
213
+ case 'TemplateLiteral':
214
+ return [{ kind: 'object', proto: 'String.prototype', primitive: true, via: [] }];
215
+ case 'Literal': {
216
+ const proto = literalPrototype(node);
217
+ if (proto === undefined)
218
+ return [];
219
+ const regex = node['regex'] !== undefined && node['regex'] !== null;
220
+ return [{ kind: 'object', proto, ...(regex ? {} : { primitive: true }), via: [] }];
221
+ }
222
+ case 'MemberExpression':
223
+ return member(node, at);
224
+ case 'ConditionalExpression':
225
+ case 'LogicalExpression':
226
+ return alternatives(node, at).flatMap((branch) => known(branch, at));
227
+ case 'CallExpression':
228
+ case 'NewExpression': {
229
+ // What the call returns: a bound function, a prototype, a member or descriptor a reader gives, or a function a
230
+ // constructor makes.
231
+ const callee = node['callee'];
232
+ const args = argsOf(node['arguments']);
233
+ const construct = node.type === 'NewExpression';
234
+ const returned = known(callee, at).flatMap((reach) => invoke(reach, () => thisOf(callee, at), args, construct, at).returns);
235
+ return returned.length > 0 || construct ? returned : holderAt(node, at);
236
+ }
237
+ case 'AssignmentExpression':
238
+ return node['operator'] === '=' ? known(node['right'], at) : [];
239
+ default:
240
+ return holderAt(node, at);
241
+ }
242
+ }
243
+ /** A name: a function or class declaration never written, a native the global object holds, or a native holder. */
244
+ function identifier(node, at) {
245
+ const name = node['name'];
246
+ const binding = scope.resolve(node);
247
+ // A function declaration, local or at the top level of a classic script (where it puts the function in the global
248
+ // object's property before the script runs), or a named function expression read by its own name, is that function
249
+ // while nothing the analyzer sees writes its name, gives it its own `constructor` or gives it a new prototype. A
250
+ // class declaration is its class literal under the same rules, once it is initialized.
251
+ const declaration = binding === 'local' ? scope.declaration(node) : binding.declaration?.kind === 'function' ? binding.declaration.node : undefined;
252
+ if ((declaration?.type === 'FunctionDeclaration' || declaration?.type === 'FunctionExpression') &&
253
+ namesFunction(node, declaration)) {
254
+ return shownFunction(declaration) ? [{ kind: 'object', proto: functionPrototype(declaration), via: [] }] : [];
255
+ }
256
+ if (declaration?.type === 'ClassDeclaration' && namesClass(node, declaration)) {
257
+ const shown = !uninitialized(node, declaration) && shownClass(declaration, at) && shownFunction(declaration);
258
+ return shown ? [{ kind: 'object', proto: 'Function.prototype', via: [] }] : [];
259
+ }
260
+ // A local name is the native it aliases (§5 Resolver capabilities 2), and otherwise no native. A name that resolves
261
+ // to a declaration of another kind is no native.
262
+ if (binding === 'local')
263
+ return aliasOf(node, at);
264
+ if (binding.declaration?.kind === 'function')
265
+ return [];
266
+ if (isGlobalCompiler(name))
267
+ return optional(inherited(global, name));
268
+ // A name the code assigns follows its value (§5 Reassigned globals).
269
+ const aliased = aliasOf(node, at);
270
+ if (aliased.length > 0)
271
+ return aliased;
272
+ const found = holderAt(node, at);
273
+ if (found.length > 0 || binding.declaration !== undefined)
274
+ return found;
275
+ // Any other name is the global object's member, if it has one (`alert`).
276
+ return optional(inherited(global, name));
277
+ }
278
+ /**
279
+ * `object.key` or `object[key]`: a holder the global resolution reaches in its own right (`window.Function`,
280
+ * `Function.prototype`), or the member `key` of a known function or object, read through the unmutated catalog. A
281
+ * computed key is each string it may fold to.
282
+ */
283
+ function member(node, at) {
284
+ const object = node['object'];
285
+ const property = node['property'];
286
+ if (object.type === 'Super')
287
+ return [];
288
+ const computed = node['computed'] === true;
289
+ const named = property.type === 'Identifier' ? property['name'] : undefined;
290
+ const keys = computed ? constantKeys(property, at) : named === undefined ? [] : [named];
291
+ if (keys.length === 0)
292
+ return [];
293
+ const computedStep = computed ? ['a computed key'] : [];
294
+ const found = holderAt(node, at);
295
+ if (found.length > 0 && keys.length === 1) {
296
+ const steps = [...computedStep, ...keyStep(keys[0])];
297
+ return found.map((holder) => ({ ...holder, via: [...holder.via, ...steps] }));
298
+ }
299
+ const bases = known(object, at);
300
+ const read = bases.flatMap((base) => keys.flatMap((key) => stepped(memberOrValue(base, key), [...base.via, ...computedStep, ...keyStep(key)])));
301
+ return [...found.map((holder) => ({ ...holder, via: [...holder.via, ...computedStep] })), ...read];
302
+ }
303
+ /** The member `key` of a known value, from its own members and then its prototype's, as the catalog gives them. */
304
+ function memberOf(value, key) {
305
+ if (value.kind === 'holder')
306
+ return inherited(value.holder, key);
307
+ if (value.kind === 'descriptor')
308
+ return undefined;
309
+ if (value.kind === 'object' && value.own !== undefined && !lacksOwn(value.own, key))
310
+ return undefined;
311
+ const proto = prototypeOf(value);
312
+ return proto === undefined ? undefined : inherited(proto, key);
313
+ }
314
+ /** A descriptor's `value`, the only member of one that a route reads. */
315
+ function descriptorValue(value, key) {
316
+ return value.kind === 'descriptor' && key === 'value' ? value.value : [];
317
+ }
318
+ /**
319
+ * The member `key` that `holder` has or inherits, in the unmutated catalog: a holder, a native function that compiles
320
+ * code, one of the functions that call, bind or construct another, or read a prototype or property, or any other
321
+ * native function, whose prototype is `Function.prototype`.
322
+ */
323
+ function inherited(holder, key) {
324
+ for (let at = holder; typeof at === 'string'; at = holders[at]?.proto) {
325
+ const found = memberAt(at, key, holder === global);
326
+ if (found !== null)
327
+ return found;
328
+ }
329
+ return undefined;
330
+ }
331
+ /** The member `key` of `holder` itself, in the unmutated catalog. */
332
+ function ownMember(holder, key) {
333
+ return memberAt(holder, key, holder === global) ?? undefined;
334
+ }
335
+ /** The member `key` that `holder` itself has: null when it has none, undefined when it is no value a route knows. */
336
+ function memberAt(holder, key, onGlobal) {
337
+ const name = memberName(holder, key);
338
+ if (!Object.hasOwn(targets, name))
339
+ return null;
340
+ const target = targets[name];
341
+ if (target.holds !== undefined)
342
+ return { kind: 'holder', holder: target.holds, via: [] };
343
+ const native = nativeFunctions.get(name);
344
+ if (native !== undefined)
345
+ return { kind: native, name, via: [] };
346
+ if (onGlobal && isGlobalCompiler(key))
347
+ return { kind: 'compiler', name: key, via: [] };
348
+ if (target.kind === 'function' || target.kind === 'operation') {
349
+ return { kind: 'object', proto: 'Function.prototype', via: [] };
350
+ }
351
+ return undefined;
352
+ }
353
+ /** The holder a known value inherits its members from. */
354
+ function prototypeOf(value) {
355
+ switch (value.kind) {
356
+ case 'holder':
357
+ return holders[value.holder]?.proto ?? undefined;
358
+ case 'object':
359
+ return value.proto;
360
+ case 'descriptor':
361
+ return 'Object.prototype';
362
+ case 'bound':
363
+ // A bound function's prototype is its target's (ECMA-262 BoundFunctionCreate).
364
+ return prototypeOf(value.target);
365
+ default:
366
+ return 'Function.prototype';
367
+ }
368
+ }
369
+ /**
370
+ * The holders, code-compiling natives, and natives that call, bind or read another function that a name aliases (§5
371
+ * Resolver capabilities 2).
372
+ */
373
+ function aliasOf(node, at) {
374
+ const via = [`a variable \`${node['name']}\``];
375
+ return resolution.aliasCallee(node, at).flatMap((value) => {
376
+ if (value.kind === 'holder')
377
+ return [{ kind: 'holder', holder: value.holder, via }];
378
+ if (value.kind !== 'native')
379
+ return [];
380
+ if (isGlobalCompiler(value.name))
381
+ return [{ kind: 'compiler', name: value.name, via }];
382
+ const native = nativeFunctions.get(value.name);
383
+ return native === undefined ? [] : [{ kind: native, name: value.name, via }];
384
+ });
385
+ }
386
+ /** The native holders the global resolution shows an expression may be, and nothing else. */
387
+ function holderAt(node, at) {
388
+ return resolution
389
+ .evaluate(node, at)
390
+ .flatMap((value) => (value.kind === 'holder' ? [{ kind: 'holder', holder: value.holder, via: [] }] : []));
391
+ }
392
+ /** The strings a computed key or key argument may fold to: none for a missing argument. */
393
+ function constantKeys(property, at) {
394
+ if (property === null || property === undefined)
395
+ return [];
396
+ const keys = resolution.evaluate(property, at).flatMap((value) => {
397
+ if (value.kind !== 'constant')
398
+ return [];
399
+ const key = toPropertyKey(value.value);
400
+ return typeof key === 'string' ? [key] : [];
401
+ });
402
+ return [...new Set(keys)];
403
+ }
404
+ /** The `this` a callee passes: the object of a member call, when the analyzer knows it. */
405
+ function thisOf(callee, at) {
406
+ const node = callee.type === 'ChainExpression' ? callee['expression'] : callee;
407
+ return node.type === 'MemberExpression' && node['object'].type !== 'Super'
408
+ ? known(node['object'], at)
409
+ : [];
410
+ }
411
+ /** An argument's value, when the analyzer knows it. A hole or a missing argument is `undefined`. */
412
+ function knownArg(arg, at) {
413
+ return arg === null || arg === undefined ? [] : known(arg, at);
414
+ }
415
+ /** The member `key` of a value, or a descriptor's `value`. */
416
+ function memberOrValue(value, key) {
417
+ return [...optional(memberOf(value, key)), ...descriptorValue(value, key)];
418
+ }
419
+ function alternatives(expression, at) {
420
+ const node = unwrapped(expression);
421
+ if (node.type === 'ConditionalExpression') {
422
+ const decided = truthiness(resolution.evaluate(node['test'], at));
423
+ const branches = [node['consequent'], node['alternate']];
424
+ const kept = decided === undefined ? branches : [decided ? branches[0] : branches[1]];
425
+ return kept.flatMap((branch) => alternatives(branch, at));
426
+ }
427
+ if (node.type === 'LogicalExpression') {
428
+ const left = node['left'];
429
+ const right = node['right'];
430
+ const values = resolution.evaluate(left, at);
431
+ // Whether the right side is surely taken (true), surely not (false), or may be.
432
+ let decided;
433
+ if (node['operator'] === '??') {
434
+ const nullish = values.map((value) => opaque(value) ? undefined : value.kind === 'constant' && (value.value === null || value.value === undefined));
435
+ decided = nullish.every((each) => each === true) ? true : nullish.every((each) => each === false) ? false : undefined;
436
+ }
437
+ else {
438
+ const truthy = truthiness(values);
439
+ decided = truthy === undefined ? undefined : node['operator'] === '||' ? !truthy : truthy;
440
+ }
441
+ const kept = decided === undefined ? [left, right] : [decided ? right : left];
442
+ return kept.flatMap((branch) => alternatives(branch, at));
443
+ }
444
+ return [expression];
445
+ }
446
+ // Which bases stop being shown (§2): a class whose statics may give it its own `constructor`, or a function whose
447
+ // name is written, and either when code the analyzer sees gives it its own `constructor` or a new prototype.
448
+ const functions = new Map();
449
+ let index;
450
+ /** Whether nothing the analyzer sees writes a function's or class declaration's name or changes what it names. */
451
+ function shownFunction(declaration) {
452
+ const shown = functions.get(declaration);
453
+ if (shown !== undefined)
454
+ return shown;
455
+ index ??= writeIndex(program, resolution);
456
+ const name = declaration['id']?.['name'];
457
+ const names = declaration.type === 'ClassDeclaration' ? namesClass : namesFunction;
458
+ const resolvesHere = (id) => {
459
+ if (id['name'] !== name)
460
+ return false;
461
+ try {
462
+ return scope.declaration(id) === declaration && names(id, declaration);
463
+ }
464
+ catch {
465
+ // A name the seam recorded no reference for is taken to be this one.
466
+ return true;
467
+ }
468
+ };
469
+ const result = !index.redeclared.has(declaration) && !index.written.some(resolvesHere) && !index.changed.some(resolvesHere);
470
+ functions.set(declaration, result);
471
+ return result;
472
+ }
473
+ let functionNodes;
474
+ /**
475
+ * Whether a read of a class declaration's name surely throws because its binding is not yet initialized (Principle
476
+ * 1): the read comes before the class body and runs as the code around the declaration runs, in no function between
477
+ * the two. Its heritage and its members' computed keys run before the binding is set, its static members after.
478
+ */
479
+ function uninitialized(id, declaration) {
480
+ const body = declaration['body'];
481
+ const within = (node) => node.start <= id.start && id.end <= node.end;
482
+ const inKey = body['body'].some((member) => member['computed'] === true && within(member['key']));
483
+ if (id.start >= body.start && !inKey)
484
+ return false;
485
+ functionNodes ??= collectFunctions(program);
486
+ const holds = (outer, inner) => outer.start <= inner.start && inner.end <= outer.end;
487
+ return !functionNodes.some((fn) => holds(fn, id) && !holds(fn, declaration));
488
+ }
489
+ function shownClass(node, at) {
490
+ if (node['superClass'] !== null)
491
+ return false;
492
+ const body = node['body'];
493
+ const members = body['body'];
494
+ for (const member of members) {
495
+ if (member['static'] !== true || member.type === 'StaticBlock')
496
+ continue;
497
+ // A static member whose key is or may be `constructor` gives the class its own.
498
+ const key = member['key'];
499
+ if (key.type === 'PrivateIdentifier')
500
+ continue;
501
+ if (member['computed'] === true) {
502
+ const values = resolution.evaluate(key, at);
503
+ if (values.some((value) => value.kind !== 'constant' || toPropertyKey(value.value) === 'constructor'))
504
+ return false;
505
+ }
506
+ else if ((key.type === 'Identifier' ? key['name'] : literalKey(key)) === 'constructor') {
507
+ return false;
508
+ }
509
+ }
510
+ // Code in its static members, where `this` is the class, and any code in it that names the class.
511
+ const statics = members.flatMap((member) => {
512
+ if (member.type === 'StaticBlock')
513
+ return [member];
514
+ if (member['static'] !== true)
515
+ return [];
516
+ const value = member['value'];
517
+ if (value === null || value === undefined)
518
+ return [];
519
+ return member.type === 'MethodDefinition' ? [value['body']] : [value];
520
+ });
521
+ if (changedIn(statics, resolution).some((subject) => subject.type === 'ThisExpression'))
522
+ return false;
523
+ const name = node['id']?.['name'];
524
+ if (name === undefined)
525
+ return true;
526
+ return !changedIn([body], resolution).some((subject) => subject.type === 'Identifier' && subject['name'] === name && scope.declaration(subject) === node);
527
+ }
528
+ return { reached, alternatives };
529
+ }
530
+ /** The natives that call, bind or construct another function, or read a prototype or property, by their target names. */
531
+ const nativeFunctions = new Map([
532
+ ['Function.prototype.call', 'call'],
533
+ ['Function.prototype.apply', 'apply'],
534
+ ['Function.prototype.bind', 'bind'],
535
+ ['Reflect.apply', 'reflect-apply'],
536
+ ['Reflect.construct', 'reflect-construct'],
537
+ ['Reflect.getPrototypeOf', 'get-prototype-of'],
538
+ ['Object.getPrototypeOf', 'get-prototype-of'],
539
+ ['Reflect.get', 'reflect-get'],
540
+ ['Reflect.getOwnPropertyDescriptor', 'get-own-property-descriptor'],
541
+ ['Object.getOwnPropertyDescriptor', 'get-own-property-descriptor'],
542
+ ]);
543
+ /**
544
+ * Whether a callee can end a route to a native that compiles code: a member read whose key is a constant it could be,
545
+ * or computed, a call that returns a function (`f.bind(t)`), or a name, which may alias one of the natives a route
546
+ * goes through. A name that aliases a native that compiles code is a direct form's native, which the resolver tries
547
+ * first.
548
+ */
549
+ function mayReach(callee) {
550
+ const node = unwrapped(callee);
551
+ if (node.type === 'MemberExpression') {
552
+ const property = node['property'];
553
+ return node['computed'] === true || (property.type === 'Identifier' && routeKeys.has(property['name']));
554
+ }
555
+ // A name may alias a native that calls another function (`const { apply } = Reflect`, §5 Resolver capabilities 2).
556
+ if (node.type === 'Identifier')
557
+ return true;
558
+ return node.type === 'CallExpression' && mayReach(node['callee']);
559
+ }
560
+ /** An expression past the comma sequences and optional chains that don't change its value. */
561
+ function unwrapped(expression) {
562
+ let node = calleeExpression(expression);
563
+ while (node.type === 'ChainExpression')
564
+ node = calleeExpression(node['expression']);
565
+ return node;
566
+ }
567
+ /** The holder a function literal or declaration inherits from, by its kind. */
568
+ function functionPrototype(node) {
569
+ const async = node['async'] === true;
570
+ const generator = node['generator'] === true;
571
+ if (async && generator)
572
+ return '%AsyncGeneratorFunction%.prototype';
573
+ if (async)
574
+ return '%AsyncFunction%.prototype';
575
+ if (generator)
576
+ return '%GeneratorFunction%.prototype';
577
+ return 'Function.prototype';
578
+ }
579
+ /** The prototype a primitive or regular expression literal's value inherits from, or none for `null`. */
580
+ function literalPrototype(node) {
581
+ if (node['regex'] !== undefined && node['regex'] !== null)
582
+ return 'RegExp.prototype';
583
+ if (node['bigint'] !== undefined && node['bigint'] !== null)
584
+ return 'BigInt.prototype';
585
+ switch (typeof node['value']) {
586
+ case 'string':
587
+ return 'String.prototype';
588
+ case 'number':
589
+ return 'Number.prototype';
590
+ case 'boolean':
591
+ return 'Boolean.prototype';
592
+ default:
593
+ return undefined;
594
+ }
595
+ }
596
+ /**
597
+ * Whether an object literal plainly has no own property `key`: every property is a plain one with a key written out
598
+ * that is another, and none sets the literal's prototype.
599
+ */
600
+ function lacksOwn(literal, key) {
601
+ return literal['properties'].every((property) => {
602
+ if (property.type !== 'Property' || property['computed'] === true)
603
+ return false;
604
+ const written = property['key'];
605
+ const name = written.type === 'Identifier' ? written['name'] : written['value'];
606
+ if (typeof name !== 'string' && typeof name !== 'number')
607
+ return false;
608
+ return String(name) !== key && String(name) !== '__proto__';
609
+ });
610
+ }
611
+ /** A call's own arguments: each up to the first spread, which gives the rest. */
612
+ function argsOf(nodes) {
613
+ const spreadAt = nodes.findIndex((node) => node.type === 'SpreadElement');
614
+ return spreadAt < 0 ? { items: [...nodes] } : { items: nodes.slice(0, spreadAt), rest: nodes[spreadAt] };
615
+ }
616
+ /** The arguments after the first `count`. Arguments the rest gives stay unknown. */
617
+ function after(args, count) {
618
+ if (args.items.length >= count)
619
+ return { items: args.items.slice(count), ...(args.rest ? { rest: args.rest } : {}) };
620
+ return args.rest === undefined ? { items: [] } : { items: [], rest: args.rest };
621
+ }
622
+ /**
623
+ * The arguments an array-like passes when spread (`f.apply(t, list)`, `Reflect.apply(f, t, list)`): an array literal's
624
+ * elements, and otherwise ones the analyzer can't list. `rest` is the spread that may hold the list. A missing,
625
+ * `undefined` or `null` list passes none to `.apply`, and makes `Reflect.apply` and `Reflect.construct` throw
626
+ * (`nullish`), and so does a primitive list to all three (ECMA-262 CreateListFromArrayLike): undefined when it throws.
627
+ */
628
+ function spread(list, rest, nullish) {
629
+ if (list === undefined && rest !== undefined)
630
+ return { items: [], rest };
631
+ if (list === undefined || list === null || isNullLiteral(list))
632
+ return nullish === 'empty' ? { items: [] } : undefined;
633
+ if (list.type === 'ArrayExpression') {
634
+ const elements = list['elements'];
635
+ const spreadAt = elements.findIndex((element) => element?.type === 'SpreadElement');
636
+ return spreadAt < 0 ? { items: [...elements] } : { items: elements.slice(0, spreadAt), rest: elements[spreadAt] };
637
+ }
638
+ if (isPrimitive(list))
639
+ return undefined;
640
+ return { items: [], rest: list };
641
+ }
642
+ /** A literal whose `value` alone doesn't say what it is: a regular expression or a bigint, whose value can be `null`. */
643
+ const isRegexOrBigint = (node) => (node['regex'] ?? node['bigint'] ?? null) !== null;
644
+ const isNullLiteral = (node) => node.type === 'Literal' && node['value'] === null && !isRegexOrBigint(node);
645
+ /** A string, number, boolean or bigint literal, or a template literal: a primitive that is no array-like object. */
646
+ function isPrimitive(node) {
647
+ if (node.type === 'TemplateLiteral')
648
+ return true;
649
+ if (node.type !== 'Literal')
650
+ return false;
651
+ if (node['regex'] !== undefined && node['regex'] !== null)
652
+ return false;
653
+ return node['value'] !== null || isRegexOrBigint(node);
654
+ }
655
+ /**
656
+ * The type of the node that stands for a tagged template's strings array: an array, which a string conversion joins
657
+ * into the template's text, located at the template.
658
+ */
659
+ export const templateStrings = 'TemplateStrings';
660
+ /** The arguments a tagged template passes its tag: its strings array, then its substitutions. */
661
+ function templateArgs(quasi) {
662
+ const strings = { type: templateStrings, start: quasi.start, end: quasi.end };
663
+ return { items: [strings, ...quasi['expressions']] };
664
+ }
665
+ /** Arguments bound first, then those a call passes. */
666
+ function joined(first, then) {
667
+ if (first.rest !== undefined)
668
+ return first;
669
+ return { items: [...first.items, ...then.items], ...(then.rest ? { rest: then.rest } : {}) };
670
+ }
671
+ /** The step a route took, added to each native that the call it leads to compiles code with. */
672
+ function routed(outcome, ...steps) {
673
+ if (outcome.compiles.length === 0)
674
+ return outcome;
675
+ const compiles = outcome.compiles.map((call) => ({ ...call, via: [...new Set([...call.via, ...steps])] }));
676
+ return { ...outcome, compiles };
677
+ }
678
+ /** What any of several calls may do. */
679
+ function merged(outcomes) {
680
+ if (outcomes.length === 1)
681
+ return outcomes[0];
682
+ return { compiles: outcomes.flatMap((each) => each.compiles), returns: distinct(outcomes.flatMap((each) => each.returns)) };
683
+ }
684
+ /** Values a route read, with the steps that reached them. */
685
+ const stepped = (found, via) => found.map((value) => ({ ...value, via: [...value.via, ...via] }));
686
+ const optional = (value) => (value === undefined ? [] : [value]);
687
+ /** The step a member read adds for its key. */
688
+ const keyStep = (key) => (key === 'constructor' ? ['.constructor'] : []);
689
+ /** Values with the same meaning once, the first route to each kept. */
690
+ function distinct(values) {
691
+ if (values.length < 2)
692
+ return [...values];
693
+ const seen = new Map();
694
+ for (const value of values) {
695
+ const key = keyOf(value);
696
+ if (!seen.has(key))
697
+ seen.set(key, value);
698
+ }
699
+ return [...seen.values()];
700
+ }
701
+ function keyOf(value) {
702
+ const at = (node) => (node === null || node === undefined ? '-' : `${node.start}:${node.end}`);
703
+ switch (value.kind) {
704
+ case 'holder':
705
+ return `holder ${value.holder}`;
706
+ case 'compiler':
707
+ return `compiler ${value.name}`;
708
+ case 'object':
709
+ return `object ${value.proto} ${at(value.own)} ${value.primitive === true}`;
710
+ case 'descriptor':
711
+ return `descriptor [${value.value.map(keyOf).join(', ')}]`;
712
+ case 'bound':
713
+ return `bound ${keyOf(value.target)} ${at(value.thisArg)} ${value.args.items.map(at).join(',')} ${at(value.args.rest)}`;
714
+ default:
715
+ return `${value.kind} ${value.name}`;
716
+ }
717
+ }
718
+ function writeIndex(program, resolution) {
719
+ const written = [];
720
+ const changed = [];
721
+ const redeclared = new Set();
722
+ const declared = new Map();
723
+ walkTree(program, {
724
+ enter(node, parent) {
725
+ switch (node.type) {
726
+ case 'AssignmentExpression':
727
+ written.push(...patternNames(node['left']));
728
+ break;
729
+ case 'UpdateExpression':
730
+ written.push(...patternNames(node['argument']));
731
+ break;
732
+ case 'ForInStatement':
733
+ case 'ForOfStatement': {
734
+ const left = node['left'];
735
+ if (left.type !== 'VariableDeclaration')
736
+ written.push(...patternNames(left));
737
+ else
738
+ for (const declarator of left['declarations'])
739
+ written.push(...patternNames(declarator['id']));
740
+ break;
741
+ }
742
+ case 'VariableDeclarator':
743
+ if (node['init'] !== null && node['init'] !== undefined)
744
+ written.push(...patternNames(node['id']));
745
+ break;
746
+ case 'FunctionDeclaration': {
747
+ const name = node['id']?.['name'];
748
+ if (parent === null || name === undefined)
749
+ break;
750
+ const byName = declared.get(parent) ?? new Map();
751
+ declared.set(parent, byName);
752
+ byName.set(name, [...(byName.get(name) ?? []), node]);
753
+ break;
754
+ }
755
+ }
756
+ const subject = changeAt(node, resolution);
757
+ if (subject?.type === 'Identifier')
758
+ changed.push(subject);
759
+ },
760
+ });
761
+ for (const byName of declared.values()) {
762
+ for (const same of byName.values())
763
+ if (same.length > 1)
764
+ for (const node of same)
765
+ redeclared.add(node);
766
+ }
767
+ return { written, changed, redeclared };
768
+ }
769
+ /**
770
+ * Whether a name that resolves to a function declaration or a named function expression is the function's own name.
771
+ * The seam gives the function for its parameters and its `arguments` too (§19), so a name inside it that one of its
772
+ * parameters binds, or `arguments`, is no function.
773
+ */
774
+ function namesFunction(id, declaration) {
775
+ const name = id['name'];
776
+ if (declaration['id']?.['name'] !== name)
777
+ return false;
778
+ if (id.start < declaration.start || id.end > declaration.end)
779
+ return true;
780
+ // Inside the function, `arguments` is its arguments object, whatever the function is called.
781
+ if (name === 'arguments')
782
+ return false;
783
+ return !declaration['params'].some((param) => patternNames(param).some((bound) => bound['name'] === name));
784
+ }
785
+ /** Whether a name that resolves to a class declaration is the class's own name. */
786
+ function namesClass(id, declaration) {
787
+ return declaration['id']?.['name'] === id['name'];
788
+ }
789
+ /** Every function in a program, whose code runs when it is called rather than where it is written. */
790
+ function collectFunctions(program) {
791
+ const found = [];
792
+ walkTree(program, {
793
+ enter(node) {
794
+ if (isFunction(node))
795
+ found.push(node);
796
+ },
797
+ });
798
+ return found;
799
+ }
800
+ /** The names a pattern or assignment target binds: none for a member. */
801
+ function patternNames(pattern) {
802
+ if (pattern === null || pattern === undefined)
803
+ return [];
804
+ switch (pattern.type) {
805
+ case 'Identifier':
806
+ return [pattern];
807
+ case 'ObjectPattern':
808
+ return pattern['properties'].flatMap((property) => patternNames((property.type === 'RestElement' ? property['argument'] : property['value'])));
809
+ case 'ArrayPattern':
810
+ return pattern['elements'].flatMap((element) => patternNames(element));
811
+ case 'AssignmentPattern':
812
+ return patternNames(pattern['left']);
813
+ case 'RestElement':
814
+ return patternNames(pattern['argument']);
815
+ default:
816
+ return [];
817
+ }
818
+ }
819
+ /**
820
+ * The subjects code in `roots` gives its own `constructor` or a new prototype, where `this` is kept only when it is the
821
+ * `this` of the roots themselves (outside any function or class in them other than an arrow function).
822
+ */
823
+ function changedIn(roots, resolution) {
824
+ const found = [];
825
+ for (const root of roots) {
826
+ let depth = 0;
827
+ const ownThis = (node) => isClassNode(node) || (isFunction(node) && node.type !== 'ArrowFunctionExpression');
828
+ walkTree(root, {
829
+ enter(node) {
830
+ const subject = changeAt(node, resolution);
831
+ if (subject !== undefined && (subject.type !== 'ThisExpression' || depth === 0))
832
+ found.push(subject);
833
+ if (ownThis(node))
834
+ depth++;
835
+ },
836
+ leave(node) {
837
+ if (ownThis(node))
838
+ depth--;
839
+ },
840
+ });
841
+ }
842
+ return found;
843
+ }
844
+ const isFunction = (node) => node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression';
845
+ const isClassNode = (node) => node.type === 'ClassExpression' || node.type === 'ClassDeclaration';
846
+ /** A position that knows no guards, for code read away from where it runs. */
847
+ const nowhere = { guard: () => undefined, topLevel: false };
848
+ /** The methods that give an object its own `constructor` or a new prototype, on `Object` and on `Reflect`. */
849
+ const changers = {
850
+ Object: new Set(['defineProperty', 'defineProperties', 'assign', 'setPrototypeOf']),
851
+ Reflect: new Set(['defineProperty', 'set', 'setPrototypeOf']),
852
+ };
853
+ /**
854
+ * The object a node surely gives its own `constructor` or a new prototype (§2), if it does: `o.constructor = g`,
855
+ * `o.__proto__ = p`, `Object.defineProperty(o, "constructor", d)`, `Reflect.set(o, "constructor", g)`,
856
+ * `Object.defineProperties(o, { constructor: d })`, `Object.assign(o, { constructor: g })` or `setPrototypeOf(o, p)`.
857
+ * A key that may be another gives the other alternative, whose `constructor` stays inherited (Principle 3).
858
+ */
859
+ function changeAt(node, resolution) {
860
+ if (node.type === 'AssignmentExpression') {
861
+ const left = node['left'];
862
+ if (left.type !== 'MemberExpression')
863
+ return undefined;
864
+ return surelyKey(left, ['constructor', '__proto__'], resolution) ? left['object'] : undefined;
865
+ }
866
+ if (node.type !== 'CallExpression')
867
+ return undefined;
868
+ const callee = unwrapped(node['callee']);
869
+ if (callee.type !== 'MemberExpression' || callee['computed'] === true)
870
+ return undefined;
871
+ const method = callee['property']['name'];
872
+ const on = resolution
873
+ .evaluate(callee['object'], nowhere)
874
+ .find((value) => value.kind === 'holder' && changers[value.holder]?.has(method) === true);
875
+ if (on === undefined)
876
+ return undefined;
877
+ const [subject, key, ...rest] = node['arguments'];
878
+ if (subject === undefined || subject.type === 'SpreadElement')
879
+ return undefined;
880
+ const object = unwrapped(subject);
881
+ switch (method) {
882
+ case 'setPrototypeOf':
883
+ return object;
884
+ case 'defineProperty':
885
+ case 'set':
886
+ return key !== undefined && surelyConstructor(key, resolution) ? object : undefined;
887
+ default: {
888
+ // `defineProperties(o, props)` and `assign(o, ...sources)`: an object literal with a `constructor` key.
889
+ const sources = method === 'defineProperties' ? [key] : [key, ...rest];
890
+ const gives = sources.some((source) => source?.type === 'ObjectExpression' &&
891
+ source['properties'].some((property) => {
892
+ if (property.type !== 'Property')
893
+ return false;
894
+ const written = property['key'];
895
+ if (property['computed'] === true)
896
+ return surelyConstructor(written, resolution);
897
+ return (written.type === 'Identifier' ? written['name'] : literalKey(written)) === 'constructor';
898
+ }));
899
+ return gives ? object : undefined;
900
+ }
901
+ }
902
+ }
903
+ /** Whether a member expression's key is surely one of `names`. */
904
+ function surelyKey(member, names, resolution) {
905
+ const property = member['property'];
906
+ if (member['computed'] !== true)
907
+ return property.type === 'Identifier' && names.includes(property['name']);
908
+ return surely(property, names, resolution);
909
+ }
910
+ const surelyConstructor = (key, resolution) => surely(key, ['constructor'], resolution);
911
+ /** Whether every value an expression may have is a key among `names`. */
912
+ function surely(node, names, resolution) {
913
+ const values = resolution.evaluate(node, nowhere);
914
+ return (values.length > 0 &&
915
+ values.every((value) => {
916
+ if (value.kind !== 'constant')
917
+ return false;
918
+ const key = toPropertyKey(value.value);
919
+ return typeof key === 'string' && names.includes(key);
920
+ }));
921
+ }