@polyengine/runtime 0.1.0-pre.g633468a
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/README.md +23 -0
- package/esm/cabi/async_values.js +162 -0
- package/esm/cabi/bulk_lists.js +198 -0
- package/esm/cabi/context.js +42 -0
- package/esm/cabi/flatten.js +145 -0
- package/esm/cabi/float.js +67 -0
- package/esm/cabi/handles.js +253 -0
- package/esm/cabi/layout.js +149 -0
- package/esm/cabi/lift.js +196 -0
- package/esm/cabi/load.js +146 -0
- package/esm/cabi/lower.js +141 -0
- package/esm/cabi/memory.js +182 -0
- package/esm/cabi/mod.js +22 -0
- package/esm/cabi/store.js +186 -0
- package/esm/cabi/strings.js +336 -0
- package/esm/cabi/trap.js +38 -0
- package/esm/cabi/types.js +264 -0
- package/esm/cabi/values.js +64 -0
- package/esm/cache/core.js +156 -0
- package/esm/cache/dir.js +170 -0
- package/esm/cache/mod.js +4 -0
- package/esm/cache/web.js +136 -0
- package/esm/digest/digest.js +332 -0
- package/esm/digest/mod.js +3 -0
- package/esm/digest/verify.js +129 -0
- package/esm/embedder/casing.js +56 -0
- package/esm/embedder/copy.js +42 -0
- package/esm/embedder/errors.js +26 -0
- package/esm/embedder/imports.js +63 -0
- package/esm/embedder/instantiate.js +978 -0
- package/esm/embedder/mod.js +40 -0
- package/esm/embedder/resources.js +406 -0
- package/esm/embedder/streams.js +770 -0
- package/esm/embedder/values.js +450 -0
- package/esm/embedder/version.js +273 -0
- package/esm/exec/boundary.js +1902 -0
- package/esm/exec/executor.js +1004 -0
- package/esm/exec/host_streams.js +818 -0
- package/esm/exec/mod.js +4 -0
- package/esm/intrinsics/async_builtins.js +510 -0
- package/esm/intrinsics/context.js +90 -0
- package/esm/intrinsics/errors.js +13 -0
- package/esm/intrinsics/fact_calls.js +865 -0
- package/esm/intrinsics/mod.js +564 -0
- package/esm/intrinsics/stream_builtins.js +578 -0
- package/esm/intrinsics/transcode.js +443 -0
- package/esm/jspi/bridge.js +579 -0
- package/esm/jspi/mechanics.js +89 -0
- package/esm/jspi/mod.js +5 -0
- package/esm/jspi/suspending.js +15 -0
- package/esm/jspi/types.js +29 -0
- package/esm/package.json +3 -0
- package/esm/plan/format.js +5 -0
- package/esm/plan/loader.js +657 -0
- package/esm/plan/mod.js +3 -0
- package/esm/shim/mod.js +2 -0
- package/esm/shim/translator.js +94 -0
- package/esm/task/mod.js +483 -0
- package/esm/task/scheduler.js +1028 -0
- package/esm/task/streams.js +786 -0
- package/esm/task/subtask.js +135 -0
- package/esm/task/thread.js +255 -0
- package/esm/task/waitable.js +144 -0
- package/package.json +91 -0
- package/types/cabi/async_values.d.ts +35 -0
- package/types/cabi/bulk_lists.d.ts +18 -0
- package/types/cabi/context.d.ts +59 -0
- package/types/cabi/flatten.d.ts +14 -0
- package/types/cabi/float.d.ts +14 -0
- package/types/cabi/handles.d.ts +70 -0
- package/types/cabi/layout.d.ts +13 -0
- package/types/cabi/lift.d.ts +25 -0
- package/types/cabi/load.d.ts +14 -0
- package/types/cabi/lower.d.ts +10 -0
- package/types/cabi/memory.d.ts +58 -0
- package/types/cabi/mod.d.ts +15 -0
- package/types/cabi/store.d.ts +12 -0
- package/types/cabi/strings.d.ts +23 -0
- package/types/cabi/trap.d.ts +11 -0
- package/types/cabi/types.d.ts +206 -0
- package/types/cabi/values.d.ts +5 -0
- package/types/cache/core.d.ts +97 -0
- package/types/cache/dir.d.ts +6 -0
- package/types/cache/mod.d.ts +3 -0
- package/types/cache/web.d.ts +10 -0
- package/types/digest/digest.d.ts +17 -0
- package/types/digest/mod.d.ts +2 -0
- package/types/digest/verify.d.ts +48 -0
- package/types/embedder/casing.d.ts +40 -0
- package/types/embedder/copy.d.ts +24 -0
- package/types/embedder/errors.d.ts +11 -0
- package/types/embedder/imports.d.ts +47 -0
- package/types/embedder/instantiate.d.ts +88 -0
- package/types/embedder/mod.d.ts +11 -0
- package/types/embedder/resources.d.ts +158 -0
- package/types/embedder/streams.d.ts +202 -0
- package/types/embedder/values.d.ts +70 -0
- package/types/embedder/version.d.ts +85 -0
- package/types/exec/boundary.d.ts +360 -0
- package/types/exec/executor.d.ts +125 -0
- package/types/exec/host_streams.d.ts +165 -0
- package/types/exec/mod.d.ts +3 -0
- package/types/intrinsics/async_builtins.d.ts +69 -0
- package/types/intrinsics/context.d.ts +28 -0
- package/types/intrinsics/errors.d.ts +5 -0
- package/types/intrinsics/fact_calls.d.ts +120 -0
- package/types/intrinsics/mod.d.ts +187 -0
- package/types/intrinsics/stream_builtins.d.ts +113 -0
- package/types/intrinsics/transcode.d.ts +21 -0
- package/types/jspi/bridge.d.ts +227 -0
- package/types/jspi/mechanics.d.ts +50 -0
- package/types/jspi/mod.d.ts +3 -0
- package/types/jspi/suspending.d.ts +1 -0
- package/types/jspi/types.d.ts +26 -0
- package/types/plan/format.d.ts +369 -0
- package/types/plan/loader.d.ts +113 -0
- package/types/plan/mod.d.ts +2 -0
- package/types/shim/mod.d.ts +1 -0
- package/types/shim/translator.d.ts +55 -0
- package/types/task/mod.d.ts +257 -0
- package/types/task/scheduler.d.ts +421 -0
- package/types/task/streams.d.ts +370 -0
- package/types/task/subtask.d.ts +96 -0
- package/types/task/thread.d.ts +73 -0
- package/types/task/waitable.d.ts +67 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { type SuspendingImport } from "./mechanics.js";
|
|
2
|
+
import type { Cancelled, SchedulableThread, Store } from "../task/mod.js";
|
|
3
|
+
/** Which suspension discipline an instantiation runs under. */
|
|
4
|
+
export type SuspensionMode = "plain" | "jspi";
|
|
5
|
+
/**
|
|
6
|
+
* Decide the mode for one instantiation.
|
|
7
|
+
*
|
|
8
|
+
* `requested` is the embedder's opt-in. We additionally require the engine to
|
|
9
|
+
* actually implement JSPI: on an engine without it every blocking site falls
|
|
10
|
+
* back to the precise `NeedsJspi` it raised before this module existed, which
|
|
11
|
+
* is the M3 browser-matrix degradation path (docs/milestones.md M3).
|
|
12
|
+
*/
|
|
13
|
+
export declare function chooseMode(requested: boolean | undefined, needed?: boolean): SuspensionMode;
|
|
14
|
+
/**
|
|
15
|
+
* Does a call through this trampoline declaration genuinely block — i.e. is
|
|
16
|
+
* it a reason a component NEEDS suspension support at all?
|
|
17
|
+
*
|
|
18
|
+
* Sharper than a kind list, and it must be: the async *form* of every copy /
|
|
19
|
+
* cancel built-in NEVER blocks — it returns `BLOCKED` and delivers the result
|
|
20
|
+
* through an event (definitions.py `stream_copy` line 2530 / `cancel_copy`
|
|
21
|
+
* 2636 / `canon_subtask_cancel` 2469, each `if not async_: ... else return
|
|
22
|
+
* BLOCKED`). Classifying by kind alone marked every instance that imports an
|
|
23
|
+
* async-form built-in as suspension-capable, which promising-wrapped its
|
|
24
|
+
* eagerly-completing FACT callees — and a wrapped eager callee reports
|
|
25
|
+
* STARTED where the reference reports RETURNED, and parks on a
|
|
26
|
+
* non-cancellable `awaitValue` where the reference delivers a synchronous
|
|
27
|
+
* cancellation (both asserted by big-interleaving-test.wast's expect-codes).
|
|
28
|
+
*
|
|
29
|
+
* Where the async-ness lives varies by kind, following wasmtime's trampoline
|
|
30
|
+
* layout (component/info.rs): copy built-ins carry an options index (the
|
|
31
|
+
* flag is `canonicalOptions[i].async`); the cancel forms and `subtask.cancel`
|
|
32
|
+
* carry `async` on the declaration itself; `waitable-set.wait`,
|
|
33
|
+
* `thread.yield` and `sync-start-call` are unconditionally block-capable
|
|
34
|
+
* (their non-blocking counterparts are separate kinds: poll, the YIELD
|
|
35
|
+
* callback code, async-start-call).
|
|
36
|
+
*/
|
|
37
|
+
export declare function trampolineNeedsSuspension(t: {
|
|
38
|
+
kind: string;
|
|
39
|
+
async?: unknown;
|
|
40
|
+
options?: unknown;
|
|
41
|
+
}, optionsAsync: (index: number) => boolean): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Must the executor hand this trampoline to wasm as a `Suspending` import in
|
|
44
|
+
* jspi mode?
|
|
45
|
+
*
|
|
46
|
+
* A superset of `trampolineNeedsSuspension` by exactly two kinds, both for
|
|
47
|
+
* the same reason: they never block in the reference or in plain mode, but
|
|
48
|
+
* under jspi they may park the CALLER until a callee's state is determinate,
|
|
49
|
+
* because the engine defers a resumed activation's continuation to a
|
|
50
|
+
* microtask (jspi pin (j), `fastpath_hop_test.ts`):
|
|
51
|
+
*
|
|
52
|
+
* * `async-start-call` — parks until the freshly-started callee reaches
|
|
53
|
+
* resolution / completion / a genuine block (fact_calls.ts);
|
|
54
|
+
* * the async form of `subtask.cancel` — parks until a cancellation
|
|
55
|
+
* delivered by settling the callee's suspension has actually landed
|
|
56
|
+
* (async_builtins.ts; cancellable.wast asserts the reference's
|
|
57
|
+
* synchronous-delivery answers).
|
|
58
|
+
*
|
|
59
|
+
* Neither is a *reason* to choose jspi mode, and neither marks its importer
|
|
60
|
+
* suspendable (`Executor.suspendableFuncs`): their parks only ever trigger
|
|
61
|
+
* when the nested callee is itself promising-wrapped, i.e. when a genuine
|
|
62
|
+
* blocker has already contaminated the adapter through the transitive import
|
|
63
|
+
* rule. Marking on these kinds is not only unnecessary — it is wrong: the
|
|
64
|
+
* FACT adapter's `[adapter-callee]*` pass-through exports are what get
|
|
65
|
+
* passed to `*-start-call` as lift callees, and marking the whole adapter
|
|
66
|
+
* instance promoted every eagerly-completing callee to promising, recreating
|
|
67
|
+
* the STARTED-vs-RETURNED divergence one level up.
|
|
68
|
+
*/
|
|
69
|
+
export declare function trampolineCanBlock(t: {
|
|
70
|
+
kind: string;
|
|
71
|
+
async?: unknown;
|
|
72
|
+
options?: unknown;
|
|
73
|
+
}, optionsAsync: (index: number) => boolean): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Does this component contain anything that can block a wasm frame?
|
|
76
|
+
*
|
|
77
|
+
* Computed from the plan, on the runtime side, so embedders (and the
|
|
78
|
+
* conformance harness) stay dumb — no plan v3 field, no flag to thread
|
|
79
|
+
* through. Deliberately a slight over-approximation: it asks "could this
|
|
80
|
+
* component ever reach a blocking built-in", not "will this call". A false
|
|
81
|
+
* positive costs a promising-wrapped entry (the export returns a Promise); a
|
|
82
|
+
* false negative would be a hard trap at the blocking site, so the bias is the
|
|
83
|
+
* safe one.
|
|
84
|
+
*
|
|
85
|
+
* The two sources of blocking, both straight from the reference:
|
|
86
|
+
*
|
|
87
|
+
* * a **stackful async lift** — async canonical options with no callback
|
|
88
|
+
* (`canon_lift` line 2179 runs the callee to completion on its own stack);
|
|
89
|
+
* * a **blocking built-in** reached synchronously — see
|
|
90
|
+
* `trampolineNeedsSuspension`.
|
|
91
|
+
*/
|
|
92
|
+
export declare function planNeedsSuspension(plan: {
|
|
93
|
+
canonicalOptions: {
|
|
94
|
+
async: boolean;
|
|
95
|
+
callback: number | null;
|
|
96
|
+
}[];
|
|
97
|
+
trampolines: {
|
|
98
|
+
kind: string;
|
|
99
|
+
async?: unknown;
|
|
100
|
+
options?: unknown;
|
|
101
|
+
}[];
|
|
102
|
+
}): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Wrap a JS→wasm entry according to the mode.
|
|
105
|
+
*
|
|
106
|
+
* In `plain` mode this is the identity. In `jspi` mode the returned function
|
|
107
|
+
* always yields a Promise (empirical fact (e)), which is why the mode is an
|
|
108
|
+
* embedder opt-in: it changes the shape of every lifted export.
|
|
109
|
+
*/
|
|
110
|
+
export declare function enterWasm<T extends (...a: never[]) => unknown>(fn: T, mode: SuspensionMode): T;
|
|
111
|
+
export declare function suspendingImport<T extends (...a: never[]) => unknown>(fn: T, mode: SuspensionMode): T | SuspendingImport;
|
|
112
|
+
/**
|
|
113
|
+
* Fail loudly at instantiate time if the two wrapping sites disagree.
|
|
114
|
+
*
|
|
115
|
+
* The dangerous direction, per jspi pin (c), is a `Suspending` import
|
|
116
|
+
* reachable from a non-`promising` activation — that traps unconditionally,
|
|
117
|
+
* even on the plain-value path. Entry wrapping in jspi mode is unconditional
|
|
118
|
+
* (every lifted export, callback, and block-capable FACT callee), so the
|
|
119
|
+
* structural invariant is `importsWrapped ⇒ entriesWrapped`, per mode.
|
|
120
|
+
*
|
|
121
|
+
* Entries-without-imports is legitimate: per-declaration classification
|
|
122
|
+
* (`trampolineNeedsSuspension`) wraps no imports in a component whose
|
|
123
|
+
* built-ins are all non-blocking async forms, while the mode can still be
|
|
124
|
+
* jspi via the lift-shape over-approximation in `planNeedsSuspension`
|
|
125
|
+
* (canonical options carrying `async` with no callback are counted whether
|
|
126
|
+
* they belong to a lift or to a copy built-in — the plan does not say which,
|
|
127
|
+
* and the false positive only costs Promise-shaped exports).
|
|
128
|
+
*/
|
|
129
|
+
export declare function assertModeConsistent(mode: SuspensionMode, entriesWrapped: boolean, importsWrapped: boolean): void;
|
|
130
|
+
export declare class SuspensionPoint<T = unknown> implements SchedulableThread {
|
|
131
|
+
#private;
|
|
132
|
+
readonly task: any;
|
|
133
|
+
/** Resumable once this holds; `null` = only an explicit resume. */
|
|
134
|
+
readonly readyFunc: (() => boolean) | null;
|
|
135
|
+
readonly cancellable: boolean;
|
|
136
|
+
/** Produces the value to hand back to wasm at resume time. */
|
|
137
|
+
private readonly produce;
|
|
138
|
+
/**
|
|
139
|
+
* `finally`-style hook: runs EXACTLY ONCE, on whichever terminal
|
|
140
|
+
* transition this point takes — produce-success, produce-throw, or
|
|
141
|
+
* `abandon` (issue #102). It is the seam a blocking built-in uses to
|
|
142
|
+
* discharge state it owns for the duration of the park (the FACT
|
|
143
|
+
* start-calls' borrow-lender scopes, contracts/intrinsics.md v0.2
|
|
144
|
+
* amendment 2) without having to trust that `produce` runs.
|
|
145
|
+
*
|
|
146
|
+
* INVARIANTS this hook must respect, so bridge.ts's own contracts are
|
|
147
|
+
* not disturbed:
|
|
148
|
+
* * it must not throw (a throw here would escape `resume` *after* the
|
|
149
|
+
* import's Promise was settled, i.e. into whatever drained the
|
|
150
|
+
* scheduler); it is called inside a `try`/`catch` that reports such
|
|
151
|
+
* a throw rather than propagating it;
|
|
152
|
+
* * it must be idempotent-safe by construction anyway, because it runs
|
|
153
|
+
* AFTER `produce` on the success path — a built-in that already did
|
|
154
|
+
* its cleanup inside `produce` (to pin cleanup ordering relative to
|
|
155
|
+
* the produced value) sees this as a no-op backstop;
|
|
156
|
+
* * it must not resume/abandon this or any other suspension point.
|
|
157
|
+
*/
|
|
158
|
+
private readonly onSettled?;
|
|
159
|
+
readonly promise: Promise<T>;
|
|
160
|
+
/**
|
|
161
|
+
* WHO the engine will resume when this point's promise settles.
|
|
162
|
+
*
|
|
163
|
+
* Captured HERE, at construction, and not derived at resume time: the
|
|
164
|
+
* blocking built-in that mints this point is running under the suspending
|
|
165
|
+
* activation's own ambient, so the ambient names that activation exactly.
|
|
166
|
+
* This is the replacement for the async-context store the scheduler
|
|
167
|
+
* used to rely on (M3A-1): same value, obtained by construction instead of
|
|
168
|
+
* by asking the platform to carry a context across the engine's resumption.
|
|
169
|
+
* `task.implicitThread` is the fallback for the one shape that has no
|
|
170
|
+
* ambient at all — a built-in reached during instantiation.
|
|
171
|
+
*/
|
|
172
|
+
readonly owner: any;
|
|
173
|
+
constructor(store: Store, task: any,
|
|
174
|
+
/** Resumable once this holds; `null` = only an explicit resume. */
|
|
175
|
+
readyFunc: (() => boolean) | null, cancellable: boolean,
|
|
176
|
+
/** Produces the value to hand back to wasm at resume time. */
|
|
177
|
+
produce: (cancelled: Cancelled) => T, owner?: any,
|
|
178
|
+
/**
|
|
179
|
+
* `finally`-style hook: runs EXACTLY ONCE, on whichever terminal
|
|
180
|
+
* transition this point takes — produce-success, produce-throw, or
|
|
181
|
+
* `abandon` (issue #102). It is the seam a blocking built-in uses to
|
|
182
|
+
* discharge state it owns for the duration of the park (the FACT
|
|
183
|
+
* start-calls' borrow-lender scopes, contracts/intrinsics.md v0.2
|
|
184
|
+
* amendment 2) without having to trust that `produce` runs.
|
|
185
|
+
*
|
|
186
|
+
* INVARIANTS this hook must respect, so bridge.ts's own contracts are
|
|
187
|
+
* not disturbed:
|
|
188
|
+
* * it must not throw (a throw here would escape `resume` *after* the
|
|
189
|
+
* import's Promise was settled, i.e. into whatever drained the
|
|
190
|
+
* scheduler); it is called inside a `try`/`catch` that reports such
|
|
191
|
+
* a throw rather than propagating it;
|
|
192
|
+
* * it must be idempotent-safe by construction anyway, because it runs
|
|
193
|
+
* AFTER `produce` on the success path — a built-in that already did
|
|
194
|
+
* its cleanup inside `produce` (to pin cleanup ordering relative to
|
|
195
|
+
* the produced value) sees this as a no-op backstop;
|
|
196
|
+
* * it must not resume/abandon this or any other suspension point.
|
|
197
|
+
*/
|
|
198
|
+
onSettled?: (() => void) | undefined);
|
|
199
|
+
waiting(): boolean;
|
|
200
|
+
ready(): boolean;
|
|
201
|
+
/** Settle the import's Promise; the engine resumes the wasm activation. */
|
|
202
|
+
resume(cancelled?: Cancelled): void;
|
|
203
|
+
/** Abandon this suspension without resuming the guest (teardown paths). */
|
|
204
|
+
abandon(reason: unknown): void;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The single shape every blocking built-in needs: "park the calling wasm frame
|
|
208
|
+
* until `readyFunc` holds, then hand it `produce()`".
|
|
209
|
+
*
|
|
210
|
+
* In `plain` mode the caller must not reach here — it raises `NeedsJspi` at
|
|
211
|
+
* its own site, with a message naming the operation, exactly as before. In
|
|
212
|
+
* `jspi` mode this returns a Promise, and because the built-in was
|
|
213
|
+
* `Suspending`-wrapped the engine suspends the activation on it.
|
|
214
|
+
*/
|
|
215
|
+
export declare function blockCurrentActivation<T>(input: {
|
|
216
|
+
store: Store;
|
|
217
|
+
task: any;
|
|
218
|
+
readyFunc: (() => boolean) | null;
|
|
219
|
+
cancellable: boolean;
|
|
220
|
+
produce: (cancelled: Cancelled) => T;
|
|
221
|
+
/**
|
|
222
|
+
* Optional `finally`-style hook — see `SuspensionPoint.onSettled`. Use it
|
|
223
|
+
* for state that must be discharged however the park ends, including the
|
|
224
|
+
* settle paths that never call `produce` (issue #102).
|
|
225
|
+
*/
|
|
226
|
+
onSettled?: () => void;
|
|
227
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opaque public alias for `WebAssembly.Suspending` instances.
|
|
3
|
+
*
|
|
4
|
+
* JSPI is not in the standard TS libs and JSR forbids global-type
|
|
5
|
+
* augmentation, so the engine surface is module-scoped (`jspiApi()` in
|
|
6
|
+
* ./types.ts) and the public API names this opaque brand; the value is
|
|
7
|
+
* exactly a `WebAssembly.Suspending`, usable anywhere an import value is
|
|
8
|
+
* expected.
|
|
9
|
+
*/
|
|
10
|
+
export type SuspendingImport = {
|
|
11
|
+
readonly __polyengineSuspending: unique symbol;
|
|
12
|
+
};
|
|
13
|
+
/** True if the current engine implements `WebAssembly.promising` and
|
|
14
|
+
* `WebAssembly.Suspending`. Both are phase-4 API surface (docs/architecture.md §3): no
|
|
15
|
+
* fallback path exists or is planned for engines without them. */
|
|
16
|
+
export declare function isSupported(): boolean;
|
|
17
|
+
/** Throws if JSPI is not available in the current engine. Call this before
|
|
18
|
+
* using anything else in this module if you want a clear error instead of a
|
|
19
|
+
* `TypeError: WebAssembly.promising is not a function`. */
|
|
20
|
+
export declare function assertSupported(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a wasm-exported function (as retrieved from
|
|
23
|
+
* `instance.exports.someExport`) so that calling it:
|
|
24
|
+
* - always returns a Promise,
|
|
25
|
+
* - suspends the underlying wasm activation (rather than trapping) the
|
|
26
|
+
* first time it calls a `Suspending`-wrapped import that itself returns
|
|
27
|
+
* a genuine Promise,
|
|
28
|
+
* - resolves that Promise with the export's return value once the wasm
|
|
29
|
+
* activation runs to completion (after zero or more suspend/resume
|
|
30
|
+
* cycles).
|
|
31
|
+
*
|
|
32
|
+
* This is a direct type-safe pass-through of `WebAssembly.promising`; it
|
|
33
|
+
* does no extra bookkeeping. `TArgs`/`TReturn` are the caller's own
|
|
34
|
+
* annotation of the underlying export's signature (not reflected — see
|
|
35
|
+
* docs/architecture.md §3's note on js-types being flagged/phase-3).
|
|
36
|
+
*/
|
|
37
|
+
export declare function makePromising<TArgs extends unknown[] = unknown[], TReturn = unknown>(wasmExport: (...args: TArgs) => TReturn): (...args: TArgs) => Promise<TReturn>;
|
|
38
|
+
/**
|
|
39
|
+
* Wrap a JS function as a `Suspending` import: when a `promising`-suspendable
|
|
40
|
+
* wasm activation calls it (per the frame rule above) and it returns a
|
|
41
|
+
* genuine Promise, the wasm activation suspends until that Promise settles;
|
|
42
|
+
* if it returns a non-Promise value (or the call site is not suspension-
|
|
43
|
+
* eligible), the value passes straight through — the "fast path", pinned in
|
|
44
|
+
* `suspending_import_test.ts::non_promise_return_is_fast_path`.
|
|
45
|
+
*
|
|
46
|
+
* The returned value is an opaque `WebAssembly.Suspending` instance; hand it
|
|
47
|
+
* directly to the instantiation `imports` object in the slot the wasm module
|
|
48
|
+
* expects a function import.
|
|
49
|
+
*/
|
|
50
|
+
export declare function makeSuspending<TArgs extends unknown[] = unknown[], TReturn = unknown>(fn: (...args: TArgs) => TReturn | Promise<TReturn>): SuspendingImport;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { anySuspendingImport, isSuspending, suspending } from "@polyengine/protocol";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine's JSPI surface, as probed.
|
|
3
|
+
*
|
|
4
|
+
* `Suspending` wraps a JS function that returns a Promise (or any value —
|
|
5
|
+
* the fast path pinned in `suspending_import_test.ts`) so it can be called
|
|
6
|
+
* as a wasm import that may suspend the calling wasm activation. It must
|
|
7
|
+
* only actually suspend while every frame between the nearest enclosing
|
|
8
|
+
* `promising`-wrapped entry and the call is a wasm frame — the "frame
|
|
9
|
+
* rule" (docs/architecture.md §5; `frame_rule_test.ts` pins the observed
|
|
10
|
+
* error shape).
|
|
11
|
+
*
|
|
12
|
+
* `promising` wraps a wasm-exported function so that calling it returns a
|
|
13
|
+
* Promise instead of (potentially) suspending the JS caller: the wasm
|
|
14
|
+
* activation becomes suspendable, and any `Suspending` import it calls
|
|
15
|
+
* transfers control back to the event loop instead of trapping.
|
|
16
|
+
*/
|
|
17
|
+
export interface JspiApi {
|
|
18
|
+
Suspending: new (fn: (...args: any[]) => any) => object;
|
|
19
|
+
promising: (fn: (...args: any[]) => any) => (...args: any[]) => Promise<any>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The engine's JSPI API, or `null` where the proposal is not implemented.
|
|
23
|
+
* The one sanctioned way to reach `WebAssembly.Suspending`/`promising`
|
|
24
|
+
* from this codebase's published modules.
|
|
25
|
+
*/
|
|
26
|
+
export declare function jspiApi(): JspiApi | null;
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/** Core wasm lane types as emitted in `coreType` and `rep` fields. */
|
|
2
|
+
export type WireCoreType = "i32" | "i64" | "f32" | "f64";
|
|
3
|
+
export interface WirePlan {
|
|
4
|
+
formatVersion: number;
|
|
5
|
+
producer: {
|
|
6
|
+
shimVersion: string;
|
|
7
|
+
wasmtimeEnviron: string;
|
|
8
|
+
features: string[];
|
|
9
|
+
};
|
|
10
|
+
component: {
|
|
11
|
+
sha256: string;
|
|
12
|
+
len: number;
|
|
13
|
+
};
|
|
14
|
+
modules: WireModule[];
|
|
15
|
+
initializers: WireInitializer[];
|
|
16
|
+
trampolines: WireTrampoline[];
|
|
17
|
+
canonicalOptions: WireCanonicalOptions[];
|
|
18
|
+
types: WireTypeDecl[];
|
|
19
|
+
resourceTables: WireResourceTable[];
|
|
20
|
+
/**
|
|
21
|
+
* Stream-table metadata (plan v2), index space == wasmtime's
|
|
22
|
+
* `TypeStreamTableIndex`; referenced by the `streamTable` field of every
|
|
23
|
+
* `stream.*` trampoline. `element` is the `T` of `stream<T>`, `null` for the
|
|
24
|
+
* zero-width payload.
|
|
25
|
+
*
|
|
26
|
+
* ISSUE #94(2): the shim never `skip_serializing_if`s this field
|
|
27
|
+
* (crates/translator-shim/src/plan.rs), so every v2 plan the producer
|
|
28
|
+
* emits carries it (`[]` when empty). Required, not optional: the loader
|
|
29
|
+
* only ever accepts `formatVersion === 2` (strict equality,
|
|
30
|
+
* `SUPPORTED_FORMAT_VERSION`), so there is no live v1-compat path that
|
|
31
|
+
* needs this to be absent.
|
|
32
|
+
*/
|
|
33
|
+
streamTables: WireAsyncTable[];
|
|
34
|
+
/** Future-table metadata (plan v2); see `streamTables`. */
|
|
35
|
+
futureTables: WireAsyncTable[];
|
|
36
|
+
/**
|
|
37
|
+
* Error-context-table metadata (plan v3), index space == wasmtime's
|
|
38
|
+
* `TypeComponentLocalErrorContextTableIndex` — the space the
|
|
39
|
+
* `error-context-transfer` trampoline's `srcTable`/`dstTable` *runtime*
|
|
40
|
+
* arguments live in. No element type: wasmtime's `TypeErrorContextTable`
|
|
41
|
+
* is `{ instance }` and nothing else.
|
|
42
|
+
*
|
|
43
|
+
* Required for the same reason as `streamTables`/`futureTables`: the shim
|
|
44
|
+
* always serializes it and the loader accepts only `formatVersion === 3`.
|
|
45
|
+
*/
|
|
46
|
+
errorContextTables: WireErrorContextTable[];
|
|
47
|
+
/**
|
|
48
|
+
* Resource types the component imports, in `ResourceIndex` order:
|
|
49
|
+
* `ResourceIndex = importedResources.length + DefinedResourceIndex`
|
|
50
|
+
* (wasmtime `Component::resource_index`).
|
|
51
|
+
*
|
|
52
|
+
* plan-format.md v0.1 amendment #2 documents this as a *gap*; the field is
|
|
53
|
+
* a **v0.2 proposal** emitted by the shim. Optional here so plans produced
|
|
54
|
+
* by a v0.1 shim still load — absent is read as "no imported resources",
|
|
55
|
+
* which is exactly what v0.1 asserted.
|
|
56
|
+
*/
|
|
57
|
+
importedResources?: WireImportedResource[];
|
|
58
|
+
imports: WireImport[];
|
|
59
|
+
exports: WireExport[];
|
|
60
|
+
worldDigest: string;
|
|
61
|
+
}
|
|
62
|
+
export type WireModule = {
|
|
63
|
+
kind: "embedded";
|
|
64
|
+
offset: number;
|
|
65
|
+
len: number;
|
|
66
|
+
} | {
|
|
67
|
+
kind: "adapter";
|
|
68
|
+
file: string;
|
|
69
|
+
len: number;
|
|
70
|
+
intrinsics: WireIntrinsicEntry[];
|
|
71
|
+
};
|
|
72
|
+
export interface WireIntrinsicEntry {
|
|
73
|
+
module: string;
|
|
74
|
+
name: string;
|
|
75
|
+
category: string;
|
|
76
|
+
def: WireCoreDef;
|
|
77
|
+
}
|
|
78
|
+
export type WireInitializer = {
|
|
79
|
+
op: "instantiate-module";
|
|
80
|
+
module: number;
|
|
81
|
+
instance: number | null;
|
|
82
|
+
args: WireCoreDef[];
|
|
83
|
+
} | {
|
|
84
|
+
op: "lower-import";
|
|
85
|
+
index: number;
|
|
86
|
+
import: number;
|
|
87
|
+
} | {
|
|
88
|
+
op: "extract-memory";
|
|
89
|
+
index: number;
|
|
90
|
+
export: WireCoreExport;
|
|
91
|
+
} | {
|
|
92
|
+
op: "extract-realloc";
|
|
93
|
+
index: number;
|
|
94
|
+
def: WireCoreDef;
|
|
95
|
+
} | {
|
|
96
|
+
op: "extract-callback";
|
|
97
|
+
index: number;
|
|
98
|
+
def: WireCoreDef;
|
|
99
|
+
} | {
|
|
100
|
+
op: "extract-post-return";
|
|
101
|
+
index: number;
|
|
102
|
+
def: WireCoreDef;
|
|
103
|
+
} | {
|
|
104
|
+
op: "extract-table";
|
|
105
|
+
index: number;
|
|
106
|
+
export: WireCoreExport;
|
|
107
|
+
} | {
|
|
108
|
+
op: "resource";
|
|
109
|
+
index: number;
|
|
110
|
+
rep: WireCoreType;
|
|
111
|
+
dtor: WireCoreDef | null;
|
|
112
|
+
instance: number;
|
|
113
|
+
};
|
|
114
|
+
export type WireCoreDef = {
|
|
115
|
+
kind: "export";
|
|
116
|
+
instance: number;
|
|
117
|
+
item: WireExportItem;
|
|
118
|
+
} | {
|
|
119
|
+
kind: "instance-flags";
|
|
120
|
+
instance: number;
|
|
121
|
+
} | {
|
|
122
|
+
kind: "trampoline";
|
|
123
|
+
index: number;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* `CoreDef::UnsafeIntrinsic` (plan v1 / contracts/plan-format.md v0.3).
|
|
127
|
+
* `intrinsic` is wasmtime's stable symbol name
|
|
128
|
+
* (`UnsafeIntrinsic::name()`), not an enum ordinal. The executor
|
|
129
|
+
* materializes `context-{get,set}-i32-{0,1}` as host functions over the
|
|
130
|
+
* current thread's context storage (definitions.py `canon_context_get` /
|
|
131
|
+
* `canon_context_set`, lines 2348/2358) and fails at instantiate time on
|
|
132
|
+
* every other symbol.
|
|
133
|
+
*/
|
|
134
|
+
| {
|
|
135
|
+
kind: "unsafe-intrinsic";
|
|
136
|
+
intrinsic: string;
|
|
137
|
+
} | {
|
|
138
|
+
kind: "task-may-block";
|
|
139
|
+
};
|
|
140
|
+
export interface WireCoreExport {
|
|
141
|
+
instance: number;
|
|
142
|
+
item: WireExportItem;
|
|
143
|
+
}
|
|
144
|
+
export interface WireExportItem {
|
|
145
|
+
name: string;
|
|
146
|
+
space: "func" | "table" | "memory" | "global" | "tag" | "unknown";
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Trampoline declarations, tag-for-tag with the wasmtime `Trampoline` enum.
|
|
150
|
+
* Only the M0-relevant variants are given precise field types; the rest are
|
|
151
|
+
* matched by `kind` and rejected at instantiate time with milestone-aware
|
|
152
|
+
* errors (contracts/intrinsics.md §B).
|
|
153
|
+
*/
|
|
154
|
+
export type WireTrampoline = {
|
|
155
|
+
kind: "lower-import";
|
|
156
|
+
index: number;
|
|
157
|
+
lowered: number;
|
|
158
|
+
options: number;
|
|
159
|
+
type: number;
|
|
160
|
+
} | {
|
|
161
|
+
kind: "trap";
|
|
162
|
+
index: number;
|
|
163
|
+
} | {
|
|
164
|
+
kind: "enter-sync-call";
|
|
165
|
+
index: number;
|
|
166
|
+
} | {
|
|
167
|
+
kind: "exit-sync-call";
|
|
168
|
+
index: number;
|
|
169
|
+
} | {
|
|
170
|
+
kind: "task-return";
|
|
171
|
+
index: number;
|
|
172
|
+
instance: number;
|
|
173
|
+
/**
|
|
174
|
+
* The **raw** wasmtime `TypeTupleIndex` of the task's declared results
|
|
175
|
+
* (plan v3; in v2 this field held the interned `plan.types` index that
|
|
176
|
+
* `resultType` now carries). It is the key FACT's `prepare-call` passes
|
|
177
|
+
* as `task_return_type` at runtime, so it is what lets a FACT callee task
|
|
178
|
+
* find its own declared result type.
|
|
179
|
+
*/
|
|
180
|
+
results: number;
|
|
181
|
+
/**
|
|
182
|
+
* `results` interned into `plan.types` as a tuple type (plan v3,
|
|
183
|
+
* contracts/plan-format.md v3 amendment 3). `null` is accepted on the
|
|
184
|
+
* wire for a task with no declared result type; the current producer
|
|
185
|
+
* never emits it (wasmtime's `TaskReturn.results` is not an `Option` —
|
|
186
|
+
* a no-result task carries the empty tuple).
|
|
187
|
+
*/
|
|
188
|
+
resultType: number | null;
|
|
189
|
+
options: number;
|
|
190
|
+
} | {
|
|
191
|
+
kind: "resource-drop" | "resource-new" | "resource-rep";
|
|
192
|
+
index: number;
|
|
193
|
+
instance: number;
|
|
194
|
+
resource: number;
|
|
195
|
+
} | {
|
|
196
|
+
kind: string;
|
|
197
|
+
index: number;
|
|
198
|
+
[field: string]: unknown;
|
|
199
|
+
};
|
|
200
|
+
export interface WireCanonicalOptions {
|
|
201
|
+
instance: number;
|
|
202
|
+
stringEncoding: "utf8" | "utf16" | "latin1+utf16";
|
|
203
|
+
memory: number | null;
|
|
204
|
+
realloc: number | null;
|
|
205
|
+
postReturn: number | null;
|
|
206
|
+
callback: number | null;
|
|
207
|
+
async: boolean;
|
|
208
|
+
cancellable: boolean;
|
|
209
|
+
coreType: {
|
|
210
|
+
params: WireCoreType[];
|
|
211
|
+
results: WireCoreType[];
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/** descriptor-ir.md ValType JSON (nested structurally). */
|
|
215
|
+
export type WireValType = {
|
|
216
|
+
kind: "bool" | "s8" | "u8" | "s16" | "u16" | "s32" | "u32" | "s64" | "u64" | "f32" | "f64" | "char" | "string" | "error-context";
|
|
217
|
+
} | {
|
|
218
|
+
kind: "list";
|
|
219
|
+
element: WireValType;
|
|
220
|
+
length?: number;
|
|
221
|
+
} | {
|
|
222
|
+
kind: "record";
|
|
223
|
+
fields: {
|
|
224
|
+
label: string;
|
|
225
|
+
type: WireValType;
|
|
226
|
+
}[];
|
|
227
|
+
} | {
|
|
228
|
+
kind: "tuple";
|
|
229
|
+
elements: WireValType[];
|
|
230
|
+
} | {
|
|
231
|
+
kind: "variant";
|
|
232
|
+
cases: {
|
|
233
|
+
label: string;
|
|
234
|
+
type: WireValType | null;
|
|
235
|
+
}[];
|
|
236
|
+
} | {
|
|
237
|
+
kind: "enum";
|
|
238
|
+
labels: string[];
|
|
239
|
+
} | {
|
|
240
|
+
kind: "option";
|
|
241
|
+
type: WireValType;
|
|
242
|
+
} | {
|
|
243
|
+
kind: "result";
|
|
244
|
+
ok: WireValType | null;
|
|
245
|
+
err: WireValType | null;
|
|
246
|
+
} | {
|
|
247
|
+
kind: "map";
|
|
248
|
+
key: WireValType;
|
|
249
|
+
value: WireValType;
|
|
250
|
+
} | {
|
|
251
|
+
kind: "flags";
|
|
252
|
+
labels: string[];
|
|
253
|
+
} | {
|
|
254
|
+
kind: "own";
|
|
255
|
+
resource: number;
|
|
256
|
+
} | {
|
|
257
|
+
kind: "borrow";
|
|
258
|
+
resource: number;
|
|
259
|
+
} | {
|
|
260
|
+
kind: "stream";
|
|
261
|
+
element: WireValType | null;
|
|
262
|
+
} | {
|
|
263
|
+
kind: "future";
|
|
264
|
+
element: WireValType | null;
|
|
265
|
+
};
|
|
266
|
+
export type WireTypeDecl = {
|
|
267
|
+
kind: "func";
|
|
268
|
+
params: {
|
|
269
|
+
label: string;
|
|
270
|
+
type: WireValType;
|
|
271
|
+
}[];
|
|
272
|
+
results: WireValType[];
|
|
273
|
+
async: boolean;
|
|
274
|
+
} | WireValType;
|
|
275
|
+
export type WireResourceTable = {
|
|
276
|
+
kind: "concrete";
|
|
277
|
+
resource: number;
|
|
278
|
+
instance: number;
|
|
279
|
+
} | {
|
|
280
|
+
kind: "abstract";
|
|
281
|
+
id: number;
|
|
282
|
+
};
|
|
283
|
+
/** One stream or future table (plan v2). */
|
|
284
|
+
/** One error-context table: the owning component instance, nothing else. */
|
|
285
|
+
export interface WireErrorContextTable {
|
|
286
|
+
instance: number;
|
|
287
|
+
}
|
|
288
|
+
export interface WireAsyncTable {
|
|
289
|
+
element: WireValType | null;
|
|
290
|
+
instance: number;
|
|
291
|
+
}
|
|
292
|
+
/** One imported resource type: back-reference into `plan.imports`. */
|
|
293
|
+
export interface WireImportedResource {
|
|
294
|
+
/** `RuntimeImportIndex` — index into `plan.imports`. */
|
|
295
|
+
import: number;
|
|
296
|
+
}
|
|
297
|
+
export interface WireImport {
|
|
298
|
+
name: string;
|
|
299
|
+
path: string[];
|
|
300
|
+
kind: string;
|
|
301
|
+
type?: number;
|
|
302
|
+
}
|
|
303
|
+
export type WireExport = {
|
|
304
|
+
kind: "lifted-func";
|
|
305
|
+
name: string;
|
|
306
|
+
coreDef: WireCoreDef;
|
|
307
|
+
options: number;
|
|
308
|
+
type: number;
|
|
309
|
+
} | {
|
|
310
|
+
kind: "instance";
|
|
311
|
+
name: string;
|
|
312
|
+
exports: WireExport[];
|
|
313
|
+
} | {
|
|
314
|
+
kind: "type";
|
|
315
|
+
name: string;
|
|
316
|
+
type: WireTypeExport;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* An exported embedded core module; `module` indexes the static module
|
|
320
|
+
* space (`modules`). plan-format.md v4 amendment 2.
|
|
321
|
+
*/
|
|
322
|
+
| {
|
|
323
|
+
kind: "module";
|
|
324
|
+
name: string;
|
|
325
|
+
module: number;
|
|
326
|
+
};
|
|
327
|
+
export type WireTypeExport = {
|
|
328
|
+
kind: "resource";
|
|
329
|
+
resource: number;
|
|
330
|
+
} | {
|
|
331
|
+
kind: "value";
|
|
332
|
+
type: number;
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* The shim's C-ABI envelope: plan + adapter artifacts in one JSON document
|
|
336
|
+
* (crates/translator-shim/README.md documents the 1:1 mapping to the
|
|
337
|
+
* contract's artifact set).
|
|
338
|
+
*/
|
|
339
|
+
export interface WireEnvelope {
|
|
340
|
+
plan?: WirePlan;
|
|
341
|
+
adapters?: {
|
|
342
|
+
file: string;
|
|
343
|
+
wasm: string;
|
|
344
|
+
}[];
|
|
345
|
+
/** Failure message (v0.1 shape; unchanged meaning). */
|
|
346
|
+
error?: string;
|
|
347
|
+
/**
|
|
348
|
+
* Structured verdict accompanying `error` (contracts v0.2 proposal). Absent
|
|
349
|
+
* from v0.1 producers; consumers must tolerate that (treat as phase
|
|
350
|
+
* `"internal"`, i.e. "not a statement about the component").
|
|
351
|
+
*/
|
|
352
|
+
errorDetail?: WireErrorDetail;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Structured translation failure.
|
|
356
|
+
*
|
|
357
|
+
* `phase` is the load-bearing field: only `"validation"` means *the component
|
|
358
|
+
* is invalid/malformed* — the verdict the official suite's `assert_invalid` /
|
|
359
|
+
* `assert_malformed` commands require. `"unsupported"` means the component is
|
|
360
|
+
* valid but uses a shape this plan-format version cannot express, and
|
|
361
|
+
* `"internal"` is a shim bug. Neither of the latter two may be scored as a
|
|
362
|
+
* correct rejection.
|
|
363
|
+
*/
|
|
364
|
+
export interface WireErrorDetail {
|
|
365
|
+
phase: "validation" | "unsupported" | "internal";
|
|
366
|
+
message: string;
|
|
367
|
+
/** Full error chain, diagnostics only. */
|
|
368
|
+
detail?: string;
|
|
369
|
+
}
|