@nimbus-sh/worker 0.12.0 → 0.13.1
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/dist/esbuild-cli-artifact.generated.d.ts +1 -1
- package/dist/esbuild-cli-artifact.generated.js +4 -4
- package/dist/esbuild-wasm-bundle.generated.d.ts +11 -10
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
- package/dist/esbuild-wasm-bundle.generated.js +11 -10
- package/dist/facets/build-facet.d.ts +53 -0
- package/dist/facets/build-facet.d.ts.map +1 -0
- package/dist/facets/build-facet.js +346 -0
- package/dist/facets/cirrus-real.d.ts +3 -0
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +1 -2
- package/dist/facets/compose.d.ts +1 -0
- package/dist/facets/compose.d.ts.map +1 -1
- package/dist/facets/compose.js +3 -0
- package/dist/facets/data-plan.d.ts +14 -30
- package/dist/facets/data-plan.d.ts.map +1 -1
- package/dist/facets/data-plan.js +153 -13
- package/dist/facets/dev-stylesheet.d.ts +27 -0
- package/dist/facets/dev-stylesheet.d.ts.map +1 -0
- package/dist/facets/dev-stylesheet.js +65 -0
- package/dist/facets/esbuild-transform.d.ts +23 -18
- package/dist/facets/esbuild-transform.d.ts.map +1 -1
- package/dist/facets/esbuild-transform.js +63 -109
- package/dist/facets/manager.d.ts +49 -9
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +238 -69
- package/dist/facets/oxc-transform.d.ts +29 -0
- package/dist/facets/oxc-transform.d.ts.map +1 -0
- package/dist/facets/oxc-transform.js +198 -0
- package/dist/facets/prebundle-pool.d.ts +23 -0
- package/dist/facets/prebundle-pool.d.ts.map +1 -0
- package/dist/facets/prebundle-pool.js +55 -0
- package/dist/facets/process.d.ts +13 -6
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +37 -31
- package/dist/facets/transform-store.d.ts +4 -4
- package/dist/facets/transform-store.js +4 -4
- package/dist/facets/vite-dev-server.d.ts +89 -12
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +66 -43
- package/dist/hosted/commands.d.ts.map +1 -1
- package/dist/hosted/commands.js +20 -4
- package/dist/hosted/runtime.d.ts.map +1 -1
- package/dist/hosted/runtime.js +7 -2
- package/dist/hosted/services.d.ts +3 -3
- package/dist/hosted/services.d.ts.map +1 -1
- package/dist/hosted/services.js +102 -95
- package/dist/hosted/session.d.ts +1 -0
- package/dist/hosted/session.d.ts.map +1 -1
- package/dist/loaders/generated-workers.d.ts +3 -0
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -0
- package/dist/napi-wasm-artifacts.generated.js +2 -2
- package/dist/node-shims-artifact.generated.d.ts +14 -1
- package/dist/node-shims-artifact.generated.d.ts.map +1 -1
- package/dist/node-shims-artifact.generated.js +23 -10
- package/dist/npm/bin-links.d.ts +7 -4
- package/dist/npm/bin-links.d.ts.map +1 -1
- package/dist/npm/bin-links.js +14 -55
- package/dist/npm/cache.d.ts +6 -0
- package/dist/npm/cache.d.ts.map +1 -1
- package/dist/npm/cache.js +26 -4
- package/dist/npm/installer.d.ts +1 -1
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +26 -52
- package/dist/npm/pre-bundle-facet.d.ts +15 -117
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +13 -511
- package/dist/oxc-facet-artifact.generated.d.ts +18 -0
- package/dist/oxc-facet-artifact.generated.d.ts.map +1 -0
- package/dist/oxc-facet-artifact.generated.js +17 -0
- package/dist/oxc-wasm-artifact.generated.d.ts +20 -0
- package/dist/oxc-wasm-artifact.generated.d.ts.map +1 -0
- package/dist/oxc-wasm-artifact.generated.js +19 -0
- package/dist/rolldown-facet-artifact.generated.d.ts +18 -0
- package/dist/rolldown-facet-artifact.generated.d.ts.map +1 -0
- package/dist/rolldown-facet-artifact.generated.js +17 -0
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +9 -0
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +1 -4
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +1 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -23
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/esbuild-wasm-bytes.js +30 -25
- package/dist/runtime/facet-loader-host.d.ts +5 -4
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +5 -4
- package/dist/runtime/node-runner.d.ts +2 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +1 -0
- package/dist/runtime/node-shims-artifact.d.ts +8 -1
- package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
- package/dist/runtime/node-shims-artifact.js +32 -4
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +145 -109
- package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
- package/dist/runtime/opencode-facet-runner.js +6 -2
- package/dist/runtime/oxc-wasm-bytes.d.ts +19 -0
- package/dist/runtime/oxc-wasm-bytes.d.ts.map +1 -0
- package/dist/runtime/oxc-wasm-bytes.js +39 -0
- package/dist/runtime/package-manager.js +1 -1
- package/dist/runtime/process-logs-api.d.ts.map +1 -1
- package/dist/runtime/process-logs-api.js +2 -0
- package/dist/runtime/project-fs.d.ts +8 -1
- package/dist/runtime/project-fs.d.ts.map +1 -1
- package/dist/runtime/project-fs.js +15 -1
- package/dist/runtime/python-repl.d.ts +40 -1
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +19 -11
- package/dist/runtime/ruby-repl.d.ts +38 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -1
- package/dist/runtime/ruby-repl.js +24 -5
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +2 -0
- package/dist/session/nimbus-session.d.ts +13 -8
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +11 -5
- package/dist/session/programmatic.d.ts +25 -30
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +65 -93
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +23 -7
- package/dist/session/rpc.d.ts +16 -6
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +53 -13
- package/dist/session/start-real-vite.d.ts +24 -9
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +40 -4
- package/dist/session/supervisor-op.d.ts +14 -0
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +49 -0
- package/dist/session/supervisor-rpc.d.ts +11 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +18 -0
- package/dist/session/vite-command.d.ts.map +1 -1
- package/dist/session/vite-command.js +23 -19
- package/dist/session/ws-relay.d.ts.map +1 -1
- package/dist/session/ws-relay.js +13 -1
- package/dist/shell/npm-bin-entrypoints.d.ts +3 -1
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +53 -25
- package/dist/vfs/facet-resident-store.d.ts.map +1 -1
- package/dist/vfs/facet-resident-store.js +11 -5
- package/dist/wrangler/nimbus-wrangler.d.ts +8 -0
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
- package/dist/wrangler/nimbus-wrangler.js +54 -57
- package/package.json +7 -5
- package/public/_assets/esbuild-0.24.2.wasm +0 -0
- package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/napi-wasm-loader.mjs +4 -4
- package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/provenance.json +2 -2
- package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.provenance.json +72 -0
- package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.wasm +0 -0
- package/public/_assets/runtime/esbuild-cli-f13e6a112b03ac07.js +1143 -0
- package/public/_assets/runtime/js-interpreter-3c37c48024f0f651.js +23 -0
- package/public/_assets/runtime/js-interpreter-ops-2f4dfec6798e70fc.js +290 -0
- package/public/_assets/runtime/js-interpreter-primordials-e620a61b7e9800fa.js +1 -0
- package/public/_assets/runtime/{node-shims-f92f16c7d7c532e9.js → node-shims-e3adc4576da31e6e.js} +651 -103
- package/public/_assets/runtime/{esbuild-cli-b7aecffd4c6a36fe.js → oxc-facet-fd86fefcdf16bf9c.js} +217 -1047
- package/public/_assets/runtime/{resident-store-92386265e8b306dc.js → resident-store-009f39539ac989fc.js} +11 -5
- package/public/_assets/runtime/rolldown-facet-09983434e2dbe93e.js +15165 -0
- package/public/_assets/runtime/{vfs-write-ledger-d33ee0034446c3e0.js → vfs-write-ledger-9e9594591425a9b8.js} +4 -1
- package/scripts/acorn-primordials.mjs +637 -0
- package/scripts/bundle-esbuild-wasm.mjs +42 -50
- package/scripts/bundle-facet-workers.mjs +247 -18
- package/scripts/bundle-node-shims.mjs +29 -2
- package/scripts/bundle-oxc-wasm.mjs +105 -0
- package/scripts/interpreter-bundle.mjs +105 -0
- package/scripts/napi-wasm/build.mjs +4 -1
- package/scripts/napi-wasm/loader/napi-wasm-loader.ts +38 -3
- package/scripts/oxc-wasm/Cargo.lock +1246 -0
- package/scripts/oxc-wasm/Cargo.toml +30 -0
- package/scripts/oxc-wasm/build.mjs +249 -0
- package/scripts/oxc-wasm/examples/memory.rs +51 -0
- package/scripts/oxc-wasm/examples/transform.rs +20 -0
- package/scripts/oxc-wasm/inputs.mjs +32 -0
- package/scripts/oxc-wasm/rust-toolchain.toml +5 -0
- package/scripts/oxc-wasm/src/abi.rs +136 -0
- package/scripts/oxc-wasm/src/diagnostics.rs +49 -0
- package/scripts/oxc-wasm/src/helpers.rs +139 -0
- package/scripts/oxc-wasm/src/lib.rs +247 -0
- package/scripts/oxc-wasm/src/module.rs +978 -0
- package/scripts/oxc-wasm/src/names.rs +175 -0
- package/scripts/oxc-wasm/src/options.rs +154 -0
- package/scripts/parser-audit.mjs +492 -0
- package/scripts/rolldown-facet/entry.mjs +21 -0
- package/scripts/rolldown-facet/shims.mjs +64 -0
- package/dist/facets/esbuild-bundle-pool.d.ts +0 -43
- package/dist/facets/esbuild-bundle-pool.d.ts.map +0 -1
- package/dist/facets/esbuild-bundle-pool.js +0 -80
- package/dist/loaders/pre-bundle-preamble.d.ts +0 -44
- package/dist/loaders/pre-bundle-preamble.d.ts.map +0 -1
- package/dist/loaders/pre-bundle-preamble.js +0 -116
- package/dist/runtime/host-wasm.d.ts +0 -8
- package/dist/runtime/host-wasm.d.ts.map +0 -1
- package/dist/runtime/host-wasm.js +0 -33
- /package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/wasi-trampoline.wasm +0 -0
|
@@ -132,7 +132,9 @@ async function __nimbusVfsRpc(issue) {
|
|
|
132
132
|
* why `fs.truncate(missing).catch(() => {})` is ordinary, correct code.
|
|
133
133
|
*
|
|
134
134
|
* ENOSPC is one of them: the session's storage ledger (N18) refuses a write
|
|
135
|
-
* before any of it is made.
|
|
135
|
+
* before any of it is made. So are EROFS (a read-only mount) and EBUSY (an
|
|
136
|
+
* exclusive-mutation lease): the namespace refuses those before the backend
|
|
137
|
+
* is called.
|
|
136
138
|
*
|
|
137
139
|
* Everything else — EIO, a dropped RPC, an authority that died, an
|
|
138
140
|
* error carrying no errno at all — is not an answer. It means the outcome of
|
|
@@ -143,6 +145,7 @@ async function __nimbusVfsRpc(issue) {
|
|
|
143
145
|
const __NIMBUS_SYSCALL_VERDICT_CODES = new Set([
|
|
144
146
|
"ENOENT", "EEXIST", "EISDIR", "ENOTDIR", "ENOTEMPTY",
|
|
145
147
|
"EBADF", "EINVAL", "EPERM", "EACCES", "ELOOP", "ENAMETOOLONG", "ENOSPC",
|
|
148
|
+
"EROFS", "EBUSY",
|
|
146
149
|
]);
|
|
147
150
|
|
|
148
151
|
function __nimbusIsDurabilityFailure(error) {
|
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acorn-primordials.mjs — acorn as the interpreter bundles it: rewritten, by
|
|
3
|
+
* its syntax tree, to reach no built-in a program can replace.
|
|
4
|
+
*
|
|
5
|
+
* The interpreter parses code a program produced, in that program's realm,
|
|
6
|
+
* after the program may have replaced any built-in. acorn as published calls
|
|
7
|
+
* the realm's built-ins and makes objects that inherit from its prototypes,
|
|
8
|
+
* so a replaced Array.prototype.push receives every node acorn builds and
|
|
9
|
+
* can change the program being parsed (core src/interpreter/parser-realm.ts
|
|
10
|
+
* says what that allows). The rewrite, of acorn's own source text, with the
|
|
11
|
+
* same names and structure:
|
|
12
|
+
*
|
|
13
|
+
* - a built-in acorn names (Object.create, String.fromCharCode, RegExp,
|
|
14
|
+
* SyntaxError, ...) is parser-realm's, captured at the launch's start;
|
|
15
|
+
* - a call of a method a string, list, regexp or function has
|
|
16
|
+
* (`s.charCodeAt(i)`, `list.push(x)`, `re.test(s)`, `f.call(t)`) is a
|
|
17
|
+
* call of parser-realm's function of that name, on any receiver but
|
|
18
|
+
* `this` (always one of acorn's own objects);
|
|
19
|
+
* - an object literal inherits nothing (parser-realm `own`), a list
|
|
20
|
+
* literal is a list that inherits nothing (`list`), a regexp (a literal,
|
|
21
|
+
* or made with RegExp) inherits only RegExp.prototype's members as
|
|
22
|
+
* captured (`regexp`), and each of acorn's constructors and its prototype
|
|
23
|
+
* inherit nothing from the statement that declares it on
|
|
24
|
+
* (`nullPrototypes`), so a field a node lacks is not looked up anywhere,
|
|
25
|
+
* no field acorn sets runs a setter, and nothing read of a regexp is the
|
|
26
|
+
* realm's;
|
|
27
|
+
* - a field set on an error acorn made with the realm's constructor is
|
|
28
|
+
* defined (`define`); a computed read of a string is parser-realm's
|
|
29
|
+
* `index`, which reads its character without looking past its end; a
|
|
30
|
+
* built-in's member acorn reads is its capture, and any other read of a
|
|
31
|
+
* built-in method's name, but a field acorn sets, stops the build.
|
|
32
|
+
*
|
|
33
|
+
* - a built-in acorn names is held only where it is called, constructed or
|
|
34
|
+
* tested with typeof, or by a variable used the same way: nothing of it is
|
|
35
|
+
* read, and nothing but RegExp and the errors constructed;
|
|
36
|
+
* - `new` constructs only acorn's own constructors (variables whose value is
|
|
37
|
+
* a function literal, made to inherit nothing), the RegExp and errors
|
|
38
|
+
* above, or `this` once parser-realm has checked it is such a constructor;
|
|
39
|
+
* `for (k in o)` enumerates `o` once parser-realm has checked it inherits
|
|
40
|
+
* nothing of the realm's; `arguments` is only read for its length or,
|
|
41
|
+
* through parser-realm's `argument`, an element it has; a caught exception
|
|
42
|
+
* is only compared and rethrown;
|
|
43
|
+
* - a method is called only by a name acorn sets somewhere, and nothing is
|
|
44
|
+
* read of a function of acorn's but one made to inherit nothing; a function
|
|
45
|
+
* is declared only at the top of a module or function;
|
|
46
|
+
* - a method called by a constant computed key is called as by its name; by
|
|
47
|
+
* any other key, only on a list acorn made.
|
|
48
|
+
*
|
|
49
|
+
* Anything the rewrite does not know how to make safe (a global it has no
|
|
50
|
+
* capture for, `instanceof`, a template literal, an iteration, a
|
|
51
|
+
* destructuring pattern, a rest parameter) stops the build, so an acorn
|
|
52
|
+
* upgrade that reaches something new fails here rather than at runtime.
|
|
53
|
+
* parser-audit.mjs checks the output with nothing of this rewrite's:
|
|
54
|
+
* tests/unit/interpreter-primordials.mjs requires it to find nothing.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
import { parse } from 'acorn';
|
|
58
|
+
|
|
59
|
+
/** The namespace the rewritten parser imports parser-realm as. */
|
|
60
|
+
const REALM = '$$';
|
|
61
|
+
|
|
62
|
+
/** Globals that are not writable or configurable. */
|
|
63
|
+
const FIXED = new Set(['undefined', 'NaN', 'Infinity']);
|
|
64
|
+
|
|
65
|
+
/** A built-in's member acorn reads, by global, and the parser-realm export that is its capture. */
|
|
66
|
+
const GLOBAL_MEMBERS = {
|
|
67
|
+
Object: { create: 'objectCreate', keys: 'objectKeys', defineProperties: 'objectDefineProperties', hasOwn: 'objectHasOwn', prototype: 'ObjectPrototypeMethods' },
|
|
68
|
+
Array: { isArray: 'arrayIsArray' },
|
|
69
|
+
String: { fromCharCode: 'stringFromCharCode' },
|
|
70
|
+
Symbol: { iterator: 'symbolIterator' },
|
|
71
|
+
console: { warn: 'consoleWarn' },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* parser-realm's exports that are records of captured methods, and the names
|
|
76
|
+
* they hold: what acorn reads of Object.prototype, through the binding it
|
|
77
|
+
* keeps it in (`var ref = Object.prototype; var toString = ref.toString`).
|
|
78
|
+
*/
|
|
79
|
+
const RECORDS = { ObjectPrototypeMethods: new Set(['hasOwnProperty', 'toString']) };
|
|
80
|
+
|
|
81
|
+
/** parser-realm's exports that are symbols, which may be a computed key: a key as they are. */
|
|
82
|
+
const SYMBOLS = new Set(['symbolIterator']);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Globals acorn names, each parser-realm's export of the same name: called,
|
|
86
|
+
* constructed or tested with typeof, never read from. RegExp is a function
|
|
87
|
+
* that makes a regexp safe however it is called; an error constructor may
|
|
88
|
+
* only be constructed where what is set on the error can be rewritten.
|
|
89
|
+
*/
|
|
90
|
+
const GLOBALS = new Set(['String', 'RegExp', 'SyntaxError', 'Error', 'BigInt', 'parseInt', 'parseFloat', 'Symbol', 'console']);
|
|
91
|
+
|
|
92
|
+
/** Methods of strings, lists, regexps and functions acorn calls: parser-realm's functions of these names. */
|
|
93
|
+
export const ROUTED_METHODS = new Set([
|
|
94
|
+
'charCodeAt', 'charAt', 'substr', 'slice', 'indexOf', 'lastIndexOf', 'push', 'pop',
|
|
95
|
+
'test', 'exec', 'replace', 'split', 'match', 'call', 'toString',
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
/** Realm constructors whose instances acorn sets fields on. */
|
|
99
|
+
const REALM_CONSTRUCTORS = new Set(['SyntaxError', 'Error']);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Names the built-in prototypes have, in the realm running the build: what
|
|
103
|
+
* a read of a string, number, list, regexp, function or error (or any object
|
|
104
|
+
* that inherits from Object.prototype) finds there. (A constructor's own
|
|
105
|
+
* members, `String.raw`, are read only through the constructor, which acorn
|
|
106
|
+
* names only as a global.) Not `length`, which each of those has as its own.
|
|
107
|
+
*/
|
|
108
|
+
const BUILTIN_NAMES = (() => {
|
|
109
|
+
const iterator = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
|
|
110
|
+
const owners = [
|
|
111
|
+
Object.prototype, Function.prototype, Array.prototype, String.prototype, Number.prototype, Boolean.prototype,
|
|
112
|
+
BigInt.prototype, Symbol.prototype, RegExp.prototype, Error.prototype, iterator,
|
|
113
|
+
];
|
|
114
|
+
const names = new Set();
|
|
115
|
+
for (const owner of owners) for (const name of Object.getOwnPropertyNames(owner)) names.add(name);
|
|
116
|
+
names.delete('length');
|
|
117
|
+
return names;
|
|
118
|
+
})();
|
|
119
|
+
|
|
120
|
+
/** Names acorn sets as fields of its objects: assigned, or keys of its object literals. */
|
|
121
|
+
function acornFieldNames(program) {
|
|
122
|
+
const names = new Set();
|
|
123
|
+
walk(program, (node) => {
|
|
124
|
+
if (node.type === 'AssignmentExpression' && node.left.type === 'MemberExpression' && !node.left.computed) names.add(node.left.property.name);
|
|
125
|
+
if (node.type === 'Property' && !node.computed && node.key.type === 'Identifier') names.add(node.key.name);
|
|
126
|
+
});
|
|
127
|
+
return names;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** A name, `this`, or a chain of fields from one: what can be read again without doing anything else. */
|
|
131
|
+
function isPlainChain(node) {
|
|
132
|
+
if (node.type === 'Identifier') return node.name !== REALM;
|
|
133
|
+
if (node.type === 'ThisExpression') return true;
|
|
134
|
+
return node.type === 'MemberExpression' && !node.computed && isPlainChain(node.object);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Whether `member` is read: not called, assigned, updated or deleted. */
|
|
138
|
+
function isRead(member, parent) {
|
|
139
|
+
if (!parent) return true;
|
|
140
|
+
if (parent.type === 'CallExpression' && parent.callee === member) return false;
|
|
141
|
+
if (parent.type === 'AssignmentExpression' && parent.left === member) return false;
|
|
142
|
+
if (parent.type === 'UpdateExpression') return false;
|
|
143
|
+
if (parent.type === 'UnaryExpression' && parent.operator === 'delete') return false;
|
|
144
|
+
if (parent.type === 'ForInStatement' && parent.left === member) return false;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const SKIP_KEYS = new Set(['type', 'start', 'end', 'loc', 'range']);
|
|
149
|
+
|
|
150
|
+
function isNode(value) {
|
|
151
|
+
return value !== null && typeof value === 'object' && typeof value.type === 'string';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* A node's child nodes in source order, each once: acorn gives `export { x }`
|
|
156
|
+
* one node for both names, and a shorthand property's key is its value's text.
|
|
157
|
+
*/
|
|
158
|
+
function childNodes(node) {
|
|
159
|
+
const out = [];
|
|
160
|
+
const add = (child) => { if (isNode(child) && !out.includes(child)) out.push(child); };
|
|
161
|
+
for (const key of Object.keys(node)) {
|
|
162
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
163
|
+
if (node.type === 'Property' && node.shorthand && key === 'key') continue;
|
|
164
|
+
const value = node[key];
|
|
165
|
+
if (Array.isArray(value)) { for (const item of value) add(item); } else add(value);
|
|
166
|
+
}
|
|
167
|
+
return out.sort((a, b) => a.start - b.start);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function walk(node, visit, parent = null) {
|
|
171
|
+
visit(node, parent);
|
|
172
|
+
for (const child of childNodes(node)) walk(child, visit, node);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function where(source, node) {
|
|
176
|
+
let line = 1;
|
|
177
|
+
for (let i = 0; i < node.start; i++) if (source.charCodeAt(i) === 10) line++;
|
|
178
|
+
return `acorn.mjs:${line}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── Scopes ──
|
|
182
|
+
|
|
183
|
+
const isFunction = (n) => n.type === 'FunctionDeclaration' || n.type === 'FunctionExpression' || n.type === 'ArrowFunctionExpression';
|
|
184
|
+
|
|
185
|
+
function patternNames(pattern, out) {
|
|
186
|
+
if (!pattern) return out;
|
|
187
|
+
switch (pattern.type) {
|
|
188
|
+
case 'Identifier': out.push(pattern); break;
|
|
189
|
+
case 'ObjectPattern': for (const p of pattern.properties) patternNames(p.type === 'RestElement' ? p.argument : p.value, out); break;
|
|
190
|
+
case 'ArrayPattern': for (const e of pattern.elements) patternNames(e, out); break;
|
|
191
|
+
case 'RestElement': patternNames(pattern.argument, out); break;
|
|
192
|
+
case 'AssignmentPattern': patternNames(pattern.left, out); break;
|
|
193
|
+
default: break;
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Each identifier reference's declaration (an Identifier node), or null when
|
|
200
|
+
* it is a global. var and function declarations belong to their function (or
|
|
201
|
+
* the module), let, const and class to their block, parameters and
|
|
202
|
+
* `arguments` to their function, a catch parameter to its clause.
|
|
203
|
+
*/
|
|
204
|
+
function resolveReferences(program) {
|
|
205
|
+
const scopes = new Map();
|
|
206
|
+
const scopeOf = (node, parent, kind) => {
|
|
207
|
+
const scope = { parent, kind, names: new Map() };
|
|
208
|
+
scopes.set(node, scope);
|
|
209
|
+
return scope;
|
|
210
|
+
};
|
|
211
|
+
const declare = (scope, id) => { if (!scope.names.has(id.name)) scope.names.set(id.name, id); };
|
|
212
|
+
const functionScope = (scope) => { let s = scope; while (s.kind !== 'function') s = s.parent; return s; };
|
|
213
|
+
const references = new Map();
|
|
214
|
+
const declarations = new Set();
|
|
215
|
+
|
|
216
|
+
// Declarations, into the scope each belongs to.
|
|
217
|
+
const declareIn = (node, scope) => {
|
|
218
|
+
if (isFunction(node)) {
|
|
219
|
+
if (node.type === 'FunctionDeclaration') { declare(functionScope(scope), node.id); declarations.add(node.id); }
|
|
220
|
+
const inner = scopeOf(node, scope, 'function');
|
|
221
|
+
if (node.type === 'FunctionExpression' && node.id) { declare(inner, node.id); declarations.add(node.id); }
|
|
222
|
+
for (const param of node.params) {
|
|
223
|
+
for (const id of patternNames(param, [])) { declare(inner, id); declarations.add(id); }
|
|
224
|
+
declareIn(param, inner);
|
|
225
|
+
}
|
|
226
|
+
if (node.type !== 'ArrowFunctionExpression') inner.names.set('arguments', null);
|
|
227
|
+
const body = node.body.type === 'BlockStatement' ? node.body : null;
|
|
228
|
+
if (body) { scopes.set(body, inner); for (const child of childNodes(body)) declareIn(child, inner); } else declareIn(node.body, inner);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
let inner = scope;
|
|
232
|
+
if (node.type === 'BlockStatement' || node.type === 'ForStatement' || node.type === 'ForInStatement' || node.type === 'ForOfStatement' || node.type === 'SwitchStatement') {
|
|
233
|
+
inner = scopeOf(node, scope, 'block');
|
|
234
|
+
} else if (node.type === 'CatchClause') {
|
|
235
|
+
inner = scopeOf(node, scope, 'block');
|
|
236
|
+
for (const id of patternNames(node.param, [])) { declare(inner, id); declarations.add(id); }
|
|
237
|
+
}
|
|
238
|
+
if (node.type === 'VariableDeclaration') {
|
|
239
|
+
const target = node.kind === 'var' ? functionScope(inner) : inner;
|
|
240
|
+
for (const d of node.declarations) for (const id of patternNames(d.id, [])) { declare(target, id); declarations.add(id); }
|
|
241
|
+
} else if (node.type === 'ClassDeclaration') {
|
|
242
|
+
declare(inner, node.id); declarations.add(node.id);
|
|
243
|
+
} else if (node.type === 'ImportSpecifier' || node.type === 'ImportDefaultSpecifier' || node.type === 'ImportNamespaceSpecifier') {
|
|
244
|
+
declare(inner, node.local); declarations.add(node.local);
|
|
245
|
+
}
|
|
246
|
+
for (const child of childNodes(node)) declareIn(child, inner);
|
|
247
|
+
};
|
|
248
|
+
const root = scopeOf(program, null, 'function');
|
|
249
|
+
for (const child of childNodes(program)) declareIn(child, root);
|
|
250
|
+
|
|
251
|
+
// References, resolved from the innermost scope out.
|
|
252
|
+
const visit = (node, parent, scope) => {
|
|
253
|
+
const own = scopes.get(node);
|
|
254
|
+
const inner = own && own !== scope ? own : scope;
|
|
255
|
+
if (node.type === 'Identifier' && !declarations.has(node) && isReference(node, parent)) {
|
|
256
|
+
let found = null;
|
|
257
|
+
for (let s = inner; s; s = s.parent) {
|
|
258
|
+
if (s.names.has(node.name)) { found = s.names.get(node.name) ?? 'arguments'; break; }
|
|
259
|
+
}
|
|
260
|
+
references.set(node, found);
|
|
261
|
+
}
|
|
262
|
+
for (const child of childNodes(node)) visit(child, node, inner);
|
|
263
|
+
};
|
|
264
|
+
visit(program, null, root);
|
|
265
|
+
return references;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function isReference(id, parent) {
|
|
269
|
+
if (!parent) return true;
|
|
270
|
+
switch (parent.type) {
|
|
271
|
+
case 'MemberExpression': return parent.object === id || parent.computed;
|
|
272
|
+
case 'Property': case 'MethodDefinition': case 'PropertyDefinition': return parent.value === id || parent.computed;
|
|
273
|
+
case 'LabeledStatement': case 'BreakStatement': case 'ContinueStatement': return false;
|
|
274
|
+
case 'ExportSpecifier': return parent.local === id;
|
|
275
|
+
case 'ImportSpecifier': case 'ImportDefaultSpecifier': case 'ImportNamespaceSpecifier': return false;
|
|
276
|
+
case 'MetaProperty': return false;
|
|
277
|
+
default: return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ── The rewrite ──
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* acorn's module source as the interpreter bundles it, importing parser-realm
|
|
285
|
+
* from `realmSpecifier`. Throws on anything it cannot make safe.
|
|
286
|
+
*/
|
|
287
|
+
export function primordialAcorn(source, realmSpecifier) {
|
|
288
|
+
const program = parse(source, { ecmaVersion: 'latest', sourceType: 'module' });
|
|
289
|
+
const references = resolveReferences(program);
|
|
290
|
+
const refuse = (node, what) => { throw new Error(`[acorn-primordials] ${where(source, node)}: ${what}`); };
|
|
291
|
+
for (const id of references.keys()) if (id.name === REALM) refuse(id, `acorn names ${REALM}, the parser realm's namespace`);
|
|
292
|
+
|
|
293
|
+
const rewrites = new Map();
|
|
294
|
+
// Expressions parser-realm checks inherit nothing of the realm's, however they are rewritten.
|
|
295
|
+
const checkedOwned = new Set();
|
|
296
|
+
const emit = (node) => {
|
|
297
|
+
const rewrite = rewrites.get(node);
|
|
298
|
+
const text = rewrite ? rewrite() : spliced(node);
|
|
299
|
+
return checkedOwned.has(node) ? `${REALM}.owned(${text})` : text;
|
|
300
|
+
};
|
|
301
|
+
const spliced = (node) => {
|
|
302
|
+
let out = '';
|
|
303
|
+
let at = node.start;
|
|
304
|
+
for (const child of childNodes(node)) {
|
|
305
|
+
out += source.slice(at, child.start) + emit(child);
|
|
306
|
+
at = child.end;
|
|
307
|
+
}
|
|
308
|
+
return out + source.slice(at, node.end);
|
|
309
|
+
};
|
|
310
|
+
const args = (list) => list.map((a) => (a.type === 'SpreadElement' ? refuse(a, 'a spread argument') : emit(a)));
|
|
311
|
+
|
|
312
|
+
// Module-level bindings: acorn's constructors, its regexp literals, and functions returning regexps.
|
|
313
|
+
const moduleBindings = new Map();
|
|
314
|
+
for (const statement of program.body) {
|
|
315
|
+
if (statement.type === 'VariableDeclaration') for (const d of statement.declarations) if (d.id.type === 'Identifier') moduleBindings.set(d.id, { statement, init: d.init });
|
|
316
|
+
if (statement.type === 'FunctionDeclaration') moduleBindings.set(statement.id, { statement, init: statement });
|
|
317
|
+
}
|
|
318
|
+
const bindingOf = (id) => {
|
|
319
|
+
const decl = references.get(id);
|
|
320
|
+
return decl && decl !== 'arguments' ? moduleBindings.get(decl) ?? null : null;
|
|
321
|
+
};
|
|
322
|
+
const acornFields = acornFieldNames(program);
|
|
323
|
+
// One of parser-realm's records: named (`Object.prototype`), or a module binding that holds it (`ref`).
|
|
324
|
+
const recordExpression = (n) => {
|
|
325
|
+
if (!n || n.type !== 'MemberExpression' || n.computed || n.object.type !== 'Identifier' || references.get(n.object) !== null) return null;
|
|
326
|
+
const members = GLOBAL_MEMBERS[n.object.name];
|
|
327
|
+
const capture = members && members[n.property.name];
|
|
328
|
+
return capture && RECORDS[capture] ? capture : null;
|
|
329
|
+
};
|
|
330
|
+
const recordOf = (n) => {
|
|
331
|
+
const binding = n.type === 'Identifier' ? bindingOf(n) : null;
|
|
332
|
+
return recordExpression(n) ?? recordExpression(binding && binding.init);
|
|
333
|
+
};
|
|
334
|
+
const parents = new Map();
|
|
335
|
+
walk(program, (node, parent) => { parents.set(node, parent); });
|
|
336
|
+
const declaratorOf = (id) => { const d = parents.get(id); return d && d.type === 'VariableDeclarator' && d.id === id ? d : null; };
|
|
337
|
+
const assigned = new Set();
|
|
338
|
+
walk(program, (node) => {
|
|
339
|
+
if (node.type === 'AssignmentExpression' && node.left.type === 'Identifier') assigned.add(references.get(node.left));
|
|
340
|
+
if (node.type === 'UpdateExpression' && node.argument.type === 'Identifier') assigned.add(references.get(node.argument));
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// What holds one of the realm's built-ins: the global itself, a member of one parser-realm captures,
|
|
344
|
+
// and a variable whose value is one of those (or another such variable).
|
|
345
|
+
const isGlobalCapture = (n) => {
|
|
346
|
+
if (n.type === 'Identifier') return references.get(n) === null && GLOBALS.has(n.name);
|
|
347
|
+
return n.type === 'MemberExpression' && !n.computed && n.object.type === 'Identifier' && references.get(n.object) === null
|
|
348
|
+
&& Boolean(GLOBAL_MEMBERS[n.object.name]) && !recordExpression(n);
|
|
349
|
+
};
|
|
350
|
+
const captureAliases = new Set();
|
|
351
|
+
const capturing = (init) => init && (isGlobalCapture(init) || (init.type === 'Identifier' && captureAliases.has(references.get(init)))
|
|
352
|
+
|| (init.type === 'MemberExpression' && !init.computed && recordOf(init.object) !== null)
|
|
353
|
+
|| (init.type === 'LogicalExpression' && init.operator === '||' && capturing(init.left)));
|
|
354
|
+
for (let size = -1; size !== captureAliases.size;) {
|
|
355
|
+
size = captureAliases.size;
|
|
356
|
+
walk(program, (node) => { if (node.type === 'VariableDeclarator' && node.id.type === 'Identifier' && capturing(node.init)) captureAliases.add(node.id); });
|
|
357
|
+
}
|
|
358
|
+
for (const id of captureAliases) if (assigned.has(id)) refuse(id, `${id.name}, which holds a built-in, is assigned`);
|
|
359
|
+
/** Where a built-in, or what holds one, may be: called, constructed, tested with typeof, held by a variable, or the function `.call` calls. */
|
|
360
|
+
/** Whether `n`'s value is only tested for truth: an if, loop or conditional test, `!`'s operand, or a logical operand tested so. */
|
|
361
|
+
const isTested = (n) => {
|
|
362
|
+
const parent = parents.get(n);
|
|
363
|
+
if ((parent.type === 'IfStatement' || parent.type === 'WhileStatement' || parent.type === 'DoWhileStatement'
|
|
364
|
+
|| parent.type === 'ConditionalExpression' || parent.type === 'ForStatement') && parent.test === n) return true;
|
|
365
|
+
if (parent.type === 'UnaryExpression' && parent.operator === '!') return true;
|
|
366
|
+
return parent.type === 'LogicalExpression' && isTested(parent);
|
|
367
|
+
};
|
|
368
|
+
const holdsCaptureSafely = (n) => {
|
|
369
|
+
const parent = parents.get(n);
|
|
370
|
+
if ((parent.type === 'CallExpression' || parent.type === 'NewExpression') && parent.callee === n) return true;
|
|
371
|
+
if (parent.type === 'UnaryExpression' && parent.operator === 'typeof') return true;
|
|
372
|
+
if (isTested(n)) return true;
|
|
373
|
+
if (parent.type === 'VariableDeclarator' && parent.init === n) return true;
|
|
374
|
+
if (parent.type === 'LogicalExpression' && parent.left === n && parents.get(parent).type === 'VariableDeclarator') return true;
|
|
375
|
+
if (parent.type === 'MemberExpression' && parent.object === n && !parent.computed && parent.property.name === 'call') {
|
|
376
|
+
const call = parents.get(parent);
|
|
377
|
+
return call.type === 'CallExpression' && call.callee === parent;
|
|
378
|
+
}
|
|
379
|
+
return false;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/** The declaration of the function a binding holds: its own, or (following aliases) another binding's. */
|
|
383
|
+
const functionOf = (decl, seen = new Set()) => {
|
|
384
|
+
if (!decl || decl === 'arguments' || seen.has(decl)) return null;
|
|
385
|
+
seen.add(decl);
|
|
386
|
+
const binding = moduleBindings.get(decl);
|
|
387
|
+
const declarator = binding ? null : declaratorOf(decl);
|
|
388
|
+
const init = binding ? binding.init : declarator && declarator.init;
|
|
389
|
+
if (!init) return null;
|
|
390
|
+
if (init.type === 'FunctionExpression' || init.type === 'FunctionDeclaration') return decl;
|
|
391
|
+
return init.type === 'Identifier' ? functionOf(references.get(init), seen) : null;
|
|
392
|
+
};
|
|
393
|
+
// acorn's own constructors: variables whose value is a function literal, never assigned.
|
|
394
|
+
const isConstructorBinding = (decl) => {
|
|
395
|
+
const binding = decl && moduleBindings.get(decl);
|
|
396
|
+
return Boolean(binding && binding.init && (binding.init.type === 'FunctionExpression' || binding.init.type === 'FunctionDeclaration') && !assigned.has(decl));
|
|
397
|
+
};
|
|
398
|
+
const constructors = new Set();
|
|
399
|
+
const realmInstances = new Set();
|
|
400
|
+
walk(program, (node, parent) => {
|
|
401
|
+
if (node.type === 'NewExpression' && node.callee.type === 'Identifier' && references.get(node.callee) !== null) {
|
|
402
|
+
const decl = references.get(node.callee);
|
|
403
|
+
if (!isConstructorBinding(decl)) refuse(node, `new ${node.callee.name}, which is not a constructor of acorn's own`);
|
|
404
|
+
constructors.add(decl);
|
|
405
|
+
}
|
|
406
|
+
if (node.type === 'NewExpression' && node.callee.type !== 'Identifier' && node.callee.type !== 'ThisExpression') {
|
|
407
|
+
refuse(node, 'new of an expression, which may be any constructor');
|
|
408
|
+
}
|
|
409
|
+
if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && bindingOf(node.object)) {
|
|
410
|
+
const binding = bindingOf(node.object);
|
|
411
|
+
const isFunctionBinding = binding.init && isFunction(binding.init);
|
|
412
|
+
const written = parent && parent.type === 'AssignmentExpression' && parent.left === node;
|
|
413
|
+
if (isFunctionBinding && ((!node.computed && node.property.name === 'prototype') || written)) {
|
|
414
|
+
if (!isConstructorBinding(references.get(node.object))) refuse(node, `${node.object.name}, a function given fields, is assigned`);
|
|
415
|
+
constructors.add(references.get(node.object));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// An error of the realm's: thrown at once, or held by a local only fields are set on before it is thrown.
|
|
419
|
+
if (node.type === 'NewExpression' && node.callee.type === 'Identifier' && references.get(node.callee) === null
|
|
420
|
+
&& REALM_CONSTRUCTORS.has(node.callee.name)) {
|
|
421
|
+
if (parent.type === 'VariableDeclarator' && parent.init === node && parent.id.type === 'Identifier') realmInstances.add(parent.id);
|
|
422
|
+
else if (parent.type !== 'ThrowStatement') refuse(node, `an error of the realm's ${node.callee.name} used other than thrown`);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
const fieldSets = new Set();
|
|
426
|
+
walk(program, (node) => {
|
|
427
|
+
if (node.type === 'AssignmentExpression' && node.operator === '=' && node.left.type === 'MemberExpression' && !node.left.computed) fieldSets.add(node.left.object);
|
|
428
|
+
});
|
|
429
|
+
walk(program, (node, parent) => {
|
|
430
|
+
if (node.type !== 'Identifier' || !realmInstances.has(references.get(node)) || realmInstances.has(node)) return;
|
|
431
|
+
if (!(parent.type === 'ThrowStatement' || fieldSets.has(node))) refuse(node, `an error of the realm's, ${node.name}, used other than to set its fields and throw it`);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// A caught exception may be the realm's: only compared and rethrown.
|
|
435
|
+
const caught = new Set();
|
|
436
|
+
walk(program, (node) => { if (node.type === 'CatchClause' && node.param) for (const id of patternNames(node.param, [])) caught.add(id); });
|
|
437
|
+
/** A list acorn made: a variable whose value is a list literal, never assigned. */
|
|
438
|
+
const isOwnList = (n) => {
|
|
439
|
+
if (n.type !== 'Identifier') return false;
|
|
440
|
+
const decl = references.get(n);
|
|
441
|
+
const declarator = decl && decl !== 'arguments' ? declaratorOf(decl) : null;
|
|
442
|
+
return Boolean(declarator && declarator.init && declarator.init.type === 'ArrayExpression' && !assigned.has(decl));
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
walk(program, (node, parent) => {
|
|
446
|
+
switch (node.type) {
|
|
447
|
+
case 'Identifier': {
|
|
448
|
+
if (!references.has(node)) return;
|
|
449
|
+
const decl = references.get(node);
|
|
450
|
+
if (decl === 'arguments') {
|
|
451
|
+
// Its length, an own property; an element, through parser-realm, which reads only one it has.
|
|
452
|
+
if (parent.type === 'MemberExpression' && parent.object === node && isRead(parent, parents.get(parent))) {
|
|
453
|
+
if (!parent.computed && parent.property.name === 'length') return;
|
|
454
|
+
if (parent.computed && !(parent.property.type === 'Literal' && typeof parent.property.value !== 'number')) {
|
|
455
|
+
rewrites.set(parent, () => `${REALM}.argument(arguments, ${emit(parent.property)})`);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
refuse(node, 'arguments used other than for its length or an element');
|
|
460
|
+
}
|
|
461
|
+
if (decl !== null) {
|
|
462
|
+
if (captureAliases.has(decl) && !captureAliases.has(node) && !holdsCaptureSafely(node) && !(parent.type === 'MemberExpression' && recordOf(parent.object) !== null)) {
|
|
463
|
+
refuse(node, `${node.name}, which holds a built-in, used other than called or tested`);
|
|
464
|
+
}
|
|
465
|
+
if (caught.has(decl) && !caught.has(node) && parent.type !== 'ThrowStatement'
|
|
466
|
+
&& !(parent.type === 'BinaryExpression' && (parent.operator === '===' || parent.operator === '!=='))) {
|
|
467
|
+
refuse(node, `the caught ${node.name}, which may be the realm's, used other than compared or rethrown`);
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (FIXED.has(node.name)) return;
|
|
472
|
+
const members = GLOBAL_MEMBERS[node.name];
|
|
473
|
+
if (members && parent.type === 'MemberExpression' && parent.object === node && !parent.computed) {
|
|
474
|
+
const capture = members[parent.property.name];
|
|
475
|
+
if (!capture) refuse(node, `acorn reads ${node.name}.${parent.property.name}, which parser-realm has no capture of`);
|
|
476
|
+
const asKey = SYMBOLS.has(capture) && parents.get(parent).type === 'MemberExpression' && parents.get(parent).computed && parents.get(parent).property === parent;
|
|
477
|
+
if (!RECORDS[capture] && !asKey && !holdsCaptureSafely(parent)) refuse(node, `${node.name}.${parent.property.name}, a built-in, used other than called or tested`);
|
|
478
|
+
if (RECORDS[capture] && !(parents.get(parent).type === 'VariableDeclarator' || (parents.get(parent).type === 'MemberExpression' && !parents.get(parent).computed))) {
|
|
479
|
+
refuse(node, `${node.name}.${parent.property.name} used other than held or read`);
|
|
480
|
+
}
|
|
481
|
+
rewrites.set(parent, () => `${REALM}.${capture}`);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (!GLOBALS.has(node.name)) refuse(node, `acorn names the global ${node.name}, which parser-realm has no capture of`);
|
|
485
|
+
if (parent.type === 'MemberExpression' && parent.object === node) refuse(node, `acorn reads ${node.name}'s members, which are the realm's`);
|
|
486
|
+
if (REALM_CONSTRUCTORS.has(node.name) && !(parent.type === 'NewExpression' && parent.callee === node)) {
|
|
487
|
+
refuse(node, `acorn holds ${node.name} other than to construct it`);
|
|
488
|
+
}
|
|
489
|
+
if (!holdsCaptureSafely(node)) refuse(node, `${node.name}, a built-in, used other than called or tested`);
|
|
490
|
+
if (parent.type === 'NewExpression' && parent.callee === node && node.name !== 'RegExp' && !REALM_CONSTRUCTORS.has(node.name)) {
|
|
491
|
+
refuse(node, `new ${node.name}, which makes an object of the realm's`);
|
|
492
|
+
}
|
|
493
|
+
rewrites.set(node, () => `${REALM}.${node.name}`);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
case 'ObjectExpression':
|
|
497
|
+
for (const p of node.properties) {
|
|
498
|
+
if (p.type !== 'Property' || p.kind !== 'init') refuse(p, 'an object literal with a spread or an accessor');
|
|
499
|
+
if (!p.computed && ((p.key.type === 'Identifier' && p.key.name === '__proto__') || (p.key.type === 'Literal' && p.key.value === '__proto__'))) {
|
|
500
|
+
refuse(p, 'an object literal naming its prototype');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
for (const p of node.properties) if (p.shorthand) rewrites.set(p, () => `${p.key.name}: ${emit(p.value)}`);
|
|
504
|
+
rewrites.set(node, () => `${REALM}.own(${spliced(node)})`);
|
|
505
|
+
return;
|
|
506
|
+
case 'ArrayExpression':
|
|
507
|
+
for (const e of node.elements) if (e === null) refuse(node, 'a list literal with a hole');
|
|
508
|
+
rewrites.set(node, () => `${REALM}.list(${args(node.elements).join(', ')})`);
|
|
509
|
+
return;
|
|
510
|
+
case 'CallExpression': {
|
|
511
|
+
const callee = node.callee;
|
|
512
|
+
if (node.optional || (callee.type === 'MemberExpression' && callee.optional)) refuse(node, 'an optional call');
|
|
513
|
+
if (callee.type !== 'MemberExpression' || callee.object.type === 'Super') return;
|
|
514
|
+
// A global's member called is its capture (the Identifier case).
|
|
515
|
+
if (callee.object.type === 'Identifier' && references.get(callee.object) === null && !callee.computed) return;
|
|
516
|
+
// A method by a constant key is the method of that name; by any other key, only one of a list acorn made.
|
|
517
|
+
const constant = callee.computed && callee.property.type === 'Literal' && typeof callee.property.value === 'string' ? callee.property.value : null;
|
|
518
|
+
if (callee.computed && constant === null) {
|
|
519
|
+
if (!isOwnList(callee.object)) refuse(node, 'a call of a method by a computed key, of something other than a list acorn made');
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const name = constant ?? callee.property.name;
|
|
523
|
+
if (!ROUTED_METHODS.has(name) && !acornFields.has(name)) refuse(node, `a call of ${name}, a name acorn sets nowhere`);
|
|
524
|
+
if (callee.object.type === 'ThisExpression' && !callee.computed) return;
|
|
525
|
+
if (ROUTED_METHODS.has(name)) {
|
|
526
|
+
if (name === 'push' && node.arguments.length !== 1) refuse(node, 'a push of other than one item');
|
|
527
|
+
rewrites.set(node, () => `${REALM}.${name}(${[emit(callee.object), ...args(node.arguments)].join(', ')})`);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (BUILTIN_NAMES.has(name) && !acornFields.has(name)) refuse(node, `a call of the built-in method ${name}`);
|
|
531
|
+
if (constant !== null) {
|
|
532
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(constant)) refuse(node, `a call of a method named ${JSON.stringify(constant)}`);
|
|
533
|
+
rewrites.set(node, () => `${emit(callee.object)}.${constant}(${args(node.arguments).join(', ')})`);
|
|
534
|
+
}
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
case 'ForInStatement':
|
|
538
|
+
// Enumerating an object reaches every enumerable name its prototypes have.
|
|
539
|
+
checkedOwned.add(node.right);
|
|
540
|
+
return;
|
|
541
|
+
case 'AssignmentExpression': {
|
|
542
|
+
const left = node.left;
|
|
543
|
+
if (node.operator !== '=' || left.type !== 'MemberExpression' || left.computed || left.object.type !== 'Identifier') return;
|
|
544
|
+
if (!realmInstances.has(references.get(left.object))) return;
|
|
545
|
+
rewrites.set(node, () => `${REALM}.define(${emit(left.object)}, ${JSON.stringify(left.property.name)}, ${emit(node.right)})`);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
case 'MemberExpression': {
|
|
549
|
+
// A global's member is the global's capture (above); `this` is always one of acorn's objects.
|
|
550
|
+
if (node.object.type === 'Identifier' && references.get(node.object) === null) return;
|
|
551
|
+
if (node.object.type === 'Identifier' && references.get(node.object) === 'arguments') return;
|
|
552
|
+
// A function of acorn's inherits Function.prototype unless it is one of its constructors. (A routed
|
|
553
|
+
// method called on one, `f.call(...)`, becomes parser-realm's, which reads nothing of it.)
|
|
554
|
+
const routed = !node.computed && ROUTED_METHODS.has(node.property.name) && parent.type === 'CallExpression' && parent.callee === node;
|
|
555
|
+
if (node.object.type === 'Identifier' && !routed) {
|
|
556
|
+
const fn = functionOf(references.get(node.object));
|
|
557
|
+
if (fn && !constructors.has(fn)) refuse(node, `a member of ${node.object.name}, a function that inherits Function.prototype`);
|
|
558
|
+
}
|
|
559
|
+
if (node.object.type === 'Identifier' && captureAliases.has(references.get(node.object)) && (node.computed || recordOf(node.object) === null)) {
|
|
560
|
+
if (!(node.property.name === 'call' && !node.computed && holdsCaptureSafely(node.object))) refuse(node, `a member of ${node.object.name}, which holds a built-in`);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (!isRead(node, parent) || node.object.type === 'ThisExpression') return;
|
|
564
|
+
// A computed read may index a string: past its end, a string would look further. Where the
|
|
565
|
+
// object is a name or a chain of fields (read twice, as reading them does nothing else), a
|
|
566
|
+
// string goes to parser-realm and anything else is read here, where V8 caches its shape.
|
|
567
|
+
if (node.computed) {
|
|
568
|
+
if (isPlainChain(node.object)) {
|
|
569
|
+
rewrites.set(node, () => {
|
|
570
|
+
// An object rewritten into a call is not read twice.
|
|
571
|
+
if (rewrites.has(node.object)) return `${REALM}.index(${emit(node.object)}, ${emit(node.property)})`;
|
|
572
|
+
const object = emit(node.object);
|
|
573
|
+
return `(typeof ${object} === "string" ? ${REALM}.index(${object}, ${emit(node.property)}) : ${object}[${emit(node.property)}])`;
|
|
574
|
+
});
|
|
575
|
+
} else {
|
|
576
|
+
rewrites.set(node, () => `${REALM}.index(${emit(node.object)}, ${emit(node.property)})`);
|
|
577
|
+
}
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const name = node.property.name;
|
|
581
|
+
const record = recordOf(node.object);
|
|
582
|
+
if (record) {
|
|
583
|
+
if (!RECORDS[record].has(name)) refuse(node, `acorn reads ${name} of ${record}, which parser-realm does not hold`);
|
|
584
|
+
rewrites.set(node, () => `${REALM}.${record}.${name}`);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (BUILTIN_NAMES.has(name) && !acornFields.has(name)) refuse(node, `a read of the built-in ${name}, which acorn has no field of`);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
case 'Literal':
|
|
591
|
+
if (node.regex) rewrites.set(node, () => `${REALM}.regexp(${source.slice(node.start, node.end)})`);
|
|
592
|
+
return;
|
|
593
|
+
case 'ThisExpression':
|
|
594
|
+
if (parent.type === 'NewExpression' && parent.callee === node) rewrites.set(node, () => `(${REALM}.ownConstructor(this))`);
|
|
595
|
+
return;
|
|
596
|
+
case 'VariableDeclaration':
|
|
597
|
+
if (node.kind !== 'var') refuse(node, `a ${node.kind} declaration, which the rewrite does not scope`);
|
|
598
|
+
return;
|
|
599
|
+
case 'FunctionDeclaration': case 'FunctionExpression': case 'ArrowFunctionExpression':
|
|
600
|
+
if (node.async || node.generator || node.type === 'ArrowFunctionExpression') refuse(node, 'an arrow, async or generator function');
|
|
601
|
+
if (node.type === 'FunctionDeclaration' && parent.type !== 'Program' && !(parent.type === 'BlockStatement' && isFunction(parents.get(parent)))) {
|
|
602
|
+
refuse(node, 'a function declared in a block, which a module scopes to the block');
|
|
603
|
+
}
|
|
604
|
+
return;
|
|
605
|
+
case 'BinaryExpression':
|
|
606
|
+
if (node.operator === 'in' || node.operator === 'instanceof') refuse(node, `\`${node.operator}\`, which consults the realm`);
|
|
607
|
+
return;
|
|
608
|
+
case 'TemplateLiteral': case 'TaggedTemplateExpression': case 'SpreadElement': case 'ForOfStatement': case 'ArrayPattern':
|
|
609
|
+
case 'ObjectPattern': case 'RestElement': case 'AssignmentPattern':
|
|
610
|
+
case 'ClassDeclaration': case 'ClassExpression': case 'YieldExpression': case 'AwaitExpression': case 'ChainExpression':
|
|
611
|
+
case 'MetaProperty': case 'ImportExpression': case 'WithStatement':
|
|
612
|
+
refuse(node, `${node.type}, which the rewrite has no safe form for`);
|
|
613
|
+
return;
|
|
614
|
+
default:
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Each constructor and its prototype inherit nothing from where it is declared on.
|
|
620
|
+
const hoisted = [];
|
|
621
|
+
for (const id of constructors) {
|
|
622
|
+
const { statement } = moduleBindings.get(id);
|
|
623
|
+
const nulling = `\n${REALM}.nullPrototypes(${id.name});`;
|
|
624
|
+
if (statement.type === 'FunctionDeclaration') {
|
|
625
|
+
hoisted.push(nulling);
|
|
626
|
+
} else {
|
|
627
|
+
const previous = rewrites.get(statement);
|
|
628
|
+
if (previous) refuse(statement, 'a constructor declared in a statement rewritten otherwise');
|
|
629
|
+
rewrites.set(statement, () => spliced(statement) + nulling);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const imports = program.body.filter((s) => s.type === 'ImportDeclaration');
|
|
634
|
+
if (imports.length > 0) refuse(imports[0], 'an import of its own');
|
|
635
|
+
const code = `import * as ${REALM} from ${JSON.stringify(realmSpecifier)};${hoisted.join('')}\n${spliced(program)}`;
|
|
636
|
+
return { code, constructors: [...constructors].map((id) => id.name) };
|
|
637
|
+
}
|