wasm-idle 1.0.5 → 1.0.7
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 +52 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/playground/assetBridge.d.ts +11 -2
- package/dist/playground/assetBridge.js +190 -46
- package/dist/playground/assets.d.ts +9 -1
- package/dist/playground/assets.js +29 -5
- package/dist/playground/awk.js +1 -0
- package/dist/playground/bash.d.ts +1 -0
- package/dist/playground/bash.js +7 -1
- package/dist/playground/bqn.js +1 -0
- package/dist/playground/bqnPreflight.d.ts +2 -1
- package/dist/playground/bqnPreflight.js +1 -0
- package/dist/playground/c3.js +1 -0
- package/dist/playground/cachedClang.d.ts +10 -0
- package/dist/playground/cachedClang.js +330 -0
- package/dist/playground/clang.d.ts +8 -0
- package/dist/playground/clang.js +47 -8
- package/dist/playground/clangWorkerProtocol.d.ts +57 -0
- package/dist/playground/clangWorkerProtocol.js +11 -0
- package/dist/playground/clojurescript.js +1 -0
- package/dist/playground/clojurescriptPreflight.d.ts +2 -1
- package/dist/playground/clojurescriptPreflight.js +1 -0
- package/dist/playground/compiledArtifactCache.d.ts +21 -0
- package/dist/playground/compiledArtifactCache.js +67 -0
- package/dist/playground/elixir.js +4 -1
- package/dist/playground/forth.js +1 -0
- package/dist/playground/forthPreflight.d.ts +2 -1
- package/dist/playground/forthPreflight.js +1 -0
- package/dist/playground/fortran.js +7 -1
- package/dist/playground/haskell.js +4 -1
- package/dist/playground/haskellAssets.d.ts +2 -1
- package/dist/playground/haskellAssets.js +8 -8
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +2 -2
- package/dist/playground/j.js +1 -0
- package/dist/playground/jPreflight.d.ts +2 -1
- package/dist/playground/jPreflight.js +1 -0
- package/dist/playground/janet.js +1 -0
- package/dist/playground/java.js +20 -8
- package/dist/playground/julia.js +1 -0
- package/dist/playground/lfortranAssets.d.ts +2 -1
- package/dist/playground/lfortranAssets.js +1 -0
- package/dist/playground/lisp.js +4 -1
- package/dist/playground/lispAssets.d.ts +2 -1
- package/dist/playground/lispAssets.js +10 -0
- package/dist/playground/lldbManifest.d.ts +2 -2
- package/dist/playground/lldbManifest.js +17 -3
- package/dist/playground/lldbSession.d.ts +1 -0
- package/dist/playground/lldbSession.js +2 -1
- package/dist/playground/nim.js +1 -0
- package/dist/playground/ocaml.d.ts +1 -0
- package/dist/playground/ocaml.js +12 -1
- package/dist/playground/options.d.ts +2 -0
- package/dist/playground/pascal.js +1 -0
- package/dist/playground/perl.js +1 -0
- package/dist/playground/prolog.js +1 -0
- package/dist/playground/python.d.ts +7 -0
- package/dist/playground/python.js +41 -8
- package/dist/playground/runtimeAssetCache.d.ts +26 -0
- package/dist/playground/runtimeAssetCache.js +84 -0
- package/dist/playground/runtimeSession.d.ts +21 -0
- package/dist/playground/runtimeSession.js +119 -0
- package/dist/playground/rust.d.ts +1 -0
- package/dist/playground/rust.js +11 -1
- package/dist/playground/rustExecutableGraph.d.ts +1 -0
- package/dist/playground/rustExecutableGraph.js +1 -0
- package/dist/playground/staticRuntimePreflight.d.ts +2 -1
- package/dist/playground/staticRuntimePreflight.js +4 -2
- package/dist/playground/staticRuntimePreflightExecute.js +1 -0
- package/dist/playground/staticRuntimePreflightProtocol.d.ts +2 -1
- package/dist/playground/staticWorkerRuntime.d.ts +3 -1
- package/dist/playground/staticWorkerRuntime.js +44 -3
- package/dist/playground/tcl.js +1 -0
- package/dist/playground/tinygo.d.ts +2 -0
- package/dist/playground/tinygo.js +57 -3
- package/dist/playground/tinygoExecutableGraph.d.ts +4 -1
- package/dist/playground/tinygoExecutableGraph.js +59 -7
- package/dist/playground/typescript.js +4 -1
- package/dist/playground/wasmGoVersion.d.ts +1 -1
- package/dist/playground/wasmGoVersion.js +1 -1
- package/dist/playground/wasmOcamlVersion.d.ts +2 -2
- package/dist/playground/wasmOcamlVersion.js +1 -1
- package/dist/playground/wasmRustVersion.d.ts +90 -24
- package/dist/playground/wasmRustVersion.js +98 -23
- package/dist/playground/wasmTinyGoVersion.d.ts +40 -29
- package/dist/playground/wasmTinyGoVersion.js +43 -31
- package/dist/playground/worker/assets.d.ts +3 -0
- package/dist/playground/worker/assets.js +41 -0
- package/dist/playground/worker/cachedClangCompile.d.ts +1 -0
- package/dist/playground/worker/cachedClangCompile.js +85 -0
- package/dist/playground/worker/cachedClangExecute.d.ts +1 -0
- package/dist/playground/worker/cachedClangExecute.js +67 -0
- package/dist/playground/worker/clang.js +2 -8
- package/dist/playground/worker/clangStreaming.d.ts +1 -1
- package/dist/playground/worker/clangStreaming.js +20 -2
- package/dist/playground/worker/elixir.js +10 -8
- package/dist/playground/worker/fortran.js +4 -7
- package/dist/playground/worker/haskell.js +2 -0
- package/dist/playground/worker/lisp.js +2 -0
- package/dist/playground/worker/ocaml.js +13 -1
- package/dist/playground/worker/python.js +3 -2
- package/dist/playground/worker/runtimeAssetFetch.d.ts +11 -1
- package/dist/playground/worker/runtimeAssetFetch.js +64 -1
- package/dist/playground/worker/runtimeModule.d.ts +8 -0
- package/dist/playground/worker/runtimeModule.js +49 -0
- package/dist/playground/worker/rust.js +9 -2
- package/dist/playground/worker/staticRuntimePreflight.js +2 -0
- package/dist/playground/worker/typescript.js +4 -1
- package/dist/playground/worker/zig.js +7 -4
- package/dist/playground/zig.js +4 -1
- package/package.json +10 -7
package/dist/playground/bash.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveBashRuntimeAssetConfig } from './assets';
|
|
2
2
|
import { BASH_WORKER_PROTOCOL_VERSION, isBashWorkerToHostMessage } from './bashWorkerProtocol';
|
|
3
3
|
import { reportWorkerProgress } from './workerProgress';
|
|
4
|
-
import { AssetTooLargeError, AssetIntegrityError, AssetNotFoundError, BusyError, CancelledError, CompileError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, ProtocolError, ResourceLimitError, RuntimeConfigurationError, RuntimeExecutionError, TimeoutError, UnsupportedBrowserFeatureError, UnsupportedLanguageError, WasmIdleError, WorkerStartupError, preflightBashRuntimeAssets, resolveExecutionLimits, validateExecutionWorkspace } from '@wasm-idle/core';
|
|
4
|
+
import { AssetTooLargeError, AssetIntegrityError, AssetNotFoundError, BusyError, CancelledError, CompileError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, ProtocolError, ResourceLimitError, RuntimeConfigurationError, RuntimeExecutionError, TimeoutError, UnsupportedBrowserFeatureError, UnsupportedLanguageError, WasmIdleError, WorkerStartupError, preflightBashRuntimeAssets, resolveExecutionLimits, resolveRuntimeAssetCacheOptions, validateExecutionWorkspace } from '@wasm-idle/core';
|
|
5
5
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
6
6
|
const textEncoder = new TextEncoder();
|
|
7
7
|
const abortReason = (signal, phase) => signal.reason !== undefined
|
|
@@ -29,6 +29,7 @@ class Bash {
|
|
|
29
29
|
activeOperation = null;
|
|
30
30
|
readyWorker = null;
|
|
31
31
|
loadedConfig = null;
|
|
32
|
+
persistentCache;
|
|
32
33
|
requestUid = 0;
|
|
33
34
|
sessionUid = 0;
|
|
34
35
|
disposed = false;
|
|
@@ -85,6 +86,9 @@ class Bash {
|
|
|
85
86
|
const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
86
87
|
const resolved = resolveBashRuntimeAssetConfig(runtimeAssets, currentUrl);
|
|
87
88
|
this.requireActive(operation);
|
|
89
|
+
this.persistentCache =
|
|
90
|
+
typeof runtimeAssets === 'string' ? undefined : runtimeAssets.persistentCache;
|
|
91
|
+
operation.persistentCache = resolveRuntimeAssetCacheOptions(this.persistentCache, options.persistentCache);
|
|
88
92
|
const config = {
|
|
89
93
|
...resolved,
|
|
90
94
|
identity: createBashConfigIdentity(resolved.preflightKey, limits),
|
|
@@ -165,6 +169,7 @@ class Bash {
|
|
|
165
169
|
this.requireActive(operation);
|
|
166
170
|
if (signal?.aborted)
|
|
167
171
|
throw abortReason(signal, operation.stage);
|
|
172
|
+
operation.persistentCache = resolveRuntimeAssetCacheOptions(this.persistentCache, options.persistentCache);
|
|
168
173
|
const limits = resolveExecutionLimits(options.limits);
|
|
169
174
|
this.requireActive(operation);
|
|
170
175
|
if (!this.readyWorker) {
|
|
@@ -481,6 +486,7 @@ class Bash {
|
|
|
481
486
|
webcUrl: config.webcUrl,
|
|
482
487
|
profile: config.preflightProfile,
|
|
483
488
|
limits: config.limits,
|
|
489
|
+
persistentCache: operation.persistentCache,
|
|
484
490
|
signal: preflightController.signal,
|
|
485
491
|
reportProgress: ({ assetKey, loadedBytes }) => {
|
|
486
492
|
loadedByAsset.set(assetKey, loadedBytes);
|
package/dist/playground/bqn.js
CHANGED
|
@@ -35,6 +35,7 @@ class Bqn extends StaticWorkerRuntimeSandbox {
|
|
|
35
35
|
manifestUrl: urls.manifestUrl || '',
|
|
36
36
|
profile,
|
|
37
37
|
limits: context.limits,
|
|
38
|
+
persistentCache: context.persistentCache,
|
|
38
39
|
signal: context.signal,
|
|
39
40
|
reportProgress(progress) {
|
|
40
41
|
if (progress.kind === 'asset') {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BqnRuntimePreflightProfile } from './assets';
|
|
2
|
-
import { type ExecutionLimits, type RuntimeAssetPreflightProgress } from '@wasm-idle/core';
|
|
2
|
+
import { type ExecutionLimits, type RuntimeAssetCacheOptions, type RuntimeAssetPreflightProgress } from '@wasm-idle/core';
|
|
3
3
|
export declare const BQN_PREFLIGHT_PROTOCOL: "wasm-idle-bqn-preflight";
|
|
4
4
|
export declare const BQN_PREFLIGHT_PROTOCOL_VERSION: 1;
|
|
5
5
|
export declare const BQN_PREFLIGHT_RUNTIME_ID: "BQN";
|
|
@@ -8,6 +8,7 @@ export interface BqnRuntimePreflightRequest {
|
|
|
8
8
|
readonly manifestUrl: string;
|
|
9
9
|
readonly profile: BqnRuntimePreflightProfile;
|
|
10
10
|
readonly limits?: Partial<ExecutionLimits>;
|
|
11
|
+
readonly persistentCache?: RuntimeAssetCacheOptions;
|
|
11
12
|
readonly signal?: AbortSignal;
|
|
12
13
|
readonly fetch?: typeof globalThis.fetch;
|
|
13
14
|
readonly reportProgress?: (progress: RuntimeAssetPreflightProgress) => void;
|
package/dist/playground/c3.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Sandbox } from '@wasm-idle/core';
|
|
2
|
+
import type { RuntimeAssetCache } from './runtimeAssetCache';
|
|
3
|
+
import { CompiledArtifactCache } from './compiledArtifactCache';
|
|
4
|
+
type WorkerFactories = {
|
|
5
|
+
compile(): Worker | Promise<Worker>;
|
|
6
|
+
execute(): Worker | Promise<Worker>;
|
|
7
|
+
};
|
|
8
|
+
/** Wrap the public sandbox contract; all other languages and debugger operations stay unchanged. */
|
|
9
|
+
export declare function createCachedClangSandbox(legacy: Sandbox, language: 'C' | 'CPP', runtimeCache: RuntimeAssetCache, cache?: CompiledArtifactCache, factories?: WorkerFactories): Sandbox;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { resolveExecutionLimits, resolveRuntimeAssetCacheOptions } from '@wasm-idle/core';
|
|
2
|
+
import { resolveRuntimeAssetConfig } from './assets';
|
|
3
|
+
import { WorkerAssetBridge } from './assetBridge';
|
|
4
|
+
import { shouldStreamBundledClang } from './clangStreamingPolicy';
|
|
5
|
+
import { CompiledArtifactCache, clangCompileKey } from './compiledArtifactCache';
|
|
6
|
+
const workers = {
|
|
7
|
+
async compile() {
|
|
8
|
+
const { default: CompileWorker } = await import('./worker/cachedClangCompile?worker');
|
|
9
|
+
return new CompileWorker();
|
|
10
|
+
},
|
|
11
|
+
async execute() {
|
|
12
|
+
const { default: ExecuteWorker } = await import('./worker/cachedClangExecute?worker');
|
|
13
|
+
return new ExecuteWorker();
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/** Wrap the public sandbox contract; all other languages and debugger operations stay unchanged. */
|
|
17
|
+
export function createCachedClangSandbox(legacy, language, runtimeCache, cache = new CompiledArtifactCache(), factories = workers) {
|
|
18
|
+
let cached = false;
|
|
19
|
+
let loadedAssets = '';
|
|
20
|
+
let runtimeBaseUrl = '';
|
|
21
|
+
let assetConfig;
|
|
22
|
+
let maxAssetBytes = 0;
|
|
23
|
+
let languageSysroots = false;
|
|
24
|
+
let activeWorker;
|
|
25
|
+
let cancelWorker;
|
|
26
|
+
let generation = 0;
|
|
27
|
+
let running = false;
|
|
28
|
+
let disposed = false;
|
|
29
|
+
let disposal;
|
|
30
|
+
let inputBuffer;
|
|
31
|
+
let inputRequested = false;
|
|
32
|
+
let pendingEof = false;
|
|
33
|
+
const pendingInput = [];
|
|
34
|
+
const encoder = new TextEncoder();
|
|
35
|
+
function resetInput() {
|
|
36
|
+
pendingInput.length = 0;
|
|
37
|
+
pendingEof = false;
|
|
38
|
+
inputRequested = false;
|
|
39
|
+
inputBuffer = undefined;
|
|
40
|
+
}
|
|
41
|
+
function flushInput() {
|
|
42
|
+
if (!inputRequested || !inputBuffer)
|
|
43
|
+
return;
|
|
44
|
+
const control = new Int32Array(inputBuffer, 0, 2);
|
|
45
|
+
const payload = new Uint8Array(inputBuffer, 8);
|
|
46
|
+
const next = pendingInput[0];
|
|
47
|
+
if (next) {
|
|
48
|
+
const length = Math.min(next.byteLength, payload.byteLength);
|
|
49
|
+
payload.set(next.subarray(0, length));
|
|
50
|
+
if (length === next.byteLength)
|
|
51
|
+
pendingInput.shift();
|
|
52
|
+
else
|
|
53
|
+
pendingInput[0] = next.subarray(length);
|
|
54
|
+
Atomics.store(control, 1, length);
|
|
55
|
+
Atomics.store(control, 0, 1);
|
|
56
|
+
}
|
|
57
|
+
else if (pendingEof) {
|
|
58
|
+
Atomics.store(control, 1, 0);
|
|
59
|
+
Atomics.store(control, 0, 2);
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
return;
|
|
63
|
+
inputRequested = false;
|
|
64
|
+
Atomics.notify(control, 0);
|
|
65
|
+
}
|
|
66
|
+
function stop() {
|
|
67
|
+
generation++;
|
|
68
|
+
running = false;
|
|
69
|
+
cancelWorker?.(new Error('Process terminated'));
|
|
70
|
+
resetInput();
|
|
71
|
+
}
|
|
72
|
+
async function workerOperation(factory, message, token, progress, compileLimit = 0, signal, executionAssetConfig = assetConfig) {
|
|
73
|
+
const worker = await factory();
|
|
74
|
+
if (token !== generation || signal?.aborted) {
|
|
75
|
+
worker.terminate();
|
|
76
|
+
throw signal?.reason ?? new Error('Process terminated');
|
|
77
|
+
}
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
let bridge;
|
|
80
|
+
try {
|
|
81
|
+
if (compileLimit)
|
|
82
|
+
bridge = new WorkerAssetBridge(worker, 'clang', executionAssetConfig, progress, compileLimit, languageSysroots, runtimeCache);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
worker.terminate();
|
|
86
|
+
reject(error);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
activeWorker = worker;
|
|
90
|
+
const abort = () => finish(signal?.reason instanceof Error
|
|
91
|
+
? signal.reason
|
|
92
|
+
: new Error('Process terminated'));
|
|
93
|
+
const finish = (error, value) => {
|
|
94
|
+
if (activeWorker !== worker)
|
|
95
|
+
return;
|
|
96
|
+
activeWorker = undefined;
|
|
97
|
+
cancelWorker = undefined;
|
|
98
|
+
signal?.removeEventListener('abort', abort);
|
|
99
|
+
bridge?.dispose();
|
|
100
|
+
worker.onmessage = null;
|
|
101
|
+
worker.onerror = null;
|
|
102
|
+
worker.onmessageerror = null;
|
|
103
|
+
worker.terminate();
|
|
104
|
+
if (error)
|
|
105
|
+
reject(error);
|
|
106
|
+
else
|
|
107
|
+
resolve(value);
|
|
108
|
+
};
|
|
109
|
+
cancelWorker = finish;
|
|
110
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
111
|
+
worker.onerror = (event) => finish(new Error(event.message || 'C/C++ worker failed'));
|
|
112
|
+
worker.onmessageerror = () => finish(new Error('C/C++ worker message could not be decoded'));
|
|
113
|
+
worker.onmessage = (event) => {
|
|
114
|
+
if (activeWorker !== worker)
|
|
115
|
+
return;
|
|
116
|
+
try {
|
|
117
|
+
if (bridge?.handleMessage(event))
|
|
118
|
+
return;
|
|
119
|
+
const { data } = event;
|
|
120
|
+
if (data.type === 'output')
|
|
121
|
+
legacy.output?.(data.output);
|
|
122
|
+
else if (data.type === 'progress') {
|
|
123
|
+
progress?.set?.(data.progress.percent / 100);
|
|
124
|
+
progress?.report?.({
|
|
125
|
+
kind: 'activity',
|
|
126
|
+
phase: 'compiling',
|
|
127
|
+
label: data.progress.message
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (data.type === 'stdin') {
|
|
131
|
+
inputRequested = true;
|
|
132
|
+
progress?.report?.({
|
|
133
|
+
kind: 'ready',
|
|
134
|
+
state: 'waiting-input',
|
|
135
|
+
reason: 'stdin-request',
|
|
136
|
+
label: 'Runtime ready for input'
|
|
137
|
+
});
|
|
138
|
+
flushInput();
|
|
139
|
+
}
|
|
140
|
+
else if (data.type === 'compiled' || data.type === 'error') {
|
|
141
|
+
if (data.stdout)
|
|
142
|
+
legacy.output?.(data.stdout);
|
|
143
|
+
if (data.stderr)
|
|
144
|
+
legacy.output?.(data.stderr);
|
|
145
|
+
if (data.type === 'error')
|
|
146
|
+
finish(new Error(data.error));
|
|
147
|
+
else
|
|
148
|
+
finish(undefined, data.artifact);
|
|
149
|
+
}
|
|
150
|
+
else if (data.type === 'done') {
|
|
151
|
+
if (data.exitCode !== 0 && data.exitCode !== null)
|
|
152
|
+
finish(new Error(`Program exited with code ${data.exitCode}`));
|
|
153
|
+
else
|
|
154
|
+
finish(undefined, true);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
try {
|
|
162
|
+
worker.postMessage(message);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const overrides = {
|
|
170
|
+
async load(assets = '', code = '', log = true, args = [], options = {}, progress) {
|
|
171
|
+
if (disposed)
|
|
172
|
+
throw new Error('C/C++ sandbox was disposed');
|
|
173
|
+
loadedAssets = assets;
|
|
174
|
+
const config = typeof assets === 'object' ? assets : undefined;
|
|
175
|
+
assetConfig = resolveRuntimeAssetConfig('clang', assets, globalThis.location?.href || 'http://localhost/', options.persistentCache);
|
|
176
|
+
const debugMode = options.debugMode || (options.debug ? 'trace' : 'none');
|
|
177
|
+
const cacheEligible = debugMode === 'none' &&
|
|
178
|
+
shouldStreamBundledClang(assetConfig) &&
|
|
179
|
+
!options.workspaceFiles?.some((file) => typeof file.content !== 'string') &&
|
|
180
|
+
(options.stdin === undefined || typeof options.stdin === 'string') &&
|
|
181
|
+
typeof SharedArrayBuffer !== 'undefined';
|
|
182
|
+
if (!cached)
|
|
183
|
+
await legacy.clear();
|
|
184
|
+
if (disposed)
|
|
185
|
+
throw new Error('C/C++ sandbox was disposed');
|
|
186
|
+
stop();
|
|
187
|
+
cached = cacheEligible;
|
|
188
|
+
if (!cached)
|
|
189
|
+
return legacy.load(assets, code, log, args, options, progress);
|
|
190
|
+
runtimeBaseUrl = assetConfig.baseUrl;
|
|
191
|
+
maxAssetBytes = resolveExecutionLimits(options.limits).maxAssetBytes;
|
|
192
|
+
languageSysroots = config?.clang?.bundledLanguageSysroots === true;
|
|
193
|
+
},
|
|
194
|
+
async run(code, prepare, log = true, progress, args = [], options = {}) {
|
|
195
|
+
if (disposed)
|
|
196
|
+
throw new Error('C/C++ sandbox was disposed');
|
|
197
|
+
if (running)
|
|
198
|
+
throw new Error('C/C++ sandbox is already running');
|
|
199
|
+
if (cached &&
|
|
200
|
+
((options.debugMode || (options.debug ? 'trace' : 'none')) !== 'none' ||
|
|
201
|
+
(options.stdin !== undefined && typeof options.stdin !== 'string') ||
|
|
202
|
+
options.workspaceFiles?.some((file) => typeof file.content !== 'string'))) {
|
|
203
|
+
await overrides.load(loadedAssets, code, log, args, {
|
|
204
|
+
...options,
|
|
205
|
+
persistentCache: assetConfig.persistentCache
|
|
206
|
+
}, progress);
|
|
207
|
+
}
|
|
208
|
+
if (!cached)
|
|
209
|
+
return legacy.run(code, prepare, log, progress, args, options);
|
|
210
|
+
const executionAssetConfig = {
|
|
211
|
+
...assetConfig,
|
|
212
|
+
persistentCache: resolveRuntimeAssetCacheOptions(assetConfig.persistentCache, options.persistentCache)
|
|
213
|
+
};
|
|
214
|
+
options.signal?.throwIfAborted();
|
|
215
|
+
const token = ++generation;
|
|
216
|
+
// Reactive UI arrays/records are Proxies and cannot cross postMessage.
|
|
217
|
+
// Snapshot known fields before awaiting compilation, so the cache key,
|
|
218
|
+
// compiler and executor also observe the same input values.
|
|
219
|
+
const request = {
|
|
220
|
+
code,
|
|
221
|
+
language,
|
|
222
|
+
activePath: options.activePath,
|
|
223
|
+
workspaceFiles: options.workspaceFiles === undefined
|
|
224
|
+
? undefined
|
|
225
|
+
: Array.from(options.workspaceFiles, (file) => ({
|
|
226
|
+
path: file.path,
|
|
227
|
+
content: file.content
|
|
228
|
+
})),
|
|
229
|
+
compileArgs: Array.from(options.compileArgs ?? args),
|
|
230
|
+
cVersion: options.cVersion,
|
|
231
|
+
cppVersion: options.cppVersion
|
|
232
|
+
};
|
|
233
|
+
const programArgs = Array.from(options.programArgs ?? []);
|
|
234
|
+
const env = options.env === undefined
|
|
235
|
+
? undefined
|
|
236
|
+
: Object.fromEntries(Object.entries(options.env));
|
|
237
|
+
const stdin = options.stdin;
|
|
238
|
+
const limit = Math.min(maxAssetBytes, resolveExecutionLimits(options.limits).maxAssetBytes);
|
|
239
|
+
const key = clangCompileKey(runtimeBaseUrl, limit, request, languageSysroots);
|
|
240
|
+
let artifact = cache.get(key);
|
|
241
|
+
running = true;
|
|
242
|
+
try {
|
|
243
|
+
if (!artifact) {
|
|
244
|
+
artifact = await workerOperation(() => factories.compile(), {
|
|
245
|
+
request,
|
|
246
|
+
runtimeBaseUrl,
|
|
247
|
+
maxAssetBytes: limit,
|
|
248
|
+
languageSysroots,
|
|
249
|
+
log,
|
|
250
|
+
assets: {
|
|
251
|
+
baseUrl: runtimeBaseUrl,
|
|
252
|
+
maxAssetBytes: limit,
|
|
253
|
+
useAssetBridge: assetConfig.useAssetBridge,
|
|
254
|
+
useModuleBridge: true
|
|
255
|
+
}
|
|
256
|
+
}, token, progress, limit, options.signal, executionAssetConfig);
|
|
257
|
+
if (token !== generation)
|
|
258
|
+
throw new Error('Process terminated');
|
|
259
|
+
cache.set(key, artifact);
|
|
260
|
+
}
|
|
261
|
+
progress?.set?.(1);
|
|
262
|
+
if (prepare)
|
|
263
|
+
return true;
|
|
264
|
+
inputBuffer = new SharedArrayBuffer(65536 + 8);
|
|
265
|
+
return await workerOperation(() => factories.execute(), {
|
|
266
|
+
artifact,
|
|
267
|
+
code,
|
|
268
|
+
activePath: request.activePath,
|
|
269
|
+
workspaceFiles: request.workspaceFiles,
|
|
270
|
+
programArgs,
|
|
271
|
+
env,
|
|
272
|
+
stdin,
|
|
273
|
+
inputBuffer
|
|
274
|
+
}, token, progress, 0, options.signal);
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
if (token === generation) {
|
|
278
|
+
running = false;
|
|
279
|
+
if (!prepare)
|
|
280
|
+
resetInput();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
write(input) {
|
|
285
|
+
if (!cached)
|
|
286
|
+
return legacy.write?.(input);
|
|
287
|
+
if (input)
|
|
288
|
+
pendingInput.push(encoder.encode(input));
|
|
289
|
+
flushInput();
|
|
290
|
+
},
|
|
291
|
+
eof() {
|
|
292
|
+
if (!cached)
|
|
293
|
+
return legacy.eof();
|
|
294
|
+
pendingEof = true;
|
|
295
|
+
flushInput();
|
|
296
|
+
},
|
|
297
|
+
async clear() {
|
|
298
|
+
stop();
|
|
299
|
+
if (!cached)
|
|
300
|
+
await legacy.clear();
|
|
301
|
+
},
|
|
302
|
+
async terminate() {
|
|
303
|
+
stop();
|
|
304
|
+
if (!cached)
|
|
305
|
+
await legacy.terminate();
|
|
306
|
+
},
|
|
307
|
+
async kill() {
|
|
308
|
+
await overrides.terminate();
|
|
309
|
+
},
|
|
310
|
+
async cancel() {
|
|
311
|
+
await overrides.terminate();
|
|
312
|
+
},
|
|
313
|
+
dispose() {
|
|
314
|
+
if (disposal)
|
|
315
|
+
return disposal;
|
|
316
|
+
disposed = true;
|
|
317
|
+
stop();
|
|
318
|
+
disposal = Promise.resolve(legacy.dispose ? legacy.dispose() : legacy.terminate());
|
|
319
|
+
return disposal;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
return new Proxy(legacy, {
|
|
323
|
+
get(target, property) {
|
|
324
|
+
if (Object.hasOwn(overrides, property))
|
|
325
|
+
return overrides[property];
|
|
326
|
+
const value = Reflect.get(target, property);
|
|
327
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DebugCommand, DebugDataBreakpoint, DebugDataBreakpointInfoArguments, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
2
|
import { WorkerAssetBridge } from './assetBridge';
|
|
3
|
+
import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
3
4
|
import { type PlaygroundRuntimeAssets, type RuntimeAssetIntegrityEntry } from './assets';
|
|
4
5
|
import type { Sandbox } from './sandbox';
|
|
5
6
|
import { type ProgressSink } from './workerProgress';
|
|
@@ -26,7 +27,12 @@ declare class Clang implements Sandbox {
|
|
|
26
27
|
debugRuntimeBaseUrl: string;
|
|
27
28
|
debugManifestUrl: string;
|
|
28
29
|
debugManifestReceipt?: Readonly<RuntimeAssetIntegrityEntry>;
|
|
30
|
+
private persistentCache?;
|
|
29
31
|
private lldbSession?;
|
|
32
|
+
private runtimeAssetCache;
|
|
33
|
+
private ownsRuntimeAssetCache;
|
|
34
|
+
private disposed;
|
|
35
|
+
private disposal?;
|
|
30
36
|
private debugMode;
|
|
31
37
|
private readonly lldbBreakpoints;
|
|
32
38
|
private debugEvaluationQueue;
|
|
@@ -56,5 +62,7 @@ declare class Clang implements Sandbox {
|
|
|
56
62
|
kill(): Promise<void>;
|
|
57
63
|
terminate(): Promise<void>;
|
|
58
64
|
clear(): Promise<void>;
|
|
65
|
+
setRuntimeAssetCache(cache: RuntimeAssetCache): void;
|
|
66
|
+
dispose(): Promise<void>;
|
|
59
67
|
}
|
|
60
68
|
export default Clang;
|
package/dist/playground/clang.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { shouldStreamBundledClang } from './clangStreamingPolicy';
|
|
2
2
|
import { WorkerAssetBridge } from './assetBridge';
|
|
3
|
+
import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
3
4
|
import { resolveDebugRuntimeUrls, resolveRuntimeAssetConfig } from './assets';
|
|
4
5
|
import { LldbSandboxSession } from './lldbSession';
|
|
5
6
|
import { normalizeDwarfWorkspacePath } from '@wasm-idle/llvm-core/clang';
|
|
@@ -38,7 +39,12 @@ class Clang {
|
|
|
38
39
|
debugRuntimeBaseUrl = '';
|
|
39
40
|
debugManifestUrl = '';
|
|
40
41
|
debugManifestReceipt;
|
|
42
|
+
persistentCache;
|
|
41
43
|
lldbSession;
|
|
44
|
+
runtimeAssetCache = new RuntimeAssetCache();
|
|
45
|
+
ownsRuntimeAssetCache = true;
|
|
46
|
+
disposed = false;
|
|
47
|
+
disposal;
|
|
42
48
|
debugMode = 'none';
|
|
43
49
|
lldbBreakpoints = new Map();
|
|
44
50
|
debugEvaluationQueue = Promise.resolve();
|
|
@@ -74,6 +80,8 @@ class Clang {
|
|
|
74
80
|
load(runtimeAssets = '', code = '', log = true, args = [], options = {}, progress) {
|
|
75
81
|
let limits;
|
|
76
82
|
try {
|
|
83
|
+
if (this.disposed)
|
|
84
|
+
throw new Error('Clang runtime has been disposed');
|
|
77
85
|
limits = resolveExecutionLimits(options.limits);
|
|
78
86
|
}
|
|
79
87
|
catch (error) {
|
|
@@ -84,9 +92,10 @@ class Clang {
|
|
|
84
92
|
this.pendingInput = [];
|
|
85
93
|
this.waitingForInput = false;
|
|
86
94
|
this.pendingEof = false;
|
|
87
|
-
const assetConfig = resolveRuntimeAssetConfig('clang', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
95
|
+
const assetConfig = resolveRuntimeAssetConfig('clang', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '', options.persistentCache);
|
|
88
96
|
const debugRuntime = resolveDebugRuntimeUrls(runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
89
97
|
this.debugRuntimeBaseUrl = debugRuntime.baseUrl;
|
|
98
|
+
this.persistentCache = assetConfig.persistentCache;
|
|
90
99
|
this.debugManifestUrl = debugRuntime.manifestUrl;
|
|
91
100
|
this.debugManifestReceipt = debugRuntime.manifestReceipt;
|
|
92
101
|
const bundledAssets = shouldStreamBundledClang(assetConfig);
|
|
@@ -107,7 +116,7 @@ class Clang {
|
|
|
107
116
|
if (!this.workerSession.attach(worker, operation))
|
|
108
117
|
return;
|
|
109
118
|
this.worker = worker;
|
|
110
|
-
this.assetBridge = new WorkerAssetBridge(this.worker, 'clang', assetConfig, progress, limits.maxAssetBytes, languageSysrootProfiles);
|
|
119
|
+
this.assetBridge = new WorkerAssetBridge(this.worker, 'clang', assetConfig, progress, limits.maxAssetBytes, languageSysrootProfiles, this.runtimeAssetCache);
|
|
111
120
|
this.worker.onmessage = (event) => {
|
|
112
121
|
if (!this.workerSession.isActive(operation))
|
|
113
122
|
return;
|
|
@@ -129,7 +138,8 @@ class Clang {
|
|
|
129
138
|
assets: {
|
|
130
139
|
baseUrl: assetConfig.baseUrl,
|
|
131
140
|
maxAssetBytes: limits.maxAssetBytes,
|
|
132
|
-
useAssetBridge: assetConfig.useAssetBridge
|
|
141
|
+
useAssetBridge: assetConfig.useAssetBridge,
|
|
142
|
+
useModuleBridge: true
|
|
133
143
|
},
|
|
134
144
|
maxAssetBytes: limits.maxAssetBytes
|
|
135
145
|
});
|
|
@@ -172,6 +182,8 @@ class Clang {
|
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
run(code, prepare, log = this.log, prog, args = [], options = {}) {
|
|
185
|
+
if (this.disposed)
|
|
186
|
+
return Promise.reject(new Error('Clang runtime has been disposed'));
|
|
175
187
|
const debugMode = options.debugMode || (options.debug ? 'trace' : 'none');
|
|
176
188
|
this.debugMode = debugMode;
|
|
177
189
|
if (debugMode !== 'none')
|
|
@@ -181,6 +193,10 @@ class Clang {
|
|
|
181
193
|
if (!this.worker)
|
|
182
194
|
return reject('Worker not loaded');
|
|
183
195
|
const operation = this.workerSession.beginRun(this.worker, reject);
|
|
196
|
+
const persistentCacheOverride = options.persistentCache;
|
|
197
|
+
if (!this.workerSession.isActive(operation))
|
|
198
|
+
return;
|
|
199
|
+
const persistentCache = this.assetBridge?.setExecutionPersistentCache(persistentCacheOverride);
|
|
184
200
|
const { compileArgs, programArgs } = resolveSandboxExecutionArgs(this.language, args, options);
|
|
185
201
|
const sourcePath = normalizeLldbSourcePath(options.debugPath || options.activePath, this.language === 'C' ? 'main.c' : 'main.cc');
|
|
186
202
|
this.lldbBreakpoints.clear();
|
|
@@ -218,6 +234,7 @@ class Clang {
|
|
|
218
234
|
if (lldbArtifact) {
|
|
219
235
|
const compilerWorker = this.worker;
|
|
220
236
|
const lldbSession = new LldbSandboxSession({
|
|
237
|
+
persistentCache: persistentCache ?? this.persistentCache,
|
|
221
238
|
manifestUrl: this.debugManifestUrl,
|
|
222
239
|
manifestReceipt: this.debugManifestReceipt,
|
|
223
240
|
runtimeBaseUrl: this.debugRuntimeBaseUrl,
|
|
@@ -422,11 +439,33 @@ class Clang {
|
|
|
422
439
|
resetBufferedStdin(this.watchResultBuffer);
|
|
423
440
|
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
424
441
|
debugBuffer.fill(0);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
442
|
+
new Uint8Array(this.interruptBuffer).fill(0);
|
|
443
|
+
}
|
|
444
|
+
setRuntimeAssetCache(cache) {
|
|
445
|
+
if (this.disposed)
|
|
446
|
+
throw new Error('Clang runtime has been disposed');
|
|
447
|
+
if (this.worker)
|
|
448
|
+
throw new Error('Cannot replace an active runtime cache');
|
|
449
|
+
if (cache === this.runtimeAssetCache)
|
|
450
|
+
return;
|
|
451
|
+
if (this.ownsRuntimeAssetCache)
|
|
452
|
+
this.runtimeAssetCache.dispose();
|
|
453
|
+
this.runtimeAssetCache = cache;
|
|
454
|
+
this.ownsRuntimeAssetCache = false;
|
|
455
|
+
}
|
|
456
|
+
dispose() {
|
|
457
|
+
if (this.disposal)
|
|
458
|
+
return this.disposal;
|
|
459
|
+
this.disposed = true;
|
|
460
|
+
this.disposal = Promise.resolve()
|
|
461
|
+
.then(() => this.clear())
|
|
462
|
+
.finally(() => {
|
|
463
|
+
if (this.ownsRuntimeAssetCache)
|
|
464
|
+
this.runtimeAssetCache.dispose();
|
|
465
|
+
this.output = null;
|
|
466
|
+
this.ondebug = undefined;
|
|
467
|
+
});
|
|
468
|
+
return this.disposal;
|
|
430
469
|
}
|
|
431
470
|
}
|
|
432
471
|
export default Clang;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { BrowserClangArtifact, BrowserClangCompileProgress, BrowserClangCompileRequest, BrowserClangWorkspaceFile } from '@wasm-idle/llvm-core/clang';
|
|
2
|
+
import type { WorkerRuntimeAssetConfig } from './worker/assets';
|
|
3
|
+
export type ClangCompileWorkerRequest = {
|
|
4
|
+
request: Omit<BrowserClangCompileRequest, 'onProgress'>;
|
|
5
|
+
runtimeBaseUrl: string;
|
|
6
|
+
maxAssetBytes?: number;
|
|
7
|
+
languageSysroots?: boolean;
|
|
8
|
+
log?: boolean;
|
|
9
|
+
assets: WorkerRuntimeAssetConfig;
|
|
10
|
+
};
|
|
11
|
+
export type ClangCompileWorkerResponse = {
|
|
12
|
+
type: 'progress';
|
|
13
|
+
progress: BrowserClangCompileProgress;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'compiled';
|
|
16
|
+
artifact: BrowserClangArtifact;
|
|
17
|
+
stdout: string;
|
|
18
|
+
stderr: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'error';
|
|
21
|
+
error: string;
|
|
22
|
+
stdout: string;
|
|
23
|
+
stderr: string;
|
|
24
|
+
};
|
|
25
|
+
export type ClangExecuteWorkerRequest = {
|
|
26
|
+
artifact: BrowserClangArtifact;
|
|
27
|
+
programArgs?: string[];
|
|
28
|
+
env?: Record<string, string>;
|
|
29
|
+
workspaceFiles?: BrowserClangWorkspaceFile[];
|
|
30
|
+
activePath?: string;
|
|
31
|
+
code?: string;
|
|
32
|
+
inputBuffer: SharedArrayBuffer;
|
|
33
|
+
stdin?: string;
|
|
34
|
+
};
|
|
35
|
+
export type ClangExecuteWorkerResponse = {
|
|
36
|
+
type: 'stdin';
|
|
37
|
+
} | {
|
|
38
|
+
type: 'output';
|
|
39
|
+
output: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: 'done';
|
|
42
|
+
exitCode: number | null;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'error';
|
|
45
|
+
error: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* One stdin response occupies two Int32 control words followed by UTF-8 bytes.
|
|
49
|
+
* Before requesting input the worker sets state to WAITING, posts `stdin`, and
|
|
50
|
+
* waits on word 0. The parent writes bytes and length, then atomically publishes
|
|
51
|
+
* DATA or EOF and notifies word 0. It writes only in response to a request.
|
|
52
|
+
* The worker copies DATA before making another request; EOF remains latched.
|
|
53
|
+
*/
|
|
54
|
+
export declare const CLANG_STDIN_HEADER_BYTES: number;
|
|
55
|
+
export declare const CLANG_STDIN_WAITING = 0;
|
|
56
|
+
export declare const CLANG_STDIN_DATA = 1;
|
|
57
|
+
export declare const CLANG_STDIN_EOF = 2;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One stdin response occupies two Int32 control words followed by UTF-8 bytes.
|
|
3
|
+
* Before requesting input the worker sets state to WAITING, posts `stdin`, and
|
|
4
|
+
* waits on word 0. The parent writes bytes and length, then atomically publishes
|
|
5
|
+
* DATA or EOF and notifies word 0. It writes only in response to a request.
|
|
6
|
+
* The worker copies DATA before making another request; EOF remains latched.
|
|
7
|
+
*/
|
|
8
|
+
export const CLANG_STDIN_HEADER_BYTES = Int32Array.BYTES_PER_ELEMENT * 2;
|
|
9
|
+
export const CLANG_STDIN_WAITING = 0;
|
|
10
|
+
export const CLANG_STDIN_DATA = 1;
|
|
11
|
+
export const CLANG_STDIN_EOF = 2;
|
|
@@ -35,6 +35,7 @@ class ClojureScript extends StaticWorkerRuntimeSandbox {
|
|
|
35
35
|
manifestUrl: urls.manifestUrl || '',
|
|
36
36
|
profile,
|
|
37
37
|
limits: context.limits,
|
|
38
|
+
persistentCache: context.persistentCache,
|
|
38
39
|
signal: context.signal,
|
|
39
40
|
reportProgress(progress) {
|
|
40
41
|
if (progress.kind === 'asset') {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ClojureScriptRuntimePreflightProfile } from './assets';
|
|
2
|
-
import { type ExecutionLimits, type RuntimeAssetPreflightProgress } from '@wasm-idle/core';
|
|
2
|
+
import { type ExecutionLimits, type RuntimeAssetCacheOptions, type RuntimeAssetPreflightProgress } from '@wasm-idle/core';
|
|
3
3
|
export declare const CLOJURESCRIPT_PREFLIGHT_PROTOCOL: "wasm-idle-clojurescript-preflight";
|
|
4
4
|
export declare const CLOJURESCRIPT_PREFLIGHT_PROTOCOL_VERSION: 1;
|
|
5
5
|
export declare const CLOJURESCRIPT_PREFLIGHT_RUNTIME_ID: "CLOJURESCRIPT";
|
|
@@ -8,6 +8,7 @@ export interface ClojureScriptRuntimePreflightRequest {
|
|
|
8
8
|
readonly manifestUrl: string;
|
|
9
9
|
readonly profile: ClojureScriptRuntimePreflightProfile;
|
|
10
10
|
readonly limits?: Partial<ExecutionLimits>;
|
|
11
|
+
readonly persistentCache?: RuntimeAssetCacheOptions;
|
|
11
12
|
readonly signal?: AbortSignal;
|
|
12
13
|
readonly fetch?: typeof globalThis.fetch;
|
|
13
14
|
readonly reportProgress?: (progress: RuntimeAssetPreflightProgress) => void;
|