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.
- package/LICENSE +202 -0
- package/NOTICE +61 -0
- package/README.md +183 -2
- package/catalog/catalog.json +552211 -0
- package/catalog/catalog.schema.json +787 -0
- package/catalog/changelog.json +3 -0
- package/dist/bin/monkeypatchlist.d.ts +2 -0
- package/dist/bin/monkeypatchlist.js +3 -0
- package/dist/catalog.d.ts +561 -0
- package/dist/catalog.js +58 -0
- package/dist/cli.d.ts +84 -0
- package/dist/cli.js +470 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lockfile-input.d.ts +9 -0
- package/dist/lockfile-input.js +91 -0
- package/dist/packages/analysis/binding.d.ts +2 -0
- package/dist/packages/analysis/binding.js +5 -0
- package/dist/packages/analysis/index.d.ts +10 -0
- package/dist/packages/analysis/index.js +13 -0
- package/dist/packages/analysis/job.d.ts +33 -0
- package/dist/packages/analysis/job.js +85 -0
- package/dist/packages/analysis/lib/absence-tags.d.ts +34 -0
- package/dist/packages/analysis/lib/absence-tags.js +340 -0
- package/dist/packages/analysis/lib/ast.d.ts +28 -0
- package/dist/packages/analysis/lib/ast.js +64 -0
- package/dist/packages/analysis/lib/binding.d.ts +36 -0
- package/dist/packages/analysis/lib/binding.js +66 -0
- package/dist/packages/analysis/lib/budget.d.ts +80 -0
- package/dist/packages/analysis/lib/budget.js +31 -0
- package/dist/packages/analysis/lib/catch-arms.d.ts +19 -0
- package/dist/packages/analysis/lib/catch-arms.js +351 -0
- package/dist/packages/analysis/lib/chunk-loads.d.ts +160 -0
- package/dist/packages/analysis/lib/chunk-loads.js +882 -0
- package/dist/packages/analysis/lib/chunk-registry.d.ts +45 -0
- package/dist/packages/analysis/lib/chunk-registry.js +334 -0
- package/dist/packages/analysis/lib/code-routes.d.ts +52 -0
- package/dist/packages/analysis/lib/code-routes.js +921 -0
- package/dist/packages/analysis/lib/collisions.d.ts +22 -0
- package/dist/packages/analysis/lib/collisions.js +103 -0
- package/dist/packages/analysis/lib/definitions.d.ts +76 -0
- package/dist/packages/analysis/lib/definitions.js +782 -0
- package/dist/packages/analysis/lib/early-exits.d.ts +39 -0
- package/dist/packages/analysis/lib/early-exits.js +161 -0
- package/dist/packages/analysis/lib/effect.d.ts +138 -0
- package/dist/packages/analysis/lib/effect.js +800 -0
- package/dist/packages/analysis/lib/global-object.d.ts +581 -0
- package/dist/packages/analysis/lib/global-object.js +8240 -0
- package/dist/packages/analysis/lib/keys.d.ts +33 -0
- package/dist/packages/analysis/lib/keys.js +82 -0
- package/dist/packages/analysis/lib/line-starts.d.ts +41 -0
- package/dist/packages/analysis/lib/line-starts.js +79 -0
- package/dist/packages/analysis/lib/link-group.d.ts +51 -0
- package/dist/packages/analysis/lib/link-group.js +487 -0
- package/dist/packages/analysis/lib/module-links.d.ts +117 -0
- package/dist/packages/analysis/lib/module-links.js +1060 -0
- package/dist/packages/analysis/lib/nested-code.d.ts +38 -0
- package/dist/packages/analysis/lib/nested-code.js +200 -0
- package/dist/packages/analysis/lib/numeric.d.ts +16 -0
- package/dist/packages/analysis/lib/numeric.js +216 -0
- package/dist/packages/analysis/lib/once-flags.d.ts +12 -0
- package/dist/packages/analysis/lib/once-flags.js +190 -0
- package/dist/packages/analysis/lib/parameter-arguments.d.ts +12 -0
- package/dist/packages/analysis/lib/parameter-arguments.js +34 -0
- package/dist/packages/analysis/lib/parse.d.ts +10 -0
- package/dist/packages/analysis/lib/parse.js +39 -0
- package/dist/packages/analysis/lib/partial.d.ts +60 -0
- package/dist/packages/analysis/lib/partial.js +518 -0
- package/dist/packages/analysis/lib/print.d.ts +3 -0
- package/dist/packages/analysis/lib/print.js +150 -0
- package/dist/packages/analysis/lib/reachability.d.ts +137 -0
- package/dist/packages/analysis/lib/reachability.js +1295 -0
- package/dist/packages/analysis/lib/resolver.d.ts +13 -0
- package/dist/packages/analysis/lib/resolver.js +2886 -0
- package/dist/packages/analysis/lib/restores.d.ts +42 -0
- package/dist/packages/analysis/lib/restores.js +482 -0
- package/dist/packages/analysis/lib/rewritten-deletes.d.ts +6 -0
- package/dist/packages/analysis/lib/rewritten-deletes.js +142 -0
- package/dist/packages/analysis/lib/scope.d.ts +110 -0
- package/dist/packages/analysis/lib/scope.js +443 -0
- package/dist/packages/analysis/lib/script-loads.d.ts +56 -0
- package/dist/packages/analysis/lib/script-loads.js +359 -0
- package/dist/packages/analysis/lib/summaries.d.ts +177 -0
- package/dist/packages/analysis/lib/summaries.js +498 -0
- package/dist/packages/analysis/lib/types.d.ts +632 -0
- package/dist/packages/analysis/lib/types.js +128 -0
- package/dist/packages/analysis/lib/wrap.d.ts +34 -0
- package/dist/packages/analysis/lib/wrap.js +407 -0
- package/dist/packages/analysis/lib/writes.d.ts +358 -0
- package/dist/packages/analysis/lib/writes.js +1612 -0
- package/dist/packages/analysis/visit-order.d.ts +3 -0
- package/dist/packages/analysis/visit-order.js +23 -0
- package/dist/packages/attribution/index.d.ts +233 -0
- package/dist/packages/attribution/index.js +444 -0
- package/dist/packages/attribution/lib/containing-package.d.ts +76 -0
- package/dist/packages/attribution/lib/containing-package.js +304 -0
- package/dist/packages/attribution/lib/first-party.d.ts +37 -0
- package/dist/packages/attribution/lib/first-party.js +153 -0
- package/dist/packages/attribution/lib/lockfile-graph.d.ts +99 -0
- package/dist/packages/attribution/lib/lockfile-graph.js +158 -0
- package/dist/packages/attribution/lib/lockfile.d.ts +33 -0
- package/dist/packages/attribution/lib/lockfile.js +330 -0
- package/dist/packages/attribution/lib/path-rules.d.ts +29 -0
- package/dist/packages/attribution/lib/path-rules.js +84 -0
- package/dist/packages/attribution/lib/pnpm.d.ts +4 -0
- package/dist/packages/attribution/lib/pnpm.js +133 -0
- package/dist/packages/attribution/lib/remote-url.d.ts +7 -0
- package/dist/packages/attribution/lib/remote-url.js +24 -0
- package/dist/packages/attribution/lib/source-map.d.ts +51 -0
- package/dist/packages/attribution/lib/source-map.js +125 -0
- package/dist/packages/attribution/lib/yaml.d.ts +9 -0
- package/dist/packages/attribution/lib/yaml.js +277 -0
- package/dist/packages/attribution/lib/yarn.d.ts +7 -0
- package/dist/packages/attribution/lib/yarn.js +227 -0
- package/dist/packages/baseline/index.d.ts +45 -0
- package/dist/packages/baseline/index.js +41 -0
- package/dist/packages/baseline/lib/error.d.ts +15 -0
- package/dist/packages/baseline/lib/error.js +25 -0
- package/dist/packages/baseline/lib/format.d.ts +49 -0
- package/dist/packages/baseline/lib/format.js +22 -0
- package/dist/packages/baseline/lib/migrations.d.ts +19 -0
- package/dist/packages/baseline/lib/migrations.js +7 -0
- package/dist/packages/baseline/lib/previous.d.ts +23 -0
- package/dist/packages/baseline/lib/previous.js +64 -0
- package/dist/packages/baseline/lib/read.d.ts +39 -0
- package/dist/packages/baseline/lib/read.js +219 -0
- package/dist/packages/baseline/lib/report.d.ts +25 -0
- package/dist/packages/baseline/lib/report.js +104 -0
- package/dist/packages/baseline/lib/skew.d.ts +51 -0
- package/dist/packages/baseline/lib/skew.js +75 -0
- package/dist/packages/compare/index.d.ts +120 -0
- package/dist/packages/compare/index.js +274 -0
- package/dist/packages/compare/lib/catalog-notes.d.ts +73 -0
- package/dist/packages/compare/lib/catalog-notes.js +224 -0
- package/dist/packages/completeness/index.d.ts +246 -0
- package/dist/packages/completeness/index.js +249 -0
- package/dist/packages/confidence/index.d.ts +54 -0
- package/dist/packages/confidence/index.js +71 -0
- package/dist/packages/config/index.d.ts +107 -0
- package/dist/packages/config/index.js +95 -0
- package/dist/packages/config/lib/command-line.d.ts +23 -0
- package/dist/packages/config/lib/command-line.js +239 -0
- package/dist/packages/config/lib/config-file.d.ts +11 -0
- package/dist/packages/config/lib/config-file.js +119 -0
- package/dist/packages/config/lib/scan-config-id.d.ts +28 -0
- package/dist/packages/config/lib/scan-config-id.js +54 -0
- package/dist/packages/config/lib/schema.d.ts +2 -0
- package/dist/packages/config/lib/schema.js +31 -0
- package/dist/packages/config/lib/types.d.ts +64 -0
- package/dist/packages/config/lib/types.js +6 -0
- package/dist/packages/config/lib/urls.d.ts +6 -0
- package/dist/packages/config/lib/urls.js +9 -0
- package/dist/packages/discovery/index.d.ts +277 -0
- package/dist/packages/discovery/index.js +1950 -0
- package/dist/packages/discovery/lib/import-map.d.ts +68 -0
- package/dist/packages/discovery/lib/import-map.js +180 -0
- package/dist/packages/discovery/lib/requests.d.ts +58 -0
- package/dist/packages/discovery/lib/requests.js +591 -0
- package/dist/packages/discovery/lib/scopes.d.ts +6 -0
- package/dist/packages/discovery/lib/scopes.js +286 -0
- package/dist/packages/discovery/lib/source-maps.d.ts +56 -0
- package/dist/packages/discovery/lib/source-maps.js +209 -0
- package/dist/packages/discovery/lib/swept-code.d.ts +27 -0
- package/dist/packages/discovery/lib/swept-code.js +107 -0
- package/dist/packages/discovery/lib/syntax.d.ts +17 -0
- package/dist/packages/discovery/lib/syntax.js +39 -0
- package/dist/packages/findings/index.d.ts +106 -0
- package/dist/packages/findings/index.js +254 -0
- package/dist/packages/findings/lib/scope.d.ts +38 -0
- package/dist/packages/findings/lib/scope.js +110 -0
- package/dist/packages/parallel/index.d.ts +2 -0
- package/dist/packages/parallel/index.js +5 -0
- package/dist/packages/parallel/lib/protocol.d.ts +15 -0
- package/dist/packages/parallel/lib/protocol.js +2 -0
- package/dist/packages/parallel/lib/runner.d.ts +36 -0
- package/dist/packages/parallel/lib/runner.js +96 -0
- package/dist/packages/parallel/lib/schedule.d.ts +16 -0
- package/dist/packages/parallel/lib/schedule.js +52 -0
- package/dist/packages/parallel/lib/worker.d.ts +1 -0
- package/dist/packages/parallel/lib/worker.js +18 -0
- package/dist/packages/policy/index.d.ts +36 -0
- package/dist/packages/policy/index.js +22 -0
- package/dist/packages/report/explain.d.ts +26 -0
- package/dist/packages/report/explain.js +99 -0
- package/dist/packages/report/index.d.ts +210 -0
- package/dist/packages/report/index.js +388 -0
- package/dist/packages/report/terminal.d.ts +11 -0
- package/dist/packages/report/terminal.js +331 -0
- package/dist/packages/source-store/fixture.d.ts +22 -0
- package/dist/packages/source-store/fixture.js +58 -0
- package/dist/packages/source-store/index.d.ts +278 -0
- package/dist/packages/source-store/index.js +322 -0
- package/dist/packages/source-store/lib/addresses.d.ts +6 -0
- package/dist/packages/source-store/lib/addresses.js +91 -0
- package/dist/packages/source-store/lib/connection.d.ts +15 -0
- package/dist/packages/source-store/lib/connection.js +135 -0
- package/dist/packages/source-store/lib/content-types.d.ts +16 -0
- package/dist/packages/source-store/lib/content-types.js +75 -0
- package/dist/packages/source-store/lib/data-urls.d.ts +10 -0
- package/dist/packages/source-store/lib/data-urls.js +85 -0
- package/dist/packages/source-store/lib/deadline.d.ts +18 -0
- package/dist/packages/source-store/lib/deadline.js +44 -0
- package/dist/packages/source-store/lib/exchange.d.ts +29 -0
- package/dist/packages/source-store/lib/exchange.js +112 -0
- package/dist/packages/source-store/lib/integrity.d.ts +19 -0
- package/dist/packages/source-store/lib/integrity.js +86 -0
- package/dist/packages/source-store/lib/outcomes.d.ts +12 -0
- package/dist/packages/source-store/lib/outcomes.js +22 -0
- package/dist/packages/source-store/lib/proxy.d.ts +11 -0
- package/dist/packages/source-store/lib/proxy.js +53 -0
- package/dist/packages/source-store/lib/session.d.ts +12 -0
- package/dist/packages/source-store/lib/session.js +139 -0
- package/dist/packages/source-store/lib/urls.d.ts +6 -0
- package/dist/packages/source-store/lib/urls.js +15 -0
- package/dist/packages/source-store/resolve.d.ts +59 -0
- package/dist/packages/source-store/resolve.js +45 -0
- package/dist/scan.d.ts +65 -0
- package/dist/scan.js +592 -0
- package/package.json +95 -4
- package/schema/config.schema.json +133 -0
- package/schema/report.schema.json +1905 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// The data that crosses the resolver seam (§19). Everything here is plain JSON-serializable data.
|
|
2
|
+
/** What a mutation does to its target (§8). It is part of identity. */
|
|
3
|
+
export const effects = ['replace', 'wrap', 'add', 'delete', 'descriptor-change', 'prototype-change'];
|
|
4
|
+
/** The syntactic means of a mutation (§3). It is evidence, not identity. */
|
|
5
|
+
export const mechanisms = [
|
|
6
|
+
'assign',
|
|
7
|
+
'define-property',
|
|
8
|
+
'define-properties',
|
|
9
|
+
'reflect-define',
|
|
10
|
+
'bulk-assign',
|
|
11
|
+
'delete',
|
|
12
|
+
'set-prototype',
|
|
13
|
+
'declare',
|
|
14
|
+
];
|
|
15
|
+
/** The closed list of resolution-trace step kinds (§14). A new kind needs a spec change. */
|
|
16
|
+
export const traceStepKinds = [
|
|
17
|
+
'binding',
|
|
18
|
+
'alias',
|
|
19
|
+
'fold',
|
|
20
|
+
'call',
|
|
21
|
+
'module-link',
|
|
22
|
+
'catalog-holder',
|
|
23
|
+
'enumeration',
|
|
24
|
+
'guard',
|
|
25
|
+
'prune',
|
|
26
|
+
'creation-site',
|
|
27
|
+
'outcome',
|
|
28
|
+
'restore',
|
|
29
|
+
'token-check',
|
|
30
|
+
'path-rule',
|
|
31
|
+
'confidence-reason',
|
|
32
|
+
'note',
|
|
33
|
+
];
|
|
34
|
+
/** The closed list of limitation kinds (§13). A new kind needs a spec change. */
|
|
35
|
+
export const limitationKinds = [
|
|
36
|
+
'parse-failure',
|
|
37
|
+
'unreadable',
|
|
38
|
+
'source-maps-unpaired',
|
|
39
|
+
'fetch-failed',
|
|
40
|
+
'sri-mismatch',
|
|
41
|
+
'remote-refused',
|
|
42
|
+
'offline-skipped',
|
|
43
|
+
'dynamic-code',
|
|
44
|
+
'unresolved-reference',
|
|
45
|
+
'realm-uncertain',
|
|
46
|
+
'unresolved-holder',
|
|
47
|
+
'unlinked-module-call',
|
|
48
|
+
'analysis-budget',
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* The syntactic forms a write reaches the global object through without a call (§5 Global resolution):
|
|
52
|
+
* - `global-property`: a free name that is the global object's property, as a bare global (`fetch` in `fetch = w`) or
|
|
53
|
+
* a bare constructor or namespace (`XMLHttpRequest` in `XMLHttpRequest.prototype.send = w`)
|
|
54
|
+
* - `global-name`: a free `window`, `self` or `globalThis`, the realm's names for its global object, and in the window
|
|
55
|
+
* realm a free `frames`, which is the window itself (§5 Other windows)
|
|
56
|
+
* - `guarded-global`: a free `global` under a `typeof global` guard
|
|
57
|
+
* - `top-level-this`: `this` at the top level of a classic script, as UMD wrappers pass it
|
|
58
|
+
* - `function-return-this`: `Function('return this')()`
|
|
59
|
+
* - `sloppy-this`: `this` in a sloppy function that a call the resolver evaluates calls without a `this`, or with `null`
|
|
60
|
+
* or `undefined` as its `thisArg`: `(function(){ return this })()`, `f()`, `f.call(null)`, an iteration
|
|
61
|
+
* native's callback with no `thisArg`
|
|
62
|
+
* - `other-window`: a window reached from another value: through `parent`, `top` or `opener` from a window, as a child
|
|
63
|
+
* window through a constant index read on a window (`window[0]`, named `[0]`) or any index read on a window spelled
|
|
64
|
+
* as the frames collection (`frames[i]`, named `[i]`), or through `defaultView` or `contentWindow` on any object
|
|
65
|
+
* (§5 Other windows). This window's `document.defaultView`, and a child's `parent` of this window, are this window.
|
|
66
|
+
* - `singleton`: a platform singleton, read from a window or another singleton through an access path in its alias
|
|
67
|
+
* group (§4): `localStorage` in `window.localStorage`, `location` in `document.location`
|
|
68
|
+
*/
|
|
69
|
+
export const globalForms = [
|
|
70
|
+
'global-property',
|
|
71
|
+
'global-name',
|
|
72
|
+
'guarded-global',
|
|
73
|
+
'top-level-this',
|
|
74
|
+
'function-return-this',
|
|
75
|
+
'sloppy-this',
|
|
76
|
+
'other-window',
|
|
77
|
+
'singleton',
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* The natives that compile code from strings (§2 Content that isn't JavaScript): `eval`, the `Function` constructor
|
|
81
|
+
* (called or constructed), and string timers.
|
|
82
|
+
*/
|
|
83
|
+
export const codeCallees = ['eval', 'Function', 'setTimeout', 'setInterval'];
|
|
84
|
+
/**
|
|
85
|
+
* Whether a platform mutation happens (§7 Values), judged by the guards on its path:
|
|
86
|
+
* - `unconditional`: no guard that matters
|
|
87
|
+
* - `guarded-native-absence`: it runs only when some native, not necessarily the target, is missing
|
|
88
|
+
* - `conditional`: a recognized guard that can't be decided statically
|
|
89
|
+
* - `unknown`: control flow the analyzer couldn't model
|
|
90
|
+
*
|
|
91
|
+
* It is not identity, and in R1 it doesn't make a finding `changed` (§11, ADR 0014).
|
|
92
|
+
*/
|
|
93
|
+
export const reachabilities = ['unconditional', 'guarded-native-absence', 'conditional', 'unknown'];
|
|
94
|
+
/**
|
|
95
|
+
* Whether a mutation runs at load or later (§7 Install context): `load` when a resolved synchronous chain reaches
|
|
96
|
+
* top-level code, `deferred` when only a later call of its install entry reaches it. Evidence, never identity: it
|
|
97
|
+
* doesn't make a finding `changed` in R1 (§11, ADR 0014).
|
|
98
|
+
*/
|
|
99
|
+
export const installContexts = ['load', 'deferred'];
|
|
100
|
+
/**
|
|
101
|
+
* The class of a guard (§14 `guard`): `transparent` (it leaves the mutation `unconditional`: the written target's own
|
|
102
|
+
* presence, a once-flag, a writability check, a test decided as a constant), `absence` (a native is missing),
|
|
103
|
+
* `broken-native` (a native is judged broken rather than missing), `config` (a config or option flag),
|
|
104
|
+
* `other-target` (the presence of a native other than the target), `unknown` (a guard the analyzer doesn't recognize,
|
|
105
|
+
* an unresolved call, or `with` or direct-`eval` taint) and `undecided` (a recognized guard that can't be decided).
|
|
106
|
+
*/
|
|
107
|
+
export const guardClasses = [
|
|
108
|
+
'transparent',
|
|
109
|
+
'absence',
|
|
110
|
+
'broken-native',
|
|
111
|
+
'config',
|
|
112
|
+
'other-target',
|
|
113
|
+
'unknown',
|
|
114
|
+
'undecided',
|
|
115
|
+
];
|
|
116
|
+
/** Why a write can't change its target (§3 Ineffective writes). */
|
|
117
|
+
export const ineffectiveReasons = [
|
|
118
|
+
'readonly',
|
|
119
|
+
'lenient-setter',
|
|
120
|
+
'non-writable',
|
|
121
|
+
'non-configurable',
|
|
122
|
+
'immutable-prototype',
|
|
123
|
+
];
|
|
124
|
+
/**
|
|
125
|
+
* Whether an ineffective write throws (§3): `always` in sloppy and strict code alike, at parse or instantiation or when
|
|
126
|
+
* the write runs; `strict-only`; or `never`, as the `Reflect` forms return false.
|
|
127
|
+
*/
|
|
128
|
+
export const throwsValues = ['always', 'strict-only', 'never'];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SerializedCatalog, Realm } from '../../../catalog.ts';
|
|
2
|
+
import { type AstNode } from './ast.ts';
|
|
3
|
+
import type { Definitions } from './definitions.ts';
|
|
4
|
+
import { type GlobalResolution, type Leaf, type Position } from './global-object.ts';
|
|
5
|
+
import type { Source } from './writes.ts';
|
|
6
|
+
export interface WrapContext {
|
|
7
|
+
resolution: GlobalResolution;
|
|
8
|
+
definitions: Definitions;
|
|
9
|
+
catalog: SerializedCatalog;
|
|
10
|
+
realm: Realm;
|
|
11
|
+
/** The realm's global holder, e.g. `Window`. */
|
|
12
|
+
global: string;
|
|
13
|
+
/** Whether a callee is a catalog native: a native function or holder, or a catalog member read from one. */
|
|
14
|
+
isCatalogNative(callee: AstNode, at: Position): boolean;
|
|
15
|
+
}
|
|
16
|
+
/** The slot a write replaces: the object written, the native it held, and the window it belongs to. */
|
|
17
|
+
export interface Replaced {
|
|
18
|
+
/** The native holder written, e.g. `Window`, `navigator`, `XMLHttpRequest.prototype`. */
|
|
19
|
+
holder: string;
|
|
20
|
+
/** The catalog target whose native the slot held: the target, or the inherited member a shadowing write shadows. */
|
|
21
|
+
original: string;
|
|
22
|
+
/** The leaves the holder written was reached from. */
|
|
23
|
+
leaves: readonly Leaf[];
|
|
24
|
+
/**
|
|
25
|
+
* For a wildcard write, whose member the analyzer doesn't know (§5 Partially resolved writes), whether an expression
|
|
26
|
+
* gives the original: a read of the same (holder, key) before the write, as the key reuse found it.
|
|
27
|
+
*/
|
|
28
|
+
isOriginal?: (node: AstNode) => boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wrap evidence over one program: for a write's value sources and the slot it replaces, whether each alternative of
|
|
32
|
+
* the value carries wrap evidence, as the set of answers the alternatives give.
|
|
33
|
+
*/
|
|
34
|
+
export declare function wrapEvidence(context: WrapContext): (sources: readonly Source[], replaced: Replaced) => Set<boolean>;
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
// Wrap evidence (§5 Resolver capabilities 10, §8 Effect and mechanism): a replacement is a `wrap` when the new value is
|
|
2
|
+
// shown to call the original, the native function the target held before the write, and a `replace` otherwise. The
|
|
3
|
+
// effect is decided by rule, never marked uncertain.
|
|
4
|
+
//
|
|
5
|
+
// The evidence:
|
|
6
|
+
// - a factory that receives the original: a call whose callee is unresolved or a user function, with the
|
|
7
|
+
// original among its arguments at any depth, whatever a resolved summary does with it. A catalog native counts only
|
|
8
|
+
// where ECMA-262 makes its result call the argument: `Function.prototype.bind`, so a `.bind` rebind is a wrap
|
|
9
|
+
// - a `Proxy` over the original
|
|
10
|
+
// - a function or class that invokes the original by any ECMA-262 route: a direct call, `.call`, `.apply`,
|
|
11
|
+
// `Reflect.apply`, `new`, `Reflect.construct`, `super()` of a class whose heritage is the original, or a call through
|
|
12
|
+
// a bound copy, in its body, in closures it creates, or in a helper the summaries resolve
|
|
13
|
+
//
|
|
14
|
+
// The original is any read that reaches the target's native function (or, for a shadowing write, the inherited member
|
|
15
|
+
// it shadows) in the unmutated catalog, through the same window. A read made when the new value runs counts only when
|
|
16
|
+
// its path doesn't go through the object written, which then holds the new value: `EventTarget.prototype.
|
|
17
|
+
// addEventListener.call(this, …)` in a new `window.addEventListener` does, `window.fetch(…)` in a new `window.fetch`
|
|
18
|
+
// doesn't.
|
|
19
|
+
//
|
|
20
|
+
// A written value whose alternatives (`?:`, `||`, the definitions of a variable, the values the resolution gives)
|
|
21
|
+
// differ in wrap evidence gives one finding per effect.
|
|
22
|
+
import { calleeExpression, walkTree } from "./ast.js";
|
|
23
|
+
import { windowOf } from "./global-object.js";
|
|
24
|
+
import { parameterArguments } from "./parameter-arguments.js";
|
|
25
|
+
import { memberName } from "./keys.js";
|
|
26
|
+
/** Functions and classes, whose bodies run when they are called. */
|
|
27
|
+
const functionTypes = new Set([
|
|
28
|
+
'FunctionDeclaration',
|
|
29
|
+
'FunctionExpression',
|
|
30
|
+
'ArrowFunctionExpression',
|
|
31
|
+
'ClassDeclaration',
|
|
32
|
+
'ClassExpression',
|
|
33
|
+
]);
|
|
34
|
+
/**
|
|
35
|
+
* Wrap evidence over one program: for a write's value sources and the slot it replaces, whether each alternative of
|
|
36
|
+
* the value carries wrap evidence, as the set of answers the alternatives give.
|
|
37
|
+
*/
|
|
38
|
+
export function wrapEvidence(context) {
|
|
39
|
+
const { resolution, definitions } = context;
|
|
40
|
+
const holders = context.catalog.holders[context.realm];
|
|
41
|
+
const targets = context.catalog.targets[context.realm];
|
|
42
|
+
const target = (name) => (Object.hasOwn(targets, name) ? targets[name] : undefined);
|
|
43
|
+
/** The holders on a holder's chain in the unmutated catalog, from it. */
|
|
44
|
+
const chainOf = (start) => {
|
|
45
|
+
const chain = [];
|
|
46
|
+
for (let at = start; at !== null; at = Object.hasOwn(holders, at) ? holders[at].proto : null)
|
|
47
|
+
chain.push(at);
|
|
48
|
+
return chain;
|
|
49
|
+
};
|
|
50
|
+
/** The catalog target a native function value's name reads: the first on the chain of the holder it is read from. */
|
|
51
|
+
const nativeTarget = (name) => {
|
|
52
|
+
const dot = name.lastIndexOf('.');
|
|
53
|
+
const holder = dot < 0 ? context.global : name.slice(0, dot);
|
|
54
|
+
const key = dot < 0 ? name : name.slice(dot + 1);
|
|
55
|
+
const found = chainOf(holder)
|
|
56
|
+
.map((at) => memberName(at, key))
|
|
57
|
+
.find((each) => target(each) !== undefined);
|
|
58
|
+
return found === undefined ? undefined : { holder, target: found };
|
|
59
|
+
};
|
|
60
|
+
/** A position with no frame and no `typeof` guard, as a definition or function body is evaluated with no call. */
|
|
61
|
+
const nowhere = { guard: () => undefined, topLevel: false };
|
|
62
|
+
const inFrame = (frame) => ({ guard: () => undefined, topLevel: false, frame });
|
|
63
|
+
return (sources, replaced) => {
|
|
64
|
+
const window = windowOf(replaced.leaves);
|
|
65
|
+
const original = target(replaced.original);
|
|
66
|
+
// The functions whose code runs when the alternative of the new value being read is called: a read in them is made
|
|
67
|
+
// at call time. `alternative` empties it for each alternative.
|
|
68
|
+
const running = [];
|
|
69
|
+
const atCallTime = (site) => running.some((fn) => site.start >= fn.start && site.end <= fn.end);
|
|
70
|
+
/** Whether a read from `holder` at call time goes through the object written, which then holds the new value. */
|
|
71
|
+
const throughWritten = (holder) => chainOf(holder).includes(replaced.holder);
|
|
72
|
+
/** Whether a value is the original, read as the rule above counts it. */
|
|
73
|
+
const isOriginal = (value) => {
|
|
74
|
+
if (window === undefined || original === undefined)
|
|
75
|
+
return false;
|
|
76
|
+
switch (value.kind) {
|
|
77
|
+
case 'holder':
|
|
78
|
+
// A constructor the target holds (`Window.XMLHttpRequest`): read at call time through the global object, it
|
|
79
|
+
// is the new value.
|
|
80
|
+
return (original.holds === value.holder &&
|
|
81
|
+
windowOf(value.leaves) === window &&
|
|
82
|
+
!value.leaves.some((leaf) => atCallTime(leaf.node)));
|
|
83
|
+
case 'member':
|
|
84
|
+
return (value.target === replaced.original &&
|
|
85
|
+
windowOf(value.leaves) === window &&
|
|
86
|
+
value.sites.some((site) => !atCallTime(site) || !throughWritten(value.holder)));
|
|
87
|
+
case 'native': {
|
|
88
|
+
const read = nativeTarget(value.name);
|
|
89
|
+
if (read?.target !== replaced.original || windowOf(value.leaves) !== window)
|
|
90
|
+
return false;
|
|
91
|
+
return (value.sites ?? []).some((site) => !atCallTime(site) || !throughWritten(read.holder));
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
/** The expressions an expression's value comes from, past parentheses, choices, sequences and assignments. */
|
|
98
|
+
const branches = (node) => {
|
|
99
|
+
switch (node.type) {
|
|
100
|
+
case 'ParenthesizedExpression':
|
|
101
|
+
case 'ChainExpression':
|
|
102
|
+
case 'TSAsExpression':
|
|
103
|
+
case 'TSNonNullExpression':
|
|
104
|
+
return branches(node['expression']);
|
|
105
|
+
case 'ConditionalExpression':
|
|
106
|
+
return [...branches(node['consequent']), ...branches(node['alternate'])];
|
|
107
|
+
case 'LogicalExpression':
|
|
108
|
+
return [...branches(node['left']), ...branches(node['right'])];
|
|
109
|
+
case 'SequenceExpression':
|
|
110
|
+
return branches(node['expressions'].at(-1));
|
|
111
|
+
case 'AssignmentExpression':
|
|
112
|
+
return node['operator'] === '=' ? branches(node['right']) : [node];
|
|
113
|
+
default:
|
|
114
|
+
return [node];
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* The sources of the definitions of a variable that reach `id`, when it is a variable the program defines, each with
|
|
119
|
+
* the position it is evaluated at: the frame of `at`, when the definition is in that frame's function's own code
|
|
120
|
+
* (`const original = source[name]` in a fill-style helper instantiated with its call's arguments), and otherwise no
|
|
121
|
+
* frame.
|
|
122
|
+
*/
|
|
123
|
+
const definitionSources = (id, at) => {
|
|
124
|
+
const key = definitions.keyOf(id);
|
|
125
|
+
if (key === undefined)
|
|
126
|
+
return undefined;
|
|
127
|
+
const reaching = definitions.reaching(id, key);
|
|
128
|
+
const found = reaching.definitions.flatMap((definition) => definition.pattern.type === 'Identifier' && definition.source !== undefined && !definition.logical
|
|
129
|
+
? [{ node: definition.source, at: framed(definition.owner, at) }]
|
|
130
|
+
: []);
|
|
131
|
+
return found.length > 0 && found.length === reaching.definitions.length ? found : undefined;
|
|
132
|
+
};
|
|
133
|
+
/** The position code owned by `owner` is evaluated at: in the frame of `at`, or one around it, that instantiates it. */
|
|
134
|
+
const framed = (owner, at) => {
|
|
135
|
+
for (let frame = at.frame; frame !== undefined; frame = frame.outer)
|
|
136
|
+
if (frame.fn === owner)
|
|
137
|
+
return inFrame(frame);
|
|
138
|
+
return nowhere;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Whether an expression may be the original, or a bound copy of it. Definitions and bound copies are followed with
|
|
142
|
+
* no depth cap, so the effect never depends on how far they go: `followed` holds the definition sources already
|
|
143
|
+
* read, which only ends a cycle.
|
|
144
|
+
*/
|
|
145
|
+
const reaches = (node, at, followed = new Set()) => {
|
|
146
|
+
if (replaced.isOriginal?.(node) === true)
|
|
147
|
+
return true;
|
|
148
|
+
if (resolution.evaluate(node, at).some(isOriginal))
|
|
149
|
+
return true;
|
|
150
|
+
return branches(node).some((branch) => {
|
|
151
|
+
if (branch.type === 'CallExpression')
|
|
152
|
+
return boundCopy(branch, at, followed);
|
|
153
|
+
if (branch.type !== 'Identifier')
|
|
154
|
+
return false;
|
|
155
|
+
return (definitionSources(branch, at) ?? []).some((source) => {
|
|
156
|
+
if (followed.has(source.node))
|
|
157
|
+
return false;
|
|
158
|
+
followed.add(source.node);
|
|
159
|
+
return reaches(source.node, source.at, followed);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
/** Whether a call makes a bound copy of the original: `o.bind(t)`, `Function.prototype.bind.call(o, t)`. */
|
|
164
|
+
const boundCopy = (call, at, followed = new Set()) => {
|
|
165
|
+
const callee = calleeExpression(call['callee']);
|
|
166
|
+
if (callee.type !== 'MemberExpression')
|
|
167
|
+
return false;
|
|
168
|
+
const key = keyOf(callee);
|
|
169
|
+
const object = callee['object'];
|
|
170
|
+
if (key === 'bind')
|
|
171
|
+
return reaches(object, at, followed);
|
|
172
|
+
if (key !== 'call' && key !== 'apply')
|
|
173
|
+
return false;
|
|
174
|
+
const binds = resolution.evaluate(object, at).some((value) => value.kind === 'native' && value.name === 'Function.prototype.bind');
|
|
175
|
+
const [first] = call['arguments'];
|
|
176
|
+
if (!binds || first === undefined)
|
|
177
|
+
return false;
|
|
178
|
+
const fn = key === 'call' ? first : firstElement(first);
|
|
179
|
+
return fn !== undefined && reaches(fn, at, followed);
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Whether the original is among a call's arguments at any depth: an argument, or a value in the objects and arrays
|
|
183
|
+
* an argument holds. A function's code is no part of its value, so reads in it are not walked.
|
|
184
|
+
*/
|
|
185
|
+
const passes = (call, at) => {
|
|
186
|
+
// The object and array literals already walked through a variable that holds them, which only ends a cycle.
|
|
187
|
+
const walked = new Set();
|
|
188
|
+
const holds = (value, at) => {
|
|
189
|
+
let found = false;
|
|
190
|
+
walkTree(value, {
|
|
191
|
+
enter(node, parent) {
|
|
192
|
+
if (found || functionTypes.has(node.type))
|
|
193
|
+
return false;
|
|
194
|
+
if (node.type !== 'Identifier' && node.type !== 'MemberExpression' && node.type !== 'ThisExpression')
|
|
195
|
+
return;
|
|
196
|
+
// A property's name, and a literal's key, are no reads.
|
|
197
|
+
if (parent?.type === 'MemberExpression' && parent['property'] === node && parent['computed'] !== true)
|
|
198
|
+
return;
|
|
199
|
+
if (parent?.type === 'Property' && parent['key'] === node && parent['computed'] !== true && parent['shorthand'] !== true)
|
|
200
|
+
return;
|
|
201
|
+
if (reaches(node, at) || (node.type === 'Identifier' && literalHolds(node, at)))
|
|
202
|
+
found = true;
|
|
203
|
+
return found ? false : undefined;
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
return found;
|
|
207
|
+
};
|
|
208
|
+
// A variable an argument reads holds the values of the object and array literals assigned to it, as a literal
|
|
209
|
+
// written in the call does (`i = { f: original }; make(i)`).
|
|
210
|
+
const literalHolds = (id, at) => (definitionSources(id, at) ?? []).some(({ node, at: where }) => branches(node).some((branch) => {
|
|
211
|
+
if (branch.type !== 'ObjectExpression' && branch.type !== 'ArrayExpression')
|
|
212
|
+
return false;
|
|
213
|
+
if (walked.has(branch))
|
|
214
|
+
return false;
|
|
215
|
+
walked.add(branch);
|
|
216
|
+
return holds(branch, where);
|
|
217
|
+
}));
|
|
218
|
+
return call['arguments'].some((arg) => holds(arg, at));
|
|
219
|
+
};
|
|
220
|
+
/** Whether a call or `new` in the new value's code invokes the original. */
|
|
221
|
+
const invokes = (call, at, seen) => {
|
|
222
|
+
const callee = calleeExpression(call['callee']);
|
|
223
|
+
const args = call['arguments'];
|
|
224
|
+
if (callee.type === 'Super')
|
|
225
|
+
return false;
|
|
226
|
+
if (reaches(callee, at))
|
|
227
|
+
return true;
|
|
228
|
+
if (call.type === 'CallExpression' && callee.type === 'MemberExpression') {
|
|
229
|
+
const key = keyOf(callee);
|
|
230
|
+
if ((key === 'call' || key === 'apply') && reaches(callee['object'], at))
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
const [first] = args;
|
|
234
|
+
const natives = resolution.evaluate(callee, at);
|
|
235
|
+
const reflects = natives.some((value) => value.kind === 'native' && (value.name === 'Reflect.apply' || value.name === 'Reflect.construct'));
|
|
236
|
+
if (reflects && first !== undefined && first.type !== 'SpreadElement' && reaches(first, at))
|
|
237
|
+
return true;
|
|
238
|
+
// A helper the summaries resolve: run with the frames its calls give it when it is passed the original, and
|
|
239
|
+
// otherwise with no call, since only what it reads itself can be the original then. `seen` ends a cycle.
|
|
240
|
+
const helpers = natives.filter(isCallable);
|
|
241
|
+
if (helpers.length === 0)
|
|
242
|
+
return false;
|
|
243
|
+
const framed = args.some((arg) => arg.type !== 'SpreadElement' && reaches(arg, at));
|
|
244
|
+
return helpers.some((value) => calls(value.heap.node, value.heap.at.frame, seen, framed));
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* Whether a function or class, or a bound function, calls the original when it runs. Its code is read in the frame
|
|
248
|
+
* of its function object, made in `closure`, and each function nested in it in a frame made in the frame around it:
|
|
249
|
+
* the closures it reads. The value itself, and a helper passed the original, are also read in the frames the
|
|
250
|
+
* summaries give them (`framed`).
|
|
251
|
+
*/
|
|
252
|
+
const calls = (fn, closure, seen, framed = false) => {
|
|
253
|
+
// A function read with its frames covers a read without them, but not the other way round: a helper first reached
|
|
254
|
+
// in a frame that doesn't pass it the original is read again when a later frame does.
|
|
255
|
+
if (seen.get(fn) === 'framed' || (seen.has(fn) && !framed))
|
|
256
|
+
return false;
|
|
257
|
+
seen.set(fn, framed ? 'framed' : 'plain');
|
|
258
|
+
if (fn.type === 'CallExpression') {
|
|
259
|
+
// A bound function calls its target.
|
|
260
|
+
const callee = calleeExpression(fn['callee']);
|
|
261
|
+
if (callee.type !== 'MemberExpression')
|
|
262
|
+
return false;
|
|
263
|
+
const target = callee['object'];
|
|
264
|
+
const at = inFrame(closure);
|
|
265
|
+
if (reaches(target, at))
|
|
266
|
+
return true;
|
|
267
|
+
return resolution
|
|
268
|
+
.evaluate(target, at)
|
|
269
|
+
.some((value) => isCallable(value) && calls(value.heap.node, value.heap.at.frame, seen, framed));
|
|
270
|
+
}
|
|
271
|
+
if (!functionTypes.has(fn.type))
|
|
272
|
+
return false;
|
|
273
|
+
running.push(fn);
|
|
274
|
+
const own = [resolution.closureFrame(fn, closure), ...(framed ? resolution.framesOf(fn, true) : [])];
|
|
275
|
+
if (fn.type === 'ClassDeclaration' || fn.type === 'ClassExpression') {
|
|
276
|
+
const heritage = fn['superClass'];
|
|
277
|
+
if (heritage != null && reaches(heritage, inFrame(closure)) && callsSuper(fn))
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
let found = false;
|
|
281
|
+
// The functions the walk is in, innermost last, each with the frames its code is read in.
|
|
282
|
+
const owners = [{ fn, frames: own }];
|
|
283
|
+
walkTree(fn, {
|
|
284
|
+
enter(node) {
|
|
285
|
+
if (found)
|
|
286
|
+
return false;
|
|
287
|
+
if (node !== fn && functionTypes.has(node.type)) {
|
|
288
|
+
running.push(node);
|
|
289
|
+
while (!within(node, owners.at(-1).fn))
|
|
290
|
+
owners.pop();
|
|
291
|
+
const outer = owners.at(-1).frames;
|
|
292
|
+
owners.push({ fn: node, frames: outer.map((frame) => resolution.closureFrame(node, frame)) });
|
|
293
|
+
}
|
|
294
|
+
if (node.type !== 'CallExpression' && node.type !== 'NewExpression')
|
|
295
|
+
return;
|
|
296
|
+
while (!within(node, owners.at(-1).fn))
|
|
297
|
+
owners.pop();
|
|
298
|
+
found = owners.at(-1).frames.some((frame) => invokes(node, inFrame(frame), seen));
|
|
299
|
+
return found ? false : undefined;
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
return found;
|
|
303
|
+
};
|
|
304
|
+
/** Whether one alternative of the value carries wrap evidence. */
|
|
305
|
+
const alternative = (node, at, followed) => {
|
|
306
|
+
// Each alternative is its own new value: the code an earlier one runs doesn't run when this one is called.
|
|
307
|
+
running.length = 0;
|
|
308
|
+
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
|
309
|
+
const callee = calleeExpression(node['callee']);
|
|
310
|
+
const args = node['arguments'];
|
|
311
|
+
const [first] = args;
|
|
312
|
+
// `new Proxy(original, handler)`.
|
|
313
|
+
if (node.type === 'NewExpression' && first !== undefined && first.type !== 'SpreadElement') {
|
|
314
|
+
const proxy = resolution.evaluate(callee, at).some((value) => value.kind === 'holder' && value.holder === 'Proxy');
|
|
315
|
+
if (proxy && reaches(first, at))
|
|
316
|
+
return [true];
|
|
317
|
+
}
|
|
318
|
+
if (node.type === 'CallExpression' && boundCopy(node, at))
|
|
319
|
+
return [true];
|
|
320
|
+
// A factory that receives the original, whatever a resolved summary returns. The spec names `new` apart from a
|
|
321
|
+
// call, so a factory is a call.
|
|
322
|
+
const factory = node.type === 'CallExpression' && callee.type !== 'Super' && !context.isCatalogNative(callee, at);
|
|
323
|
+
if (factory && passes(node, at))
|
|
324
|
+
return [true];
|
|
325
|
+
}
|
|
326
|
+
if (node.type === 'Identifier') {
|
|
327
|
+
const found = definitionSources(node, at);
|
|
328
|
+
if (found !== undefined) {
|
|
329
|
+
// A definition already followed (a cycle) adds no alternative of its own.
|
|
330
|
+
const fresh = found.filter((source) => !followed.has(source.node));
|
|
331
|
+
for (const source of fresh)
|
|
332
|
+
followed.add(source.node);
|
|
333
|
+
return fresh.flatMap((source) => branches(source.node).flatMap((each) => alternative(each, source.at, followed)));
|
|
334
|
+
}
|
|
335
|
+
// A helper's parameter holds what its calls pass. The values the resolution gives it are read first, since they
|
|
336
|
+
// keep what the call's frame knows (a loop's key); the arguments add only what the values can't show: that every
|
|
337
|
+
// one is a factory's result given the original, whatever the factory returns.
|
|
338
|
+
const passed = parameterArguments(definitions, node, at);
|
|
339
|
+
if (passed !== undefined) {
|
|
340
|
+
const own = valueAnswers(node, at);
|
|
341
|
+
if (own.every(Boolean))
|
|
342
|
+
return own;
|
|
343
|
+
const fresh = passed.filter((source) => !followed.has(source.node));
|
|
344
|
+
for (const source of fresh)
|
|
345
|
+
followed.add(source.node);
|
|
346
|
+
const answers = fresh.flatMap((source) => branches(source.node).flatMap((each) => alternative(each, source.at, followed)));
|
|
347
|
+
return answers.length > 0 && answers.every(Boolean) ? [true] : own;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return valueAnswers(node, at);
|
|
351
|
+
};
|
|
352
|
+
/** Whether each value the resolution gives an expression carries wrap evidence. */
|
|
353
|
+
const valueAnswers = (node, at) => {
|
|
354
|
+
const values = resolution.evaluate(node, at);
|
|
355
|
+
if (values.length === 0)
|
|
356
|
+
return [false];
|
|
357
|
+
// The value's own function objects are also those its frames give it: an instance's method, a closure made in a
|
|
358
|
+
// frame of the function around it, whether or not the write was instantiated in that frame.
|
|
359
|
+
return values.map((value) => {
|
|
360
|
+
running.length = 0;
|
|
361
|
+
return isCallable(value) && calls(value.heap.node, value.heap.at.frame, new Map(), true);
|
|
362
|
+
});
|
|
363
|
+
};
|
|
364
|
+
const answers = new Set();
|
|
365
|
+
for (const { node, at } of sources) {
|
|
366
|
+
for (const branch of branches(node))
|
|
367
|
+
for (const answer of alternative(branch, at, new Set()))
|
|
368
|
+
answers.add(answer);
|
|
369
|
+
}
|
|
370
|
+
return answers;
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/** Whether a value is a function the code makes: a function or class, or a bound function. */
|
|
374
|
+
const isCallable = (value) => value.kind === 'object' && value.heap !== undefined && (value.heap.kind === 'function' || value.heap.kind === 'bound');
|
|
375
|
+
/** Whether a derived class calls `super()`: its constructor does, or it has none, and the default one does. */
|
|
376
|
+
function callsSuper(cls) {
|
|
377
|
+
const body = cls['body']['body'];
|
|
378
|
+
const ctor = body.find((member) => member.type === 'MethodDefinition' && member['kind'] === 'constructor');
|
|
379
|
+
if (ctor === undefined)
|
|
380
|
+
return true;
|
|
381
|
+
let found = false;
|
|
382
|
+
walkTree(ctor, {
|
|
383
|
+
enter(node) {
|
|
384
|
+
if (found)
|
|
385
|
+
return false;
|
|
386
|
+
if (node.type === 'CallExpression' && node['callee'].type === 'Super')
|
|
387
|
+
found = true;
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
return found;
|
|
391
|
+
}
|
|
392
|
+
/** Whether `node` lies within `outer`. */
|
|
393
|
+
const within = (node, outer) => node.start >= outer.start && node.end <= outer.end;
|
|
394
|
+
/** The name a member expression reads, when it is a plain name or a string. */
|
|
395
|
+
function keyOf(member) {
|
|
396
|
+
const property = member['property'];
|
|
397
|
+
if (member['computed'] !== true)
|
|
398
|
+
return property.type === 'Identifier' ? property['name'] : undefined;
|
|
399
|
+
return property.type === 'Literal' && typeof property['value'] === 'string' ? property['value'] : undefined;
|
|
400
|
+
}
|
|
401
|
+
/** The first element of an array literal, as `.apply` passes it. */
|
|
402
|
+
function firstElement(list) {
|
|
403
|
+
if (list.type !== 'ArrayExpression')
|
|
404
|
+
return undefined;
|
|
405
|
+
const [first] = list['elements'];
|
|
406
|
+
return first === null || first === undefined || first.type === 'SpreadElement' ? undefined : first;
|
|
407
|
+
}
|