@loopdive/js2 0.57.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 +24 -14
- package/dist/codegen/array-element-typing.d.ts +28 -14
- package/dist/codegen/context/types.d.ts +68 -10
- package/dist/codegen/fnctor-escape-gate.d.ts +82 -0
- package/dist/codegen/host-import-allowlist.d.ts +2 -2
- package/dist/codegen/index.d.ts +53 -1
- package/dist/codegen/literals.d.ts +27 -0
- package/dist/codegen/member-set-dispatch.d.ts +2 -2
- package/dist/codegen/property-access.d.ts +2 -2
- package/dist/codegen/registry/imports.d.ts +19 -1
- package/dist/codegen/shared.d.ts +24 -0
- package/dist/codegen/statements/loops.d.ts +30 -0
- package/dist/compiler.d.ts +11 -0
- package/dist/index.d.ts +42 -12
- package/dist/index.js +41 -40
- package/dist/ir/types.d.ts +1 -0
- package/dist/{runtime-C-4q_KwU.js → runtime-UuI75J3h.js} +81892 -80429
- package/dist/runtime.js +9 -9
- package/package.json +45 -42
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
|
@@ -13,7 +13,7 @@ const args = process.argv.slice(2);
|
|
|
13
13
|
if (args.includes("--ts7")) {
|
|
14
14
|
process.env.JS2WASM_TS7 = "1";
|
|
15
15
|
}
|
|
16
|
-
const { compile } = await import("./index.js");
|
|
16
|
+
const { compile, compileProject, entryHasRelativeImports } = await import("./index.js");
|
|
17
17
|
const { buildDefaultDefines } = await import("./define-substitution-BcUeKC2A.js").then((n) => n.d);
|
|
18
18
|
if (args.includes("--version") || args.includes("-v")) {
|
|
19
19
|
console.log(getCliVersion());
|
|
@@ -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") {
|
|
@@ -276,20 +285,21 @@ if (!emulateExplicit && !emulateNode && /['"]node:[A-Za-z0-9_./-]+['"]/.test(sou
|
|
|
276
285
|
}
|
|
277
286
|
const name = basename(absInput, ".ts");
|
|
278
287
|
const dir = outDir ? resolve(outDir) : dirname(absInput);
|
|
279
|
-
const
|
|
288
|
+
const compileOptions = {
|
|
280
289
|
...optimize ? { optimize } : {},
|
|
281
290
|
...target ? { target } : {},
|
|
282
291
|
...allocator ? { allocator } : {},
|
|
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,
|
|
290
299
|
...strictNoHostImports !== void 0 ? { strictNoHostImports } : {},
|
|
291
300
|
...Object.keys(defines).length > 0 ? { define: defines } : {}
|
|
292
|
-
}
|
|
301
|
+
};
|
|
302
|
+
const result = entryHasRelativeImports(source) ? await compileProject(absInput, compileOptions) : await compile(source, compileOptions);
|
|
293
303
|
if (!result.success) {
|
|
294
304
|
for (const e of result.errors) {
|
|
295
305
|
const severity = e.severity === "warning" ? "warning" : "error";
|
|
@@ -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). */
|
|
@@ -1819,6 +1866,17 @@ export interface CodegenContext {
|
|
|
1819
1866
|
* Wasm byte-identical.
|
|
1820
1867
|
*/
|
|
1821
1868
|
fnctorEscapeGate?: import('../fnctor-escape-gate.js').FnctorEscapeGateResult;
|
|
1869
|
+
/**
|
|
1870
|
+
* #2773 S1 (keystone) — fnctor name → reserved `$__fnctor_<Name>` struct type
|
|
1871
|
+
* index. Populated up-front by `reserveFnctorStructTypes` (index.ts) at the
|
|
1872
|
+
* deterministic type-init phase so the index is IDENTICAL across the hoist pass
|
|
1873
|
+
* and the emit pass (the on-demand registration at the `new F()` site landed at
|
|
1874
|
+
* a pass-dependent index → `ref.test`/`struct.get` desync). When a name is
|
|
1875
|
+
* present here, `compileNewFunctionDeclaration` FILLS the reserved slot in place
|
|
1876
|
+
* instead of pushing a new type (which would re-shift every downstream typeIdx).
|
|
1877
|
+
* Empty for fnctor-free modules ⇒ byte-identical no-op.
|
|
1878
|
+
*/
|
|
1879
|
+
fnctorReservedTypeIdx: Map<string, number>;
|
|
1822
1880
|
/**
|
|
1823
1881
|
* #1886 Slice B — Func index of the lazily-emitted
|
|
1824
1882
|
* `__lin_u8_alloc(len:i32)->i32` bump allocator for linear-backed Uint8Array
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ts } from '../ts-api.js';
|
|
2
|
+
import { FieldDef } from '../ir/types.js';
|
|
2
3
|
import { CodegenContext, FunctionContext } from './context/types.js';
|
|
3
4
|
/** Classification of a `new F()` fnctor allocation site. */
|
|
4
5
|
export type FnctorGateClass =
|
|
@@ -52,6 +53,28 @@ export interface FnctorEscapeGateResult {
|
|
|
52
53
|
* `resolveReceiverStruct`; no lowering reads it, so emitted Wasm is byte-identical.
|
|
53
54
|
*/
|
|
54
55
|
readonly receiverStruct: ReadonlyMap<ts.Expression, string>;
|
|
56
|
+
/**
|
|
57
|
+
* #2773 S2b — fnctor NAMES that own a `new this()` reconstruct site (a
|
|
58
|
+
* `new this()` inside a static / prototype method classified as an `F`
|
|
59
|
+
* reconstruct). The #2773 S1 up-front struct-type reservation
|
|
60
|
+
* (`reserveFnctorStructTypes`) unions this with {@link approvedNames} so a
|
|
61
|
+
* `Parser` reconstructed via `new this()` also gets a reserved
|
|
62
|
+
* `$__fnctor_Parser` slot. **S1 ships this as an EMPTY set** — S1 only READS it
|
|
63
|
+
* (so the reservation union is a no-op today); S2b populates it. Landing the
|
|
64
|
+
* field shape here keeps S2b purely additive.
|
|
65
|
+
*/
|
|
66
|
+
readonly newThisOwnerNames: ReadonlySet<string>;
|
|
67
|
+
/**
|
|
68
|
+
* #2773 S1 — fnctor NAME → its function-like declaration (the body-bearer whose
|
|
69
|
+
* `this.<field> = …` writes derive the `$__fnctor_<Name>` struct shape). Covers
|
|
70
|
+
* EVERY fnctor `new F()` site seen (not just approved ones) so
|
|
71
|
+
* `reserveFnctorStructTypes` can resolve a name to the SAME declaration the
|
|
72
|
+
* on-demand `compileNewFunctionDeclaration` path uses — guaranteeing identical
|
|
73
|
+
* field derivation. A name with ≥2 distinct declarations keeps the first
|
|
74
|
+
* (deterministic by source order); ambiguity here only affects WHICH body shapes
|
|
75
|
+
* the reserved slot (matching the on-demand resolution at the dominant site).
|
|
76
|
+
*/
|
|
77
|
+
readonly ctorDeclByName: ReadonlyMap<string, ts.FunctionDeclaration | ts.FunctionExpression>;
|
|
55
78
|
}
|
|
56
79
|
/**
|
|
57
80
|
* Whether `expr` resolves to a plain function constructor (fnctor) rather than a
|
|
@@ -62,6 +85,43 @@ export interface FnctorEscapeGateResult {
|
|
|
62
85
|
* `undefined`.
|
|
63
86
|
*/
|
|
64
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;
|
|
65
125
|
/**
|
|
66
126
|
* #2660 PART-1 — resolve the WasmGC struct a member-access RECEIVER expression
|
|
67
127
|
* concretely is, for the dynamic read/write/compound dispatch to PIN to.
|
|
@@ -90,3 +150,25 @@ export declare function resolveReceiverStruct(ctx: CodegenContext, fctx: Functio
|
|
|
90
150
|
* sites exist (so the pass is a no-op for class-only / fnctor-free code).
|
|
91
151
|
*/
|
|
92
152
|
export declare function analyzeFnctorEscapeGate(checker: ts.TypeChecker, sourceFile: ts.SourceFile): FnctorEscapeGateResult;
|
|
153
|
+
/**
|
|
154
|
+
* #2773 S1 (keystone) — derive the WasmGC field shape of a fnctor's
|
|
155
|
+
* `$__fnctor_<Name>` struct from its constructor body's `this.<field> = …`
|
|
156
|
+
* assignments. This is the **single source of truth** for the field set,
|
|
157
|
+
* EXTRACTED verbatim from the on-demand inline logic that lived in
|
|
158
|
+
* `compileNewFunctionDeclaration` (new-super.ts) so both the up-front reservation
|
|
159
|
+
* pass and the legacy on-demand fallback produce the SAME shape — divergent field
|
|
160
|
+
* order would give `struct.new` a different arity than the reserved type and trap.
|
|
161
|
+
*
|
|
162
|
+
* Mirrors the original logic exactly:
|
|
163
|
+
* - collects EVERY `this.<field>` LHS across (possibly CHAINED) assignments
|
|
164
|
+
* (`this.a = this.b = expr`), recursing into if/else and loop blocks;
|
|
165
|
+
* - prefers the RHS type when the LHS is `any` (externref) — the RHS carries the
|
|
166
|
+
* concrete type (e.g. number → f64);
|
|
167
|
+
* - widens non-null `ref` fields to `ref_null` so `struct.new`'s `ref.null`
|
|
168
|
+
* default-init is well-typed (a struct.new can't default a non-null ref).
|
|
169
|
+
*
|
|
170
|
+
* @param ctx codegen context (for the checker + `resolveWasmType`)
|
|
171
|
+
* @param funcDecl the fnctor's function-like declaration (its body is read)
|
|
172
|
+
* @returns the ordered field set, or `[]` for a body-less / empty-body fnctor.
|
|
173
|
+
*/
|
|
174
|
+
export declare function deriveFnctorFields(ctx: CodegenContext, funcDecl: ts.FunctionDeclaration | ts.FunctionExpression): FieldDef[];
|
|
@@ -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.).
|
|
@@ -183,6 +200,41 @@ export declare function reserveTypedArraySubviewTypes(ctx: CodegenContext): void
|
|
|
183
200
|
* fallback can't fire without a class).
|
|
184
201
|
*/
|
|
185
202
|
export declare function reserveObjVecArrType(ctx: CodegenContext): void;
|
|
203
|
+
/**
|
|
204
|
+
* #2773 S1 (KEYSTONE) — reserve every reconstructed-fnctor `$__fnctor_<Name>`
|
|
205
|
+
* struct type at the deterministic up-front type-init phase (the same stable
|
|
206
|
+
* point as `reserveTypedArraySubviewTypes` / `reserveObjVecArrType`), so the type
|
|
207
|
+
* index is IDENTICAL across the hoist pass and the emit pass.
|
|
208
|
+
*
|
|
209
|
+
* ROOT CAUSE this fixes: the on-demand registration at the `new F()` call site
|
|
210
|
+
* (`compileNewFunctionDeclaration`, new-super.ts — `ctx.mod.types.length`) assigns
|
|
211
|
+
* the index at a NON-deterministic mid-compile point that depends on which
|
|
212
|
+
* function the compiler reached first. The two-pass type numbering then desyncs:
|
|
213
|
+
* a typed-receiver `ref.test $__fnctor_<Name>` baked in the hoist pass misses the
|
|
214
|
+
* emit-pass `struct.new` index, and a read site compiled before the `new` site is
|
|
215
|
+
* excluded from `findAlternateStructsForField`'s candidate set. Reserving up-front
|
|
216
|
+
* collapses BOTH facets — the index is pass-invariant AND the candidate set is
|
|
217
|
+
* complete at every read site. This is the one thing the #2674 finalize
|
|
218
|
+
* dispatcher cannot retroactively fix (it can't rewrite a baked typeIdx).
|
|
219
|
+
*
|
|
220
|
+
* Two sub-passes — the ORDER is load-bearing:
|
|
221
|
+
* (1) reserve ALL indices + names FIRST (placeholder struct, `structMap`,
|
|
222
|
+
* `typeIdxToStructName`, `fnctorReservedTypeIdx`), so a cross-fnctor ref
|
|
223
|
+
* field in sub-pass 2 (a `Parser` field typed `Scope` →
|
|
224
|
+
* `(ref null $__fnctor_Scope)`) resolves against an already-registered
|
|
225
|
+
* `structMap` entry. Do NOT collapse the two sub-passes.
|
|
226
|
+
* (2) FILL each placeholder's fields via the shared `deriveFnctorFields`
|
|
227
|
+
* (single source of truth — identical to the on-demand derivation) and
|
|
228
|
+
* record `structFields` for candidate-set completeness.
|
|
229
|
+
*
|
|
230
|
+
* Determinism: the name set is SORTED, and the call-site position is fixed, so the
|
|
231
|
+
* reserved index is identical across the hoist pass and the emit pass (the entire
|
|
232
|
+
* point of the slice). Gated on a non-empty approved set ⇒ fnctor-free modules are
|
|
233
|
+
* byte-identical (a true no-op). Runs in BOTH host and standalone — the on-demand
|
|
234
|
+
* struct path is target-independent. A reserved-but-never-constructed placeholder
|
|
235
|
+
* is unreferenced ⇒ `dead-elimination` prunes + renumbers it cleanly.
|
|
236
|
+
*/
|
|
237
|
+
export declare function reserveFnctorStructTypes(ctx: CodegenContext): void;
|
|
186
238
|
export declare function ensureLinearU8AllocHelper(ctx: CodegenContext): number;
|
|
187
239
|
/**
|
|
188
240
|
* #1618: Ensure __wasi_write_any_string(s: ref NativeString) -> void exists and
|
|
@@ -210,7 +262,7 @@ export declare function ensureWasiWriteAnyStringHelper(ctx: CodegenContext, useS
|
|
|
210
262
|
* it to the *runtime* fd. This backs the STRING overload of `node:fs`
|
|
211
263
|
* `writeSync(fd, str, position?, encoding?)`, where the fd is an arbitrary
|
|
212
264
|
* integer (not just stdout/stderr). Two modes (#2655):
|
|
213
|
-
* - shim (`--link
|
|
265
|
+
* - shim (`--link node:fs`): `writeSync(fd, ptr, len)` returns the byte count.
|
|
214
266
|
* - direct (standalone `--target wasi`): build a `{ base=ptr, len }` iovec at
|
|
215
267
|
* memory[0..7], call `fd_write(fd, iovs=0, 1, nwritten=8)`, load nwritten.
|
|
216
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
|
|
@@ -289,6 +289,6 @@ export declare function emitThisReceiverGuardConvert(ctx: CodegenContext, fctx:
|
|
|
289
289
|
* `ref.is_null`, and emit the index + read only in the non-null arm.
|
|
290
290
|
*/
|
|
291
291
|
export declare function compileOptionalElementAccess(ctx: CodegenContext, fctx: FunctionContext, expr: ts.ElementAccessExpression): ValType | null;
|
|
292
|
-
export declare function compileElementAccess(ctx: CodegenContext, fctx: FunctionContext, expr: ts.ElementAccessExpression): ValType | null;
|
|
292
|
+
export declare function compileElementAccess(ctx: CodegenContext, fctx: FunctionContext, expr: ts.ElementAccessExpression, expectedType?: ValType): ValType | null;
|
|
293
293
|
/** Inner element access logic — assumes objType is on the stack and non-null */
|
|
294
|
-
export declare function compileElementAccessBody(ctx: CodegenContext, fctx: FunctionContext, expr: ts.ElementAccessExpression, objType: ValType): ValType | null;
|
|
294
|
+
export declare function compileElementAccessBody(ctx: CodegenContext, fctx: FunctionContext, expr: ts.ElementAccessExpression, objType: ValType, expectedType?: ValType): ValType | null;
|
|
@@ -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;
|
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
import { ts } from '../../ts-api.js';
|
|
2
2
|
import { CodegenContext, FunctionContext } from '../context/types.js';
|
|
3
3
|
export declare function compileWhileStatement(ctx: CodegenContext, fctx: FunctionContext, stmt: ts.WhileStatement): void;
|
|
4
|
+
/**
|
|
5
|
+
* #1196: Detect mutations of the loop index or array binding inside a for-loop
|
|
6
|
+
* body. Used by the bounds-check elimination pass — we can only elide bounds
|
|
7
|
+
* checks for `arr[i]` if both `i` and `arr` are stable across every iteration.
|
|
8
|
+
*
|
|
9
|
+
* Returns `true` if the body contains anything that could mutate either
|
|
10
|
+
* binding:
|
|
11
|
+
* - Direct assignment / compound assignment to `i` or `arr`
|
|
12
|
+
* (`i = …`, `i += …`, `arr = …`, etc.)
|
|
13
|
+
* - `i++ / ++i / i-- / --i` or the same on `arr`
|
|
14
|
+
* - Method calls on `arr` (`arr.push()`, `arr.length = …`, etc.)
|
|
15
|
+
* - `arr.length = …` assignment
|
|
16
|
+
* - Any nested function / arrow / class — closures could capture and mutate
|
|
17
|
+
* either binding outside our static view (conservative).
|
|
18
|
+
*
|
|
19
|
+
* Notes:
|
|
20
|
+
* - `arr[k] = v` writes through the array but does not change the binding
|
|
21
|
+
* itself or `arr.length` (when `k < arr.length`), so element writes are
|
|
22
|
+
* allowed — they're the whole point of the optimisation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loopBodyMutatesIndexOrArray(body: ts.Statement, indexName: string, arrayName: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* #2682: the increment must strictly INCREASE `i` so that, combined with a
|
|
27
|
+
* non-negative init and the strict `i < recv.length` condition, `0 <= i < len`
|
|
28
|
+
* holds at every body point. `i++`/`++i` and `i += <positive int literal>`
|
|
29
|
+
* qualify; `i--`/`i -= k`/`i += <non-positive>` do NOT (would break the proof).
|
|
30
|
+
* Narrower than `detectI32LoopVar`'s incrementor check, which also accepts the
|
|
31
|
+
* decreasing forms.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isIncreasingStep(incr: ts.Expression | undefined, name: string): boolean;
|
|
4
34
|
export declare function compileForStatement(ctx: CodegenContext, fctx: FunctionContext, stmt: ts.ForStatement): void;
|
|
5
35
|
export declare function compileDoWhileStatement(ctx: CodegenContext, fctx: FunctionContext, stmt: ts.DoStatement): void;
|
|
6
36
|
export declare function compileForOfStatement(ctx: CodegenContext, fctx: FunctionContext, stmt: ts.ForOfStatement): void;
|