@scriptc/compiler 0.0.0 → 0.0.2
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/ambient/package.json +3 -0
- package/ambient/scriptc-node-fallback.d.ts +3001 -0
- package/ambient/scriptc-overrides.d.ts +95 -0
- package/ambient/scriptc.d.ts +74 -0
- package/dist/backend/cc.d.ts +169 -0
- package/dist/backend/cc.js +897 -0
- package/dist/backend/cc.js.map +1 -0
- package/dist/backend/emission/emit-async.d.ts +145 -0
- package/dist/backend/emission/emit-async.js +996 -0
- package/dist/backend/emission/emit-async.js.map +1 -0
- package/dist/backend/emission/emit-exprs.d.ts +3 -0
- package/dist/backend/emission/emit-exprs.js +5949 -0
- package/dist/backend/emission/emit-exprs.js.map +1 -0
- package/dist/backend/emission/emit-island.d.ts +45 -0
- package/dist/backend/emission/emit-island.js +271 -0
- package/dist/backend/emission/emit-island.js.map +1 -0
- package/dist/backend/emission/emit-shapes.d.ts +142 -0
- package/dist/backend/emission/emit-shapes.js +575 -0
- package/dist/backend/emission/emit-shapes.js.map +1 -0
- package/dist/backend/emission/emit-stmts.d.ts +78 -0
- package/dist/backend/emission/emit-stmts.js +960 -0
- package/dist/backend/emission/emit-stmts.js.map +1 -0
- package/dist/backend/emission/emit-types.d.ts +65 -0
- package/dist/backend/emission/emit-types.js +652 -0
- package/dist/backend/emission/emit-types.js.map +1 -0
- package/dist/backend/emission/emit-walkers.d.ts +154 -0
- package/dist/backend/emission/emit-walkers.js +1587 -0
- package/dist/backend/emission/emit-walkers.js.map +1 -0
- package/dist/backend/emission/emitter.d.ts +452 -0
- package/dist/backend/emission/emitter.js +1249 -0
- package/dist/backend/emission/emitter.js.map +1 -0
- package/dist/backend/emission/may-throw.d.ts +15 -0
- package/dist/backend/emission/may-throw.js +250 -0
- package/dist/backend/emission/may-throw.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +21 -0
- package/dist/backend/llvm/blocks.js +68 -0
- package/dist/backend/llvm/blocks.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +84 -0
- package/dist/backend/llvm/classes.js +438 -0
- package/dist/backend/llvm/classes.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +123 -0
- package/dist/backend/llvm/dyn.js +2624 -0
- package/dist/backend/llvm/dyn.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +10102 -0
- package/dist/backend/llvm/emitter.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +78 -0
- package/dist/backend/llvm/shapes.js +754 -0
- package/dist/backend/llvm/shapes.js.map +1 -0
- package/dist/backend/llvm/unsupported.d.ts +6 -0
- package/dist/backend/llvm/unsupported.js +12 -0
- package/dist/backend/llvm/unsupported.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +58 -0
- package/dist/backend/llvm/walkers.js +793 -0
- package/dist/backend/llvm/walkers.js.map +1 -0
- package/dist/backend/mangle.d.ts +124 -0
- package/dist/backend/mangle.js +232 -0
- package/dist/backend/mangle.js.map +1 -0
- package/dist/coverage/report.d.ts +65 -0
- package/dist/coverage/report.js +238 -0
- package/dist/coverage/report.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +140 -0
- package/dist/diagnostics/diagnostic.js +458 -0
- package/dist/diagnostics/diagnostic.js.map +1 -0
- package/dist/diagnostics/render.d.ts +11 -0
- package/dist/diagnostics/render.js +58 -0
- package/dist/diagnostics/render.js.map +1 -0
- package/dist/frontend/cjs-lexer.d.ts +20 -0
- package/dist/frontend/cjs-lexer.js +813 -0
- package/dist/frontend/cjs-lexer.js.map +1 -0
- package/dist/frontend/lowering/http2-constants.d.ts +1 -0
- package/dist/frontend/lowering/http2-constants.js +251 -0
- package/dist/frontend/lowering/http2-constants.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
- package/dist/frontend/lowering/lib-boundary.js +143 -0
- package/dist/frontend/lowering/lib-boundary.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.d.ts +18 -0
- package/dist/frontend/lowering/lower-assert.js +1269 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
- package/dist/frontend/lowering/lower-builtins.js +5249 -0
- package/dist/frontend/lowering/lower-builtins.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.d.ts +581 -0
- package/dist/frontend/lowering/lower-calls.js +6286 -0
- package/dist/frontend/lowering/lower-calls.js.map +1 -0
- package/dist/frontend/lowering/lower-classes.d.ts +727 -0
- package/dist/frontend/lowering/lower-classes.js +4701 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -0
- package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
- package/dist/frontend/lowering/lower-comptime.js +244 -0
- package/dist/frontend/lowering/lower-comptime.js.map +1 -0
- package/dist/frontend/lowering/lower-containers.d.ts +486 -0
- package/dist/frontend/lowering/lower-containers.js +6345 -0
- package/dist/frontend/lowering/lower-containers.js.map +1 -0
- package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
- package/dist/frontend/lowering/lower-dgram.js +319 -0
- package/dist/frontend/lowering/lower-dgram.js.map +1 -0
- package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
- package/dist/frontend/lowering/lower-emitter.js +688 -0
- package/dist/frontend/lowering/lower-emitter.js.map +1 -0
- package/dist/frontend/lowering/lower-enums.d.ts +22 -0
- package/dist/frontend/lowering/lower-enums.js +235 -0
- package/dist/frontend/lowering/lower-enums.js.map +1 -0
- package/dist/frontend/lowering/lower-expando.d.ts +35 -0
- package/dist/frontend/lowering/lower-expando.js +276 -0
- package/dist/frontend/lowering/lower-expando.js.map +1 -0
- package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
- package/dist/frontend/lowering/lower-exprs.js +8111 -0
- package/dist/frontend/lowering/lower-exprs.js.map +1 -0
- package/dist/frontend/lowering/lower-generators.d.ts +50 -0
- package/dist/frontend/lowering/lower-generators.js +421 -0
- package/dist/frontend/lowering/lower-generators.js.map +1 -0
- package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
- package/dist/frontend/lowering/lower-inspect.js +1074 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -0
- package/dist/frontend/lowering/lower-island.d.ts +108 -0
- package/dist/frontend/lowering/lower-island.js +650 -0
- package/dist/frontend/lowering/lower-island.js.map +1 -0
- package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
- package/dist/frontend/lowering/lower-mixins.js +534 -0
- package/dist/frontend/lowering/lower-mixins.js.map +1 -0
- package/dist/frontend/lowering/lower-modules.d.ts +135 -0
- package/dist/frontend/lowering/lower-modules.js +1613 -0
- package/dist/frontend/lowering/lower-modules.js.map +1 -0
- package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
- package/dist/frontend/lowering/lower-namespaces.js +706 -0
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
- package/dist/frontend/lowering/lower-server.d.ts +90 -0
- package/dist/frontend/lowering/lower-server.js +3471 -0
- package/dist/frontend/lowering/lower-server.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
- package/dist/frontend/lowering/lower-stmts.js +5341 -0
- package/dist/frontend/lowering/lower-stmts.js.map +1 -0
- package/dist/frontend/lowering/lower-stream.d.ts +88 -0
- package/dist/frontend/lowering/lower-stream.js +1533 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -0
- package/dist/frontend/lowering/lower-test.d.ts +27 -0
- package/dist/frontend/lowering/lower-test.js +353 -0
- package/dist/frontend/lowering/lower-test.js.map +1 -0
- package/dist/frontend/lowering/lowerer.d.ts +1748 -0
- package/dist/frontend/lowering/lowerer.js +6257 -0
- package/dist/frontend/lowering/lowerer.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +272 -0
- package/dist/frontend/lowering/surfaces.js +1096 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +55 -0
- package/dist/frontend/npm-static.js +300 -0
- package/dist/frontend/npm-static.js.map +1 -0
- package/dist/frontend/npm.d.ts +330 -0
- package/dist/frontend/npm.js +1259 -0
- package/dist/frontend/npm.js.map +1 -0
- package/dist/frontend/program.d.ts +186 -0
- package/dist/frontend/program.js +2255 -0
- package/dist/frontend/program.js.map +1 -0
- package/dist/frontend/provenance-registry.d.ts +48 -0
- package/dist/frontend/provenance-registry.js +87 -0
- package/dist/frontend/provenance-registry.js.map +1 -0
- package/dist/frontend/provenance.d.ts +6 -0
- package/dist/frontend/provenance.js +459 -0
- package/dist/frontend/provenance.js.map +1 -0
- package/dist/frontend/resolve.d.ts +56 -0
- package/dist/frontend/resolve.js +682 -0
- package/dist/frontend/resolve.js.map +1 -0
- package/dist/frontend/shared.d.ts +67 -0
- package/dist/frontend/shared.js +241 -0
- package/dist/frontend/shared.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +7 -0
- package/dist/frontend/ts7/adapter.js +54 -0
- package/dist/frontend/ts7/adapter.js.map +1 -0
- package/dist/frontend/ts7/ast.d.ts +50 -0
- package/dist/frontend/ts7/ast.js +211 -0
- package/dist/frontend/ts7/ast.js.map +1 -0
- package/dist/frontend/ts7/census-check.d.ts +1 -0
- package/dist/frontend/ts7/census-check.js +12 -0
- package/dist/frontend/ts7/census-check.js.map +1 -0
- package/dist/frontend/ts7/checker.d.ts +140 -0
- package/dist/frontend/ts7/checker.js +544 -0
- package/dist/frontend/ts7/checker.js.map +1 -0
- package/dist/frontend/ts7/enums.d.ts +18 -0
- package/dist/frontend/ts7/enums.js +43 -0
- package/dist/frontend/ts7/enums.js.map +1 -0
- package/dist/frontend/ts7/program.d.ts +99 -0
- package/dist/frontend/ts7/program.js +278 -0
- package/dist/frontend/ts7/program.js.map +1 -0
- package/dist/frontend/ts7/world-check.d.ts +3 -0
- package/dist/frontend/ts7/world-check.js +34 -0
- package/dist/frontend/ts7/world-check.js.map +1 -0
- package/dist/frontend/types.d.ts +205 -0
- package/dist/frontend/types.js +2485 -0
- package/dist/frontend/types.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +429 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/nodes.d.ts +4334 -0
- package/dist/ir/nodes.js +1906 -0
- package/dist/ir/nodes.js.map +1 -0
- package/dist/ir/serialize.d.ts +4 -0
- package/dist/ir/serialize.js +34 -0
- package/dist/ir/serialize.js.map +1 -0
- package/dist/ir/validate.d.ts +36 -0
- package/dist/ir/validate.js +4885 -0
- package/dist/ir/validate.js.map +1 -0
- package/package.json +30 -6
- package/README.md +0 -3
- package/index.js +0 -2
package/dist/ir/nodes.js
ADDED
|
@@ -0,0 +1,1906 @@
|
|
|
1
|
+
/* scriptc IR — the only interface between frontend and backends.
|
|
2
|
+
*
|
|
3
|
+
* Design rules (see docs/ir.md for node-by-node semantics):
|
|
4
|
+
* - Plain JSON-safe discriminated unions: no classes, no parent pointers,
|
|
5
|
+
* no cycles. Cross-references are by string id.
|
|
6
|
+
* - Every node carries `loc` (source span) and every expression carries its
|
|
7
|
+
* computed `type` — backends never re-derive types.
|
|
8
|
+
* - Structured control flow (statement tree), not basic blocks: the only
|
|
9
|
+
* current backend is C and there are no optimization passes yet. When a mid-end
|
|
10
|
+
* arrives, a structured→CFG lowering pass slots in after validation.
|
|
11
|
+
* - The type union is deliberately written for extension (`dyn` shipped
|
|
12
|
+
* with JSON, `promise` with async). All switches over IrType/IrExpr/
|
|
13
|
+
* IrStmt must have exhaustive `never` default arms so adding a member
|
|
14
|
+
* turns into compile errors, not silent misbehavior.
|
|
15
|
+
*/
|
|
16
|
+
/** The ref kinds whose values are JS OBJECTS for truthiness: always true
|
|
17
|
+
* ([] and {} included) — toBool accepts them (the operand still evaluates;
|
|
18
|
+
* the test is constant), and per-union truthiness helpers answer their
|
|
19
|
+
* arms with `true`. */
|
|
20
|
+
export const REF_TRUTHY_KINDS = new Set([
|
|
21
|
+
// symbol is not a JS object, but every symbol is truthy — the same
|
|
22
|
+
// constant-true answer.
|
|
23
|
+
"symbol",
|
|
24
|
+
"array", "map", "set", "regex", "url", "searchParams", "stats", "spawnRes", "child",
|
|
25
|
+
"netServer", "netSocket", "http2Session", "http2Stream", "dgramSocket", "testCtx", "httpReq", "httpRes", "httpClientReq",
|
|
26
|
+
"secureCtx", "fsWatcher", "childStream", "procStream", "bytes", "func", "object", "record", "promise",
|
|
27
|
+
// A generator object is a JS object: always truthy.
|
|
28
|
+
"generator",
|
|
29
|
+
// A class object is a JS object (constructors are functions): always truthy.
|
|
30
|
+
"classval",
|
|
31
|
+
]);
|
|
32
|
+
export const F64 = { kind: "f64" };
|
|
33
|
+
export const BYTES_U8 = { kind: "bytes", elem: "u8" };
|
|
34
|
+
export const STRING = { kind: "string" };
|
|
35
|
+
export const BOOL = { kind: "bool" };
|
|
36
|
+
export const REGEX = { kind: "regex" };
|
|
37
|
+
export const URL_T = { kind: "url" };
|
|
38
|
+
export const SEARCH_PARAMS_T = { kind: "searchParams" };
|
|
39
|
+
export const SYMBOL_T = { kind: "symbol" };
|
|
40
|
+
export const STATS_T = { kind: "stats" };
|
|
41
|
+
export const SPAWNRES_T = { kind: "spawnRes" };
|
|
42
|
+
export const CHILD_T = { kind: "child" };
|
|
43
|
+
export const NETSERVER_T = { kind: "netServer" };
|
|
44
|
+
export const NETSOCKET_T = { kind: "netSocket" };
|
|
45
|
+
export const HTTP2SESSION_T = { kind: "http2Session" };
|
|
46
|
+
export const HTTP2STREAM_T = { kind: "http2Stream" };
|
|
47
|
+
export const DGRAMSOCK_T = { kind: "dgramSocket" };
|
|
48
|
+
export const TESTCTX_T = { kind: "testCtx" };
|
|
49
|
+
export const HTTPREQ_T = { kind: "httpReq" };
|
|
50
|
+
export const HTTPRES_T = { kind: "httpRes" };
|
|
51
|
+
export const HTTPCLIENTREQ_T = { kind: "httpClientReq" };
|
|
52
|
+
export const SECURECTX_T = { kind: "secureCtx" };
|
|
53
|
+
export const FSWATCHER_T = { kind: "fsWatcher" };
|
|
54
|
+
export const CHILDSTREAM_T = { kind: "childStream" };
|
|
55
|
+
export const PROCSTREAM_T = { kind: "procStream" };
|
|
56
|
+
export const VOID = { kind: "void" };
|
|
57
|
+
export const DYN = { kind: "dyn" };
|
|
58
|
+
export const JSVAL = { kind: "jsval" };
|
|
59
|
+
export const CAUGHT = { kind: "caught" };
|
|
60
|
+
export const UNDEFINED_T = { kind: "undefinedT" };
|
|
61
|
+
export const NULL_T = { kind: "nullT" };
|
|
62
|
+
/** True for the payload-less unit kinds (`undefined`/`null`). Unit values
|
|
63
|
+
* exist only inside unions: a unit-armed union instance is tag-only, so
|
|
64
|
+
* wrapping allocates no payload, narrowing to a unit arm produces no value
|
|
65
|
+
* (the frontend never emits it), and releasing has nothing to release. */
|
|
66
|
+
export function isUnitType(t) {
|
|
67
|
+
return t.kind === "undefinedT" || t.kind === "nullT";
|
|
68
|
+
}
|
|
69
|
+
export function arrayOf(elem) {
|
|
70
|
+
return { kind: "array", elem };
|
|
71
|
+
}
|
|
72
|
+
export function bytesOf(elem) {
|
|
73
|
+
return { kind: "bytes", elem };
|
|
74
|
+
}
|
|
75
|
+
export function mapOf(key, value) {
|
|
76
|
+
return { kind: "map", key, value };
|
|
77
|
+
}
|
|
78
|
+
export function setOf(elem) {
|
|
79
|
+
return { kind: "set", elem };
|
|
80
|
+
}
|
|
81
|
+
/** The Map KEY fence: string (content) or number (SameValueZero) — the two
|
|
82
|
+
* kinds a hash of the VALUE is honest for. Booleans, objects, and the rest
|
|
83
|
+
* of JS's anything-goes keys stay out. Shared by the frontend's
|
|
84
|
+
* type mapping/diagnostics and the validator. Set ELEMENTS use the same
|
|
85
|
+
* fence: a set is hashed storage of its elements exactly as a map is of
|
|
86
|
+
* its keys (isSupportedSetElem is this predicate under its own name). */
|
|
87
|
+
export function isSupportedMapKey(t) {
|
|
88
|
+
return t.kind === "f64" || t.kind === "string";
|
|
89
|
+
}
|
|
90
|
+
/** The Set ELEMENT fence — Map's key fence plus the refcounted HANDLE
|
|
91
|
+
* kinds stored under identity hashing (SameValueZero for JS objects IS
|
|
92
|
+
* reference identity, so a Set of server handles — portless's auxiliary-
|
|
93
|
+
* server registry — is honest hashed storage; SCR_MAP_KEY_REF in the
|
|
94
|
+
* runtime). netServer is the one handle admitted so far: it drops its
|
|
95
|
+
* listener closures at close, so a set-in-listener cycle is temporary —
|
|
96
|
+
* the child precedent's story. Symbols are identity values by DESIGN —
|
|
97
|
+
* SameValueZero on a symbol IS pointer identity, so a Set of symbols (the
|
|
98
|
+
* sentinel-registry idiom) is the same honest hashed storage with no
|
|
99
|
+
* cycle risk at all (symbols hold only strings). */
|
|
100
|
+
export function isSupportedSetElem(t) {
|
|
101
|
+
return isSupportedMapKey(t) || t.kind === "netServer" || t.kind === "symbol";
|
|
102
|
+
}
|
|
103
|
+
/** The Map VALUE fence: scalars plus every refcounted kind EXCEPT
|
|
104
|
+
* func/promise/dyn/jsval (and map itself — no maps of maps).
|
|
105
|
+
* Record/object/union values can point back at the map holding them, which
|
|
106
|
+
* is exactly why ref-valued maps are cycle-capable (see the backend's
|
|
107
|
+
* cycle analysis and docs/memory.md). Shared frontend/validator. */
|
|
108
|
+
export function isSupportedMapValue(t) {
|
|
109
|
+
switch (t.kind) {
|
|
110
|
+
case "f64":
|
|
111
|
+
case "string":
|
|
112
|
+
case "bool":
|
|
113
|
+
case "record":
|
|
114
|
+
case "object":
|
|
115
|
+
case "union":
|
|
116
|
+
case "array":
|
|
117
|
+
return true;
|
|
118
|
+
// A spawned child handle (Map<string, ChildProcess> — the mdns
|
|
119
|
+
// publisher registry): an ordinary refcounted pointer value (the
|
|
120
|
+
// scr_child_retain/release adapters), stored and read like any ref.
|
|
121
|
+
// It holds closures only until its terminal event fires (then drops
|
|
122
|
+
// them), so a child in a map cannot cycle through the map; the RC
|
|
123
|
+
// adapters handle its lifetime.
|
|
124
|
+
case "child":
|
|
125
|
+
return true;
|
|
126
|
+
// A SecureContext handle (Map<string, tls.SecureContext> — the SNI
|
|
127
|
+
// callback's per-hostname cache): immutable, holds no references —
|
|
128
|
+
// the same ordinary refcounted pointer story as child, without even
|
|
129
|
+
// the pre-settle closures.
|
|
130
|
+
case "secureCtx":
|
|
131
|
+
return true;
|
|
132
|
+
// A promise (Map<string, Promise<SecureContext>> — the SNI callback's
|
|
133
|
+
// in-flight dedupe map): refcounted, with trace/cycle support already
|
|
134
|
+
// in place (scr_promise_trace_v — the race machinery's refcounting).
|
|
135
|
+
// It holds reaction closures only until it settles (settled promises
|
|
136
|
+
// drop them), so the child rule's temporary-cycle story applies: a
|
|
137
|
+
// pending promise whose callbacks capture the map is a cycle only
|
|
138
|
+
// until settlement, and the collector handles the never-settling case.
|
|
139
|
+
case "promise":
|
|
140
|
+
return true;
|
|
141
|
+
// A class object (Map<string, typeof Shape> — the registry/factory
|
|
142
|
+
// idiom): an immortal static behind no-op RC adapters — it holds no
|
|
143
|
+
// references at all, so no trace, no cycles, ever.
|
|
144
|
+
case "classval":
|
|
145
|
+
return true;
|
|
146
|
+
default:
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** The INDEX-SIGNATURE value fence (`{ [k: string]: V }` shapes): the map
|
|
151
|
+
* VALUE kinds — the overflow portion IS a string-keyed map — plus dyn
|
|
152
|
+
* (`unknown`, an unknown-valued pricing-table shape: overflow reads surface ordinary
|
|
153
|
+
* dyn values validated by the usual checked casts). Shared frontend
|
|
154
|
+
* (mapType) / validator. */
|
|
155
|
+
export function isSupportedIndexValue(t) {
|
|
156
|
+
return t.kind === "dyn" || isSupportedMapValue(t);
|
|
157
|
+
}
|
|
158
|
+
export function funcOf(params, ret) {
|
|
159
|
+
return { kind: "func", params, ret };
|
|
160
|
+
}
|
|
161
|
+
/** Canonical, injective text form of an IrType — the building block of
|
|
162
|
+
* shape/union identity keys, generic-function instantiation keys, and the
|
|
163
|
+
* backend's per-type helper interning (jsonStringify/dynCheck walkers).
|
|
164
|
+
* Nested records/unions are represented by their (already interned, already
|
|
165
|
+
* canonical) shapeId/unionId, so keys stay finite and comparable. Lives in
|
|
166
|
+
* the IR (not the frontend) because both ends need it. */
|
|
167
|
+
export function typeKey(t) {
|
|
168
|
+
switch (t.kind) {
|
|
169
|
+
case "f64":
|
|
170
|
+
case "string":
|
|
171
|
+
case "bool":
|
|
172
|
+
case "regex":
|
|
173
|
+
case "url":
|
|
174
|
+
case "searchParams":
|
|
175
|
+
case "symbol":
|
|
176
|
+
case "stats":
|
|
177
|
+
case "spawnRes":
|
|
178
|
+
case "child":
|
|
179
|
+
case "netServer":
|
|
180
|
+
case "netSocket":
|
|
181
|
+
case "http2Session":
|
|
182
|
+
case "http2Stream":
|
|
183
|
+
case "dgramSocket":
|
|
184
|
+
case "testCtx":
|
|
185
|
+
case "httpReq":
|
|
186
|
+
case "httpRes":
|
|
187
|
+
case "httpClientReq":
|
|
188
|
+
case "secureCtx":
|
|
189
|
+
case "fsWatcher":
|
|
190
|
+
case "childStream":
|
|
191
|
+
case "procStream":
|
|
192
|
+
case "dyn":
|
|
193
|
+
case "jsval":
|
|
194
|
+
case "caught":
|
|
195
|
+
case "void":
|
|
196
|
+
return t.kind;
|
|
197
|
+
case "undefinedT":
|
|
198
|
+
return "undefined";
|
|
199
|
+
case "nullT":
|
|
200
|
+
return "null";
|
|
201
|
+
case "array":
|
|
202
|
+
return `array<${typeKey(t.elem)}>`;
|
|
203
|
+
case "bytes":
|
|
204
|
+
return `bytes<${t.elem}>`;
|
|
205
|
+
case "map":
|
|
206
|
+
return `map<${typeKey(t.key)},${typeKey(t.value)}>`;
|
|
207
|
+
case "set":
|
|
208
|
+
return `set<${typeKey(t.elem)}>`;
|
|
209
|
+
case "func":
|
|
210
|
+
return `func(${[...t.params.map(typeKey), ...(t.rest ? ["...dyn[]"] : [])].join(",")})=>${typeKey(t.ret)}`;
|
|
211
|
+
case "object":
|
|
212
|
+
return `object:${t.className}`;
|
|
213
|
+
case "classval":
|
|
214
|
+
return `classval:${t.className}`;
|
|
215
|
+
case "record":
|
|
216
|
+
return `record:${t.shapeId}`;
|
|
217
|
+
case "union":
|
|
218
|
+
return `union:${t.unionId}`;
|
|
219
|
+
case "promise":
|
|
220
|
+
return `promise<${typeKey(t.inner)}>`;
|
|
221
|
+
case "generator":
|
|
222
|
+
return `generator<${typeKey(t.yieldT)},${typeKey(t.retT)},${typeKey(t.nextT)}>`;
|
|
223
|
+
default: {
|
|
224
|
+
const _exhaustive = t;
|
|
225
|
+
void _exhaustive;
|
|
226
|
+
throw new Error("unreachable");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
export function typeEquals(a, b) {
|
|
231
|
+
if (a.kind === "array")
|
|
232
|
+
return b.kind === "array" && typeEquals(a.elem, b.elem);
|
|
233
|
+
if (a.kind === "bytes")
|
|
234
|
+
return b.kind === "bytes" && a.elem === b.elem;
|
|
235
|
+
if (a.kind === "map") {
|
|
236
|
+
return b.kind === "map" && typeEquals(a.key, b.key) && typeEquals(a.value, b.value);
|
|
237
|
+
}
|
|
238
|
+
if (a.kind === "set")
|
|
239
|
+
return b.kind === "set" && typeEquals(a.elem, b.elem);
|
|
240
|
+
if (a.kind === "func") {
|
|
241
|
+
return (b.kind === "func" &&
|
|
242
|
+
a.params.length === b.params.length &&
|
|
243
|
+
(a.rest === true) === (b.rest === true) &&
|
|
244
|
+
a.params.every((p, i) => typeEquals(p, b.params[i])) &&
|
|
245
|
+
typeEquals(a.ret, b.ret));
|
|
246
|
+
}
|
|
247
|
+
if (a.kind === "object")
|
|
248
|
+
return b.kind === "object" && a.className === b.className;
|
|
249
|
+
if (a.kind === "classval")
|
|
250
|
+
return b.kind === "classval" && a.className === b.className;
|
|
251
|
+
// The frontend deduplicates shapes structurally (one shapeId per canonical
|
|
252
|
+
// field list), so id equality IS structural equality.
|
|
253
|
+
if (a.kind === "record")
|
|
254
|
+
return b.kind === "record" && a.shapeId === b.shapeId;
|
|
255
|
+
// Unions are interned like shapes: one unionId per canonical arm list.
|
|
256
|
+
if (a.kind === "union")
|
|
257
|
+
return b.kind === "union" && a.unionId === b.unionId;
|
|
258
|
+
if (a.kind === "promise")
|
|
259
|
+
return b.kind === "promise" && typeEquals(a.inner, b.inner);
|
|
260
|
+
if (a.kind === "generator") {
|
|
261
|
+
return (b.kind === "generator" &&
|
|
262
|
+
typeEquals(a.yieldT, b.yieldT) &&
|
|
263
|
+
typeEquals(a.retT, b.retT) &&
|
|
264
|
+
typeEquals(a.nextT, b.nextT));
|
|
265
|
+
}
|
|
266
|
+
return a.kind === b.kind;
|
|
267
|
+
}
|
|
268
|
+
/** True for types whose values are heap-allocated and reference-counted.
|
|
269
|
+
* The single dispatch point for the backend's RC machinery: retains,
|
|
270
|
+
* releases, frame/scope tracking, and NULL-initialized locals all key off
|
|
271
|
+
* this — adding a refcounted kind must not grow new per-kind checks outside
|
|
272
|
+
* the type-directed helpers. */
|
|
273
|
+
export function isRefCounted(t) {
|
|
274
|
+
return (t.kind === "string" ||
|
|
275
|
+
t.kind === "array" ||
|
|
276
|
+
t.kind === "map" ||
|
|
277
|
+
t.kind === "set" ||
|
|
278
|
+
// Every regex value is an immortal interned literal today, so its
|
|
279
|
+
// retains/releases are no-ops — riding the uniform machinery anyway
|
|
280
|
+
// means dynamic construction can arrive without a redesign.
|
|
281
|
+
t.kind === "regex" ||
|
|
282
|
+
// URL, Stats, and spawnSync-result instances are ordinary refcounted
|
|
283
|
+
// heap values (immutable, no cycles — strings/scalars inside only).
|
|
284
|
+
t.kind === "url" ||
|
|
285
|
+
// URLSearchParams lists are ordinary refcounted heap values (strings
|
|
286
|
+
// plus at most the owning-URL edge — no cycles).
|
|
287
|
+
t.kind === "searchParams" ||
|
|
288
|
+
// Symbols are the same story: immutable identity values holding at
|
|
289
|
+
// most two strings.
|
|
290
|
+
t.kind === "symbol" ||
|
|
291
|
+
t.kind === "stats" ||
|
|
292
|
+
t.kind === "spawnRes" ||
|
|
293
|
+
t.kind === "child" ||
|
|
294
|
+
// net handles are refcounted like child (listeners drop at settle,
|
|
295
|
+
// so lean allocation — see the IrType comment).
|
|
296
|
+
t.kind === "netServer" ||
|
|
297
|
+
t.kind === "netSocket" ||
|
|
298
|
+
// h2 sessions/streams are refcounted like the net pair (listeners
|
|
299
|
+
// drop at settlement; the session→stream↔session cycle breaks there).
|
|
300
|
+
t.kind === "http2Session" ||
|
|
301
|
+
t.kind === "http2Stream" ||
|
|
302
|
+
t.kind === "dgramSocket" ||
|
|
303
|
+
// TestContext handles are refcounted like dgramSocket (the runner
|
|
304
|
+
// tree owns children; no cycles through the handle).
|
|
305
|
+
t.kind === "testCtx" ||
|
|
306
|
+
t.kind === "httpReq" ||
|
|
307
|
+
t.kind === "httpRes" ||
|
|
308
|
+
t.kind === "httpClientReq" ||
|
|
309
|
+
// SecureContext handles are refcounted like url/stats (immutable, no
|
|
310
|
+
// cycles — parsed cert/key material inside only).
|
|
311
|
+
t.kind === "secureCtx" ||
|
|
312
|
+
// FSWatcher handles are refcounted like child (listeners drop at
|
|
313
|
+
// close, so lean allocation — see the IrType comment).
|
|
314
|
+
t.kind === "fsWatcher" ||
|
|
315
|
+
// Child-output streams are refcounted like child (listeners drop at
|
|
316
|
+
// EOF — see the IrType comment).
|
|
317
|
+
t.kind === "childStream" ||
|
|
318
|
+
// Typed arrays / Buffers are ordinary refcounted heap values (mutable,
|
|
319
|
+
// no cycles — raw bytes inside only).
|
|
320
|
+
t.kind === "bytes" ||
|
|
321
|
+
t.kind === "func" ||
|
|
322
|
+
t.kind === "object" ||
|
|
323
|
+
// Every class object is an immortal emitted static, so its
|
|
324
|
+
// retains/releases are no-ops — riding the uniform machinery (the
|
|
325
|
+
// regex-literal stance) keeps every container and box path unchanged.
|
|
326
|
+
t.kind === "classval" ||
|
|
327
|
+
t.kind === "record" ||
|
|
328
|
+
// The union CONTAINER is heap/refcounted regardless of which arm it
|
|
329
|
+
// holds (uniform representation keeps the RC discipline simple).
|
|
330
|
+
t.kind === "union" ||
|
|
331
|
+
t.kind === "promise" ||
|
|
332
|
+
// A generator object is a refcounted handle over its paused fiber and
|
|
333
|
+
// typed channel slots (ScrGen — lean allocation, no cycle header).
|
|
334
|
+
t.kind === "generator" ||
|
|
335
|
+
// A dyn value is a refcounted JSON DOM tree (ScrDyn).
|
|
336
|
+
t.kind === "dyn" ||
|
|
337
|
+
// An island value is a refcounted cell owning one engine value.
|
|
338
|
+
t.kind === "jsval" ||
|
|
339
|
+
// A catch binding is a refcounted snapshot box (ScrCaught).
|
|
340
|
+
t.kind === "caught");
|
|
341
|
+
}
|
|
342
|
+
/** The runtime-provided error classes, keyed by IR class name. The names
|
|
343
|
+
* are '%'-prefixed ('%' cannot appear in a TS identifier, so a user's own
|
|
344
|
+
* `class Error` can never collide). `lib` is the standard-library name the
|
|
345
|
+
* frontend recognizes; `kind` is the runtime's SCR_ERR_* index (backends
|
|
346
|
+
* stamp scr_error_vts[kind] and pick constructor kinds by it). Every
|
|
347
|
+
* emitted module carries all four class defs (flagged `runtime`) so the
|
|
348
|
+
* program's preorder numbering always covers them — the runtime's own
|
|
349
|
+
* throws (JSON/dynCheck/regex) mint instances of these classes whether or
|
|
350
|
+
* not user code mentions Error. */
|
|
351
|
+
export const RUNTIME_ERROR_CLASSES = new Map([
|
|
352
|
+
["%Error", { lib: "Error", kind: 0, base: null }],
|
|
353
|
+
["%TypeError", { lib: "TypeError", kind: 1, base: "%Error" }],
|
|
354
|
+
["%RangeError", { lib: "RangeError", kind: 2, base: "%Error" }],
|
|
355
|
+
["%SyntaxError", { lib: "SyntaxError", kind: 3, base: "%Error" }],
|
|
356
|
+
// DOMException — the web-standard error shape (a Node global since
|
|
357
|
+
// v17). Its extra state (the legacy numeric code, the options form's
|
|
358
|
+
// cause) lives in runtime-side slots BEYOND the ScrError prefix the IR
|
|
359
|
+
// fields describe, reached only through the error.dom* libCalls — so
|
|
360
|
+
// user `extends DOMException` is fenced (the subclass layout would
|
|
361
|
+
// overlap the hidden slots), where the other four extend freely.
|
|
362
|
+
["%DOMException", { lib: "DOMException", kind: 4, base: "%Error" }],
|
|
363
|
+
]);
|
|
364
|
+
/** The runtime-provided node:events EventEmitter class (ScrEmitter /
|
|
365
|
+
* scr_emitter_*, scr_events_emitter.c — link-gated by moduleUsesEmitter,
|
|
366
|
+
* so unlike the error classes its def rides a module only when the
|
|
367
|
+
* program touches the surface). The '%' name keeps it clear of user
|
|
368
|
+
* identifiers, exactly like the error classes. Backends emit no struct/
|
|
369
|
+
* RC/vtable for it; user subclasses embed the ScrEmitter prefix (the
|
|
370
|
+
* registry pointer and the display-name slot, stamped by the emitted
|
|
371
|
+
* allocation) and main() stamps the runtime vtable's preorder interval.
|
|
372
|
+
* The emitter hierarchy is UNCONDITIONALLY cycle-capable: the registry
|
|
373
|
+
* owns listener closures, whatever the subclass fields say. */
|
|
374
|
+
export const RUNTIME_EMITTER_CLASS = "%EventEmitter";
|
|
375
|
+
/** The runtime-provided node:stream classes (ScrStream / scr_stream_*,
|
|
376
|
+
* scr_stream.c — link-gated by moduleUsesStream). They root at the
|
|
377
|
+
* emitter class (base chains below), so the emitter method surface and
|
|
378
|
+
* upcasts apply unchanged; every instance shares ONE runtime layout (the
|
|
379
|
+
* ScrEmitter prefix plus a lazily-allocated stream-state pointer), so a
|
|
380
|
+
* Duplex upcast to Readable is the usual pointer reinterpret. `sides`
|
|
381
|
+
* names which halves the class carries — the lowering admits readable
|
|
382
|
+
* members on "r"-siders, writable members on "w"-siders. User `extends`
|
|
383
|
+
* compiles (phase 2): subclass structs embed the full ScrStream prefix
|
|
384
|
+
* (registry, display name, state pointer — one slot past the emitter
|
|
385
|
+
* prefix), construction runs the emitted allocation then a stream .init
|
|
386
|
+
* libCall at super(options), and overridden underscore methods bind as
|
|
387
|
+
* synthesized wrapper closures dispatching through the vtable; main()
|
|
388
|
+
* stamps each runtime vtable's preorder interval like the emitter's. */
|
|
389
|
+
export const RUNTIME_STREAM_CLASSES = new Map([
|
|
390
|
+
["%Readable", { lib: "Readable", base: RUNTIME_EMITTER_CLASS, sides: "r" }],
|
|
391
|
+
["%Writable", { lib: "Writable", base: RUNTIME_EMITTER_CLASS, sides: "w" }],
|
|
392
|
+
["%Duplex", { lib: "Duplex", base: "%Readable", sides: "rw" }],
|
|
393
|
+
["%Transform", { lib: "Transform", base: "%Duplex", sides: "rw" }],
|
|
394
|
+
["%PassThrough", { lib: "PassThrough", base: "%Transform", sides: "rw" }],
|
|
395
|
+
]);
|
|
396
|
+
/** Object-literal ACCESSOR properties (`{ get x() {...}, set x(v) {...} }`)
|
|
397
|
+
* live on the shape as reserved '%'-fields holding closures: `%get:x` a
|
|
398
|
+
* `() => T` invoked once per property READ (side effects and all — JS's
|
|
399
|
+
* evaluation), `%set:x` a `(v: T) => void` invoked per WRITE. The property
|
|
400
|
+
* name itself has NO data slot. Like every '%'-field the slots stay out of
|
|
401
|
+
* declaredOrder — and because the slot types are funcs, accessor-carrying
|
|
402
|
+
* shapes are never JSON-safe or dyn-convertible; the enumeration surfaces
|
|
403
|
+
* Node would answer differently (Object.keys includes accessor names,
|
|
404
|
+
* values/entries and spread invoke the getters) fence by name at their
|
|
405
|
+
* lowerings. */
|
|
406
|
+
export function accessorSlotProp(fieldName) {
|
|
407
|
+
if (fieldName.startsWith("%get:"))
|
|
408
|
+
return { kind: "get", prop: fieldName.slice(5) };
|
|
409
|
+
if (fieldName.startsWith("%set:"))
|
|
410
|
+
return { kind: "set", prop: fieldName.slice(5) };
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
/** True when the shape carries at least one accessor slot (see
|
|
414
|
+
* accessorSlotProp) — the predicate behind every enumeration-surface
|
|
415
|
+
* fence. */
|
|
416
|
+
export function shapeHasAccessorSlots(shape) {
|
|
417
|
+
return shape.fields.some((f) => accessorSlotProp(f.name) !== null);
|
|
418
|
+
}
|
|
419
|
+
/** The bytesIntrinsic methods that can raise a catchable error — backends'
|
|
420
|
+
* may-throw analyses seed on these exactly like MAY_THROW_LIB_FNS. */
|
|
421
|
+
export const MAY_THROW_BYTES_METHODS = new Set([
|
|
422
|
+
"setFrom",
|
|
423
|
+
"readNum",
|
|
424
|
+
"writeNum",
|
|
425
|
+
"readNumVar",
|
|
426
|
+
"writeNumVar",
|
|
427
|
+
"compareBuf",
|
|
428
|
+
"fill",
|
|
429
|
+
"fillNum",
|
|
430
|
+
"fillStr",
|
|
431
|
+
"copy",
|
|
432
|
+
"swap16",
|
|
433
|
+
"swap32",
|
|
434
|
+
"swap64",
|
|
435
|
+
"writeStr",
|
|
436
|
+
"dataViewNew",
|
|
437
|
+
"dvGetUint8",
|
|
438
|
+
"dvGetInt8",
|
|
439
|
+
"dvGetUint16",
|
|
440
|
+
"dvGetInt16",
|
|
441
|
+
"dvGetUint32",
|
|
442
|
+
"dvGetInt32",
|
|
443
|
+
"dvGetFloat32",
|
|
444
|
+
"dvGetFloat64",
|
|
445
|
+
"dvGetBigUint64Number",
|
|
446
|
+
"dvGetBigInt64Number",
|
|
447
|
+
]);
|
|
448
|
+
/** Result-type rule for each island op (the validator enforces it; the
|
|
449
|
+
* frontend constructs nodes with exactly these). */
|
|
450
|
+
export function jsOpResultKind(op) {
|
|
451
|
+
switch (op) {
|
|
452
|
+
case "add":
|
|
453
|
+
case "sub":
|
|
454
|
+
case "mul":
|
|
455
|
+
case "div":
|
|
456
|
+
case "mod":
|
|
457
|
+
case "pow":
|
|
458
|
+
case "neg":
|
|
459
|
+
case "plus":
|
|
460
|
+
case "getProp":
|
|
461
|
+
case "getIdx":
|
|
462
|
+
case "callMethod":
|
|
463
|
+
case "callFn":
|
|
464
|
+
case "construct":
|
|
465
|
+
case "globalGet":
|
|
466
|
+
case "objLit":
|
|
467
|
+
case "arrLit":
|
|
468
|
+
case "undefLit":
|
|
469
|
+
case "nullLit":
|
|
470
|
+
case "iterNew":
|
|
471
|
+
case "optCallMethod":
|
|
472
|
+
return "jsval";
|
|
473
|
+
case "lt":
|
|
474
|
+
case "le":
|
|
475
|
+
case "gt":
|
|
476
|
+
case "ge":
|
|
477
|
+
case "eq":
|
|
478
|
+
case "neq":
|
|
479
|
+
case "instanceOf":
|
|
480
|
+
case "truthy":
|
|
481
|
+
case "not":
|
|
482
|
+
return "bool";
|
|
483
|
+
case "typeof":
|
|
484
|
+
case "toStr":
|
|
485
|
+
return "string";
|
|
486
|
+
case "setProp":
|
|
487
|
+
case "setIdx":
|
|
488
|
+
return "void";
|
|
489
|
+
default: {
|
|
490
|
+
const _exhaustive = op;
|
|
491
|
+
void _exhaustive;
|
|
492
|
+
throw new Error("unreachable");
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/** True when a type is a union with an undefined arm — the optional-flavored
|
|
497
|
+
* slot marker shared by the frontend (record literals may omit such fields)
|
|
498
|
+
* and backends (JSON serializers DROP such fields when they hold undefined,
|
|
499
|
+
* dynCheck builders produce the undefined arm for a MISSING key). Note the
|
|
500
|
+
* question is about the TYPE, not a declaration's `?:` token: without
|
|
501
|
+
* exactOptionalPropertyTypes, `{a?: string}` and `{a: string | undefined}`
|
|
502
|
+
* are the same shape and behave identically — which is exactly Node's rule
|
|
503
|
+
* (JSON.stringify drops ANY undefined-valued field, declared optional or
|
|
504
|
+
* not). */
|
|
505
|
+
export function isUndefinedArmedUnion(t, getUnion) {
|
|
506
|
+
if (t.kind !== "union")
|
|
507
|
+
return false;
|
|
508
|
+
const def = getUnion(t.unionId);
|
|
509
|
+
return !!def && def.arms.some((a) => a.kind === "undefinedT");
|
|
510
|
+
}
|
|
511
|
+
/** True when a type is JSON-representable — the shared fence for
|
|
512
|
+
* `jsonStringify` (what can be serialized) and `dynCheck` (what a dyn value
|
|
513
|
+
* can be validated against): f64, string, bool, records, arrays, and unions
|
|
514
|
+
* of those, recursively. Closures, class instances, dyn itself, and void are
|
|
515
|
+
* not JSON. Registry lookups are parameters because the frontend holds
|
|
516
|
+
* registries and the validator/backend hold maps; recursive shapes/unions
|
|
517
|
+
* cannot exist (the frontend rejects them), so the recursion terminates. */
|
|
518
|
+
export function isJsonSafeType(t, getRecord, getUnion) {
|
|
519
|
+
return isJsonSafeAt(t, getRecord, getUnion, false);
|
|
520
|
+
}
|
|
521
|
+
/** The recursion behind isJsonSafeType. `inRecordField` is the ONE position
|
|
522
|
+
* where an undefined arm is JSON-representable: an undefined-armed union as
|
|
523
|
+
* a record field (the `a?: T` spelling) serializes by DROPPING the field
|
|
524
|
+
* when it holds undefined and validates a MISSING key as the undefined arm
|
|
525
|
+
* — both exactly Node. Everywhere else (a bare `T | undefined` value,
|
|
526
|
+
* stringified whole or cast to) exactness is unreachable: Node's stringify
|
|
527
|
+
* of bare undefined is not a string at all, and JSON text can never contain
|
|
528
|
+
* a value that matches the arm. */
|
|
529
|
+
function isJsonSafeAt(t, getRecord, getUnion, inRecordField) {
|
|
530
|
+
switch (t.kind) {
|
|
531
|
+
case "f64":
|
|
532
|
+
case "string":
|
|
533
|
+
case "bool":
|
|
534
|
+
return true;
|
|
535
|
+
case "array":
|
|
536
|
+
return isJsonSafeAt(t.elem, getRecord, getUnion, false);
|
|
537
|
+
case "record": {
|
|
538
|
+
const shape = getRecord(t.shapeId);
|
|
539
|
+
if (!shape)
|
|
540
|
+
return false;
|
|
541
|
+
// TUPLE positions are array slots, not droppable object keys: an
|
|
542
|
+
// undefined-armed position would stringify as `null` in JS (not
|
|
543
|
+
// drop), so tuples keep the array rule for their fields.
|
|
544
|
+
if (!shape.fields.every((f) => isJsonSafeAt(f.type, getRecord, getUnion, !shape.tuple))) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
// Overflow values sit in record-key position too: dyn is JSON-safe
|
|
548
|
+
// HERE (the DOM serializes itself; undefined-valued entries drop
|
|
549
|
+
// like any undefined-valued key), everything else follows the
|
|
550
|
+
// record-field rule.
|
|
551
|
+
if (shape.indexValue && shape.indexValue.kind !== "dyn") {
|
|
552
|
+
return isJsonSafeAt(shape.indexValue, getRecord, getUnion, true);
|
|
553
|
+
}
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
case "union": {
|
|
557
|
+
const def = getUnion(t.unionId);
|
|
558
|
+
return !!def && def.arms.every((a) => isJsonSafeAt(a, getRecord, getUnion, inRecordField));
|
|
559
|
+
}
|
|
560
|
+
case "func":
|
|
561
|
+
case "object":
|
|
562
|
+
// Class values stringify as "{}" husks in Node (own enumerable statics
|
|
563
|
+
// aside — not representable type-directedly); rejected like Maps.
|
|
564
|
+
case "classval":
|
|
565
|
+
// Maps are not JSON (JSON.stringify(new Map()) is "{}" in Node — an
|
|
566
|
+
// empty-object husk nobody wants; stringify/dynCheck reject instead).
|
|
567
|
+
case "map":
|
|
568
|
+
// Sets stringify as the same "{}" husk — rejected like Maps.
|
|
569
|
+
case "set":
|
|
570
|
+
// Regexes are not JSON (JSON.stringify(/a/) is "{}" in Node — the same
|
|
571
|
+
// empty-object husk as Maps; stringify/dynCheck reject instead).
|
|
572
|
+
case "regex":
|
|
573
|
+
// URLs stringify as "{}" husks in Node too (data properties live on
|
|
574
|
+
// internal slots) — rejected the same way; use url.href instead.
|
|
575
|
+
case "url":
|
|
576
|
+
// URLSearchParams stringifies as the same "{}" husk — rejected; use
|
|
577
|
+
// sp.toString() instead.
|
|
578
|
+
case "searchParams":
|
|
579
|
+
// Symbols are DROPPED by Node's stringify (undefined at the top level,
|
|
580
|
+
// omitted as object values) — silent divergence banned; rejected.
|
|
581
|
+
case "symbol":
|
|
582
|
+
// Typed arrays stringify as index-keyed objects ({"0":1,...}) and
|
|
583
|
+
// Buffers as {type:"Buffer",data:[...]} in Node — neither shape is
|
|
584
|
+
// representable type-directedly; rejected like Maps.
|
|
585
|
+
case "bytes":
|
|
586
|
+
case "stats":
|
|
587
|
+
case "spawnRes":
|
|
588
|
+
case "child":
|
|
589
|
+
case "netServer":
|
|
590
|
+
case "netSocket":
|
|
591
|
+
case "http2Session":
|
|
592
|
+
case "http2Stream":
|
|
593
|
+
case "dgramSocket":
|
|
594
|
+
case "testCtx":
|
|
595
|
+
case "httpReq":
|
|
596
|
+
case "httpRes":
|
|
597
|
+
case "httpClientReq":
|
|
598
|
+
case "secureCtx":
|
|
599
|
+
case "fsWatcher":
|
|
600
|
+
case "childStream":
|
|
601
|
+
case "procStream":
|
|
602
|
+
case "dyn":
|
|
603
|
+
case "jsval":
|
|
604
|
+
case "caught":
|
|
605
|
+
case "promise":
|
|
606
|
+
case "generator":
|
|
607
|
+
case "void":
|
|
608
|
+
return false;
|
|
609
|
+
// Representable exactly as a union arm in record-field position (the
|
|
610
|
+
// optional-field story above); bare undefined-armed unions stay fenced
|
|
611
|
+
// out of both stringify and dynCheck (narrow with '!== undefined'
|
|
612
|
+
// first, or model absence with a null arm).
|
|
613
|
+
case "undefinedT":
|
|
614
|
+
return inRecordField;
|
|
615
|
+
// JSON null ↔ the nullT arm: null-armed unions stringify (`null`) and
|
|
616
|
+
// validate (a JSON null matches exactly the nullT arm).
|
|
617
|
+
case "nullT":
|
|
618
|
+
return true;
|
|
619
|
+
default: {
|
|
620
|
+
const _exhaustive = t;
|
|
621
|
+
void _exhaustive;
|
|
622
|
+
throw new Error("unreachable");
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/** THE island boundary predicate: true when a static type can cross into
|
|
627
|
+
* the island (jsMarshal — primitives by value, composites as deep JSON
|
|
628
|
+
* copies) and back out (jsExit — strict primitive extraction, composites
|
|
629
|
+
* through the dynCheck walker). Primitives are JSON-safe, so the rule
|
|
630
|
+
* coincides with isJsonSafeType; it has its own name because the boundary
|
|
631
|
+
* is its own concept — the frontend's implicit coercions, the explicit-cast
|
|
632
|
+
* lowering, and the validator's jsMarshal/jsExit rules all ask this ONE
|
|
633
|
+
* question, and the boundary rejection messages describe exactly this set. */
|
|
634
|
+
export function canCrossIslandBoundary(t, getRecord, getUnion) {
|
|
635
|
+
return isJsonSafeType(t, getRecord, getUnion);
|
|
636
|
+
}
|
|
637
|
+
/** True for a closure type that can cross INTO the island as a host
|
|
638
|
+
* function: every parameter jsval (the engine's arguments pass through as
|
|
639
|
+
* handles — no per-type extraction exists inside a host call) and the
|
|
640
|
+
* result jsval, void, or a primitive (which marshals back by value —
|
|
641
|
+
* `(x) => x * 2` on an 'any' x infers a number return). Contextual typing
|
|
642
|
+
* produces exactly this shape for callbacks passed to package APIs
|
|
643
|
+
* (`.action((a, b) => ...)` against `(...args: any[]) => void`). Arity is
|
|
644
|
+
* capped by the runtime's host-call argument buffer (scr_island.c). */
|
|
645
|
+
export const MAX_ISLAND_CALLBACK_ARITY = 16;
|
|
646
|
+
export function canMarshalFuncIntoIsland(t) {
|
|
647
|
+
return (t.kind === "func" &&
|
|
648
|
+
t.params.length <= MAX_ISLAND_CALLBACK_ARITY &&
|
|
649
|
+
t.params.every((p) => p.kind === "jsval") &&
|
|
650
|
+
(t.ret.kind === "jsval" || t.ret.kind === "void" ||
|
|
651
|
+
t.ret.kind === "f64" || t.ret.kind === "bool" || t.ret.kind === "string"));
|
|
652
|
+
}
|
|
653
|
+
/** Parameter types a TYPED closure may declare when it crosses INTO the
|
|
654
|
+
* island as a host function: jsval params take the engine argument as a
|
|
655
|
+
* handle (the all-'any' shape above); every other admitted type converts
|
|
656
|
+
* AT CALL TIME through the validated-exit machinery — strict primitives,
|
|
657
|
+
* JSON round-trip composites (the dynCheck walker: width-tolerant records,
|
|
658
|
+
* path-annotated failures). On top of the jsExit set, a bare `T | undefined`
|
|
659
|
+
* union is admitted here — an absent or undefined engine argument takes the
|
|
660
|
+
* undefined arm, exactly the missing-optional-field rule (and exactly the
|
|
661
|
+
* commander case: `.action((text: string | undefined, opts) => ...)` sees
|
|
662
|
+
* undefined when the command argument is omitted). */
|
|
663
|
+
export function isIslandCallbackParamType(t, getRecord, getUnion) {
|
|
664
|
+
if (t.kind === "jsval")
|
|
665
|
+
return true;
|
|
666
|
+
if (isJsonSafeType(t, getRecord, getUnion))
|
|
667
|
+
return true;
|
|
668
|
+
if (t.kind === "union") {
|
|
669
|
+
// A bare undefined-armed union: every non-undefined arm must be
|
|
670
|
+
// JSON-safe (arms never nest unions, so plain isJsonSafeType applies).
|
|
671
|
+
const def = getUnion(t.unionId);
|
|
672
|
+
return !!def && def.arms.every((a) => a.kind === "undefinedT" || isJsonSafeType(a, getRecord, getUnion));
|
|
673
|
+
}
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
/** Classify a typed island callback's RETURN for adapter synthesis: sync
|
|
677
|
+
* kinds marshal back by value ('void'|'jsval'|'f64'|'bool'|'string' — the
|
|
678
|
+
* canMarshalFuncIntoIsland set), 'json' marshals a JSON-safe composite
|
|
679
|
+
* through the type-directed serializer + engine parse (the jsMarshal
|
|
680
|
+
* composite path — commander's option-argument collectors return arrays
|
|
681
|
+
* this way), and a Promise of the by-value kinds wraps as an engine
|
|
682
|
+
* promise settled when the scriptc promise settles (async callbacks —
|
|
683
|
+
* the `.action(async ...)` case). Null for anything else (Promise of
|
|
684
|
+
* composites: still fenced). */
|
|
685
|
+
export function islandCallbackRet(t, getRecord, getUnion) {
|
|
686
|
+
const tagOf = (r) => r.kind === "void" || r.kind === "jsval" || r.kind === "f64" || r.kind === "bool" || r.kind === "string"
|
|
687
|
+
? r.kind
|
|
688
|
+
: null;
|
|
689
|
+
if (t.kind === "promise") {
|
|
690
|
+
const tag = tagOf(t.inner);
|
|
691
|
+
return tag ? { async: true, tag } : null;
|
|
692
|
+
}
|
|
693
|
+
const tag = tagOf(t);
|
|
694
|
+
if (tag)
|
|
695
|
+
return { async: false, tag };
|
|
696
|
+
return isJsonSafeType(t, getRecord, getUnion) ? { async: false, tag: "json" } : null;
|
|
697
|
+
}
|
|
698
|
+
/** The TYPED extension of canMarshalFuncIntoIsland (a strict superset):
|
|
699
|
+
* closures whose params are per-argument-convertible at call time
|
|
700
|
+
* (isIslandCallbackParamType) and whose return classifies
|
|
701
|
+
* (islandCallbackRet). Same arity cap — the runtime's host-call argument
|
|
702
|
+
* buffer. Closures taking closures and 'unknown'-typed params stay fenced
|
|
703
|
+
* (no per-type extraction exists for them inside a host call). */
|
|
704
|
+
export function canMarshalTypedFuncIntoIsland(t, getRecord, getUnion) {
|
|
705
|
+
return (t.kind === "func" &&
|
|
706
|
+
t.params.length <= MAX_ISLAND_CALLBACK_ARITY &&
|
|
707
|
+
t.params.every((p) => isIslandCallbackParamType(p, getRecord, getUnion)) &&
|
|
708
|
+
islandCallbackRet(t.ret, getRecord, getUnion) !== null);
|
|
709
|
+
}
|
|
710
|
+
/* ── the checked-dynamic FUNCTION boundary ──────────────────────────────
|
|
711
|
+
* The STATIC twin of the island's typed-function marshaling
|
|
712
|
+
* (canMarshalTypedFuncIntoIsland): closures cross the dyn boundary as a
|
|
713
|
+
* callable DOM kind carrying the closure + a compiled per-signature call
|
|
714
|
+
* thunk. Two directions, two predicates, mutually recursive with the
|
|
715
|
+
* conversion domains (function types cannot ride records/unions — jsonSafe
|
|
716
|
+
* excludes them — so the recursion terminates):
|
|
717
|
+
*
|
|
718
|
+
* IN (canBoxFuncIntoDyn, the dynFrom domain's function arm): calling the
|
|
719
|
+
* box happens with DYN arguments, so every declared param must be
|
|
720
|
+
* dynCheckABLE (the thunk validates each argument into it) and the result
|
|
721
|
+
* must convert BACK to dyn (dynFrom's domain, functions included — a
|
|
722
|
+
* wrapper returning a wrapper boxes recursively).
|
|
723
|
+
*
|
|
724
|
+
* OUT (canAdaptDynFuncTo, the dynCheck domain's function arm): a dyn
|
|
725
|
+
* function landing in a typed func slot either unwraps directly (the boxed
|
|
726
|
+
* signature key equals the target's — same type, same ABI) or wraps in a
|
|
727
|
+
* per-target ADAPTER closure that converts each typed argument INTO dyn
|
|
728
|
+
* (so params must be dyn-convertible) and validates the dyn result into
|
|
729
|
+
* the target's return type (so the return must be dynCheckable).
|
|
730
|
+
*
|
|
731
|
+
* Deliberately fenced OUT of both directions: generic signatures (no
|
|
732
|
+
* concrete IR type exists), this-parameters, construct signatures (`new`
|
|
733
|
+
* through a dyn value), properties ON function values, and params/results
|
|
734
|
+
* outside the conversion domains (Maps, class instances, ...). Promises
|
|
735
|
+
* CONVERT in (canConvertToDyn's promise arm — an async dyn-boxed closure's
|
|
736
|
+
* return) but do not check OUT (`u as Promise<T>` stays fenced).
|
|
737
|
+
*/
|
|
738
|
+
/** The runtime HANDLE kinds that cross the checked-dynamic boundary as
|
|
739
|
+
* the DOM's HANDLE kind (SCR_DYN_HANDLE): boxed by REFERENCE (identity —
|
|
740
|
+
* stateful I/O objects never copy), unboxed by tag check, members
|
|
741
|
+
* dispatched at runtime onto the same entry points the static lowerings
|
|
742
|
+
* use. The set is deliberately the handles whose member surfaces have
|
|
743
|
+
* complete static lowerings (the http/net receiver surface —
|
|
744
|
+
* `server.on('request', mustCall((req, res) => ...))` is the canonical
|
|
745
|
+
* crossing); other handle kinds keep the honest cannot-box fence. Each
|
|
746
|
+
* entry carries the runtime tag spelling and the class display name
|
|
747
|
+
* (dynCheck's "expected IncomingMessage ..." texts). */
|
|
748
|
+
export const DYN_HANDLE_KINDS = new Map([
|
|
749
|
+
["httpReq", { tag: "SCR_DYNH_HTTP_REQ", cls: "IncomingMessage" }],
|
|
750
|
+
["httpRes", { tag: "SCR_DYNH_HTTP_RES", cls: "ServerResponse" }],
|
|
751
|
+
["netSocket", { tag: "SCR_DYNH_NET_SOCKET", cls: "Socket" }],
|
|
752
|
+
["netServer", { tag: "SCR_DYNH_NET_SERVER", cls: "Server" }],
|
|
753
|
+
["http2Session", { tag: "SCR_DYNH_H2_SESSION", cls: "Http2Session" }],
|
|
754
|
+
["http2Stream", { tag: "SCR_DYNH_H2_STREAM", cls: "Http2Stream" }],
|
|
755
|
+
]);
|
|
756
|
+
/** A static type that CONVERTS into a dyn DOM value — the dynFrom domain:
|
|
757
|
+
* JSON-safe data, bytes<u8> (payload copied), undefined-armed unions of
|
|
758
|
+
* JSON-safe arms, boxable function types, and the runtime HANDLE kinds
|
|
759
|
+
* (boxed by reference — DYN_HANDLE_KINDS). */
|
|
760
|
+
export function canConvertToDyn(t, getRecord, getUnion) {
|
|
761
|
+
if (isJsonSafeType(t, getRecord, getUnion))
|
|
762
|
+
return true;
|
|
763
|
+
// bytes<u8> is a DOM kind the walker boxes ANYWHERE (payload copied),
|
|
764
|
+
// including nested in records/arrays/unions — the tls/https options
|
|
765
|
+
// record's cert/key/ca Buffers. isJsonSafeType rejects nested bytes
|
|
766
|
+
// (no JSON-exact round trip), but dynFrom needs only that the walker
|
|
767
|
+
// can build the DOM value, which it can — so canConvertToDyn folds the
|
|
768
|
+
// bytes-bearing composites in beyond the JSON-safe core.
|
|
769
|
+
if (canBoxBytesComposite(t, getRecord, getUnion))
|
|
770
|
+
return true;
|
|
771
|
+
if (t.kind === "bytes" && t.elem === "u8")
|
|
772
|
+
return true;
|
|
773
|
+
// %Error converts as the DOM's error encoding ({%error, name, message,
|
|
774
|
+
// code?} — the caughtToDyn shape, scr_dyn_from_error): the dyn 'error'
|
|
775
|
+
// listener boundary (a mustCall-wrapped handler receiving the payload).
|
|
776
|
+
if (t.kind === "object" && t.className === "%Error")
|
|
777
|
+
return true;
|
|
778
|
+
if (t.kind === "func")
|
|
779
|
+
return canBoxFuncIntoDyn(t, getRecord, getUnion);
|
|
780
|
+
if (DYN_HANDLE_KINDS.has(t.kind))
|
|
781
|
+
return true;
|
|
782
|
+
// Promises box by REFERENCE (SCR_DYN_PROMISE): promise<dyn> carries its
|
|
783
|
+
// ScrPromise directly (the payload is already a DOM value), any other
|
|
784
|
+
// convertible-or-void inner boxes an ADAPTER promise whose emitted
|
|
785
|
+
// settle callback converts the payload (rejections copy raw — reasons
|
|
786
|
+
// are dynamically tagged). The dc tracePromise boundary and dyn-boxed
|
|
787
|
+
// async closures are the crossings.
|
|
788
|
+
if (t.kind === "promise") {
|
|
789
|
+
return (t.inner.kind === "dyn" ||
|
|
790
|
+
t.inner.kind === "void" ||
|
|
791
|
+
canConvertToDyn(t.inner, getRecord, getUnion));
|
|
792
|
+
}
|
|
793
|
+
if (t.kind === "union") {
|
|
794
|
+
const def = getUnion(t.unionId);
|
|
795
|
+
return !!def && def.arms.every((a) => a.kind === "undefinedT" || isJsonSafeType(a, getRecord, getUnion));
|
|
796
|
+
}
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
/** The bytes-bearing extension of the dynFrom domain: JSON-safe scalars
|
|
800
|
+
* plus bytes<u8> anywhere, recursing through records (fields + index
|
|
801
|
+
* value), arrays, and unit-armed unions — exactly the sc_td_* walker's
|
|
802
|
+
* capability for the tls/https options-record shapes. Returns false for
|
|
803
|
+
* a composite carrying any kind the walker cannot box (funcs, Maps,
|
|
804
|
+
* handles nested in a record); those still fence. */
|
|
805
|
+
function canBoxBytesComposite(t, getRecord, getUnion) {
|
|
806
|
+
switch (t.kind) {
|
|
807
|
+
case "f64":
|
|
808
|
+
case "string":
|
|
809
|
+
case "bool":
|
|
810
|
+
case "dyn":
|
|
811
|
+
case "undefinedT":
|
|
812
|
+
case "nullT":
|
|
813
|
+
return true;
|
|
814
|
+
case "bytes":
|
|
815
|
+
return t.elem === "u8";
|
|
816
|
+
case "array":
|
|
817
|
+
return canBoxBytesComposite(t.elem, getRecord, getUnion);
|
|
818
|
+
case "record": {
|
|
819
|
+
const shape = getRecord(t.shapeId);
|
|
820
|
+
if (!shape)
|
|
821
|
+
return false;
|
|
822
|
+
if (!shape.fields.every((f) => canBoxBytesComposite(f.type, getRecord, getUnion)))
|
|
823
|
+
return false;
|
|
824
|
+
return !shape.indexValue || canBoxBytesComposite(shape.indexValue, getRecord, getUnion);
|
|
825
|
+
}
|
|
826
|
+
case "union": {
|
|
827
|
+
const def = getUnion(t.unionId);
|
|
828
|
+
return !!def && def.arms.every((a) => canBoxBytesComposite(a, getRecord, getUnion));
|
|
829
|
+
}
|
|
830
|
+
default:
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
/** A type a dyn value can be VALIDATED into — the dynCheck domain:
|
|
835
|
+
* JSON-safe data, bytes<u8> (a fresh copy out), the %Error extraction,
|
|
836
|
+
* undefined-armed unions of JSON-safe arms, adaptable function types,
|
|
837
|
+
* and the runtime HANDLE kinds (a tag-checked reference unwrap —
|
|
838
|
+
* DYN_HANDLE_KINDS). */
|
|
839
|
+
export function canDynCheckTo(t, getRecord, getUnion) {
|
|
840
|
+
if (isJsonSafeType(t, getRecord, getUnion))
|
|
841
|
+
return true;
|
|
842
|
+
if (t.kind === "bytes" && t.elem === "u8")
|
|
843
|
+
return true;
|
|
844
|
+
if (t.kind === "object" && t.className === "%Error")
|
|
845
|
+
return true;
|
|
846
|
+
if (t.kind === "func")
|
|
847
|
+
return canAdaptDynFuncTo(t, getRecord, getUnion);
|
|
848
|
+
if (DYN_HANDLE_KINDS.has(t.kind))
|
|
849
|
+
return true;
|
|
850
|
+
if (t.kind === "union") {
|
|
851
|
+
const def = getUnion(t.unionId);
|
|
852
|
+
return !!def && def.arms.every((a) => a.kind === "undefinedT" || isJsonSafeType(a, getRecord, getUnion));
|
|
853
|
+
}
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
/** A closure type that can BOX into the DOM's function kind (dynFrom):
|
|
857
|
+
* every param dyn or dynCheckable (the thunk validates dyn arguments into
|
|
858
|
+
* them), return void, dyn, or dyn-convertible (the thunk converts it
|
|
859
|
+
* back). */
|
|
860
|
+
export function canBoxFuncIntoDyn(t, getRecord, getUnion) {
|
|
861
|
+
return (t.kind === "func" &&
|
|
862
|
+
t.params.every((p) => p.kind === "dyn" || canDynCheckTo(p, getRecord, getUnion)) &&
|
|
863
|
+
(t.ret.kind === "void" || t.ret.kind === "dyn" || canConvertToDyn(t.ret, getRecord, getUnion)));
|
|
864
|
+
}
|
|
865
|
+
/** A closure type a dyn function value can ADAPT to (dynCheck): every
|
|
866
|
+
* param dyn or dyn-convertible (the adapter converts typed arguments into
|
|
867
|
+
* dyn), return void, dyn, or dynCheckable (the adapter validates the dyn
|
|
868
|
+
* result). */
|
|
869
|
+
export function canAdaptDynFuncTo(t, getRecord, getUnion) {
|
|
870
|
+
return (t.kind === "func" &&
|
|
871
|
+
// A variadic (rest-marked) target would need the trailing rest-array
|
|
872
|
+
// param synthesized by the adapter — no adapter models that; variadic
|
|
873
|
+
// values live boxed and are called through their own thunks.
|
|
874
|
+
t.rest !== true &&
|
|
875
|
+
t.params.every((p) => p.kind === "dyn" || canConvertToDyn(p, getRecord, getUnion)) &&
|
|
876
|
+
(t.ret.kind === "void" || t.ret.kind === "dyn" || canDynCheckTo(t.ret, getRecord, getUnion)));
|
|
877
|
+
}
|
|
878
|
+
/** The MARSHAL-direction boundary (jsMarshal): everything that can cross
|
|
879
|
+
* out and back (canCrossIslandBoundary) plus qualifying closures — those
|
|
880
|
+
* enter as host functions but never EXIT (jsExit keeps the narrower
|
|
881
|
+
* predicate). */
|
|
882
|
+
export function canMarshalIntoIsland(t, getRecord, getUnion) {
|
|
883
|
+
return isJsonSafeType(t, getRecord, getUnion) || canMarshalFuncIntoIsland(t);
|
|
884
|
+
}
|
|
885
|
+
/** The EXIT-direction boundary (jsExit): everything round-trippable
|
|
886
|
+
* (canCrossIslandBoundary) plus BARE undefined/null-armed unions whose
|
|
887
|
+
* data arms are all JSON-safe — the engine's undefined takes the
|
|
888
|
+
* undefined arm before the JSON detour (JSON cannot spell undefined, and
|
|
889
|
+
* bare undefined-armed unions are JSON-unsafe for exactly that reason),
|
|
890
|
+
* null and data ride the round trip into the union's dynCheck. The
|
|
891
|
+
* package-API shape `result.headers` : `Record<string, string> |
|
|
892
|
+
* undefined` is the motivating case. */
|
|
893
|
+
export function canExitIslandToType(t, getRecord, getUnion) {
|
|
894
|
+
if (canCrossIslandBoundary(t, getRecord, getUnion))
|
|
895
|
+
return true;
|
|
896
|
+
// Uint8Array exits with a validated kind check + copy (engine Buffers
|
|
897
|
+
// pass — they ARE Uint8Arrays); other element widths stay out.
|
|
898
|
+
if (t.kind === "bytes" && t.elem === "u8")
|
|
899
|
+
return true;
|
|
900
|
+
if (t.kind === "union") {
|
|
901
|
+
const def = getUnion(t.unionId);
|
|
902
|
+
return (!!def &&
|
|
903
|
+
def.arms.some((a) => a.kind === "undefinedT") &&
|
|
904
|
+
def.arms.every((a) => isUnitType(a) || isJsonSafeType(a, getRecord, getUnion)));
|
|
905
|
+
}
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
/** True when the module contains any regex construct — a regexLit /
|
|
909
|
+
* regexIntrinsic node or a regex-typed slot anywhere. This is the link
|
|
910
|
+
* switch that pulls scr_regex.c + the vendored libregexp into the binary
|
|
911
|
+
* (cc.ts); regex-free programs keep the historical command line. A generic
|
|
912
|
+
* JSON walk: `kind` discriminants live only on IR objects, so user string
|
|
913
|
+
* VALUES can never false-positive. */
|
|
914
|
+
export function moduleUsesRegex(mod) {
|
|
915
|
+
let found = false;
|
|
916
|
+
const visit = (v) => {
|
|
917
|
+
if (found || v === null || typeof v !== "object")
|
|
918
|
+
return;
|
|
919
|
+
if (Array.isArray(v)) {
|
|
920
|
+
for (const item of v)
|
|
921
|
+
visit(item);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const kind = v.kind;
|
|
925
|
+
if (kind === "regex" || kind === "regexLit" || kind === "regexIntrinsic") {
|
|
926
|
+
found = true;
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
// The lre-backed case conversions live in scr_regex.c (libunicode's
|
|
930
|
+
// case tables): they ride the same link switch as regex nodes.
|
|
931
|
+
if (kind === "strIntrinsic") {
|
|
932
|
+
const method = v.method;
|
|
933
|
+
if (method === "toLowerCase" || method === "toUpperCase") {
|
|
934
|
+
found = true;
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
// RegExp.escape lives in scr_regex.c too (needing no engine — it
|
|
939
|
+
// keeps the always-linked string TU out of hello-world's size class).
|
|
940
|
+
if (kind === "libCall" && v.fn === "regexp.escape") {
|
|
941
|
+
found = true;
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
for (const key of Object.keys(v))
|
|
945
|
+
visit(v[key]);
|
|
946
|
+
};
|
|
947
|
+
visit(mod);
|
|
948
|
+
return found;
|
|
949
|
+
}
|
|
950
|
+
/** True when the embedded npm graph references fetch — the link switch
|
|
951
|
+
* that pulls scr_fetch.c + its socket/tls/zlib dependencies into the binary (cc.ts) and
|
|
952
|
+
* has the emitted main call scr_fetch_install. A word-boundary scan over
|
|
953
|
+
* the embedded SOURCES, erring toward linking: a false positive costs one
|
|
954
|
+
* dylib reference; a false negative would leave embedded code without the
|
|
955
|
+
* global at runtime. Static builds and fetch-free graphs keep their exact
|
|
956
|
+
* historical link lines. */
|
|
957
|
+
export function moduleUsesFetch(mod) {
|
|
958
|
+
const embedded = mod.embedded;
|
|
959
|
+
if (embedded && embedded.modules.some((m) => /\bfetch\b/.test(m.source)))
|
|
960
|
+
return true;
|
|
961
|
+
// USER-code fetch (the island-backed ambient): its lowering reads the
|
|
962
|
+
// engine's fetch global — the same jsOp walk shape as moduleUsesZlib.
|
|
963
|
+
let found = false;
|
|
964
|
+
const visit = (v) => {
|
|
965
|
+
if (found || v === null || typeof v !== "object")
|
|
966
|
+
return;
|
|
967
|
+
if (Array.isArray(v)) {
|
|
968
|
+
for (const item of v)
|
|
969
|
+
visit(item);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const node = v;
|
|
973
|
+
if (node.kind === "jsOp" && node.op === "globalGet" && node.name === "fetch") {
|
|
974
|
+
found = true;
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
for (const key of Object.keys(v))
|
|
978
|
+
visit(v[key]);
|
|
979
|
+
};
|
|
980
|
+
visit(mod);
|
|
981
|
+
return found;
|
|
982
|
+
}
|
|
983
|
+
/** The libCall fns served by the OPTIONAL events unit (scr_events.c):
|
|
984
|
+
* process signal/exit listeners and the piped-stdin surface. */
|
|
985
|
+
const PROCESS_EVENT_LIB_FNS = new Set([
|
|
986
|
+
"process.onSignal",
|
|
987
|
+
"process.offSignal",
|
|
988
|
+
"process.onExit",
|
|
989
|
+
"process.offExit",
|
|
990
|
+
"stdin.onData",
|
|
991
|
+
"stdin.onEnd",
|
|
992
|
+
"stdin.onError",
|
|
993
|
+
"stdin.nextChunk",
|
|
994
|
+
// node:readline rides the stdin unit (scr_readline.c links beside
|
|
995
|
+
// scr_events.c under the same gate).
|
|
996
|
+
"rl.create",
|
|
997
|
+
"rl.question",
|
|
998
|
+
"rl.close",
|
|
999
|
+
"rl.onClose",
|
|
1000
|
+
]);
|
|
1001
|
+
/** True when the module uses the process-events surface — the link switch
|
|
1002
|
+
* that pulls scr_events.c into the binary and has the emitted main call
|
|
1003
|
+
* scr_events_install (cc.ts + emitter; the scr_regex/scr_fetch/scr_zlib
|
|
1004
|
+
* gating precedent). Event-free programs pay zero bytes and keep their
|
|
1005
|
+
* exact link line. Same generic-walk shape as moduleUsesRegex. */
|
|
1006
|
+
export function moduleUsesProcessEvents(mod) {
|
|
1007
|
+
let found = false;
|
|
1008
|
+
const visit = (v) => {
|
|
1009
|
+
if (found || v === null || typeof v !== "object")
|
|
1010
|
+
return;
|
|
1011
|
+
if (Array.isArray(v)) {
|
|
1012
|
+
for (const item of v)
|
|
1013
|
+
visit(item);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const node = v;
|
|
1017
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && PROCESS_EVENT_LIB_FNS.has(node.fn)) {
|
|
1018
|
+
found = true;
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
for (const key of Object.keys(v))
|
|
1022
|
+
visit(v[key]);
|
|
1023
|
+
};
|
|
1024
|
+
visit(mod);
|
|
1025
|
+
return found;
|
|
1026
|
+
}
|
|
1027
|
+
/** True when the module uses the node:events EventEmitter surface — the
|
|
1028
|
+
* link switch that pulls scr_events_emitter.c into the binary (cc.ts; the
|
|
1029
|
+
* scr_events.c gating precedent, but pure data structure: no install, no
|
|
1030
|
+
* loop hooks). Two signals: the `%EventEmitter` class def rides the
|
|
1031
|
+
* module (any emitter-typed value or `extends EventEmitter` subclass
|
|
1032
|
+
* references it, and the emitted RC/trace helpers call scr_emitter_*),
|
|
1033
|
+
* or an emitter.* libCall survived (the defaultMaxListeners statics carry
|
|
1034
|
+
* no emitter-typed value). Emitter-free programs pay zero bytes and keep
|
|
1035
|
+
* their exact link line. */
|
|
1036
|
+
export function moduleUsesEmitter(mod) {
|
|
1037
|
+
if ((mod.classes ?? []).some((c) => c.name === RUNTIME_EMITTER_CLASS))
|
|
1038
|
+
return true;
|
|
1039
|
+
let found = false;
|
|
1040
|
+
const visit = (v) => {
|
|
1041
|
+
if (found || v === null || typeof v !== "object")
|
|
1042
|
+
return;
|
|
1043
|
+
if (Array.isArray(v)) {
|
|
1044
|
+
for (const item of v)
|
|
1045
|
+
visit(item);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
const node = v;
|
|
1049
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("emitter.")) {
|
|
1050
|
+
found = true;
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
for (const key of Object.keys(v))
|
|
1054
|
+
visit(v[key]);
|
|
1055
|
+
};
|
|
1056
|
+
visit(mod);
|
|
1057
|
+
return found;
|
|
1058
|
+
}
|
|
1059
|
+
/** True when the program touches the node:stream surface (scr_stream.c —
|
|
1060
|
+
* the moduleUsesEmitter story: the class defs ride the module whenever a
|
|
1061
|
+
* stream-typed value exists, and every stream libCall names its unit).
|
|
1062
|
+
* Stream programs always use the emitter unit too — the stream class
|
|
1063
|
+
* defs pull `%EventEmitter` through their base chain, so
|
|
1064
|
+
* moduleUsesEmitter answers true whenever this does. Stream-free
|
|
1065
|
+
* programs pay zero bytes and keep their exact link line. */
|
|
1066
|
+
export function moduleUsesStream(mod) {
|
|
1067
|
+
if ((mod.classes ?? []).some((c) => RUNTIME_STREAM_CLASSES.has(c.name)))
|
|
1068
|
+
return true;
|
|
1069
|
+
let found = false;
|
|
1070
|
+
const visit = (v) => {
|
|
1071
|
+
if (found || v === null || typeof v !== "object")
|
|
1072
|
+
return;
|
|
1073
|
+
if (Array.isArray(v)) {
|
|
1074
|
+
for (const item of v)
|
|
1075
|
+
visit(item);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const node = v;
|
|
1079
|
+
if (node.kind === "libCall" &&
|
|
1080
|
+
typeof node.fn === "string" &&
|
|
1081
|
+
(node.fn.startsWith("readable.") || node.fn.startsWith("writable.") ||
|
|
1082
|
+
node.fn.startsWith("duplex.") || node.fn.startsWith("transform.") ||
|
|
1083
|
+
node.fn.startsWith("passthrough.") ||
|
|
1084
|
+
node.fn === "stream.destroy" || node.fn === "stream.destroyErr" ||
|
|
1085
|
+
node.fn === "stream.prop" || node.fn === "stream.errored" ||
|
|
1086
|
+
node.fn.startsWith("stream.set"))) {
|
|
1087
|
+
found = true;
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
for (const key of Object.keys(v))
|
|
1091
|
+
visit(v[key]);
|
|
1092
|
+
};
|
|
1093
|
+
visit(mod);
|
|
1094
|
+
return found;
|
|
1095
|
+
}
|
|
1096
|
+
/** True when the embedded npm graph has an edge into `builtin` — the
|
|
1097
|
+
* island shim needs the corresponding native bridge linked (zlib's is
|
|
1098
|
+
* the first; the emitted main installs it before any island entry). */
|
|
1099
|
+
export function moduleEmbedsBuiltin(mod, builtin) {
|
|
1100
|
+
return mod.embedded !== undefined && mod.embedded.edges.some((e) => e.to === builtin);
|
|
1101
|
+
}
|
|
1102
|
+
/** Embedded module texts at least this long are DEFLATE-compressed into
|
|
1103
|
+
* the emitted C (emit-island.ts; each stays plain when deflate does not
|
|
1104
|
+
* shrink it) and inflated lazily by the island's module loader at first
|
|
1105
|
+
* load. Below it the zlib round trip cannot pay for itself. */
|
|
1106
|
+
export const NPM_COMPRESS_MIN = 1024;
|
|
1107
|
+
/** True when the emitted npm tables will carry compressed module text —
|
|
1108
|
+
* the SAME candidate test emit-island.ts compresses by, so index.ts's
|
|
1109
|
+
* zlib link switch and emitter.ts's inflater installation stay in
|
|
1110
|
+
* lockstep with the emission (a candidate whose deflate happens not to
|
|
1111
|
+
* shrink stays plain; the installed inflater is then just unused). */
|
|
1112
|
+
export function moduleEmbedsCompressedNpm(mod) {
|
|
1113
|
+
return (mod.embedded !== undefined &&
|
|
1114
|
+
mod.embedded.modules.some((m) => m.source.length >= NPM_COMPRESS_MIN || (m.esm ?? "").length >= NPM_COMPRESS_MIN));
|
|
1115
|
+
}
|
|
1116
|
+
/** True when the module contains any zlib libCall (the static lowering)
|
|
1117
|
+
* OR the embedded npm graph imports node:zlib (the island shim) — the
|
|
1118
|
+
* link switch that pulls scr_zlib.c + the system libz into the binary
|
|
1119
|
+
* (cc.ts); zlib-free programs keep their exact link line. Same
|
|
1120
|
+
* generic-walk shape as moduleUsesRegex: `kind`/`fn` discriminants live
|
|
1121
|
+
* only on IR objects. */
|
|
1122
|
+
export function moduleUsesZlib(mod) {
|
|
1123
|
+
if (moduleEmbedsBuiltin(mod, "node:zlib"))
|
|
1124
|
+
return true;
|
|
1125
|
+
let found = false;
|
|
1126
|
+
const visit = (v) => {
|
|
1127
|
+
if (found || v === null || typeof v !== "object")
|
|
1128
|
+
return;
|
|
1129
|
+
if (Array.isArray(v)) {
|
|
1130
|
+
for (const item of v)
|
|
1131
|
+
visit(item);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const node = v;
|
|
1135
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("zlib.")) {
|
|
1136
|
+
found = true;
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
for (const key of Object.keys(v))
|
|
1140
|
+
visit(v[key]);
|
|
1141
|
+
};
|
|
1142
|
+
visit(mod);
|
|
1143
|
+
return found;
|
|
1144
|
+
}
|
|
1145
|
+
/** True when the module contains any dc.* libCall — the link switch that
|
|
1146
|
+
* pulls scr_dc.c (the diagnostics_channel registry and pub/sub) into the
|
|
1147
|
+
* binary (cc.ts). Channel-free binaries keep their exact size class.
|
|
1148
|
+
* Same walk shape as moduleUsesZlib. */
|
|
1149
|
+
export function moduleUsesDc(mod) {
|
|
1150
|
+
let found = false;
|
|
1151
|
+
const visit = (v) => {
|
|
1152
|
+
if (found || v === null || typeof v !== "object")
|
|
1153
|
+
return;
|
|
1154
|
+
if (Array.isArray(v)) {
|
|
1155
|
+
for (const item of v)
|
|
1156
|
+
visit(item);
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
const node = v;
|
|
1160
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("dc.")) {
|
|
1161
|
+
found = true;
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
for (const key of Object.keys(v))
|
|
1165
|
+
visit(v[key]);
|
|
1166
|
+
};
|
|
1167
|
+
visit(mod);
|
|
1168
|
+
return found;
|
|
1169
|
+
}
|
|
1170
|
+
/** True when the module contains any assert libCall — the link switch
|
|
1171
|
+
* that pulls scr_assert.c into the binary (cc.ts). scr_regex.c calls the
|
|
1172
|
+
* assert throw/inspect helpers (assert.match lives there), so the regex
|
|
1173
|
+
* switch also pulls scr_assert.c; assert-free, regex-free binaries keep
|
|
1174
|
+
* the historical command line and size. Same walk shape as
|
|
1175
|
+
* moduleUsesZlib. */
|
|
1176
|
+
export function moduleUsesAssert(mod) {
|
|
1177
|
+
let found = false;
|
|
1178
|
+
const visit = (v) => {
|
|
1179
|
+
if (found || v === null || typeof v !== "object")
|
|
1180
|
+
return;
|
|
1181
|
+
if (Array.isArray(v)) {
|
|
1182
|
+
for (const item of v)
|
|
1183
|
+
visit(item);
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
const node = v;
|
|
1187
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("assert.")) {
|
|
1188
|
+
found = true;
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
for (const key of Object.keys(v))
|
|
1192
|
+
visit(v[key]);
|
|
1193
|
+
};
|
|
1194
|
+
visit(mod);
|
|
1195
|
+
return found;
|
|
1196
|
+
}
|
|
1197
|
+
/** True when the module contains any dynInvoke node or dyn.defineProps
|
|
1198
|
+
* libCall — the link switch that pulls scr_dyn_invoke.c (the prototype-
|
|
1199
|
+
* method dispatch on dyn receivers, plus scr_dyn_display and
|
|
1200
|
+
* scr_dyn_define_props) into the binary (cc.ts; the assert gating
|
|
1201
|
+
* precedent — dispatch-free binaries keep their exact size class). Same
|
|
1202
|
+
* walk shape as moduleUsesZlib. */
|
|
1203
|
+
export function moduleUsesDynInvoke(mod) {
|
|
1204
|
+
let found = false;
|
|
1205
|
+
const visit = (v) => {
|
|
1206
|
+
if (found || v === null || typeof v !== "object")
|
|
1207
|
+
return;
|
|
1208
|
+
if (Array.isArray(v)) {
|
|
1209
|
+
for (const item of v)
|
|
1210
|
+
visit(item);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
const node = v;
|
|
1214
|
+
if (node.kind === "dynInvoke" || (node.kind === "libCall" && node.fn === "dyn.defineProps")) {
|
|
1215
|
+
found = true;
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
for (const key of Object.keys(v))
|
|
1219
|
+
visit(v[key]);
|
|
1220
|
+
};
|
|
1221
|
+
visit(mod);
|
|
1222
|
+
return found;
|
|
1223
|
+
}
|
|
1224
|
+
/** True when the module contains any insp libCall — the link switch that
|
|
1225
|
+
* pulls scr_inspect.c into the binary (cc.ts; the assert gating
|
|
1226
|
+
* precedent — inspect-free binaries keep the historical command line and
|
|
1227
|
+
* size class). Same walk shape as moduleUsesZlib. */
|
|
1228
|
+
/** True when the module needs scr_async_dyn.c — the checked-dynamic
|
|
1229
|
+
* async surfaces (DOM-promise then/catch/finally reactions, await of a
|
|
1230
|
+
* dyn value, `new Promise(setImmediate)`, AsyncLocalStorage, the
|
|
1231
|
+
* unhandledRejection/warning process events). Also pulled by the
|
|
1232
|
+
* dynInvoke and dc gates (their TUs call into this one) — cc.ts. Same
|
|
1233
|
+
* walk shape as moduleUsesZlib. */
|
|
1234
|
+
export function moduleUsesDynAsync(mod) {
|
|
1235
|
+
const fns = new Set([
|
|
1236
|
+
"async.awaitDyn", "timers.immediatePromise",
|
|
1237
|
+
"process.onUnhandledRejection", "process.onWarning", "process.offWarning", "process.emitWarning",
|
|
1238
|
+
"als.new", "als.get", "als.run", "als.exitRun", "als.enterWith", "als.disable",
|
|
1239
|
+
"dc.chanBindStore", "dc.chanUnbindStore", "dc.chanRunStores",
|
|
1240
|
+
]);
|
|
1241
|
+
let found = false;
|
|
1242
|
+
const visit = (v) => {
|
|
1243
|
+
if (found || v === null || typeof v !== "object")
|
|
1244
|
+
return;
|
|
1245
|
+
if (Array.isArray(v)) {
|
|
1246
|
+
for (const item of v)
|
|
1247
|
+
visit(item);
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
const node = v;
|
|
1251
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && fns.has(node.fn)) {
|
|
1252
|
+
found = true;
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
// A DYN-typed await reads through scr_await_dyn (the DOM-crossing
|
|
1256
|
+
// await lives in the gated TU) — promise<dyn> receivers' awaits and
|
|
1257
|
+
// the lifted then/catch helpers alike.
|
|
1258
|
+
if (node.kind === "awaitExpr" && node.type !== undefined && node.type.kind === "dyn") {
|
|
1259
|
+
found = true;
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
for (const key of Object.keys(v))
|
|
1263
|
+
visit(v[key]);
|
|
1264
|
+
};
|
|
1265
|
+
visit(mod);
|
|
1266
|
+
return found;
|
|
1267
|
+
}
|
|
1268
|
+
export function moduleUsesInspect(mod) {
|
|
1269
|
+
let found = false;
|
|
1270
|
+
const visit = (v) => {
|
|
1271
|
+
if (found || v === null || typeof v !== "object")
|
|
1272
|
+
return;
|
|
1273
|
+
if (Array.isArray(v)) {
|
|
1274
|
+
for (const item of v)
|
|
1275
|
+
visit(item);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const node = v;
|
|
1279
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("insp.")) {
|
|
1280
|
+
found = true;
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
for (const key of Object.keys(v))
|
|
1284
|
+
visit(v[key]);
|
|
1285
|
+
};
|
|
1286
|
+
visit(mod);
|
|
1287
|
+
return found;
|
|
1288
|
+
}
|
|
1289
|
+
/** True when the module contains any net libCall — the link switch that
|
|
1290
|
+
* pulls scr_net.c into the binary and has the emitted main call
|
|
1291
|
+
* scr_net_install (cc.ts + emitter; the scr_events gating precedent).
|
|
1292
|
+
* Net-free programs pay zero bytes and keep their exact link line. Same
|
|
1293
|
+
* generic-walk shape as moduleUsesZlib. */
|
|
1294
|
+
export function moduleUsesNet(mod) {
|
|
1295
|
+
let found = false;
|
|
1296
|
+
const visit = (v) => {
|
|
1297
|
+
if (found || v === null || typeof v !== "object")
|
|
1298
|
+
return;
|
|
1299
|
+
if (Array.isArray(v)) {
|
|
1300
|
+
for (const item of v)
|
|
1301
|
+
visit(item);
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
const node = v;
|
|
1305
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1306
|
+
(node.fn.startsWith("net.") || node.fn.startsWith("http.") ||
|
|
1307
|
+
node.fn.startsWith("tls.") || node.fn.startsWith("https.") ||
|
|
1308
|
+
node.fn.startsWith("http2."))) {
|
|
1309
|
+
// http/tls/https/http2 all ride ON net: their libCalls need scr_net.c
|
|
1310
|
+
// linked and installed too, so the net switch answers for every
|
|
1311
|
+
// server-family prefix.
|
|
1312
|
+
found = true;
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
// A net-family HANDLE TYPE anywhere in the IR (a global or local whose
|
|
1316
|
+
// initializing statement compiled to a runtime fence still carries the
|
|
1317
|
+
// type): its emitted release call needs the unit linked even when no
|
|
1318
|
+
// libCall survived the fencing.
|
|
1319
|
+
if (node.kind === "netServer" || node.kind === "netSocket" ||
|
|
1320
|
+
node.kind === "http2Session" || node.kind === "http2Stream" ||
|
|
1321
|
+
node.kind === "httpReq" || node.kind === "httpRes" ||
|
|
1322
|
+
node.kind === "httpClientReq" || node.kind === "secureCtx") {
|
|
1323
|
+
found = true;
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
for (const key of Object.keys(v))
|
|
1327
|
+
visit(v[key]);
|
|
1328
|
+
};
|
|
1329
|
+
visit(mod);
|
|
1330
|
+
return found;
|
|
1331
|
+
}
|
|
1332
|
+
/** True when the module contains any sym.* libCall or a symbol-kind type
|
|
1333
|
+
* anywhere in the IR — the link switch that pulls scr_symbol.c into the
|
|
1334
|
+
* binary (cc.ts; the scr_net gating precedent — no install call, the
|
|
1335
|
+
* Symbol.for registry initializes lazily). The TYPE check matters like
|
|
1336
|
+
* net's: a symbol-typed local whose initializer compiled to a runtime
|
|
1337
|
+
* fence still emits release calls that need the unit linked. Symbol-free
|
|
1338
|
+
* programs pay zero bytes and keep their exact link line. */
|
|
1339
|
+
export function moduleUsesSymbol(mod) {
|
|
1340
|
+
let found = false;
|
|
1341
|
+
const visit = (v) => {
|
|
1342
|
+
if (found || v === null || typeof v !== "object")
|
|
1343
|
+
return;
|
|
1344
|
+
if (Array.isArray(v)) {
|
|
1345
|
+
for (const item of v)
|
|
1346
|
+
visit(item);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const node = v;
|
|
1350
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("sym.")) {
|
|
1351
|
+
found = true;
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
if (node.kind === "symbol") {
|
|
1355
|
+
found = true;
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
for (const key of Object.keys(v))
|
|
1359
|
+
visit(v[key]);
|
|
1360
|
+
};
|
|
1361
|
+
visit(mod);
|
|
1362
|
+
return found;
|
|
1363
|
+
}
|
|
1364
|
+
/** True when the module uses the URLSearchParams surface — sp.* libCalls,
|
|
1365
|
+
* the url.searchParams getter, or a searchParams-kind type anywhere on
|
|
1366
|
+
* the IR (a fenced statement can leave a typed local whose release call
|
|
1367
|
+
* still needs the unit linked) — the link switch that pulls
|
|
1368
|
+
* scr_url_params.c into the binary (the moduleUsesSymbol precedent: pure
|
|
1369
|
+
* data structure, no loop hooks, cross-compiles everywhere). sp-free
|
|
1370
|
+
* programs keep their exact link line; scr_url.c itself stays
|
|
1371
|
+
* always-linked and never references the unit. */
|
|
1372
|
+
export function moduleUsesSearchParams(mod) {
|
|
1373
|
+
let found = false;
|
|
1374
|
+
const visit = (v) => {
|
|
1375
|
+
if (found || v === null || typeof v !== "object")
|
|
1376
|
+
return;
|
|
1377
|
+
if (Array.isArray(v)) {
|
|
1378
|
+
for (const item of v)
|
|
1379
|
+
visit(item);
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
const node = v;
|
|
1383
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1384
|
+
(node.fn.startsWith("sp.") || node.fn === "url.searchParams")) {
|
|
1385
|
+
found = true;
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
if (node.kind === "searchParams") {
|
|
1389
|
+
found = true;
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
for (const key of Object.keys(v))
|
|
1393
|
+
visit(v[key]);
|
|
1394
|
+
};
|
|
1395
|
+
visit(mod);
|
|
1396
|
+
return found;
|
|
1397
|
+
}
|
|
1398
|
+
/** True when the module contains any fs.watch/watcher.* libCall — the
|
|
1399
|
+
* link switch that pulls scr_watch.c into the binary and has the emitted
|
|
1400
|
+
* main call scr_watch_install (cc.ts + emitter; the scr_net gating
|
|
1401
|
+
* precedent). Watch-free programs pay zero bytes and keep their exact
|
|
1402
|
+
* link line. Same generic-walk shape as moduleUsesZlib. */
|
|
1403
|
+
export function moduleUsesFsWatch(mod) {
|
|
1404
|
+
let found = false;
|
|
1405
|
+
const visit = (v) => {
|
|
1406
|
+
if (found || v === null || typeof v !== "object")
|
|
1407
|
+
return;
|
|
1408
|
+
if (Array.isArray(v)) {
|
|
1409
|
+
for (const item of v)
|
|
1410
|
+
visit(item);
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
const node = v;
|
|
1414
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1415
|
+
(node.fn.startsWith("fs.watch") || node.fn.startsWith("watcher."))) {
|
|
1416
|
+
found = true;
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
// A watcher HANDLE TYPE left behind by a fenced statement still emits
|
|
1420
|
+
// scr_watcher_release — the unit must link.
|
|
1421
|
+
if (node.kind === "fsWatcher") {
|
|
1422
|
+
found = true;
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
for (const key of Object.keys(v))
|
|
1426
|
+
visit(v[key]);
|
|
1427
|
+
};
|
|
1428
|
+
visit(mod);
|
|
1429
|
+
return found;
|
|
1430
|
+
}
|
|
1431
|
+
/** True when the module contains any test.* libCall or a testCtx handle
|
|
1432
|
+
* type — the link switch that pulls scr_test.c into the binary and has
|
|
1433
|
+
* the emitted main return scr_test_exit_code() after the loop drains
|
|
1434
|
+
* (cc.ts + emitter; the moduleUsesDgram shape). Test-free programs pay
|
|
1435
|
+
* zero bytes and keep their exact link line. */
|
|
1436
|
+
export function moduleUsesNodeTest(mod) {
|
|
1437
|
+
let found = false;
|
|
1438
|
+
const visit = (v) => {
|
|
1439
|
+
if (found || v === null || typeof v !== "object")
|
|
1440
|
+
return;
|
|
1441
|
+
if (Array.isArray(v)) {
|
|
1442
|
+
for (const item of v)
|
|
1443
|
+
visit(item);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
const node = v;
|
|
1447
|
+
if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("test.")) {
|
|
1448
|
+
found = true;
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
// A TestContext HANDLE TYPE left behind by a fenced statement still
|
|
1452
|
+
// emits a release call — the unit must link (the dgram type story).
|
|
1453
|
+
if (node.kind === "testCtx") {
|
|
1454
|
+
found = true;
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
for (const key of Object.keys(v))
|
|
1458
|
+
visit(v[key]);
|
|
1459
|
+
};
|
|
1460
|
+
visit(mod);
|
|
1461
|
+
return found;
|
|
1462
|
+
}
|
|
1463
|
+
/** True when the module contains any dgram.* or dns.* libCall — the
|
|
1464
|
+
* link switch that pulls scr_dgram.c into the binary and has the emitted
|
|
1465
|
+
* main call scr_dgram_install (cc.ts + emitter; the scr_net gating
|
|
1466
|
+
* precedent — dns.lookup lives in the same unit, so either prefix
|
|
1467
|
+
* answers). Dgram-free programs pay zero bytes and keep their exact link
|
|
1468
|
+
* line. Same generic-walk shape as moduleUsesZlib. */
|
|
1469
|
+
export function moduleUsesDgram(mod) {
|
|
1470
|
+
let found = false;
|
|
1471
|
+
const visit = (v) => {
|
|
1472
|
+
if (found || v === null || typeof v !== "object")
|
|
1473
|
+
return;
|
|
1474
|
+
if (Array.isArray(v)) {
|
|
1475
|
+
for (const item of v)
|
|
1476
|
+
visit(item);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const node = v;
|
|
1480
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1481
|
+
(node.fn.startsWith("dgram.") || node.fn.startsWith("dns."))) {
|
|
1482
|
+
found = true;
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
// A dgram HANDLE TYPE left behind by a fenced statement still emits a
|
|
1486
|
+
// release call — the unit must link (the moduleUsesNet type story).
|
|
1487
|
+
if (node.kind === "dgramSocket") {
|
|
1488
|
+
found = true;
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
for (const key of Object.keys(v))
|
|
1492
|
+
visit(v[key]);
|
|
1493
|
+
};
|
|
1494
|
+
visit(mod);
|
|
1495
|
+
return found;
|
|
1496
|
+
}
|
|
1497
|
+
/** True when the module contains any http.* libCall — the link switch
|
|
1498
|
+
* that pulls scr_http.c into the binary (cc.ts; moduleUsesNet already
|
|
1499
|
+
* answers true for these, so scr_net.c comes along). */
|
|
1500
|
+
export function moduleUsesHttpServer(mod) {
|
|
1501
|
+
let found = false;
|
|
1502
|
+
const visit = (v) => {
|
|
1503
|
+
if (found || v === null || typeof v !== "object")
|
|
1504
|
+
return;
|
|
1505
|
+
if (Array.isArray(v)) {
|
|
1506
|
+
for (const item of v)
|
|
1507
|
+
visit(item);
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
const node = v;
|
|
1511
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1512
|
+
(node.fn.startsWith("http.") || node.fn.startsWith("tls.") || node.fn.startsWith("https.") ||
|
|
1513
|
+
node.fn.startsWith("http2."))) {
|
|
1514
|
+
// scr_tls.c calls into scr_http.c unconditionally (the https server
|
|
1515
|
+
// and client are the http ones over a transport), so any tls/https
|
|
1516
|
+
// use pulls the http unit too.
|
|
1517
|
+
found = true;
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
// An http-family HANDLE TYPE left behind by a fenced statement still
|
|
1521
|
+
// emits its release call — the unit must link (the moduleUsesNet type
|
|
1522
|
+
// story; secureCtx rides here because scr_tls.c calls into scr_http.c).
|
|
1523
|
+
if (node.kind === "httpReq" || node.kind === "httpRes" ||
|
|
1524
|
+
node.kind === "httpClientReq" || node.kind === "secureCtx") {
|
|
1525
|
+
found = true;
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
for (const key of Object.keys(v))
|
|
1529
|
+
visit(v[key]);
|
|
1530
|
+
};
|
|
1531
|
+
visit(mod);
|
|
1532
|
+
return found;
|
|
1533
|
+
}
|
|
1534
|
+
/** The legacy http2.* libCalls implemented by scr_http.c/scr_tls.c (the
|
|
1535
|
+
* allowHTTP1 compatibility slice) — they must NOT pull scr_http2.c, so
|
|
1536
|
+
* divergence-57 binaries keep their exact link line. */
|
|
1537
|
+
const HTTP2_LEGACY_FNS = new Set([
|
|
1538
|
+
"http2.createSecureServer", "http2.createSecureServerSni",
|
|
1539
|
+
"http2.serverOnSessionError", "http2.streamNoop", "http2.streamUndefCall",
|
|
1540
|
+
]);
|
|
1541
|
+
/** True when the module uses the REAL h2 surface (scr_http2.c): any core
|
|
1542
|
+
* http2.* libCall, or an h2 handle type left behind by a fenced statement
|
|
1543
|
+
* (its emitted release call needs the unit — the moduleUsesNet story). */
|
|
1544
|
+
export function moduleUsesHttp2(mod) {
|
|
1545
|
+
let found = false;
|
|
1546
|
+
const visit = (v) => {
|
|
1547
|
+
if (found || v === null || typeof v !== "object")
|
|
1548
|
+
return;
|
|
1549
|
+
if (Array.isArray(v)) {
|
|
1550
|
+
for (const item of v)
|
|
1551
|
+
visit(item);
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
const node = v;
|
|
1555
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1556
|
+
node.fn.startsWith("http2.") && !HTTP2_LEGACY_FNS.has(node.fn)) {
|
|
1557
|
+
found = true;
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (node.kind === "http2Session" || node.kind === "http2Stream") {
|
|
1561
|
+
found = true;
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
for (const key of Object.keys(v))
|
|
1565
|
+
visit(v[key]);
|
|
1566
|
+
};
|
|
1567
|
+
visit(mod);
|
|
1568
|
+
return found;
|
|
1569
|
+
}
|
|
1570
|
+
/** True when the module contains any tls.* or https.* libCall — the link
|
|
1571
|
+
* switch that pulls scr_tls.c and the vendored mbedTLS archive into the
|
|
1572
|
+
* binary (cc.ts; moduleUsesNet and moduleUsesHttpServer already answer
|
|
1573
|
+
* true for these, so scr_net.c and scr_http.c come along). TLS-free
|
|
1574
|
+
* programs keep their exact link line and never build mbedTLS. */
|
|
1575
|
+
export function moduleUsesTls(mod) {
|
|
1576
|
+
let found = false;
|
|
1577
|
+
const visit = (v) => {
|
|
1578
|
+
if (found || v === null || typeof v !== "object")
|
|
1579
|
+
return;
|
|
1580
|
+
if (Array.isArray(v)) {
|
|
1581
|
+
for (const item of v)
|
|
1582
|
+
visit(item);
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
const node = v;
|
|
1586
|
+
if (node.kind === "libCall" && typeof node.fn === "string" &&
|
|
1587
|
+
(node.fn.startsWith("tls.") || node.fn.startsWith("https.") ||
|
|
1588
|
+
node.fn.startsWith("http2."))) {
|
|
1589
|
+
found = true;
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
// A secureCtx HANDLE TYPE left behind by a fenced statement still
|
|
1593
|
+
// emits scr_secure_ctx_release — the unit must link.
|
|
1594
|
+
if (node.kind === "secureCtx") {
|
|
1595
|
+
found = true;
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
for (const key of Object.keys(v))
|
|
1599
|
+
visit(v[key]);
|
|
1600
|
+
};
|
|
1601
|
+
visit(mod);
|
|
1602
|
+
return found;
|
|
1603
|
+
}
|
|
1604
|
+
/** The may-throw seed: libCall members that can raise. Every fs.* member
|
|
1605
|
+
* EXCEPT existsSync (which, like Node's, swallows errors and returns false)
|
|
1606
|
+
* throws a catchable error on failure; json.parse throws a catchable
|
|
1607
|
+
* SyntaxError-shaped string on malformed input; process.* members never
|
|
1608
|
+
* throw. Backends' may-throw analyses must treat a function containing one
|
|
1609
|
+
* of these as throwing, exactly like a `throw` statement (and must ALSO
|
|
1610
|
+
* seed on `dynCheck` and `awaitExpr` nodes, which throw on validation
|
|
1611
|
+
* failure / promise rejection). */
|
|
1612
|
+
export const MAY_THROW_LIB_FNS = new Set([
|
|
1613
|
+
"insp.jsonDyn",
|
|
1614
|
+
// diagnostics_channel: publish runs subscribers synchronously (a throw
|
|
1615
|
+
// propagates — the documented divergence from triggerUncaughtException);
|
|
1616
|
+
// the subscribe/unsubscribe forms throw Node's ERR_INVALID_ARG_TYPE for
|
|
1617
|
+
// non-function subscribers.
|
|
1618
|
+
"dc.publish",
|
|
1619
|
+
"dc.subscribe",
|
|
1620
|
+
"dc.unsubscribe",
|
|
1621
|
+
"dc.chanSubscribe",
|
|
1622
|
+
"dc.chanUnsubscribe",
|
|
1623
|
+
"dc.tcSubscribe",
|
|
1624
|
+
"dc.tcUnsubscribe",
|
|
1625
|
+
"dc.tcTraceSync",
|
|
1626
|
+
"dc.tcTraceCallback",
|
|
1627
|
+
"dc.tcTracePromise",
|
|
1628
|
+
"process.onUnhandledRejection",
|
|
1629
|
+
"async.awaitDyn",
|
|
1630
|
+
"als.run",
|
|
1631
|
+
"als.exitRun",
|
|
1632
|
+
"dc.chanBindStore",
|
|
1633
|
+
"dc.chanRunStores",
|
|
1634
|
+
"http.resWriteHeadDyn",
|
|
1635
|
+
"net.sockSetEncoding",
|
|
1636
|
+
"http.reqSetEncoding",
|
|
1637
|
+
"fs.readFileSyncBuf",
|
|
1638
|
+
"fs.readFileSyncDyn",
|
|
1639
|
+
// tls/https runtime options records: the walks throw the catchable
|
|
1640
|
+
// runtime fence for out-of-bounds members and non-PEM values (the
|
|
1641
|
+
// divergence-66 stance).
|
|
1642
|
+
"tls.pemDyn",
|
|
1643
|
+
"tls.createServerDyn",
|
|
1644
|
+
"tls.createServerDynCb",
|
|
1645
|
+
"https.createServerDyn",
|
|
1646
|
+
"https.createServerDynCb",
|
|
1647
|
+
"tls.connect",
|
|
1648
|
+
"tls.connectCb",
|
|
1649
|
+
// EventEmitter dispatch runs user listeners synchronously (emit), and
|
|
1650
|
+
// the mutation forms fire the newListener/removeListener meta events —
|
|
1651
|
+
// any of them can throw. emitError additionally THROWS its payload when
|
|
1652
|
+
// 'error' has no listener, Node's unhandled-'error' contract.
|
|
1653
|
+
"emitter.emit",
|
|
1654
|
+
"emitter.emitError",
|
|
1655
|
+
"emitter.on",
|
|
1656
|
+
"emitter.off",
|
|
1657
|
+
"emitter.checkListener",
|
|
1658
|
+
"emitter.onDyn",
|
|
1659
|
+
"emitter.offDyn",
|
|
1660
|
+
"emitter.onData",
|
|
1661
|
+
"emitter.onDataDyn",
|
|
1662
|
+
"emitter.emitData",
|
|
1663
|
+
"readable.nextChunk",
|
|
1664
|
+
"readable.nextChunkDyn",
|
|
1665
|
+
// The checked-dynamic chunk forms throw Node's ERR_INVALID_ARG_TYPE
|
|
1666
|
+
// chunk TypeError on a non-string/Buffer DOM kind.
|
|
1667
|
+
"net.sockWriteDyn",
|
|
1668
|
+
"net.sockEndDyn",
|
|
1669
|
+
"http.resWriteDyn",
|
|
1670
|
+
"http.resEndDyn",
|
|
1671
|
+
"http.clientWriteDyn",
|
|
1672
|
+
"http.clientEndDyn",
|
|
1673
|
+
// The URL-string client form throws catchably on an unparsable input
|
|
1674
|
+
// ("Invalid URL") or a non-http scheme (ERR_INVALID_PROTOCOL).
|
|
1675
|
+
"http.requestUrl",
|
|
1676
|
+
"http.requestUrlCb",
|
|
1677
|
+
// The h2 client's throwing entries: connect on a bad/non-http
|
|
1678
|
+
// authority (ERR_INVALID_URL / ERR_INVALID_PROTOCOL), request on a
|
|
1679
|
+
// closed/destroyed session (ERR_HTTP2_INVALID_SESSION), setEncoding on
|
|
1680
|
+
// an unknown name (ERR_UNKNOWN_ENCODING). Event fires run user code
|
|
1681
|
+
// synchronously (the stream-operations stance below), so the data/
|
|
1682
|
+
// write paths that can fire inline join too.
|
|
1683
|
+
"http2.connect",
|
|
1684
|
+
"http2.connectCb",
|
|
1685
|
+
"http2.sessionRequest",
|
|
1686
|
+
"http2.streamSetEncoding",
|
|
1687
|
+
"http2.streamRespond",
|
|
1688
|
+
"http2.streamWrite",
|
|
1689
|
+
"http2.streamWriteBytes",
|
|
1690
|
+
"http2.streamEnd",
|
|
1691
|
+
"http2.streamEndStr",
|
|
1692
|
+
"http2.streamEndBytes",
|
|
1693
|
+
"emitter.removeAll",
|
|
1694
|
+
// Stream operations run user code synchronously (option callbacks and
|
|
1695
|
+
// 'data'/'drain'/... listeners on the caller's stack, like emit) — any
|
|
1696
|
+
// of them may leave a listener's throw pending. destroy with no 'error'
|
|
1697
|
+
// listener throws the payload itself (the emitter.emitError contract).
|
|
1698
|
+
// The dyn-options constructors walk the record at runtime: a consumed-
|
|
1699
|
+
// but-unlowered option (objectMode, construct, ...) throws the compile
|
|
1700
|
+
// fence's runtime twin; a bad encoding throws ERR_UNKNOWN_ENCODING.
|
|
1701
|
+
"stream.finished",
|
|
1702
|
+
"stream.finishedDyn",
|
|
1703
|
+
"stream.pipeline",
|
|
1704
|
+
"stream.pipelineDyn",
|
|
1705
|
+
"readable.newDyn",
|
|
1706
|
+
"writable.newDyn",
|
|
1707
|
+
"duplex.newDyn",
|
|
1708
|
+
"transform.newDyn",
|
|
1709
|
+
"passthrough.newDyn",
|
|
1710
|
+
"readable.initDyn",
|
|
1711
|
+
"writable.initDyn",
|
|
1712
|
+
"duplex.initDyn",
|
|
1713
|
+
"transform.initDyn",
|
|
1714
|
+
"passthrough.initDyn",
|
|
1715
|
+
"readable.push",
|
|
1716
|
+
"readable.pushStr",
|
|
1717
|
+
"readable.pushNull",
|
|
1718
|
+
"readable.pushU",
|
|
1719
|
+
"readable.pushDyn",
|
|
1720
|
+
"readable.unshift",
|
|
1721
|
+
"readable.unshiftStr",
|
|
1722
|
+
"readable.read",
|
|
1723
|
+
"readable.resume",
|
|
1724
|
+
"readable.pipe",
|
|
1725
|
+
"readable.unpipe",
|
|
1726
|
+
"writable.write",
|
|
1727
|
+
"writable.writeStr",
|
|
1728
|
+
"writable.writeU",
|
|
1729
|
+
"writable.writeDyn",
|
|
1730
|
+
"writable.end",
|
|
1731
|
+
"writable.uncork",
|
|
1732
|
+
"stream.destroy",
|
|
1733
|
+
"stream.destroyErr",
|
|
1734
|
+
// setMaxListeners(n) throws Node's ERR_OUT_OF_RANGE RangeError for
|
|
1735
|
+
// negative/NaN arguments; the static form's default-max write validates
|
|
1736
|
+
// identically (Node's validateNumber(n, "setMaxListeners", 0)).
|
|
1737
|
+
"emitter.setMax",
|
|
1738
|
+
"emitter.setDefaultMax",
|
|
1739
|
+
// The createConnection request forms run the caller's dialer closure
|
|
1740
|
+
// synchronously — a throw there propagates like Node's.
|
|
1741
|
+
"http.requestConn",
|
|
1742
|
+
"http.requestConnCb",
|
|
1743
|
+
"process.kill",
|
|
1744
|
+
"process.killNum",
|
|
1745
|
+
// cpuUsage(prev)'s field validation: negative/non-finite prev fields
|
|
1746
|
+
// throw Node's ERR_INVALID_ARG_VALUE RangeError, catchably.
|
|
1747
|
+
"process.cpuPrevValidate",
|
|
1748
|
+
// Non-TTY stdin: Node's stdin has no setRawMode member, so the call is
|
|
1749
|
+
// the exact catchable TypeError.
|
|
1750
|
+
"process.stdinSetRawMode",
|
|
1751
|
+
// The unguarded h2-only stream call: always throws Node's TypeError.
|
|
1752
|
+
"http2.streamUndefCall",
|
|
1753
|
+
// A read of a declare-d const nothing defines: always throws Node's
|
|
1754
|
+
// catchable ReferenceError.
|
|
1755
|
+
"global.undefRead",
|
|
1756
|
+
"child.kill",
|
|
1757
|
+
// The caller's lookup runs synchronously inside the connect call — a
|
|
1758
|
+
// throw there propagates like Node's.
|
|
1759
|
+
"net.connectLookup",
|
|
1760
|
+
"cp.execSync",
|
|
1761
|
+
"cp.execCapture",
|
|
1762
|
+
// The client request constructors validate the method token
|
|
1763
|
+
// synchronously (Node's ClientRequest ctor throw:
|
|
1764
|
+
// ERR_INVALID_HTTP_TOKEN).
|
|
1765
|
+
"http.request",
|
|
1766
|
+
"http.requestCb",
|
|
1767
|
+
"http.requestConn",
|
|
1768
|
+
"http.requestConnCb",
|
|
1769
|
+
"https.request",
|
|
1770
|
+
"https.requestCb",
|
|
1771
|
+
"https.requestFn",
|
|
1772
|
+
"https.requestFnCb",
|
|
1773
|
+
"rl.question",
|
|
1774
|
+
"island.eval",
|
|
1775
|
+
"island.import",
|
|
1776
|
+
"island.castFail",
|
|
1777
|
+
"json.parse",
|
|
1778
|
+
// decodeURIComponent throws the spec's URIError on bad hex/invalid
|
|
1779
|
+
// UTF-8 octets (encodeURIComponent never throws — see the IrLibFn doc).
|
|
1780
|
+
"str.decodeUriComponent",
|
|
1781
|
+
// The base64 globals: atob/btoa throw the catchable DOMException
|
|
1782
|
+
// InvalidCharacterError on malformed input; the zero-argument form
|
|
1783
|
+
// always throws Node's TypeError [ERR_MISSING_ARGS].
|
|
1784
|
+
"str.atob",
|
|
1785
|
+
"str.btoa",
|
|
1786
|
+
"str.b64Missing",
|
|
1787
|
+
// queueMicrotask's checked-dynamic form throws ERR_INVALID_ARG_TYPE
|
|
1788
|
+
// synchronously on a non-function argument (the closure form never
|
|
1789
|
+
// throws — the callback's own throw surfaces at drain, uncaught).
|
|
1790
|
+
"timers.queueMicrotaskDyn",
|
|
1791
|
+
// structuredClone: option validation throws Node's TypeErrors,
|
|
1792
|
+
// functions/handles the spec's DataCloneError, cycles the scriptc
|
|
1793
|
+
// fence; the zero-argument form always throws ERR_MISSING_ARGS. The
|
|
1794
|
+
// DOMException clone shares the option validation.
|
|
1795
|
+
"dyn.structuredClone",
|
|
1796
|
+
"dyn.cloneMissing",
|
|
1797
|
+
"dyn.cloneTransferFail",
|
|
1798
|
+
// the dyn Object walks throw on null/undefined receivers
|
|
1799
|
+
"dyn.objKeys",
|
|
1800
|
+
"dyn.objValues",
|
|
1801
|
+
"dyn.objEntries",
|
|
1802
|
+
"error.domClone",
|
|
1803
|
+
// new RegExp compiles the pattern eagerly: an invalid pattern or flag
|
|
1804
|
+
// throws Node's catchable SyntaxError at construction.
|
|
1805
|
+
"regex.new",
|
|
1806
|
+
"dyn.keySet",
|
|
1807
|
+
"dyn.toString",
|
|
1808
|
+
"dyn.defineProps",
|
|
1809
|
+
"process.chdir",
|
|
1810
|
+
"fs.realpathSync",
|
|
1811
|
+
"fs.readFileSync",
|
|
1812
|
+
"fs.writeFileSync",
|
|
1813
|
+
"fs.appendFileSync",
|
|
1814
|
+
"fs.mkdirSync",
|
|
1815
|
+
"fs.rmSync",
|
|
1816
|
+
"fs.rmdirSync",
|
|
1817
|
+
"fs.readdirSync",
|
|
1818
|
+
"fs.readdirTypesSync",
|
|
1819
|
+
"url.new",
|
|
1820
|
+
"url.fileURLToPathUrl",
|
|
1821
|
+
"url.fileURLToPathStr",
|
|
1822
|
+
// The win32-target flavor of pathToFileURL (same runtime entry point —
|
|
1823
|
+
// the bridge dispatches by the binary's platform): Node's win32 arm
|
|
1824
|
+
// raises ERR_INVALID_ARG_VALUE TypeErrors for malformed UNC inputs, so
|
|
1825
|
+
// only THIS flavor seeds may-throw — posix emission stays untouched.
|
|
1826
|
+
"url.pathToFileURLWin32",
|
|
1827
|
+
// URLSearchParams from a string[][]: Node's ERR_INVALID_TUPLE TypeError
|
|
1828
|
+
// on a row that is not a [name, value] pair. The rest of the sp family
|
|
1829
|
+
// never throws.
|
|
1830
|
+
"sp.fromPairs",
|
|
1831
|
+
"fs.statSync",
|
|
1832
|
+
"crypto.randomBytesToString",
|
|
1833
|
+
"crypto.randomBytes",
|
|
1834
|
+
"buffer.concatLen",
|
|
1835
|
+
"fs.readFileSyncBytes",
|
|
1836
|
+
"fs.writeFileSyncBytes",
|
|
1837
|
+
"zlib.inflateSync",
|
|
1838
|
+
"date.toISOString",
|
|
1839
|
+
"fs.mkdirRecursiveSync",
|
|
1840
|
+
"fs.rmOptsSync",
|
|
1841
|
+
"fs.rmRetrySync",
|
|
1842
|
+
"fs.mkdtempSync",
|
|
1843
|
+
"fs.accessSync",
|
|
1844
|
+
"fs.readFdSync",
|
|
1845
|
+
"fs.readFdSyncBytes",
|
|
1846
|
+
// node:dgram state errors (Node throws synchronously): bind on a bound
|
|
1847
|
+
// socket, connect on a connected one, send/close/address on a closed or
|
|
1848
|
+
// never-bound one.
|
|
1849
|
+
"dgram.bind",
|
|
1850
|
+
"dgram.bindCb",
|
|
1851
|
+
"dgram.connect",
|
|
1852
|
+
"dgram.connectCb",
|
|
1853
|
+
"dgram.sendStr",
|
|
1854
|
+
"dgram.sendBytes",
|
|
1855
|
+
"dgram.address",
|
|
1856
|
+
"dgram.close",
|
|
1857
|
+
"dgram.closeCb",
|
|
1858
|
+
// The assert surface: every entry point except sameValue, bytesDeepEq,
|
|
1859
|
+
// and the shape accumulator's begin/slot/test calls throws the
|
|
1860
|
+
// catchable AssertionError on failure.
|
|
1861
|
+
"assert.ok",
|
|
1862
|
+
"assert.eqF64",
|
|
1863
|
+
"assert.eqStr",
|
|
1864
|
+
"assert.eqBool",
|
|
1865
|
+
"assert.eqSym",
|
|
1866
|
+
"assert.eqDyn",
|
|
1867
|
+
"assert.deepResult",
|
|
1868
|
+
"assert.match",
|
|
1869
|
+
"assert.refEqBytes",
|
|
1870
|
+
"assert.refEqFn",
|
|
1871
|
+
"assert.throwsNone",
|
|
1872
|
+
"assert.throwsMismatch",
|
|
1873
|
+
"assert.throwsRegex",
|
|
1874
|
+
"assert.shapeEnd",
|
|
1875
|
+
"assert.unwantedRejection",
|
|
1876
|
+
"assert.expectsErrDyn",
|
|
1877
|
+
"assert.ifErrorErr",
|
|
1878
|
+
"assert.ifErrorF64",
|
|
1879
|
+
"assert.ifErrorStr",
|
|
1880
|
+
"assert.ifErrorBool",
|
|
1881
|
+
"assert.ifErrorDyn",
|
|
1882
|
+
// util.inspect of an island `any` composite: the runtime tag is all
|
|
1883
|
+
// there is, so the honest answer is a catchable TypeError.
|
|
1884
|
+
"insp.jsval",
|
|
1885
|
+
// The wider sync fs slice: same catchable errno throws as the rest.
|
|
1886
|
+
"fs.unlinkSync",
|
|
1887
|
+
"fs.chmodSync",
|
|
1888
|
+
"fs.chownSync",
|
|
1889
|
+
"fs.copyFileSync",
|
|
1890
|
+
"fs.lstatSync",
|
|
1891
|
+
"fs.writeFileModeSync",
|
|
1892
|
+
"fs.mkdirModeSync",
|
|
1893
|
+
"fs.mkdirRecursiveModeSync",
|
|
1894
|
+
"fs.openSync",
|
|
1895
|
+
"fs.readSync",
|
|
1896
|
+
"fs.closeSync",
|
|
1897
|
+
"fs.watch",
|
|
1898
|
+
"fs.watchCb",
|
|
1899
|
+
"crypto.x509Fingerprint",
|
|
1900
|
+
"crypto.x509FingerprintStr",
|
|
1901
|
+
"crypto.x509ValidFrom",
|
|
1902
|
+
"crypto.x509ValidFromStr",
|
|
1903
|
+
"crypto.x509ValidTo",
|
|
1904
|
+
"crypto.x509ValidToStr",
|
|
1905
|
+
]);
|
|
1906
|
+
//# sourceMappingURL=nodes.js.map
|