wasm-idle 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -37
- package/dist/playground/assetBridge.js +106 -31
- package/dist/playground/assets.d.ts +2 -2
- package/dist/playground/c3.js +22 -0
- package/dist/playground/c3Memory.d.ts +19 -0
- package/dist/playground/c3Memory.js +117 -0
- package/dist/playground/cachedClang.js +143 -34
- package/dist/playground/clangAssetIntegrity.js +6 -6
- package/dist/playground/clangWorkerProtocol.d.ts +12 -1
- package/dist/playground/cobol.d.ts +3 -0
- package/dist/playground/cobol.js +10 -1
- package/dist/playground/commonlispAssets.d.ts +2 -2
- package/dist/playground/compiledArtifactCache.d.ts +3 -1
- package/dist/playground/compiledArtifactCache.js +31 -5
- package/dist/playground/container/ContainerExplorer.svelte +307 -0
- package/dist/playground/container/ContainerExplorer.svelte.d.ts +14 -0
- package/dist/playground/container/binding.d.ts +26 -0
- package/dist/playground/container/binding.js +247 -0
- package/dist/playground/container/clang-runtime.d.ts +38 -0
- package/dist/playground/container/clang-runtime.js +343 -0
- package/dist/playground/container/client.d.ts +15 -0
- package/dist/playground/container/client.js +189 -0
- package/dist/playground/container/explorer.d.ts +31 -0
- package/dist/playground/container/explorer.js +112 -0
- package/dist/playground/container/owner.worker.js +390 -0
- package/dist/playground/container/protocol.d.ts +73 -0
- package/dist/playground/container/protocol.js +28 -0
- package/dist/playground/container/release.d.ts +31 -0
- package/dist/playground/container/release.js +612 -0
- package/dist/playground/container/runtime-adapter.d.ts +73 -0
- package/dist/playground/container/runtime-adapter.js +474 -0
- package/dist/playground/container/shell-control.d.ts +49 -0
- package/dist/playground/container/shell-control.js +303 -0
- package/dist/playground/container/workspace.d.ts +8 -0
- package/dist/playground/container/workspace.js +29 -0
- package/dist/playground/d.d.ts +6 -1
- package/dist/playground/d.js +83 -5
- package/dist/playground/dotnet.js +6 -8
- package/dist/playground/fortran.d.ts +3 -0
- package/dist/playground/fortran.js +10 -1
- package/dist/playground/go.js +14 -3
- package/dist/playground/goDebugInstrumentation.js +184 -54
- package/dist/playground/java.js +3 -2
- package/dist/playground/javaRuntimeErrors.d.ts +9 -0
- package/dist/playground/javaRuntimeErrors.js +60 -0
- package/dist/playground/lean4Assets.d.ts +4 -4
- package/dist/playground/lfortran.js +10 -0
- package/dist/playground/lfortranAssets.d.ts +2 -2
- package/dist/playground/nim.js +22 -0
- package/dist/playground/objectivec.d.ts +3 -0
- package/dist/playground/objectivec.js +11 -2
- package/dist/playground/options.d.ts +2 -0
- package/dist/playground/python.js +8 -0
- package/dist/playground/runtimeAssetCache.d.ts +1 -32
- package/dist/playground/runtimeAssetCache.js +1 -193
- package/dist/playground/runtimeSession.d.ts +2 -2
- package/dist/playground/runtimeSession.js +11 -11
- package/dist/playground/rust.d.ts +6 -0
- package/dist/playground/rust.js +103 -6
- package/dist/playground/staticStdinRing.d.ts +2 -1
- package/dist/playground/staticStdinRing.js +21 -10
- package/dist/playground/staticWorkerRuntime.d.ts +15 -1
- package/dist/playground/staticWorkerRuntime.js +119 -17
- package/dist/playground/tinygo.d.ts +3 -0
- package/dist/playground/tinygo.js +81 -6
- package/dist/playground/v.d.ts +3 -0
- package/dist/playground/v.js +10 -1
- package/dist/playground/wasmAwkVersion.d.ts +13 -13
- package/dist/playground/wasmAwkVersion.js +4 -4
- package/dist/playground/wasmBqnVersion.d.ts +2 -2
- package/dist/playground/wasmBqnVersion.js +2 -2
- package/dist/playground/wasmC3Version.d.ts +2 -2
- package/dist/playground/wasmC3Version.js +2 -2
- package/dist/playground/wasmClojureScriptVersion.d.ts +2 -2
- package/dist/playground/wasmClojureScriptVersion.js +2 -2
- package/dist/playground/wasmCommonLispVersion.d.ts +2 -2
- package/dist/playground/wasmCommonLispVersion.js +2 -2
- package/dist/playground/wasmDIntegrity.d.ts +5 -5
- package/dist/playground/wasmDIntegrity.js +5 -5
- package/dist/playground/wasmDVersion.d.ts +16 -1
- package/dist/playground/wasmDVersion.js +16 -1
- package/dist/playground/wasmDebugVersion.d.ts +1 -1
- package/dist/playground/wasmDebugVersion.js +1 -1
- package/dist/playground/wasmForthVersion.d.ts +2 -2
- package/dist/playground/wasmForthVersion.js +2 -2
- package/dist/playground/wasmGleamVersion.d.ts +2 -2
- package/dist/playground/wasmGleamVersion.js +2 -2
- package/dist/playground/wasmGrainVersion.d.ts +2 -2
- package/dist/playground/wasmGrainVersion.js +2 -2
- package/dist/playground/wasmJVersion.d.ts +2 -2
- package/dist/playground/wasmJVersion.js +2 -2
- package/dist/playground/wasmJanetVersion.js +2 -2
- package/dist/playground/wasmJuliaVersion.js +2 -2
- package/dist/playground/wasmLean4Version.d.ts +5 -5
- package/dist/playground/wasmLean4Version.js +5 -5
- package/dist/playground/wasmLfortranVersion.d.ts +2 -2
- package/dist/playground/wasmLfortranVersion.js +2 -2
- package/dist/playground/wasmNimVersion.d.ts +9 -9
- package/dist/playground/wasmNimVersion.js +6 -6
- package/dist/playground/wasmOcamlVersion.d.ts +2 -2
- package/dist/playground/wasmOcamlVersion.js +1 -1
- package/dist/playground/wasmPascalVersion.js +2 -2
- package/dist/playground/wasmPerlVersion.js +2 -2
- package/dist/playground/wasmPrologVersion.d.ts +2 -2
- package/dist/playground/wasmPrologVersion.js +2 -2
- package/dist/playground/wasmReScriptVersion.d.ts +2 -2
- package/dist/playground/wasmReScriptVersion.js +2 -2
- package/dist/playground/wasmRustVersion.d.ts +8 -8
- package/dist/playground/wasmRustVersion.js +7 -7
- package/dist/playground/wasmTclVersion.d.ts +4 -4
- package/dist/playground/wasmTclVersion.js +2 -2
- package/dist/playground/wasmTinyGoVersion.d.ts +14 -14
- package/dist/playground/wasmTinyGoVersion.js +14 -14
- package/dist/playground/wasmTypeScriptVersion.d.ts +5 -5
- package/dist/playground/wasmTypeScriptVersion.js +5 -5
- package/dist/playground/worker/cachedClangCompile.js +52 -4
- package/dist/playground/worker/cachedClangExecute.js +6 -5
- package/dist/playground/worker/clang.js +32 -10
- package/dist/playground/worker/cobol.js +2 -2
- package/dist/playground/worker/d.js +12 -5
- package/dist/playground/worker/fortran.js +2 -2
- package/dist/playground/worker/go.js +16 -5
- package/dist/playground/worker/haskell.js +10 -7
- package/dist/playground/worker/java.js +75 -10
- package/dist/playground/worker/lua.js +2 -3
- package/dist/playground/worker/php.js +15 -9
- package/dist/playground/worker/python.js +46 -9
- package/dist/playground/worker/pythonExecution.js +17 -1
- package/dist/playground/worker/pythonWorkspaceFiles.d.ts +6 -0
- package/dist/playground/worker/pythonWorkspaceFiles.js +26 -0
- package/dist/playground/worker/pythonWorkspaceImports.d.ts +4 -0
- package/dist/playground/worker/pythonWorkspaceImports.js +129 -0
- package/dist/playground/worker/r.js +13 -6
- package/dist/playground/worker/rust.js +34 -2
- package/dist/playground/worker/sqlite.js +4 -3
- package/dist/playground/worker/tinygo.js +11 -3
- package/dist/playground/worker/typescript.js +2 -3
- package/dist/playground/worker/v.js +2 -2
- package/dist/playground/worker/wat.js +2 -3
- package/dist/playground/worker/zig.js +20 -6
- package/dist/playground/workerSession.d.ts +1 -28
- package/dist/playground/workerSession.js +1 -164
- package/package.json +20 -13
- package/dist/playground/worker/python.runtime.cases.js +0 -374
- /package/dist/playground/{worker/python.runtime.cases.d.ts → container/owner.worker.d.ts} +0 -0
|
@@ -1,193 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/** Immutable bootstrap assets only: never store a worker, Wasm instance, or user filesystem. */
|
|
3
|
-
export class RuntimeAssetCache {
|
|
4
|
-
maxBytes;
|
|
5
|
-
maxEntries;
|
|
6
|
-
entries = new Map();
|
|
7
|
-
inFlight = new Map();
|
|
8
|
-
inFlightKeyBytes = 0;
|
|
9
|
-
identities = new WeakMap();
|
|
10
|
-
nextIdentity = 0;
|
|
11
|
-
bytes = 0;
|
|
12
|
-
hits = 0;
|
|
13
|
-
misses = 0;
|
|
14
|
-
disposed = false;
|
|
15
|
-
constructor(maxBytes = 256 * 1024 * 1024, maxEntries = 64) {
|
|
16
|
-
this.maxBytes = maxBytes;
|
|
17
|
-
this.maxEntries = maxEntries;
|
|
18
|
-
if (!Number.isSafeInteger(maxBytes) ||
|
|
19
|
-
maxBytes < 0 ||
|
|
20
|
-
!Number.isSafeInteger(maxEntries) ||
|
|
21
|
-
maxEntries < 0) {
|
|
22
|
-
throw new TypeError('Runtime cache limits must be non-negative safe integers');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/** Identity is owner-local; a custom loader must never alias another loader. */
|
|
26
|
-
identity(value) {
|
|
27
|
-
if (!value)
|
|
28
|
-
return 0;
|
|
29
|
-
let identity = this.identities.get(value);
|
|
30
|
-
if (identity === undefined) {
|
|
31
|
-
identity = ++this.nextIdentity;
|
|
32
|
-
this.identities.set(value, identity);
|
|
33
|
-
}
|
|
34
|
-
return identity;
|
|
35
|
-
}
|
|
36
|
-
get(key) {
|
|
37
|
-
if (this.disposed)
|
|
38
|
-
return undefined;
|
|
39
|
-
const entry = this.entries.get(key);
|
|
40
|
-
if (!entry) {
|
|
41
|
-
this.misses++;
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
this.hits++;
|
|
45
|
-
this.entries.delete(key);
|
|
46
|
-
this.entries.set(key, entry);
|
|
47
|
-
return entry.value;
|
|
48
|
-
}
|
|
49
|
-
/** Only the trusted host publishes fully verified results; failures are never retained. */
|
|
50
|
-
set(key, value, bytes) {
|
|
51
|
-
if (this.disposed)
|
|
52
|
-
return;
|
|
53
|
-
if (!Number.isSafeInteger(bytes) || bytes < 0)
|
|
54
|
-
throw new TypeError('Invalid cache byte count');
|
|
55
|
-
const charge = bytes + key.length * 2;
|
|
56
|
-
if (this.maxEntries === 0 || charge > this.maxBytes)
|
|
57
|
-
return;
|
|
58
|
-
const previous = this.entries.get(key);
|
|
59
|
-
if (previous)
|
|
60
|
-
this.bytes -= previous.bytes;
|
|
61
|
-
this.entries.delete(key);
|
|
62
|
-
this.entries.set(key, { value, bytes: charge });
|
|
63
|
-
this.bytes += charge;
|
|
64
|
-
while (this.entries.size > this.maxEntries || this.bytes > this.maxBytes) {
|
|
65
|
-
const oldest = this.entries.keys().next().value;
|
|
66
|
-
this.bytes -= this.entries.get(oldest).bytes;
|
|
67
|
-
this.entries.delete(oldest);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/** Share work, but give every consumer its own cancellation and progress subscription. */
|
|
71
|
-
share(key, signal, start, onProgress) {
|
|
72
|
-
if (signal.aborted)
|
|
73
|
-
return Promise.reject(abortReason(signal));
|
|
74
|
-
let operation = this.inFlight.get(key);
|
|
75
|
-
if (!operation) {
|
|
76
|
-
// A disabled or saturated owner cache still loads assets without retaining operations.
|
|
77
|
-
if (this.disposed ||
|
|
78
|
-
this.inFlight.size >= this.maxEntries ||
|
|
79
|
-
this.inFlightKeyBytes + key.length * 2 > this.maxBytes) {
|
|
80
|
-
return Promise.resolve().then(() => {
|
|
81
|
-
if (signal.aborted)
|
|
82
|
-
throw abortReason(signal);
|
|
83
|
-
return start(signal, (progress) => onProgress?.(progress));
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
const pending = {
|
|
87
|
-
controller: new AbortController(),
|
|
88
|
-
promise: undefined,
|
|
89
|
-
consumers: new Set(),
|
|
90
|
-
progress: new Map(),
|
|
91
|
-
settled: false,
|
|
92
|
-
keyBytes: key.length * 2
|
|
93
|
-
};
|
|
94
|
-
this.inFlight.set(key, pending);
|
|
95
|
-
this.inFlightKeyBytes += pending.keyBytes;
|
|
96
|
-
pending.promise = Promise.resolve().then(() => {
|
|
97
|
-
if (pending.controller.signal.aborted)
|
|
98
|
-
throw abortReason(pending.controller.signal);
|
|
99
|
-
return start(pending.controller.signal, (progress, progressKey = '') => {
|
|
100
|
-
pending.progress.set(progressKey, progress);
|
|
101
|
-
for (const consumer of pending.consumers) {
|
|
102
|
-
try {
|
|
103
|
-
consumer(progress);
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
// One progress listener cannot interrupt another consumer's load.
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
const cleanup = () => {
|
|
112
|
-
pending.settled = true;
|
|
113
|
-
pending.progress.clear();
|
|
114
|
-
this.removeOperation(key, pending);
|
|
115
|
-
};
|
|
116
|
-
void pending.promise.then(cleanup, cleanup);
|
|
117
|
-
operation = pending;
|
|
118
|
-
}
|
|
119
|
-
const shared = operation;
|
|
120
|
-
return new Promise((resolve, reject) => {
|
|
121
|
-
let active = true;
|
|
122
|
-
const receiveProgress = (progress) => {
|
|
123
|
-
if (active)
|
|
124
|
-
onProgress?.(progress);
|
|
125
|
-
};
|
|
126
|
-
const release = () => {
|
|
127
|
-
active = false;
|
|
128
|
-
signal.removeEventListener('abort', onAbort);
|
|
129
|
-
shared.consumers.delete(receiveProgress);
|
|
130
|
-
if (!shared.settled && shared.consumers.size === 0) {
|
|
131
|
-
this.removeOperation(key, shared);
|
|
132
|
-
shared.controller.abort(abortReason(signal));
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
const onAbort = () => {
|
|
136
|
-
if (!active)
|
|
137
|
-
return;
|
|
138
|
-
release();
|
|
139
|
-
reject(abortReason(signal));
|
|
140
|
-
};
|
|
141
|
-
shared.consumers.add(receiveProgress);
|
|
142
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
143
|
-
void shared.promise.then((value) => {
|
|
144
|
-
if (!active)
|
|
145
|
-
return;
|
|
146
|
-
release();
|
|
147
|
-
resolve(value);
|
|
148
|
-
}, (error) => {
|
|
149
|
-
if (!active)
|
|
150
|
-
return;
|
|
151
|
-
release();
|
|
152
|
-
reject(error);
|
|
153
|
-
});
|
|
154
|
-
for (const progress of shared.progress.values()) {
|
|
155
|
-
try {
|
|
156
|
-
receiveProgress(progress);
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// Progress is advisory, including replay to a late subscriber.
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (signal.aborted)
|
|
163
|
-
onAbort();
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
removeOperation(key, operation) {
|
|
167
|
-
if (this.inFlight.get(key) !== operation)
|
|
168
|
-
return;
|
|
169
|
-
this.inFlight.delete(key);
|
|
170
|
-
this.inFlightKeyBytes -= operation.keyBytes;
|
|
171
|
-
}
|
|
172
|
-
stats() {
|
|
173
|
-
return {
|
|
174
|
-
hits: this.hits,
|
|
175
|
-
misses: this.misses,
|
|
176
|
-
entries: this.entries.size,
|
|
177
|
-
bytes: this.bytes,
|
|
178
|
-
inFlight: this.inFlight.size,
|
|
179
|
-
disposed: this.disposed
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
dispose() {
|
|
183
|
-
if (this.disposed)
|
|
184
|
-
return;
|
|
185
|
-
this.disposed = true;
|
|
186
|
-
this.entries.clear();
|
|
187
|
-
// Existing consumers may finish, but a disposed owner never retains their results.
|
|
188
|
-
this.inFlight.clear();
|
|
189
|
-
this.inFlightKeyBytes = 0;
|
|
190
|
-
this.identities = new WeakMap();
|
|
191
|
-
this.bytes = 0;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
1
|
+
export { RuntimeAssetCache } from '@wasm-idle/core';
|
|
@@ -15,7 +15,7 @@ export interface PlaygroundRuntimeSession {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* A browser-owned
|
|
19
|
-
* compiler
|
|
18
|
+
* A browser-owned runtime session. Panels own disposable execution leases, while
|
|
19
|
+
* verified compiler assets survive problem, editor and language changes.
|
|
20
20
|
*/
|
|
21
21
|
export declare function createRuntimeSession(runtimeAssets: SandboxRuntimeAssets, options?: PlaygroundBindingOptions): PlaygroundRuntimeSession;
|
|
@@ -4,16 +4,16 @@ import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
|
4
4
|
import { CompiledArtifactCache } from './compiledArtifactCache';
|
|
5
5
|
import { createCachedClangSandbox } from './cachedClang';
|
|
6
6
|
/**
|
|
7
|
-
* A browser-owned
|
|
8
|
-
* compiler
|
|
7
|
+
* A browser-owned runtime session. Panels own disposable execution leases, while
|
|
8
|
+
* verified compiler assets survive problem, editor and language changes.
|
|
9
9
|
*/
|
|
10
10
|
export function createRuntimeSession(runtimeAssets, options = {}) {
|
|
11
11
|
let language;
|
|
12
12
|
let epoch = 0;
|
|
13
13
|
let disposed = false;
|
|
14
14
|
let disposal;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const assetCache = new RuntimeAssetCache();
|
|
16
|
+
const artifactCache = new CompiledArtifactCache();
|
|
17
17
|
const leases = new Set();
|
|
18
18
|
let retiring = Promise.resolve();
|
|
19
19
|
const stale = () => new RuntimeConfigurationError('Runtime session was disposed or its language changed', {
|
|
@@ -21,14 +21,17 @@ export function createRuntimeSession(runtimeAssets, options = {}) {
|
|
|
21
21
|
});
|
|
22
22
|
const release = (owned) => {
|
|
23
23
|
const previous = retiring;
|
|
24
|
-
|
|
24
|
+
const released = Promise.allSettled([
|
|
25
|
+
previous,
|
|
26
|
+
...owned.map((lease) => lease.dispose())
|
|
27
|
+
]).then((results) => {
|
|
25
28
|
const failure = results.find((result) => result.status === 'rejected');
|
|
26
29
|
if (failure?.status === 'rejected')
|
|
27
30
|
throw failure.reason;
|
|
28
31
|
});
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
return
|
|
32
|
+
// Keep ordering as a completion barrier; report this failure only to its owner.
|
|
33
|
+
retiring = released.catch(() => undefined);
|
|
34
|
+
return released;
|
|
32
35
|
};
|
|
33
36
|
const session = {
|
|
34
37
|
async selectLanguage(value) {
|
|
@@ -41,10 +44,7 @@ export function createRuntimeSession(runtimeAssets, options = {}) {
|
|
|
41
44
|
return await retiring;
|
|
42
45
|
language = next;
|
|
43
46
|
epoch++;
|
|
44
|
-
assetCache.dispose();
|
|
45
47
|
artifactCache.clear();
|
|
46
|
-
assetCache = new RuntimeAssetCache();
|
|
47
|
-
artifactCache = new CompiledArtifactCache();
|
|
48
48
|
await release([...leases].filter((lease) => lease.epoch !== undefined));
|
|
49
49
|
},
|
|
50
50
|
createBinding() {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PlaygroundRuntimeAssets, type RuntimeAssetIntegrityEntry } from './assets';
|
|
2
2
|
import { type DebugCommand, type DebugDataBreakpoint, type DebugDataBreakpointInfoArguments, type DebugSessionEvent, type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
|
|
3
3
|
import type { Sandbox, SandboxProgress } from './sandbox';
|
|
4
|
+
import { RuntimeAssetCache } from '@wasm-idle/core';
|
|
4
5
|
declare class Rust implements Sandbox {
|
|
5
6
|
output: any;
|
|
6
7
|
ondebug?: (event: DebugSessionEvent) => void;
|
|
@@ -19,6 +20,9 @@ declare class Rust implements Sandbox {
|
|
|
19
20
|
debugManifestUrl: string;
|
|
20
21
|
debugManifestReceipt?: Readonly<RuntimeAssetIntegrityEntry>;
|
|
21
22
|
executableGraphFingerprint: string;
|
|
23
|
+
private runtimeAssetsKey;
|
|
24
|
+
private runtimeAssetCache;
|
|
25
|
+
private ownsRuntimeAssetCache;
|
|
22
26
|
private persistentCacheBaseline?;
|
|
23
27
|
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
24
28
|
waitingForInput: boolean;
|
|
@@ -58,6 +62,8 @@ declare class Rust implements Sandbox {
|
|
|
58
62
|
debugDataBreakpointInfo(arguments_: DebugDataBreakpointInfoArguments): Promise<import("@wasm-idle/core").DebugDataBreakpointInfo | null>;
|
|
59
63
|
debugSetDataBreakpoints(breakpoints: DebugDataBreakpoint[]): Promise<import("@wasm-idle/core").DebugResolvedDataBreakpoint[]>;
|
|
60
64
|
kill(): Promise<void>;
|
|
65
|
+
setRuntimeAssetCache(cache: RuntimeAssetCache): void;
|
|
66
|
+
dispose(): Promise<void>;
|
|
61
67
|
terminate(): Promise<void>;
|
|
62
68
|
clear(): Promise<void>;
|
|
63
69
|
}
|
package/dist/playground/rust.js
CHANGED
|
@@ -8,7 +8,7 @@ import { resolveRustNonDebugResourceLimits } from './rustWorkerLimits';
|
|
|
8
8
|
import { WASM_RUST_EXECUTABLE_GRAPH_PROFILE, WASM_RUST_RUNTIME_PROFILE } from './wasmRustVersion';
|
|
9
9
|
import { WorkerSession } from './workerSession';
|
|
10
10
|
import { reportWorkerInputReady, reportWorkerProgress } from './workerProgress';
|
|
11
|
-
import { resolveExecutionLimits, resolveRuntimeAssetCacheOptions, TimeoutError } from '@wasm-idle/core';
|
|
11
|
+
import { createRuntimeAssetsKey, RuntimeAssetCache, resolveExecutionLimits, resolveRuntimeAssetCacheOptions, serveRuntimeWasmModule, TimeoutError } from '@wasm-idle/core';
|
|
12
12
|
const debugBreakpointBufferInts = 1028;
|
|
13
13
|
const rustLldbSourcePath = '/workspace/main.rs';
|
|
14
14
|
const outputEncoder = new TextEncoder();
|
|
@@ -54,6 +54,9 @@ class Rust {
|
|
|
54
54
|
debugManifestUrl = '';
|
|
55
55
|
debugManifestReceipt;
|
|
56
56
|
executableGraphFingerprint = '';
|
|
57
|
+
runtimeAssetsKey = '';
|
|
58
|
+
runtimeAssetCache = new RuntimeAssetCache();
|
|
59
|
+
ownsRuntimeAssetCache = true;
|
|
57
60
|
persistentCacheBaseline;
|
|
58
61
|
oncompilerdiagnostic;
|
|
59
62
|
waitingForInput = false;
|
|
@@ -122,9 +125,10 @@ class Rust {
|
|
|
122
125
|
let candidateLoadReject = null;
|
|
123
126
|
let clearCandidateWait = () => { };
|
|
124
127
|
try {
|
|
125
|
-
const currentUrl =
|
|
128
|
+
const currentUrl = globalThis.location?.href ?? '';
|
|
126
129
|
const persistentCache = resolveRuntimeAssetCacheOptions(typeof runtimeAssets === 'object' ? runtimeAssets.persistentCache : undefined, _options.persistentCache);
|
|
127
130
|
const nextCompilerUrl = resolveRustCompilerUrl(runtimeAssets, currentUrl);
|
|
131
|
+
const nextRuntimeAssetsKey = createRuntimeAssetsKey(runtimeAssets) ?? '';
|
|
128
132
|
const nextDebugModuleUrl = resolveRustDebugModuleUrl(runtimeAssets, currentUrl);
|
|
129
133
|
const debugRuntime = resolveDebugRuntimeUrls(runtimeAssets, currentUrl);
|
|
130
134
|
const nextDebugRuntimeBaseUrl = debugRuntime.baseUrl;
|
|
@@ -133,9 +137,8 @@ class Rust {
|
|
|
133
137
|
const nextAssetPath = typeof runtimeAssets === 'string'
|
|
134
138
|
? runtimeAssets
|
|
135
139
|
: runtimeAssets?.rootUrl ||
|
|
136
|
-
(
|
|
137
|
-
|
|
138
|
-
: '');
|
|
140
|
+
globalThis.location?.pathname.replace(/\/$/, '') ||
|
|
141
|
+
'';
|
|
139
142
|
if (!nextCompilerUrl) {
|
|
140
143
|
throw new Error('Rust runtime is not configured. Set runtimeAssets.rust.compilerUrl.');
|
|
141
144
|
}
|
|
@@ -174,6 +177,7 @@ class Rust {
|
|
|
174
177
|
this.compilerUrl !== nextCompilerUrl ||
|
|
175
178
|
this.debugModuleUrl !== nextDebugModuleUrl ||
|
|
176
179
|
this.assetPath !== nextAssetPath ||
|
|
180
|
+
this.runtimeAssetsKey !== nextRuntimeAssetsKey ||
|
|
177
181
|
this.executableGraphFingerprint !==
|
|
178
182
|
WASM_RUST_EXECUTABLE_GRAPH_PROFILE.fingerprint;
|
|
179
183
|
if (!needsWorkerReset) {
|
|
@@ -279,6 +283,7 @@ class Rust {
|
|
|
279
283
|
this.waitingForInput = false;
|
|
280
284
|
this.pendingEof = false;
|
|
281
285
|
this.compilerUrl = nextCompilerUrl;
|
|
286
|
+
this.runtimeAssetsKey = nextRuntimeAssetsKey;
|
|
282
287
|
this.persistentCacheBaseline = persistentCache;
|
|
283
288
|
this.debugModuleUrl = nextDebugModuleUrl;
|
|
284
289
|
this.assetPath = nextAssetPath;
|
|
@@ -383,6 +388,20 @@ class Rust {
|
|
|
383
388
|
let phaseTimeoutState;
|
|
384
389
|
let outputBytes = 0;
|
|
385
390
|
let diagnosticCount = 0;
|
|
391
|
+
let moduleBroker;
|
|
392
|
+
const moduleServices = new Set();
|
|
393
|
+
const closeCompilerModuleBroker = () => {
|
|
394
|
+
if (moduleBroker) {
|
|
395
|
+
moduleBroker.port1.onmessage = null;
|
|
396
|
+
moduleBroker.port1.onmessageerror = null;
|
|
397
|
+
moduleBroker.port1.close();
|
|
398
|
+
moduleBroker.port2.close();
|
|
399
|
+
moduleBroker = undefined;
|
|
400
|
+
}
|
|
401
|
+
for (const service of moduleServices)
|
|
402
|
+
service.close();
|
|
403
|
+
moduleServices.clear();
|
|
404
|
+
};
|
|
386
405
|
const clearScheduledPhaseTimeout = () => {
|
|
387
406
|
if (phaseTimeout !== undefined)
|
|
388
407
|
clearTimeout(phaseTimeout);
|
|
@@ -459,6 +478,7 @@ class Rust {
|
|
|
459
478
|
return;
|
|
460
479
|
settled = true;
|
|
461
480
|
this.runActive = false;
|
|
481
|
+
closeCompilerModuleBroker();
|
|
462
482
|
clearPhaseTimeout();
|
|
463
483
|
if (this.activeDebugPhaseTimeout === debugPhaseTimeout) {
|
|
464
484
|
this.activeDebugPhaseTimeout = undefined;
|
|
@@ -471,6 +491,7 @@ class Rust {
|
|
|
471
491
|
return;
|
|
472
492
|
settled = true;
|
|
473
493
|
this.runActive = false;
|
|
494
|
+
closeCompilerModuleBroker();
|
|
474
495
|
clearPhaseTimeout();
|
|
475
496
|
if (this.activeDebugPhaseTimeout === debugPhaseTimeout) {
|
|
476
497
|
this.activeDebugPhaseTimeout = undefined;
|
|
@@ -572,6 +593,7 @@ class Rust {
|
|
|
572
593
|
if (debugEvent)
|
|
573
594
|
forwardDebugEvent(debugEvent);
|
|
574
595
|
if (lldbArtifact) {
|
|
596
|
+
closeCompilerModuleBroker();
|
|
575
597
|
startPhaseTimeout('run', limits.runTimeoutMs);
|
|
576
598
|
const compilerWorker = this.worker;
|
|
577
599
|
const lldbSession = new LldbSandboxSession({
|
|
@@ -679,8 +701,63 @@ class Rust {
|
|
|
679
701
|
this.worker.onmessage = handler;
|
|
680
702
|
this.begin = Date.now();
|
|
681
703
|
try {
|
|
704
|
+
if (typeof MessageChannel === 'function' && typeof MessagePort === 'function') {
|
|
705
|
+
moduleBroker = new MessageChannel();
|
|
706
|
+
const isActiveModuleBroker = () => !settled && _uid === this.uid && this.workerSession.isActive(operation);
|
|
707
|
+
moduleBroker.port1.onmessageerror = () => {
|
|
708
|
+
if (isActiveModuleBroker()) {
|
|
709
|
+
this.workerSession.terminate(new Error('Rust module broker message failed'));
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
moduleBroker.port1.onmessage = (event) => {
|
|
713
|
+
const message = event.data;
|
|
714
|
+
if (!isActiveModuleBroker()) {
|
|
715
|
+
if (message?.port instanceof MessagePort) {
|
|
716
|
+
try {
|
|
717
|
+
message.port.close();
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
// A retired endpoint cannot affect the current worker operation.
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (!(message?.port instanceof MessagePort)) {
|
|
726
|
+
this.workerSession.terminate(new Error('Invalid Rust module service request'));
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (message.type !== 'rustc-module-service') {
|
|
730
|
+
message.port.close();
|
|
731
|
+
this.workerSession.terminate(new Error('Invalid Rust module service request'));
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
try {
|
|
735
|
+
const receipt = WASM_RUST_RUNTIME_PROFILE.assetReceipts['wasm-rust/runtime/rustc/rustc.wasm.gz'];
|
|
736
|
+
moduleServices.add(serveRuntimeWasmModule(message.port, {
|
|
737
|
+
cache: this.runtimeAssetCache,
|
|
738
|
+
receipt: {
|
|
739
|
+
bytes: receipt.uncompressedBytes,
|
|
740
|
+
sha256: receipt.uncompressedSha256
|
|
741
|
+
},
|
|
742
|
+
identity: JSON.stringify([
|
|
743
|
+
this.runtimeAssetsKey,
|
|
744
|
+
this.compilerUrl,
|
|
745
|
+
WASM_RUST_RUNTIME_PROFILE.profileId,
|
|
746
|
+
this.executableGraphFingerprint
|
|
747
|
+
]),
|
|
748
|
+
compileKey: 'rustc-native-default-v1',
|
|
749
|
+
maxAssetBytes: limits.maxAssetBytes
|
|
750
|
+
}));
|
|
751
|
+
}
|
|
752
|
+
catch (error) {
|
|
753
|
+
message.port.close();
|
|
754
|
+
this.workerSession.terminate(error);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
}
|
|
682
758
|
this.worker.postMessage({
|
|
683
759
|
persistentCache,
|
|
760
|
+
...(moduleBroker ? { rustcModuleBroker: moduleBroker.port2 } : {}),
|
|
684
761
|
code,
|
|
685
762
|
prepare,
|
|
686
763
|
buffer: this.buffer,
|
|
@@ -704,7 +781,7 @@ class Rust {
|
|
|
704
781
|
}
|
|
705
782
|
: {})
|
|
706
783
|
}
|
|
707
|
-
});
|
|
784
|
+
}, moduleBroker ? [moduleBroker.port2] : []);
|
|
708
785
|
}
|
|
709
786
|
catch (error) {
|
|
710
787
|
this.workerSession.terminate(error);
|
|
@@ -776,6 +853,26 @@ class Rust {
|
|
|
776
853
|
kill() {
|
|
777
854
|
return this.terminate();
|
|
778
855
|
}
|
|
856
|
+
setRuntimeAssetCache(cache) {
|
|
857
|
+
if (cache === this.runtimeAssetCache)
|
|
858
|
+
return;
|
|
859
|
+
if (this.worker || this.loadController || this.runActive) {
|
|
860
|
+
throw new Error('Cannot replace the Rust runtime asset cache while its worker is active');
|
|
861
|
+
}
|
|
862
|
+
if (this.ownsRuntimeAssetCache)
|
|
863
|
+
this.runtimeAssetCache.dispose();
|
|
864
|
+
this.runtimeAssetCache = cache;
|
|
865
|
+
this.ownsRuntimeAssetCache = false;
|
|
866
|
+
}
|
|
867
|
+
async dispose() {
|
|
868
|
+
try {
|
|
869
|
+
await this.terminate();
|
|
870
|
+
}
|
|
871
|
+
finally {
|
|
872
|
+
if (this.ownsRuntimeAssetCache)
|
|
873
|
+
this.runtimeAssetCache.dispose();
|
|
874
|
+
}
|
|
875
|
+
}
|
|
779
876
|
async terminate() {
|
|
780
877
|
this.loadGeneration += 1;
|
|
781
878
|
this.loadController?.abort(new Error('Rust runtime terminated'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const STATIC_STDIN_RING_PROTOCOL: "wasm-idle-static-stdin-ring";
|
|
2
|
-
export declare const STATIC_STDIN_RING_PROTOCOL_VERSION:
|
|
2
|
+
export declare const STATIC_STDIN_RING_PROTOCOL_VERSION: 2;
|
|
3
3
|
export declare const STATIC_STDIN_RING_CONTROL_SLOTS: 4;
|
|
4
4
|
export declare const STATIC_STDIN_RING_CONTROL_BYTES: number;
|
|
5
5
|
export declare const STATIC_STDIN_RING_WRITE_INDEX: 0;
|
|
@@ -27,6 +27,7 @@ export declare class StaticStdinRingHost {
|
|
|
27
27
|
readonly maxBufferedBytes: number;
|
|
28
28
|
private readonly control;
|
|
29
29
|
private readonly data;
|
|
30
|
+
private readonly counterRange;
|
|
30
31
|
private readonly pending;
|
|
31
32
|
private pendingBytes;
|
|
32
33
|
private pendingOffset;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const STATIC_STDIN_RING_PROTOCOL = 'wasm-idle-static-stdin-ring';
|
|
2
|
-
export const STATIC_STDIN_RING_PROTOCOL_VERSION =
|
|
2
|
+
export const STATIC_STDIN_RING_PROTOCOL_VERSION = 2;
|
|
3
3
|
export const STATIC_STDIN_RING_CONTROL_SLOTS = 4;
|
|
4
4
|
export const STATIC_STDIN_RING_CONTROL_BYTES = STATIC_STDIN_RING_CONTROL_SLOTS * Int32Array.BYTES_PER_ELEMENT;
|
|
5
5
|
export const STATIC_STDIN_RING_WRITE_INDEX = 0;
|
|
@@ -24,6 +24,7 @@ export class StaticStdinRingHost {
|
|
|
24
24
|
maxBufferedBytes;
|
|
25
25
|
control;
|
|
26
26
|
data;
|
|
27
|
+
counterRange;
|
|
27
28
|
pending = [];
|
|
28
29
|
pendingBytes = 0;
|
|
29
30
|
pendingOffset = 0;
|
|
@@ -43,12 +44,18 @@ export class StaticStdinRingHost {
|
|
|
43
44
|
if (maxBufferedBytes < capacity) {
|
|
44
45
|
throw new TypeError('Static stdin maximum buffered bytes must cover the ring capacity');
|
|
45
46
|
}
|
|
47
|
+
if (capacity > 2 ** 31) {
|
|
48
|
+
throw new TypeError('Static stdin ring capacity must not exceed 2^31 bytes');
|
|
49
|
+
}
|
|
46
50
|
if (typeof SharedArrayBuffer !== 'function') {
|
|
47
51
|
throw new TypeError('SharedArrayBuffer is required for streaming static stdin');
|
|
48
52
|
}
|
|
49
53
|
const buffer = new SharedArrayBuffer(STATIC_STDIN_RING_CONTROL_BYTES + capacity);
|
|
50
54
|
this.control = new Int32Array(buffer, 0, STATIC_STDIN_RING_CONTROL_SLOTS);
|
|
51
55
|
this.data = new Uint8Array(buffer, STATIC_STDIN_RING_CONTROL_BYTES, capacity);
|
|
56
|
+
// A capacity-aligned modulus preserves physical offsets at rollover for
|
|
57
|
+
// non-power-of-two rings too. At least two full rings fit in this range.
|
|
58
|
+
this.counterRange = Math.floor(2 ** 32 / capacity) * capacity;
|
|
52
59
|
this.maxBufferedBytes = maxBufferedBytes;
|
|
53
60
|
this.descriptor = Object.freeze({
|
|
54
61
|
protocol: STATIC_STDIN_RING_PROTOCOL,
|
|
@@ -107,10 +114,12 @@ export class StaticStdinRingHost {
|
|
|
107
114
|
this.flush();
|
|
108
115
|
}
|
|
109
116
|
ringBytes() {
|
|
110
|
-
const write = Atomics.load(this.control, STATIC_STDIN_RING_WRITE_INDEX);
|
|
111
|
-
const read = Atomics.load(this.control, STATIC_STDIN_RING_READ_INDEX);
|
|
112
|
-
const used = write - read;
|
|
113
|
-
if (
|
|
117
|
+
const write = Atomics.load(this.control, STATIC_STDIN_RING_WRITE_INDEX) >>> 0;
|
|
118
|
+
const read = Atomics.load(this.control, STATIC_STDIN_RING_READ_INDEX) >>> 0;
|
|
119
|
+
const used = (write - read + this.counterRange) % this.counterRange;
|
|
120
|
+
if (write >= this.counterRange ||
|
|
121
|
+
read >= this.counterRange ||
|
|
122
|
+
used > this.data.byteLength) {
|
|
114
123
|
throw new Error('Static stdin ring counters are invalid');
|
|
115
124
|
}
|
|
116
125
|
return used;
|
|
@@ -118,10 +127,12 @@ export class StaticStdinRingHost {
|
|
|
118
127
|
flush() {
|
|
119
128
|
if (this.cancelled)
|
|
120
129
|
return;
|
|
121
|
-
let write = Atomics.load(this.control, STATIC_STDIN_RING_WRITE_INDEX);
|
|
122
|
-
const read = Atomics.load(this.control, STATIC_STDIN_RING_READ_INDEX);
|
|
123
|
-
const used = write - read;
|
|
124
|
-
if (
|
|
130
|
+
let write = Atomics.load(this.control, STATIC_STDIN_RING_WRITE_INDEX) >>> 0;
|
|
131
|
+
const read = Atomics.load(this.control, STATIC_STDIN_RING_READ_INDEX) >>> 0;
|
|
132
|
+
const used = (write - read + this.counterRange) % this.counterRange;
|
|
133
|
+
if (write >= this.counterRange ||
|
|
134
|
+
read >= this.counterRange ||
|
|
135
|
+
used > this.data.byteLength) {
|
|
125
136
|
throw new Error('Static stdin ring counters are invalid');
|
|
126
137
|
}
|
|
127
138
|
let available = this.data.byteLength - used;
|
|
@@ -136,7 +147,7 @@ export class StaticStdinRingHost {
|
|
|
136
147
|
if (first < count) {
|
|
137
148
|
this.data.set(chunk.subarray(this.pendingOffset + first, this.pendingOffset + count), 0);
|
|
138
149
|
}
|
|
139
|
-
write
|
|
150
|
+
write = (write + count) % this.counterRange;
|
|
140
151
|
available -= count;
|
|
141
152
|
this.pendingOffset += count;
|
|
142
153
|
this.pendingBytes -= count;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PlaygroundRuntimeAssets } from './assets';
|
|
2
|
-
import { type ExecutionLimits, type RuntimeAssetIntegrityEntry, type RuntimeAssetCacheOptions, type RuntimeStdinMode, type RuntimeWorkerLifetimePolicy } from '@wasm-idle/core';
|
|
2
|
+
import { type ExecutionLimits, type RuntimeAssetIntegrityEntry, type RuntimeAssetCacheOptions, type RuntimeAssetCache, type RuntimeWasmModuleReceipt, type RuntimeStdinMode, type RuntimeWorkerLifetimePolicy } from '@wasm-idle/core';
|
|
3
3
|
import { type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
|
|
4
4
|
import type { Sandbox, SandboxProgress } from './sandbox';
|
|
5
5
|
type StaticWorkerReceipt = Readonly<Required<Pick<RuntimeAssetIntegrityEntry, 'bytes' | 'sha256'>>>;
|
|
@@ -55,8 +55,16 @@ export interface StaticWorkerRuntimeConfig {
|
|
|
55
55
|
workerLifetime?: RuntimeWorkerLifetimePolicy;
|
|
56
56
|
resolveRuntimeAssets: (runtimeAssets: string | PlaygroundRuntimeAssets, currentUrl: string) => StaticWorkerRuntimeUrls;
|
|
57
57
|
preflightRuntimeAssets?: (urls: StaticWorkerRuntimeUrls, context: StaticWorkerRuntimePreflightContext) => unknown | Promise<unknown>;
|
|
58
|
+
/** Only the host selects verified logical receipts and deterministic compilation settings. */
|
|
59
|
+
compilerModules?: (urls: StaticWorkerRuntimeUrls, limits: ExecutionLimits) => Readonly<Record<string, StaticWorkerCompilerModule>>;
|
|
58
60
|
runtimePreflightDelivery?: 'structured-clone' | 'transfer-owned' | 'transfer-owned-worker-cache';
|
|
59
61
|
}
|
|
62
|
+
export interface StaticWorkerCompilerModule {
|
|
63
|
+
readonly receipt: RuntimeWasmModuleReceipt;
|
|
64
|
+
/** Include the transform implementation version, options and any enforced memory cap. */
|
|
65
|
+
readonly compileKey: string;
|
|
66
|
+
readonly compile?: (ownedBytes: Uint8Array<ArrayBuffer>) => Promise<WebAssembly.Module>;
|
|
67
|
+
}
|
|
60
68
|
type StdinWaiter = {
|
|
61
69
|
reject: (reason: unknown) => void;
|
|
62
70
|
resolve: () => void;
|
|
@@ -101,7 +109,13 @@ export declare class StaticWorkerRuntimeSandbox implements Sandbox {
|
|
|
101
109
|
private readonly workerRuntimePreflightMaxAssetBytes;
|
|
102
110
|
private readonly workersWithCachedRuntimePreflight;
|
|
103
111
|
private workerStartMaxAssetBytes;
|
|
112
|
+
private runtimeAssetCache?;
|
|
113
|
+
private readonly workerCompilerModuleServices;
|
|
104
114
|
constructor(config: StaticWorkerRuntimeConfig);
|
|
115
|
+
/** The application owns this cache; worker or sandbox disposal never disposes it. */
|
|
116
|
+
setRuntimeAssetCache(cache: RuntimeAssetCache): void;
|
|
117
|
+
private createCompilerModulePorts;
|
|
118
|
+
private closeCompilerModuleServices;
|
|
105
119
|
private usesOwnedRuntimePreflight;
|
|
106
120
|
private usesWorkerCachedRuntimePreflight;
|
|
107
121
|
private resetWorkerForLowerAssetLimit;
|