@loopdive/js2 0.58.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +20 -11
- package/dist/codegen/array-element-typing.d.ts +28 -14
- package/dist/codegen/context/types.d.ts +57 -10
- package/dist/codegen/fnctor-escape-gate.d.ts +37 -0
- package/dist/codegen/host-import-allowlist.d.ts +2 -2
- package/dist/codegen/index.d.ts +18 -1
- package/dist/codegen/literals.d.ts +27 -0
- package/dist/codegen/member-set-dispatch.d.ts +2 -2
- package/dist/codegen/registry/imports.d.ts +19 -1
- package/dist/codegen/shared.d.ts +24 -0
- package/dist/index.d.ts +41 -12
- package/dist/index.js +2 -2
- package/dist/{runtime-d_SmcBlN.js → runtime-UuI75J3h.js} +1623 -734
- package/dist/runtime.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ STATUS.md rather than duplicating numbers that go stale. Standalone
|
|
|
57
57
|
README until the current standalone regression is fixed.
|
|
58
58
|
|
|
59
59
|
<!-- AUTO:conformance-start -->
|
|
60
|
-
**test262 conformance**: 32,
|
|
60
|
+
**test262 conformance**: 32,704 / 43,135 (75.8 %)
|
|
61
61
|
<!-- AUTO:conformance-end -->
|
|
62
62
|
|
|
63
63
|
## Current Status
|
package/dist/cli.js
CHANGED
|
@@ -66,13 +66,17 @@ Options:
|
|
|
66
66
|
is ON by default; this restores the pre-#1950 behaviour.
|
|
67
67
|
(No-op when binaryen/wasm-opt is unavailable — that path
|
|
68
68
|
already degrades to a one-line note, never a failure.)
|
|
69
|
-
--link
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
--link <ns> (WASI, #2783) Leave the external namespace <ns> as link-time
|
|
70
|
+
imports (repeatable) instead of inline-lowering it. Satisfied
|
|
71
|
+
at instantiation by a preloaded provider module (e.g.
|
|
72
|
+
'wasmtime --preload <ns>=provider.wasm'). Any namespace works
|
|
73
|
+
(leave-as-import is universal); '--link node:fs' additionally
|
|
74
|
+
selects the import-and-link std-IO path: the module imports
|
|
75
|
+
readSync/writeSync + its memory from node:fs (no
|
|
76
|
+
wasi_snapshot_preview1 for stream IO) and links node-fs.wasm.
|
|
77
|
+
console.log / process.std*.write lower to writeSync(1|2, ...),
|
|
78
|
+
stdin is readSync(0, ...). Off by default — every namespace is
|
|
79
|
+
inline-lowered into a self-contained module.
|
|
76
80
|
--emulate <env> Emulate a host runtime's globals so they type-check without
|
|
77
81
|
@types/node. 'node' = ambient process/etc.; 'none' = off.
|
|
78
82
|
Auto-enabled (type-level only) when the source imports a
|
|
@@ -127,7 +131,7 @@ let allowFs = false;
|
|
|
127
131
|
let quiet = false;
|
|
128
132
|
let utf8Storage = false;
|
|
129
133
|
let strictNoHostImports;
|
|
130
|
-
|
|
134
|
+
const linkedNamespaces = /* @__PURE__ */ new Set();
|
|
131
135
|
let emulateNode = false;
|
|
132
136
|
let emulateExplicit = false;
|
|
133
137
|
let platform;
|
|
@@ -186,8 +190,13 @@ for (let i = 0; i < args.length; i++) {
|
|
|
186
190
|
quiet = true;
|
|
187
191
|
} else if (arg === "--utf8-storage") {
|
|
188
192
|
utf8Storage = true;
|
|
189
|
-
} else if (arg === "--link
|
|
190
|
-
|
|
193
|
+
} else if (arg === "--link" || arg.startsWith("--link=")) {
|
|
194
|
+
const ns = arg.startsWith("--link=") ? arg.slice("--link=".length) : args[++i];
|
|
195
|
+
if (!ns) {
|
|
196
|
+
console.error("--link requires a namespace argument (e.g. --link node:fs)");
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
linkedNamespaces.add(ns);
|
|
191
200
|
} else if (arg === "--emulate" || arg.startsWith("--emulate=")) {
|
|
192
201
|
const env = arg.startsWith("--emulate=") ? arg.slice("--emulate=".length) : args[++i];
|
|
193
202
|
if (env === "node") {
|
|
@@ -283,7 +292,7 @@ const compileOptions = {
|
|
|
283
292
|
...emitWit ? { wit: witPackageName ? { packageName: witPackageName } : true } : {},
|
|
284
293
|
...allowFs ? { allowFs: true } : {},
|
|
285
294
|
...utf8Storage ? { utf8Storage: true } : {},
|
|
286
|
-
...
|
|
295
|
+
...linkedNamespaces.size ? { link: [...linkedNamespaces] } : {},
|
|
287
296
|
...emulateNode ? { emulateNode: true } : {},
|
|
288
297
|
...platform ? { platform } : {},
|
|
289
298
|
fileName: absInput,
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
import { ts } from '../ts-api.js';
|
|
2
2
|
/**
|
|
3
|
-
* Return true if `expr` provably produces a 32-bit
|
|
4
|
-
*
|
|
3
|
+
* Return true if `expr` provably produces a **canonical** signed-32-bit integer
|
|
4
|
+
* at runtime — a value `v ∈ ℤ ∩ [-2^31, 2^31)` whose f64 image is bit-identical
|
|
5
|
+
* to `v` (so `v` is not `-0`, not fractional, not NaN/±Inf, not `|v| ≥ 2^31`).
|
|
6
|
+
* `i32Locals` is the set of locals already known to hold i32.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - comparison ops (return boolean = i32)
|
|
12
|
-
* - unary `+` / `-` / `~` of an i32-safe operand
|
|
13
|
-
* - `+` / `-` / `*` of two i32-safe operands (overflow wraps; receiver is i32)
|
|
14
|
-
* - parenthesised / `as`-cast / non-null-asserted i32-safe expr
|
|
8
|
+
* The "canonical" property is the soundness contract for #2789: when EVERY write
|
|
9
|
+
* to a packed array yields a canonical i32, the i32-stored value read back as f64
|
|
10
|
+
* equals what the f64 backing would have stored, so NO read can observe a
|
|
11
|
+
* distinction i32 erases. That discharges the read-side proof obligation in the
|
|
12
|
+
* hybrid fast-path audit (Row 3) for free — there is no distinction to observe.
|
|
15
13
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* Recognised canonical-i32 forms (mirrors `isI32SafeExpr` in function-body.ts;
|
|
15
|
+
* intentionally narrow — we err on the side of disqualification):
|
|
16
|
+
* - integer numeric literal in [-2^31, 2^31) (always +0, never -0)
|
|
17
|
+
* - identifier referencing a known-i32 local (physically i32 ⇒ canonical)
|
|
18
|
+
* - bitwise `|`, `&`, `^`, `<<`, `>>` (ECMAScript defines these to yield a
|
|
19
|
+
* value that equals ToInt32 of itself ⇒ canonical regardless of operands)
|
|
20
|
+
* - comparison ops (return boolean = i32 0/1)
|
|
21
|
+
* - `~x` (canonical int32) and unary `+x` of an i32-safe operand
|
|
22
|
+
* - unary `-<non-zero integer literal>` only (a `-1`-style sentinel) — NOT
|
|
23
|
+
* `-x` / `-(expr)`, which can be `-0` (#2789)
|
|
24
|
+
* - parenthesised / `as`-cast / non-null-asserted canonical-i32 expr
|
|
25
|
+
*
|
|
26
|
+
* Deliberately EXCLUDED (would break canonicality → MISCOMPILE if packed):
|
|
27
|
+
* - `+` / `-` / `*` arithmetic — f64 result stored via `i32.trunc_sat_f64_s`,
|
|
28
|
+
* which SATURATES on overflow rather than yielding the spec-correct f64
|
|
29
|
+
* (#2789, mirrors the #1236 scalar-local fix). Wrap-canonicalising patterns
|
|
30
|
+
* like `(a*b) | 0` still qualify via the top-level bitwise op.
|
|
31
|
+
* - `>>>` — produces uint32 which can sit above 2^31, reinterpreted as a
|
|
32
|
+
* negative i32 on store.
|
|
19
33
|
*/
|
|
20
34
|
export declare function isI32SafeExprForArray(expr: ts.Expression | undefined, i32Locals: ReadonlySet<string>, depth?: number): boolean;
|
|
21
35
|
/**
|
|
@@ -55,22 +55,29 @@ export interface CodegenOptions {
|
|
|
55
55
|
/** WASI target: emit WASI imports (fd_write, proc_exit) instead of JS host imports */
|
|
56
56
|
wasi?: boolean;
|
|
57
57
|
/**
|
|
58
|
-
* #
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `readSync`/`writeSync`
|
|
58
|
+
* #2783 — the dynamic-linking axis: namespaces to leave as link-time imports
|
|
59
|
+
* (satisfied by a preloaded provider) instead of inline-lowering. `["node:fs"]`
|
|
60
|
+
* routes std-IO through the `node:fs` shim (the user module imports
|
|
61
|
+
* `readSync`/`writeSync` + its linear memory from `node:fs` and carries NO
|
|
62
62
|
* `wasi_snapshot_preview1` import for the stream IO path; console.log /
|
|
63
|
-
* process.std*.write lower to `writeSync(1|2, …)
|
|
64
|
-
* the interface over WASI.
|
|
65
|
-
*
|
|
63
|
+
* process.std*.write lower to `writeSync(1|2, …)`; `node-fs.wasm` implements
|
|
64
|
+
* the interface over WASI). WASI-gated in `create-context.ts` (ignored for
|
|
65
|
+
* non-WASI targets). Default empty — the inline fd_read/fd_write path stays.
|
|
66
66
|
*/
|
|
67
|
-
|
|
67
|
+
link?: string[];
|
|
68
68
|
/** Standalone target (#1470): pure WasmGC, no JS host imports and no WASI
|
|
69
69
|
* runtime. Implies `nativeStrings: true` and refuses to emit any
|
|
70
70
|
* `wasm:js-string` namespace or `env::__concat_*` / `__extern_toString` /
|
|
71
71
|
* `__unbox_string` JS-host string imports. Used so the compiled module is
|
|
72
72
|
* runnable under pure-Wasm engines (wasmtime, wasmer) without a JS host. */
|
|
73
73
|
standalone?: boolean;
|
|
74
|
+
/** (#2796) Diff-test-harness fidelity: in JS-host mode, export the top-level
|
|
75
|
+
* `__module_init` and do NOT run it via the wasm `start` section, so the host
|
|
76
|
+
* invokes it AFTER `setExports` (symmetric with the standalone `_start`
|
|
77
|
+
* model). Default false → top-level runs in the start section. See
|
|
78
|
+
* `CompileOptions.deferTopLevelInit`. WASI is unaffected (it already exports
|
|
79
|
+
* `_start`). */
|
|
80
|
+
deferTopLevelInit?: boolean;
|
|
74
81
|
/**
|
|
75
82
|
* Experimental: route a narrow set of functions through the middle-end IR
|
|
76
83
|
* (see `src/ir/`). Defaults to **on** since #1131 (the front-end driver
|
|
@@ -842,6 +849,24 @@ export interface CodegenContext {
|
|
|
842
849
|
*/
|
|
843
850
|
holeTypeIdx: number;
|
|
844
851
|
holeGlobalIdx: number | undefined;
|
|
852
|
+
/**
|
|
853
|
+
* (#2800) The mutable i32 `__in_module_init` flag — 1 for the duration of
|
|
854
|
+
* `__module_init` (the Wasm `start` section in gc/host mode, which runs INSIDE
|
|
855
|
+
* `WebAssembly.instantiate`, BEFORE the host wires struct getters via
|
|
856
|
+
* `__setExports`), 0 otherwise. The delete-aware `any`-receiver READ
|
|
857
|
+
* (`tryEmitDeleteAwareDynamicGet`) branches on it: while init runs (host
|
|
858
|
+
* `__extern_get` can't reach `__sget_<field>` → returns undefined for every
|
|
859
|
+
* struct field), read the slot HOST-FREE via the `__get_member_<name>`
|
|
860
|
+
* dispatcher; at runtime use the tombstone-aware host `__extern_get`.
|
|
861
|
+
*
|
|
862
|
+
* `inModuleInitFlagReads` collects the `global.get` flag-read Instr objects
|
|
863
|
+
* emitted at read sites (with a placeholder index); `finalizeInModuleInitFlag`
|
|
864
|
+
* allocates the i32 global AFTER every import settles and patches their
|
|
865
|
+
* `.index` + records the final slot in `inModuleInitGlobalIdx`. Undefined/empty
|
|
866
|
+
* for delete-free / standalone / WASI modules (byte-identical).
|
|
867
|
+
*/
|
|
868
|
+
inModuleInitFlagReads: Instr[] | undefined;
|
|
869
|
+
inModuleInitGlobalIdx: number | undefined;
|
|
845
870
|
/**
|
|
846
871
|
* (#2580 M0) Value-rep dynamic-read substrate. Set true by a call site that
|
|
847
872
|
* needs the runtime property-presence read primitives (`__dyn_has` /
|
|
@@ -1675,6 +1700,11 @@ export interface CodegenContext {
|
|
|
1675
1700
|
* `__unbox_string`, `__str_from_mem`, `__str_to_mem`,
|
|
1676
1701
|
* `__str_extern_len`). Implies `nativeStrings === true`. */
|
|
1677
1702
|
standalone: boolean;
|
|
1703
|
+
/** (#2796) Diff-test-harness fidelity: in JS-host mode, export the top-level
|
|
1704
|
+
* `__module_init` and do NOT wire the wasm `start` section to it, so the host
|
|
1705
|
+
* runs it after `setExports` (symmetric with the standalone `_start` model).
|
|
1706
|
+
* Default false. WASI is unaffected. */
|
|
1707
|
+
deferTopLevelInit: boolean;
|
|
1678
1708
|
/** (#2179) True when the module body contains any `delete` of a property or
|
|
1679
1709
|
* element access (e.g. `delete o.a` / `delete o[k]`). Pre-scanned once at
|
|
1680
1710
|
* module setup. When true, `any`/`unknown`-typed property READS in JS-host
|
|
@@ -1745,10 +1775,27 @@ export interface CodegenContext {
|
|
|
1745
1775
|
* `node:fs` `readSync`/`writeSync` calls (over a shim-owned, imported linear
|
|
1746
1776
|
* memory) instead of inline `fd_read`/`fd_write`. console.log/warn/error and
|
|
1747
1777
|
* process.std*.write lower to `writeSync(1|2, …)`; the bespoke
|
|
1748
|
-
* `js2wasm:node-process` shim was retired (#2633).
|
|
1749
|
-
* `
|
|
1778
|
+
* `js2wasm:node-process` shim was retired (#2633). Driven by the `link` set
|
|
1779
|
+
* (`["node:fs"]`).
|
|
1780
|
+
*
|
|
1781
|
+
* #2783 — an INTERNAL convenience boolean, **derived** from
|
|
1782
|
+
* `linkedNamespaces.has("node:fs")` (there is no user-facing `linkNodeShims`
|
|
1783
|
+
* option anymore — `link: string[]` is the only input). The two
|
|
1784
|
+
* are computed together in `create-context.ts` from the same (WASI-gated)
|
|
1785
|
+
* `link` set so they can never drift. Keeping this boolean lets the ~30
|
|
1786
|
+
* existing `ctx.linkNodeShims` read sites stay zero-churn while the underlying
|
|
1787
|
+
* state generalizes to an arbitrary set of linked namespaces.
|
|
1750
1788
|
*/
|
|
1751
1789
|
linkNodeShims: boolean;
|
|
1790
|
+
/**
|
|
1791
|
+
* #2783 — the set of external namespaces left as **link-time imports** for
|
|
1792
|
+
* this compile (WASI-gated; empty for non-WASI targets). `node:fs` membership
|
|
1793
|
+
* additionally drives the import-and-link std-IO codegen path (see
|
|
1794
|
+
* `linkNodeShims`, derived from this set). For an arbitrary namespace,
|
|
1795
|
+
* membership only permits its imports past the strict `--no-host-imports` /
|
|
1796
|
+
* WASI leaked-host-import gate (`assertNoLeakedHostImports`).
|
|
1797
|
+
*/
|
|
1798
|
+
linkedNamespaces: ReadonlySet<string>;
|
|
1752
1799
|
/** #2631/#2633: func index of the imported `node:fs::readSync` (fd,ptr,len)->i32 (-1 = not registered). */
|
|
1753
1800
|
nodeFsReadSyncIdx: number;
|
|
1754
1801
|
/** #2631/#2633: func index of the imported `node:fs::writeSync` (fd,ptr,len)->i32 (-1 = not registered). */
|
|
@@ -85,6 +85,43 @@ export interface FnctorEscapeGateResult {
|
|
|
85
85
|
* `undefined`.
|
|
86
86
|
*/
|
|
87
87
|
export declare function resolveFnctorSymbol(checker: ts.TypeChecker, calleeExpr: ts.Expression): ts.Symbol | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* #2681/#2686 — resolve the fnctor `F` that OWNS the enclosing method a node sits
|
|
90
|
+
* in, for a `new this(…)` site or a lifted method body. `this` inside a method
|
|
91
|
+
* `F.method = function(){…}` / `F.prototype.m = function(){…}` / aliased `var pp =
|
|
92
|
+
* F.prototype; pp.m = function(){…}` binds to `F` (static) or an `F` instance
|
|
93
|
+
* (prototype). Walks up to the nearest non-arrow function (arrows do not rebind
|
|
94
|
+
* `this`) and resolves its defining assignment's holder to a fnctor symbol.
|
|
95
|
+
*
|
|
96
|
+
* Returns `{ name, sym, viaPrototype }` where `viaPrototype` is true for a
|
|
97
|
+
* prototype/aliased method (`this` is an INSTANCE — the read-dispatch case) and
|
|
98
|
+
* false for a direct static method (`this` is the CONSTRUCTOR — the `new this()`
|
|
99
|
+
* reconstruct case). `undefined` when the enclosing function is not a fnctor
|
|
100
|
+
* method, or the holder does not resolve to a user fnctor.
|
|
101
|
+
*/
|
|
102
|
+
export declare function resolveEnclosingFnctorOwner(checker: ts.TypeChecker, node: ts.Node): {
|
|
103
|
+
name: string;
|
|
104
|
+
sym: ts.Symbol;
|
|
105
|
+
viaPrototype: boolean;
|
|
106
|
+
} | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* #2681/#2686 A3 — the `__fnctor_<F>` struct name a lifted PROTOTYPE method's
|
|
109
|
+
* `this` receiver resolves to, when `F` is approved for reconstruction. Sets
|
|
110
|
+
* `FunctionContext.thisStructName` (closures.ts) so the dynamic `this.<field>`
|
|
111
|
+
* read dispatch (property-access.ts) routes through the finalize-filled
|
|
112
|
+
* `__get_member_<name>` dispatcher.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately NOT gated on `ctx.structMap.has(__fnctor_<F>)`: the reader method
|
|
115
|
+
* frequently compiles BEFORE the `new this()` site that registers the struct
|
|
116
|
+
* (acorn defines `pp.parseExprAtom` long before the static `Parser.parse`). The
|
|
117
|
+
* dispatcher is reserved at the read site and FILLED at finalize over the
|
|
118
|
+
* COMPLETE type table, so a struct registered later is still enumerated — pinning
|
|
119
|
+
* on `approvedNames` (frozen pre-codegen at index.ts) is order-independent and
|
|
120
|
+
* correct, while a `structMap.has` gate would race the compile order and miss.
|
|
121
|
+
* Excludes static methods (`viaPrototype === false`) — their `this` is the
|
|
122
|
+
* constructor function-value, not an instance.
|
|
123
|
+
*/
|
|
124
|
+
export declare function resolveLiftedMethodThisStruct(ctx: CodegenContext, fn: ts.FunctionExpression | ts.ArrowFunction): string | undefined;
|
|
88
125
|
/**
|
|
89
126
|
* #2660 PART-1 — resolve the WasmGC struct a member-access RECEIVER expression
|
|
90
127
|
* concretely is, for the dynamic read/write/compound dispatch to PIN to.
|
|
@@ -80,7 +80,7 @@ export declare function lookupAllowlistEntry(name: string): HostImportAllowlistE
|
|
|
80
80
|
* `nativeStrings` mode which strict mode auto-enables; they should not
|
|
81
81
|
* appear when strict mode is on.)
|
|
82
82
|
*/
|
|
83
|
-
export declare function isHostImportAllowed(module: string, name: string): {
|
|
83
|
+
export declare function isHostImportAllowed(module: string, name: string, linkedNamespaces?: ReadonlySet<string>): {
|
|
84
84
|
allowed: true;
|
|
85
85
|
} | {
|
|
86
86
|
allowed: false;
|
|
@@ -129,7 +129,7 @@ export interface LeakedHostImport {
|
|
|
129
129
|
export declare function scanForLeakedHostImports(imports: ReadonlyArray<{
|
|
130
130
|
module: string;
|
|
131
131
|
name: string;
|
|
132
|
-
}>): LeakedHostImport[];
|
|
132
|
+
}>, linkedNamespaces?: ReadonlySet<string>): LeakedHostImport[];
|
|
133
133
|
/**
|
|
134
134
|
* (#2094) Build the structured compile error for a host import that leaked
|
|
135
135
|
* into a finished standalone/strict binary. Distinct from
|
package/dist/codegen/index.d.ts
CHANGED
|
@@ -89,6 +89,23 @@ export declare function generateModule(ast: TypedAST, options?: CodegenOptions):
|
|
|
89
89
|
message: string;
|
|
90
90
|
}[];
|
|
91
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* Emit __vec_get(externref, i32) -> externref and __vec_len(externref) -> i32
|
|
94
|
+
* exports so the runtime can iterate WasmGC vec structs that were coerced to
|
|
95
|
+
* externref (e.g. arrays stored in `any`-typed variables).
|
|
96
|
+
*
|
|
97
|
+
* For each registered vec type, emits ref.test/ref.cast dispatch to extract
|
|
98
|
+
* the length or the indexed element, boxing the result to externref.
|
|
99
|
+
*/
|
|
100
|
+
/**
|
|
101
|
+
* (#2784 S3) Reserve a `__vec_push` / `__vec_pop` helper funcIdx UP FRONT so the
|
|
102
|
+
* native-vec method dispatch (calls.ts) can bake the call at compile time — the
|
|
103
|
+
* helper bodies are only built in the finalize `emitVecAccessExports` pass, which
|
|
104
|
+
* runs AFTER the method-call site compiles. Pushes a valid placeholder body +
|
|
105
|
+
* export + funcMap entry (shift-tracked); the finalize pass FILLS the body in
|
|
106
|
+
* place (fill-or-build in `_emitVecAccessExportsInner`). Idempotent.
|
|
107
|
+
*/
|
|
108
|
+
export declare function reserveVecMethodHelper(ctx: CodegenContext, kind: "push" | "pop" | "get"): number;
|
|
92
109
|
/**
|
|
93
110
|
* Compile multiple typed source files into a single WasmModule IR.
|
|
94
111
|
* All source files share the same codegen context (funcMap, structMap, etc.).
|
|
@@ -245,7 +262,7 @@ export declare function ensureWasiWriteAnyStringHelper(ctx: CodegenContext, useS
|
|
|
245
262
|
* it to the *runtime* fd. This backs the STRING overload of `node:fs`
|
|
246
263
|
* `writeSync(fd, str, position?, encoding?)`, where the fd is an arbitrary
|
|
247
264
|
* integer (not just stdout/stderr). Two modes (#2655):
|
|
248
|
-
* - shim (`--link
|
|
265
|
+
* - shim (`--link node:fs`): `writeSync(fd, ptr, len)` returns the byte count.
|
|
249
266
|
* - direct (standalone `--target wasi`): build a `{ base=ptr, len }` iovec at
|
|
250
267
|
* memory[0..7], call `fd_write(fd, iovs=0, 1, nwritten=8)`, load nwritten.
|
|
251
268
|
*/
|
|
@@ -42,6 +42,33 @@ export declare function compileObjectLiteralAsExternref(ctx: CodegenContext, fct
|
|
|
42
42
|
* value semantics suffice; plain data structs keep `extern.convert_any`.
|
|
43
43
|
*/
|
|
44
44
|
export declare function materializeStructAsDynamicObject(ctx: CodegenContext, fctx: FunctionContext, structTypeIdx: number): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* (#2714 / #2804) True when a spread-containing object literal must be built via
|
|
47
|
+
* the host plain-object (`$Object`/externref) path rather than a closed struct,
|
|
48
|
+
* because its evaluation context does not pin a CONCRETE object SHAPE the struct
|
|
49
|
+
* path could faithfully build/enumerate: `any` / `unknown` / `object`, NO
|
|
50
|
+
* contextual type at all, or a shapeless object type with zero own properties
|
|
51
|
+
* (e.g. the `object` param of `Object.keys`).
|
|
52
|
+
*
|
|
53
|
+
* This is the single source of truth for the routing decision below AND for the
|
|
54
|
+
* variable-declaration local typing (statements/variables.ts, index.ts var
|
|
55
|
+
* hoist). Keeping them in lockstep is what fixes #2804: `const b = { ...a, z: 3 }`
|
|
56
|
+
* (no annotation) has NO contextual type, so the literal takes the host path —
|
|
57
|
+
* but the receiving variable's INFERRED type is a concrete struct `{x;y;z}`, so
|
|
58
|
+
* without this shared predicate the local was typed as that struct while the
|
|
59
|
+
* initializer produced a host `$Object`, and the externref→struct coercion
|
|
60
|
+
* (ref.test/ref.cast) failed at runtime → `b.x` read NaN / null. The variable
|
|
61
|
+
* sites consult this predicate and force an externref local so the local
|
|
62
|
+
* representation matches the host-object value (and `b.x` routes through
|
|
63
|
+
* `__extern_get`, preserving the spread's insertion-order keys + values, which
|
|
64
|
+
* the struct path cannot — its field order follows TS's own-prop-first inferred
|
|
65
|
+
* type, not the runtime CopyDataProperties insertion order).
|
|
66
|
+
*
|
|
67
|
+
* A CONCRETE annotated target (`const x: { a: number } = { ...o }`, ≥1 property)
|
|
68
|
+
* has a specific contextual type → returns false → keeps the struct path so
|
|
69
|
+
* typed consumers still receive a struct (#2714 control).
|
|
70
|
+
*/
|
|
71
|
+
export declare function objectLiteralSpreadTakesHostPath(ctx: CodegenContext, expr: ts.ObjectLiteralExpression): boolean;
|
|
45
72
|
export declare function compileObjectLiteral(ctx: CodegenContext, fctx: FunctionContext, expr: ts.ObjectLiteralExpression): ValType | null;
|
|
46
73
|
/**
|
|
47
74
|
* Try to evaluate an expression to a constant numeric or string value at compile time.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CodegenContext } from './context/types.js';
|
|
1
|
+
import { CodegenContext, FunctionContext } from './context/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Reserve (or fetch) the member-set dispatcher `__set_member_<name>(recv, val)`
|
|
4
4
|
* funcIdx with a placeholder body. The real body is built by
|
|
@@ -14,7 +14,7 @@ import { CodegenContext } from './context/types.js';
|
|
|
14
14
|
* - `__box_number`/`__unbox_number` (union imports — the per-struct arms may
|
|
15
15
|
* unbox the externref value into an f64/i32 field via `coercionInstrs`).
|
|
16
16
|
*/
|
|
17
|
-
export declare function reserveMemberSetDispatch(ctx: CodegenContext, propName: string, strict: boolean): number | undefined;
|
|
17
|
+
export declare function reserveMemberSetDispatch(ctx: CodegenContext, propName: string, strict: boolean, fctx?: FunctionContext): number | undefined;
|
|
18
18
|
/**
|
|
19
19
|
* Fill every reserved `__set_member_<name>` dispatcher body at FINALIZE, after
|
|
20
20
|
* every struct type (incl. late-registered fnctor structs) is known. READ-ONLY
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Import } from '../../ir/types.js';
|
|
1
|
+
import { Import, Instr } from '../../ir/types.js';
|
|
2
2
|
import { CodegenContext } from '../context/types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Register an import (`module.name`) on the current module.
|
|
@@ -37,6 +37,24 @@ export declare function addImport(ctx: CodegenContext, module: string, name: str
|
|
|
37
37
|
export declare function addStringConstantGlobal(ctx: CodegenContext, value: string): void;
|
|
38
38
|
/** Return the absolute Wasm global index for a new module-defined global. */
|
|
39
39
|
export declare function nextModuleGlobalIdx(ctx: CodegenContext): number;
|
|
40
|
+
/**
|
|
41
|
+
* (#2800) Record a `global.get __in_module_init` instruction for FINALIZE-time
|
|
42
|
+
* index resolution. Returns a fresh `global.get` Instr with a PLACEHOLDER index
|
|
43
|
+
* and registers it on `ctx.inModuleInitFlagReads`; the caller bakes this exact
|
|
44
|
+
* object into its body. `finalizeInModuleInitFlag` (codegen/index.ts) allocates
|
|
45
|
+
* the i32 flag global AFTER every import global has settled and patches each
|
|
46
|
+
* recorded instr's `.index` to the final slot — so no read can desync when a
|
|
47
|
+
* later string-constant import shifts the module-global range (the live-baked
|
|
48
|
+
* index hazard #2043 across closure bodies the per-add fixup can miss).
|
|
49
|
+
*
|
|
50
|
+
* The flag is 1 only while `__module_init` runs; the delete-aware `any`-receiver
|
|
51
|
+
* read branches on it (init → host-free `__get_member_<name>` slot dispatcher;
|
|
52
|
+
* runtime → tombstone-aware host `__extern_get`). gc/host runs `__module_init`
|
|
53
|
+
* via the Wasm `start` section INSIDE `WebAssembly.instantiate`, before the host
|
|
54
|
+
* wires struct getters (`__setExports`), so the host read returns undefined for
|
|
55
|
+
* every struct field at init — this flag is what makes init reads correct.
|
|
56
|
+
*/
|
|
57
|
+
export declare function recordInModuleInitFlagRead(ctx: CodegenContext): Instr;
|
|
40
58
|
/** Convert an absolute Wasm global index to a local module-globals array index. */
|
|
41
59
|
export declare function localGlobalIdx(ctx: CodegenContext, absIdx: number): number;
|
|
42
60
|
/**
|
package/dist/codegen/shared.d.ts
CHANGED
|
@@ -64,6 +64,30 @@ export declare function flushLateImportShifts(ctx: CodegenContext, fctx: Functio
|
|
|
64
64
|
* Check if a ValType is the any-value boxed type used for TS `any`.
|
|
65
65
|
*/
|
|
66
66
|
export declare function isAnyValue(type: ValType, ctx: CodegenContext): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* (#2770, S5b of #2773) Brand an extern-method result ValType as a *boolean*
|
|
69
|
+
* when the method's declared TS return type is exactly `boolean`, so the
|
|
70
|
+
* `any`/return coercion boxes it via `__box_boolean` (→ `true`/`false`) instead
|
|
71
|
+
* of `__box_number` (→ `1`/`0`).
|
|
72
|
+
*
|
|
73
|
+
* Why a per-call-site wrap (not just registration): a boolean extern method's
|
|
74
|
+
* func type carries an `i32` result, and `funcTypeKey` (registry/types.ts) keys
|
|
75
|
+
* results on `.kind` only — so a branded `{i32,boolean:true}` result func type
|
|
76
|
+
* dedups to a pre-existing *unbranded* `…->i32` type. `getWasmFuncReturnType`
|
|
77
|
+
* then reads back the deduped unbranded i32 and the brand is lost at every
|
|
78
|
+
* `getWasmFuncReturnType(ctx, idx) ?? resolveWasmType(ctx, retType)` dispatch
|
|
79
|
+
* site. Re-branding from the call's TS return type here recovers it regardless
|
|
80
|
+
* of dedup. (Registration is also branded so the direct `methodInfo.results[0]`
|
|
81
|
+
* path in extern.ts is honest at source.)
|
|
82
|
+
*
|
|
83
|
+
* Over-boxing guards — idempotent, never widens:
|
|
84
|
+
* - only a *bare* `i32` is touched (f64/externref/ref/ref_null pass through);
|
|
85
|
+
* - an already `{i32,boolean:true}` value short-circuits (idempotent);
|
|
86
|
+
* - only an *exactly*-`boolean` declared return is branded (numbers, unions,
|
|
87
|
+
* `void`, etc. pass through unchanged — `map.get`/`indexOf`/`.size` stay
|
|
88
|
+
* numbers).
|
|
89
|
+
*/
|
|
90
|
+
export declare function brandExternMethodResult(_ctx: CodegenContext, tsReturnType: ts.Type | undefined, valType: ValType): ValType;
|
|
67
91
|
type EnsureAnyHelpersFn = (ctx: CodegenContext) => void;
|
|
68
92
|
export declare function registerEnsureAnyHelpers(fn: EnsureAnyHelpersFn): void;
|
|
69
93
|
type AddUnionImportsFn = (ctx: CodegenContext) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -315,19 +315,48 @@ export interface CompileOptions {
|
|
|
315
315
|
* Default: false (calls to fs.readFileSync / fs.writeFileSync raise a compile error). */
|
|
316
316
|
allowFs?: boolean;
|
|
317
317
|
/**
|
|
318
|
-
* #
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* `
|
|
322
|
-
* `
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
318
|
+
* (#2796) Differential-test-harness fidelity flag. In the default JS-host
|
|
319
|
+
* (WasmGC) target, top-level module code runs via the wasm `start` section —
|
|
320
|
+
* i.e. DURING `WebAssembly.instantiate`, BEFORE the host can call
|
|
321
|
+
* `setExports(instance.exports)`. Top-level code that introspects WasmGC
|
|
322
|
+
* structs (`for…in` / `Object.keys` over a runtime-shaped object) needs the
|
|
323
|
+
* `__struct_field_names` / `__sget_*` exports, which only exist once the
|
|
324
|
+
* instance is constructed — so during the start section they resolve to
|
|
325
|
+
* nothing and a `for…in` enumerates zero keys. The standalone/WASI path does
|
|
326
|
+
* NOT hit this: it runs top-level code via an explicitly-called `_start`
|
|
327
|
+
* export AFTER instantiation, when every export is reachable.
|
|
328
|
+
*
|
|
329
|
+
* When `true`, emit the top-level `__module_init` as an EXPORT and do NOT run
|
|
330
|
+
* it via the wasm `start` section, so the host can invoke
|
|
331
|
+
* `instance.exports.__module_init()` AFTER wiring `setExports` — symmetric
|
|
332
|
+
* with the standalone `_start` model. The differential-test harness
|
|
333
|
+
* (`scripts/diff-test.ts`) sets this so the HOST lane runs top-level code with
|
|
334
|
+
* the same fully-wired runtime the standalone lane uses, rather than tripping
|
|
335
|
+
* over an exports-timing artifact of the harness. Default `false` →
|
|
336
|
+
* byte-identical output (top-level runs in the wasm `start` section) for every
|
|
337
|
+
* other consumer (website, playground, test262, library users).
|
|
338
|
+
*/
|
|
339
|
+
deferTopLevelInit?: boolean;
|
|
340
|
+
/**
|
|
341
|
+
* #2783 — general `--link <namespace>` dynamic-linking axis (the ONLY
|
|
342
|
+
* link-vs-inline control; the old `linkNodeShims` boolean was removed). Each
|
|
343
|
+
* listed namespace is left as a **link-time import** (satisfied at
|
|
344
|
+
* instantiation by a preloaded provider module, e.g.
|
|
345
|
+
* `wasmtime --preload node:fs=node-fs.wasm`) instead of being inline-lowered to
|
|
346
|
+
* a self-contained module. "Leave-as-import" is the universal capability (any
|
|
347
|
+
* external namespace can be a wasm import); "inline-lower" is the special
|
|
348
|
+
* capability the compiler only has for a known few (`node:fs` fd IO). So for an
|
|
349
|
+
* arbitrary namespace `link: ["acme:telemetry"]` simply permits its imports
|
|
350
|
+
* past the strict `--no-host-imports` / WASI gate; for `node:fs` it
|
|
351
|
+
* additionally selects the import-and-link std-IO path (the user module imports
|
|
352
|
+
* `readSync`/`writeSync` + its linear memory from `node:fs` and carries no
|
|
353
|
+
* `wasi_snapshot_preview1` import for stream IO; console.log /
|
|
354
|
+
* process.std*.write lower to `writeSync(1|2, …)`, stdin is `readSync(0, …)`).
|
|
355
|
+
*
|
|
356
|
+
* WASI-gated: ignored for non-WASI targets. Default empty — every namespace
|
|
357
|
+
* stays standalone / inline-lowered. CLI: `--link <ns>` (repeatable).
|
|
329
358
|
*/
|
|
330
|
-
|
|
359
|
+
link?: string[];
|
|
331
360
|
/**
|
|
332
361
|
* Node API emulation (#2603). Opt-in via `--emulate node`. When set, the
|
|
333
362
|
* checker is given an ambient `process` declaration so Node globals js2wasm
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
|
-
import { i as isKnownLibName, g as getLibSourceFile, a as getDefaultEnvironment, r as rewriteCjsRequire, f as forEachChild, e as entryHasRelativeImports, c as compileSource, b as compileMultiSource, d as buildImports, h as compileFilesSource, j as compileToObjectSource } from "./runtime-
|
|
3
|
-
import { k, l, m, n, o, p, q, s, t } from "./runtime-
|
|
2
|
+
import { i as isKnownLibName, g as getLibSourceFile, a as getDefaultEnvironment, r as rewriteCjsRequire, f as forEachChild, e as entryHasRelativeImports, c as compileSource, b as compileMultiSource, d as buildImports, h as compileFilesSource, j as compileToObjectSource } from "./runtime-UuI75J3h.js";
|
|
3
|
+
import { k, l, m, n, o, p, q, s, t } from "./runtime-UuI75J3h.js";
|
|
4
4
|
import ts from "typescript";
|
|
5
5
|
class IncrementalLanguageService {
|
|
6
6
|
currentSource = "";
|