wasm-idle 0.7.0 → 0.7.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 +42 -32
- package/dist/clang/gccCompat.d.ts +15 -0
- package/dist/clang/gccCompat.js +767 -0
- package/dist/clang/index.js +5 -1
- package/dist/clang/url.d.ts +4 -4
- package/dist/clang/url.js +5 -4
- package/dist/clangd/session.d.ts +5 -1
- package/dist/clangd/session.js +22 -4
- package/dist/clangd/worker.js +32 -47
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lsp/cpp/server.js +17 -6
- package/dist/lsp/index.d.ts +1 -1
- package/dist/lsp/index.js +1 -1
- package/dist/lsp/python/server.js +1 -1
- package/dist/lsp/runtime.d.ts +2 -0
- package/dist/lsp/runtime.js +9 -8
- package/dist/lsp/types.d.ts +2 -0
- package/dist/playground/assetBridge.js +8 -2
- package/dist/playground/assets.d.ts +5 -1
- package/dist/playground/assets.js +88 -47
- package/dist/playground/clang.d.ts +4 -2
- package/dist/playground/clang.js +35 -4
- package/dist/playground/ocaml.js +2 -0
- package/dist/playground/options.d.ts +4 -0
- package/dist/playground/tinygo.d.ts +4 -1
- package/dist/playground/tinygo.js +11 -6
- package/dist/playground/wasmOcamlVersion.d.ts +1 -1
- package/dist/playground/wasmOcamlVersion.js +1 -1
- package/dist/playground/wasmTinyGoVersion.d.ts +1 -1
- package/dist/playground/wasmTinyGoVersion.js +1 -1
- package/dist/playground/worker/clang.js +7 -2
- package/dist/playground/worker/elixir.js +296 -22
- package/dist/playground/worker/ocaml.js +5 -4
- package/dist/playground/worker/tinygo.js +7 -4
- package/dist/terminal/Terminal.svelte +4 -0
- package/dist/utils/vscodeJsonrpcBrowser.d.ts +5 -0
- package/dist/utils/vscodeJsonrpcBrowser.js +4 -0
- package/package.json +1 -1
package/dist/playground/clang.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { WorkerAssetBridge } from './assetBridge';
|
|
2
|
+
import { resolveRuntimeAssetConfig } from './assets';
|
|
1
3
|
import { resolveSandboxExecutionArgs } from './options';
|
|
2
4
|
import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
|
|
3
5
|
const debugBreakpointBufferInts = 1028;
|
|
@@ -20,23 +22,48 @@ class Clang {
|
|
|
20
22
|
waitingForInput = false;
|
|
21
23
|
pendingEof = false;
|
|
22
24
|
exit = true;
|
|
25
|
+
assetBridge = null;
|
|
23
26
|
activeReject = null;
|
|
24
27
|
constructor(language) {
|
|
25
28
|
this.language = language;
|
|
26
29
|
}
|
|
27
|
-
load(runtimeAssets = '', code = '', log = true, args = [], _options = {},
|
|
30
|
+
load(runtimeAssets = '', code = '', log = true, args = [], _options = {}, progress) {
|
|
28
31
|
return new Promise(async (resolve) => {
|
|
29
32
|
this.log = log;
|
|
30
33
|
this.pendingInput = [];
|
|
31
34
|
this.waitingForInput = false;
|
|
32
35
|
this.pendingEof = false;
|
|
33
|
-
const
|
|
36
|
+
const assetConfig = resolveRuntimeAssetConfig('clang', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
37
|
+
const needsWorkerReset = !this.worker || !this.assetBridge || !this.assetBridge.matches(assetConfig);
|
|
38
|
+
if (needsWorkerReset && this.worker) {
|
|
39
|
+
this.worker.terminate();
|
|
40
|
+
delete this.worker;
|
|
41
|
+
this.assetBridge = null;
|
|
42
|
+
}
|
|
34
43
|
if (!this.worker) {
|
|
35
44
|
this.worker = new (await import('./worker/clang?worker')).default();
|
|
36
|
-
this.
|
|
37
|
-
this.worker.
|
|
45
|
+
this.assetBridge = new WorkerAssetBridge(this.worker, 'clang', assetConfig, progress);
|
|
46
|
+
this.worker.onmessage = (event) => {
|
|
47
|
+
if (this.assetBridge?.handleMessage(event))
|
|
48
|
+
return;
|
|
49
|
+
if (event.data?.progress != null)
|
|
50
|
+
progress?.set?.(event.data.progress);
|
|
51
|
+
if (event.data?.load)
|
|
52
|
+
resolve();
|
|
53
|
+
};
|
|
54
|
+
this.worker.postMessage({
|
|
55
|
+
load: true,
|
|
56
|
+
log,
|
|
57
|
+
code,
|
|
58
|
+
args,
|
|
59
|
+
assets: {
|
|
60
|
+
baseUrl: assetConfig.baseUrl,
|
|
61
|
+
useAssetBridge: assetConfig.useAssetBridge
|
|
62
|
+
}
|
|
63
|
+
});
|
|
38
64
|
}
|
|
39
65
|
else {
|
|
66
|
+
this.assetBridge?.rebind(this.worker, assetConfig, progress);
|
|
40
67
|
this.worker.postMessage({ log });
|
|
41
68
|
resolve();
|
|
42
69
|
}
|
|
@@ -74,6 +101,8 @@ class Clang {
|
|
|
74
101
|
this.setBreakpoints(options.debug ? [...(options.breakpoints || [])] : []);
|
|
75
102
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
76
103
|
const handler = (event) => {
|
|
104
|
+
if (this.assetBridge?.handleMessage(event))
|
|
105
|
+
return;
|
|
77
106
|
if (!this.worker)
|
|
78
107
|
return reject('Worker not loaded');
|
|
79
108
|
if (_uid !== this.uid)
|
|
@@ -180,6 +209,7 @@ class Clang {
|
|
|
180
209
|
Atomics.notify(control, 0);
|
|
181
210
|
this.worker?.terminate?.();
|
|
182
211
|
delete this.worker;
|
|
212
|
+
this.assetBridge = null;
|
|
183
213
|
this.exit = true;
|
|
184
214
|
}
|
|
185
215
|
async clear() {
|
|
@@ -198,6 +228,7 @@ class Clang {
|
|
|
198
228
|
if (!this.exit) {
|
|
199
229
|
this.worker?.terminate?.();
|
|
200
230
|
delete this.worker;
|
|
231
|
+
this.assetBridge = null;
|
|
201
232
|
this.exit = true;
|
|
202
233
|
}
|
|
203
234
|
}
|
package/dist/playground/ocaml.js
CHANGED
|
@@ -96,6 +96,7 @@ class Ocaml {
|
|
|
96
96
|
if (!this.worker)
|
|
97
97
|
return reject('Worker not loaded');
|
|
98
98
|
const target = options.ocamlBackend || 'wasm';
|
|
99
|
+
const wasmBinaryenMode = options.ocamlWasmBinaryenMode || 'fast';
|
|
99
100
|
const _uid = ++this.uid;
|
|
100
101
|
this.activeReject = reject;
|
|
101
102
|
const handler = async (event) => {
|
|
@@ -308,6 +309,7 @@ class Ocaml {
|
|
|
308
309
|
code,
|
|
309
310
|
prepare,
|
|
310
311
|
target,
|
|
312
|
+
wasmBinaryenMode,
|
|
311
313
|
log: _log,
|
|
312
314
|
buffer: this.buffer
|
|
313
315
|
});
|
|
@@ -15,7 +15,9 @@ export interface CompilerDiagnostic {
|
|
|
15
15
|
}
|
|
16
16
|
export type RustTargetTriple = 'wasm32-wasip1' | 'wasm32-wasip2' | 'wasm32-wasip3';
|
|
17
17
|
export type GoTarget = 'wasip1/wasm' | 'wasip2/wasm' | 'wasip3/wasm' | 'js/wasm';
|
|
18
|
+
export type TinyGoTarget = 'wasm' | 'wasip1' | 'wasip2' | 'wasip3';
|
|
18
19
|
export type OcamlBackend = 'js' | 'wasm';
|
|
20
|
+
export type OcamlWasmBinaryenMode = 'fast' | 'full';
|
|
19
21
|
export type DebugVariableKind = 'number' | 'bool' | 'array' | 'text';
|
|
20
22
|
export type DebugArrayElementKind = 'int' | 'float' | 'double' | 'bool' | 'char';
|
|
21
23
|
export interface DebugStructFieldMetadata {
|
|
@@ -62,7 +64,9 @@ export interface SandboxExecutionOptions {
|
|
|
62
64
|
cVersion?: string;
|
|
63
65
|
rustTargetTriple?: RustTargetTriple;
|
|
64
66
|
goTarget?: GoTarget;
|
|
67
|
+
tinygoTarget?: TinyGoTarget;
|
|
65
68
|
ocamlBackend?: OcamlBackend;
|
|
69
|
+
ocamlWasmBinaryenMode?: OcamlWasmBinaryenMode;
|
|
66
70
|
}
|
|
67
71
|
export interface ResolvedSandboxExecutionArgs {
|
|
68
72
|
compileArgs: string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type PlaygroundRuntimeAssets, type TinyGoRuntimeAssetLoader, type TinyGoRuntimeAssetPackReference } from './assets';
|
|
2
|
-
import { type SandboxExecutionOptions } from './options';
|
|
2
|
+
import { type SandboxExecutionOptions, type TinyGoTarget } from './options';
|
|
3
3
|
import type { Sandbox } from './sandbox';
|
|
4
4
|
type TinyGoRuntimeHooks = {
|
|
5
5
|
boot(): Promise<void>;
|
|
@@ -15,6 +15,9 @@ type TinyGoRuntimeHooks = {
|
|
|
15
15
|
entrypoint?: '_start' | '_initialize' | 'main' | null;
|
|
16
16
|
reason?: 'bootstrap-artifact' | 'missing-wasi-entrypoint';
|
|
17
17
|
} | null;
|
|
18
|
+
setBuildRequestOverrides?(overrides: {
|
|
19
|
+
target?: TinyGoTarget;
|
|
20
|
+
} | null): void;
|
|
18
21
|
setWorkspaceFiles(files: Record<string, string> | null): void;
|
|
19
22
|
dispose?(): void;
|
|
20
23
|
};
|
|
@@ -146,7 +146,8 @@ class TinyGo {
|
|
|
146
146
|
}
|
|
147
147
|
const moduleUrl = this.moduleUrl;
|
|
148
148
|
this.runtimePromise = (async () => {
|
|
149
|
-
const runtimeModule = (await import(
|
|
149
|
+
const runtimeModule = (await import(
|
|
150
|
+
/* @vite-ignore */ moduleUrl));
|
|
150
151
|
if (typeof runtimeModule.createBundledTinyGoRuntime === 'function') {
|
|
151
152
|
this.runtime = runtimeModule.createBundledTinyGoRuntime({
|
|
152
153
|
assetLoader: this.assetLoader,
|
|
@@ -212,7 +213,6 @@ class TinyGo {
|
|
|
212
213
|
});
|
|
213
214
|
return this.runtime;
|
|
214
215
|
}
|
|
215
|
-
;
|
|
216
216
|
throw new Error('TinyGo runtime module must export createBundledTinyGoRuntime or createTinyGoRuntime');
|
|
217
217
|
})().catch((error) => {
|
|
218
218
|
this.disposeRuntime();
|
|
@@ -245,11 +245,13 @@ class TinyGo {
|
|
|
245
245
|
}
|
|
246
246
|
return 'TinyGo compilation failed';
|
|
247
247
|
}
|
|
248
|
-
async compileArtifact(code, log = true, prog) {
|
|
248
|
+
async compileArtifact(code, target = 'wasm', log = true, prog) {
|
|
249
|
+
const hostCompileTarget = target === 'wasm' ? 'wasip1' : target;
|
|
249
250
|
const compileCacheKey = JSON.stringify({
|
|
250
251
|
code,
|
|
251
252
|
hostCompileUrls: this.hostCompileUrls,
|
|
252
|
-
moduleUrl: this.moduleUrl
|
|
253
|
+
moduleUrl: this.moduleUrl,
|
|
254
|
+
target
|
|
253
255
|
});
|
|
254
256
|
if (this.compiledArtifact && this.compiledCacheKey === compileCacheKey) {
|
|
255
257
|
return;
|
|
@@ -266,7 +268,8 @@ class TinyGo {
|
|
|
266
268
|
'content-type': 'application/json'
|
|
267
269
|
},
|
|
268
270
|
body: JSON.stringify({
|
|
269
|
-
source: code
|
|
271
|
+
source: code,
|
|
272
|
+
target: hostCompileTarget
|
|
270
273
|
})
|
|
271
274
|
});
|
|
272
275
|
}
|
|
@@ -337,6 +340,7 @@ class TinyGo {
|
|
|
337
340
|
runtime.reset();
|
|
338
341
|
this.lastActivityLog = runtime.readActivityLog();
|
|
339
342
|
runtime.setWorkspaceFiles({ 'main.go': code });
|
|
343
|
+
runtime.setBuildRequestOverrides?.({ target });
|
|
340
344
|
this.runtimeProgress = prog;
|
|
341
345
|
this.runtimeProgressAssets.clear();
|
|
342
346
|
this.runtimeProgressStart = 0.05;
|
|
@@ -420,7 +424,8 @@ class TinyGo {
|
|
|
420
424
|
try {
|
|
421
425
|
this.begin = Date.now();
|
|
422
426
|
await this.ensureWorker();
|
|
423
|
-
|
|
427
|
+
const target = options.tinygoTarget || 'wasm';
|
|
428
|
+
await this.compileArtifact(code, target, _log, prepare ? _prog : undefined);
|
|
424
429
|
if (prepare) {
|
|
425
430
|
this.elapse = Date.now() - this.begin;
|
|
426
431
|
this.exit = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const WASM_OCAML_ASSET_VERSION = "
|
|
1
|
+
export declare const WASM_OCAML_ASSET_VERSION = "5215e843db69fc28";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const WASM_OCAML_ASSET_VERSION = "
|
|
1
|
+
export const WASM_OCAML_ASSET_VERSION = "5215e843db69fc28";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const WASM_TINYGO_ASSET_VERSION = "
|
|
1
|
+
export declare const WASM_TINYGO_ASSET_VERSION = "be561e38c81e2e23";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const WASM_TINYGO_ASSET_VERSION =
|
|
1
|
+
export const WASM_TINYGO_ASSET_VERSION = 'be561e38c81e2e23';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { waitForBufferedStdin } from '../stdinBuffer';
|
|
2
|
+
import { configureWorkerRuntimeAssets, handleWorkerAssetMessage } from './assets';
|
|
2
3
|
self.document = {
|
|
3
4
|
querySelectorAll() {
|
|
4
5
|
return [];
|
|
@@ -17,9 +18,13 @@ async function loadClang(path, log) {
|
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
self.onmessage = async (event) => {
|
|
20
|
-
|
|
21
|
+
if (handleWorkerAssetMessage(event.data))
|
|
22
|
+
return;
|
|
23
|
+
const { code, buffer, debugBuffer, watchBuffer, watchResultBuffer, load, interrupt, log, path, assets, prepare, language, compileArgs, programArgs, cppVersion, cVersion, debug, breakpoints, pauseOnEntry } = event.data;
|
|
21
24
|
if (load) {
|
|
22
|
-
|
|
25
|
+
const runtimeAssets = assets;
|
|
26
|
+
configureWorkerRuntimeAssets(runtimeAssets || null);
|
|
27
|
+
await loadClang(runtimeAssets?.baseUrl || path || '', log);
|
|
23
28
|
postMessage({ load: true });
|
|
24
29
|
}
|
|
25
30
|
else if (prepare) {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import atomVmWasmUrl from '../../../../node_modules/@swmansion/popcorn/dist/AtomVM.wasm?url';
|
|
2
|
-
import initAtomVm from '../../../../node_modules/@swmansion/popcorn/dist/AtomVM.mjs';
|
|
3
1
|
import { waitForBufferedStdin } from '../stdinBuffer';
|
|
4
2
|
const workerDocument = {
|
|
5
3
|
querySelectorAll() {
|
|
@@ -33,6 +31,130 @@ let bundleUrl = '';
|
|
|
33
31
|
let loadedBundleUrl = '';
|
|
34
32
|
let runtimePromise = null;
|
|
35
33
|
let stdinBufferElixir = null;
|
|
34
|
+
const elixirStdinCallNames = [
|
|
35
|
+
'IO.gets',
|
|
36
|
+
'IO.read',
|
|
37
|
+
'IO.binread',
|
|
38
|
+
'IO.getn',
|
|
39
|
+
':io.get_line',
|
|
40
|
+
':io.get_chars'
|
|
41
|
+
];
|
|
42
|
+
function resolveElixirRuntimeAssetUrl(assetName, bundleAssetUrl) {
|
|
43
|
+
const absoluteBundleUrl = new URL(bundleAssetUrl.startsWith('/') ? `http://localhost${bundleAssetUrl}` : bundleAssetUrl, globalThis.location?.href || 'http://localhost/');
|
|
44
|
+
return new URL(assetName, absoluteBundleUrl).toString();
|
|
45
|
+
}
|
|
46
|
+
async function resolveAtomVmInitializer(moduleAssetUrl) {
|
|
47
|
+
const injectedInitializer = globalThis.__wasmIdleAtomVmInit;
|
|
48
|
+
if (typeof injectedInitializer === 'function') {
|
|
49
|
+
return injectedInitializer;
|
|
50
|
+
}
|
|
51
|
+
const module = (await import(/* @vite-ignore */ moduleAssetUrl));
|
|
52
|
+
return module.default;
|
|
53
|
+
}
|
|
54
|
+
function skipQuotedLiteral(source, start) {
|
|
55
|
+
const heredoc = source.slice(start, start + 3);
|
|
56
|
+
if (heredoc === '"""' || heredoc === "'''") {
|
|
57
|
+
const end = source.indexOf(heredoc, start + 3);
|
|
58
|
+
return end === -1 ? source.length : end + 3;
|
|
59
|
+
}
|
|
60
|
+
const quote = source[start];
|
|
61
|
+
let index = start + 1;
|
|
62
|
+
while (index < source.length) {
|
|
63
|
+
if (source[index] === '\\') {
|
|
64
|
+
index += 2;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (source[index] === quote) {
|
|
68
|
+
return index + 1;
|
|
69
|
+
}
|
|
70
|
+
index += 1;
|
|
71
|
+
}
|
|
72
|
+
return source.length;
|
|
73
|
+
}
|
|
74
|
+
function findClosingParen(source, openParenIndex) {
|
|
75
|
+
let depth = 1;
|
|
76
|
+
let index = openParenIndex + 1;
|
|
77
|
+
while (index < source.length) {
|
|
78
|
+
const current = source[index];
|
|
79
|
+
if (current === '"' || current === "'") {
|
|
80
|
+
index = skipQuotedLiteral(source, index);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (current === '#') {
|
|
84
|
+
const newlineIndex = source.indexOf('\n', index);
|
|
85
|
+
if (newlineIndex === -1) {
|
|
86
|
+
return -1;
|
|
87
|
+
}
|
|
88
|
+
index = newlineIndex + 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (current === '(') {
|
|
92
|
+
depth += 1;
|
|
93
|
+
}
|
|
94
|
+
else if (current === ')') {
|
|
95
|
+
depth -= 1;
|
|
96
|
+
if (depth === 0) {
|
|
97
|
+
return index;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
index += 1;
|
|
101
|
+
}
|
|
102
|
+
return -1;
|
|
103
|
+
}
|
|
104
|
+
function splitTopLevelArgs(source) {
|
|
105
|
+
const parts = [];
|
|
106
|
+
let start = 0;
|
|
107
|
+
let parenDepth = 0;
|
|
108
|
+
let braceDepth = 0;
|
|
109
|
+
let bracketDepth = 0;
|
|
110
|
+
let index = 0;
|
|
111
|
+
while (index < source.length) {
|
|
112
|
+
const current = source[index];
|
|
113
|
+
if (current === '"' || current === "'") {
|
|
114
|
+
index = skipQuotedLiteral(source, index);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (current === '#') {
|
|
118
|
+
const newlineIndex = source.indexOf('\n', index);
|
|
119
|
+
if (newlineIndex === -1)
|
|
120
|
+
break;
|
|
121
|
+
index = newlineIndex + 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (current === '(')
|
|
125
|
+
parenDepth += 1;
|
|
126
|
+
if (current === ')')
|
|
127
|
+
parenDepth -= 1;
|
|
128
|
+
if (current === '{')
|
|
129
|
+
braceDepth += 1;
|
|
130
|
+
if (current === '}')
|
|
131
|
+
braceDepth -= 1;
|
|
132
|
+
if (current === '[')
|
|
133
|
+
bracketDepth += 1;
|
|
134
|
+
if (current === ']')
|
|
135
|
+
bracketDepth -= 1;
|
|
136
|
+
if (current === ',' && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
|
|
137
|
+
parts.push(source.slice(start, index).trim());
|
|
138
|
+
start = index + 1;
|
|
139
|
+
}
|
|
140
|
+
index += 1;
|
|
141
|
+
}
|
|
142
|
+
const trailing = source.slice(start).trim();
|
|
143
|
+
if (trailing) {
|
|
144
|
+
parts.push(trailing);
|
|
145
|
+
}
|
|
146
|
+
return parts;
|
|
147
|
+
}
|
|
148
|
+
function parseLiteralCount(source) {
|
|
149
|
+
const normalized = source.trim().replaceAll('_', '');
|
|
150
|
+
if (/^\d+$/.test(normalized)) {
|
|
151
|
+
const parsed = Number.parseInt(normalized, 10);
|
|
152
|
+
if (Number.isSafeInteger(parsed)) {
|
|
153
|
+
return parsed;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
36
158
|
function deserialize(module, message) {
|
|
37
159
|
return JSON.parse(message, (_key, value) => {
|
|
38
160
|
const isRef = typeof value === 'object' &&
|
|
@@ -146,7 +268,7 @@ function configureMessagingBridge(module) {
|
|
|
146
268
|
};
|
|
147
269
|
module.call = (process, args) => originalCall(process, module.serialize(args));
|
|
148
270
|
}
|
|
149
|
-
async function startVm(avmBundle, log) {
|
|
271
|
+
async function startVm(avmBundle, bundleAssetUrl, log) {
|
|
150
272
|
let resolveProcess = null;
|
|
151
273
|
const processPromise = new Promise((resolve, reject) => {
|
|
152
274
|
const timeout = setTimeout(() => {
|
|
@@ -159,6 +281,9 @@ async function startVm(avmBundle, log) {
|
|
|
159
281
|
resolve(process);
|
|
160
282
|
};
|
|
161
283
|
});
|
|
284
|
+
const atomVmModuleUrl = resolveElixirRuntimeAssetUrl('AtomVM.mjs', bundleAssetUrl);
|
|
285
|
+
const atomVmWasmUrl = resolveElixirRuntimeAssetUrl('AtomVM.wasm', bundleAssetUrl);
|
|
286
|
+
const initAtomVm = await resolveAtomVmInitializer(atomVmModuleUrl);
|
|
162
287
|
const module = (await initAtomVm({
|
|
163
288
|
locateFile(path) {
|
|
164
289
|
if (path === 'AtomVM.wasm') {
|
|
@@ -211,7 +336,7 @@ async function loadRuntime(nextBundleUrl, log) {
|
|
|
211
336
|
}
|
|
212
337
|
return response.arrayBuffer();
|
|
213
338
|
});
|
|
214
|
-
return await startVm(new Int8Array(bundleBuffer), log);
|
|
339
|
+
return await startVm(new Int8Array(bundleBuffer), nextBundleUrl, log);
|
|
215
340
|
})();
|
|
216
341
|
return await runtimePromise;
|
|
217
342
|
}
|
|
@@ -265,24 +390,173 @@ self.onmessage = async (event) => {
|
|
|
265
390
|
console.log(`[wasm-idle:elixir-worker] eval bytes=${code.length}`);
|
|
266
391
|
}
|
|
267
392
|
let evalCode = code;
|
|
268
|
-
if (stdinBufferElixir &&
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
393
|
+
if (stdinBufferElixir && /(?:\bIO\.|\:io\.)/.test(code)) {
|
|
394
|
+
let bufferedInput = '';
|
|
395
|
+
let reachedEof = false;
|
|
396
|
+
const pullStdinChunk = () => {
|
|
397
|
+
if (reachedEof)
|
|
398
|
+
return false;
|
|
399
|
+
const nextChunk = waitForBufferedStdin(stdinBufferElixir, () => postMessage({ buffer: true }));
|
|
400
|
+
if (nextChunk === null) {
|
|
401
|
+
reachedEof = true;
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
bufferedInput += nextChunk;
|
|
405
|
+
return true;
|
|
406
|
+
};
|
|
407
|
+
const readLineLiteral = (eofLiteral) => {
|
|
408
|
+
while (true) {
|
|
409
|
+
const newlineIndex = bufferedInput.indexOf('\n');
|
|
410
|
+
if (newlineIndex !== -1) {
|
|
411
|
+
const line = bufferedInput.slice(0, newlineIndex + 1);
|
|
412
|
+
bufferedInput = bufferedInput.slice(newlineIndex + 1);
|
|
413
|
+
return JSON.stringify(line);
|
|
414
|
+
}
|
|
415
|
+
if (!pullStdinChunk()) {
|
|
416
|
+
if (!bufferedInput) {
|
|
417
|
+
return eofLiteral;
|
|
418
|
+
}
|
|
419
|
+
const remainder = bufferedInput;
|
|
420
|
+
bufferedInput = '';
|
|
421
|
+
return JSON.stringify(remainder);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
const readCountLiteral = (count, eofLiteral) => {
|
|
426
|
+
if (count === 0) {
|
|
427
|
+
return JSON.stringify('');
|
|
428
|
+
}
|
|
429
|
+
while (bufferedInput.length < count && pullStdinChunk()) {
|
|
430
|
+
// Keep requesting chunks until the requested count or EOF is available.
|
|
431
|
+
}
|
|
432
|
+
if (!bufferedInput) {
|
|
433
|
+
return eofLiteral;
|
|
434
|
+
}
|
|
435
|
+
const consumed = bufferedInput.slice(0, Math.min(count, bufferedInput.length));
|
|
436
|
+
bufferedInput = bufferedInput.slice(consumed.length);
|
|
437
|
+
return JSON.stringify(consumed);
|
|
438
|
+
};
|
|
439
|
+
const readAllLiteral = (eofLiteral) => {
|
|
440
|
+
while (pullStdinChunk()) {
|
|
441
|
+
// Drain stdin until EOF for :all reads.
|
|
442
|
+
}
|
|
443
|
+
if (!bufferedInput) {
|
|
444
|
+
return eofLiteral;
|
|
445
|
+
}
|
|
446
|
+
const consumed = bufferedInput;
|
|
447
|
+
bufferedInput = '';
|
|
448
|
+
return JSON.stringify(consumed);
|
|
449
|
+
};
|
|
450
|
+
const wrapWithDeviceEvaluation = (deviceArg, valueExpr) => deviceArg
|
|
451
|
+
? `((fn __wasm_idle_device__ -> _ = __wasm_idle_device__; ${valueExpr} end).(${deviceArg}))`
|
|
452
|
+
: valueExpr;
|
|
453
|
+
const wrapWithPromptOutput = (promptArg, valueExpr, deviceArg) => deviceArg
|
|
454
|
+
? `((fn __wasm_idle_device__, __wasm_idle_prompt__ -> _ = __wasm_idle_device__; IO.write(__wasm_idle_prompt__); ${valueExpr} end).(${deviceArg}, ${promptArg}))`
|
|
455
|
+
: `((fn __wasm_idle_prompt__ -> IO.write(__wasm_idle_prompt__); ${valueExpr} end).(${promptArg}))`;
|
|
456
|
+
let rewritten = '';
|
|
457
|
+
let cursor = 0;
|
|
458
|
+
while (cursor < code.length) {
|
|
459
|
+
const current = code[cursor];
|
|
460
|
+
if (current === '"' || current === "'") {
|
|
461
|
+
const nextCursor = skipQuotedLiteral(code, cursor);
|
|
462
|
+
rewritten += code.slice(cursor, nextCursor);
|
|
463
|
+
cursor = nextCursor;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (current === '#') {
|
|
467
|
+
const newlineIndex = code.indexOf('\n', cursor);
|
|
468
|
+
if (newlineIndex === -1) {
|
|
469
|
+
rewritten += code.slice(cursor);
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
rewritten += code.slice(cursor, newlineIndex + 1);
|
|
473
|
+
cursor = newlineIndex + 1;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const callName = elixirStdinCallNames.find((candidate) => {
|
|
477
|
+
if (!code.startsWith(candidate, cursor)) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
const previous = code[cursor - 1] || '';
|
|
481
|
+
return !/[A-Za-z0-9_:.?!]/.test(previous);
|
|
482
|
+
});
|
|
483
|
+
if (!callName) {
|
|
484
|
+
rewritten += current;
|
|
485
|
+
cursor += 1;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
let openParenIndex = cursor + callName.length;
|
|
489
|
+
while (/\s/.test(code[openParenIndex] || '')) {
|
|
490
|
+
openParenIndex += 1;
|
|
491
|
+
}
|
|
492
|
+
if (code[openParenIndex] !== '(') {
|
|
493
|
+
rewritten += current;
|
|
494
|
+
cursor += 1;
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
const closeParenIndex = findClosingParen(code, openParenIndex);
|
|
498
|
+
if (closeParenIndex === -1) {
|
|
499
|
+
rewritten += code.slice(cursor);
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
const originalCall = code.slice(cursor, closeParenIndex + 1);
|
|
503
|
+
const args = splitTopLevelArgs(code.slice(openParenIndex + 1, closeParenIndex));
|
|
504
|
+
let replacement = originalCall;
|
|
505
|
+
if (callName === 'IO.gets' && (args.length === 1 || args.length === 2)) {
|
|
506
|
+
replacement =
|
|
507
|
+
args.length === 2
|
|
508
|
+
? wrapWithPromptOutput(args[1], readLineLiteral('nil'), args[0])
|
|
509
|
+
: wrapWithPromptOutput(args[0], readLineLiteral('nil'));
|
|
510
|
+
}
|
|
511
|
+
else if ((callName === 'IO.read' || callName === 'IO.binread') &&
|
|
512
|
+
(args.length === 1 || args.length === 2)) {
|
|
513
|
+
const deviceArg = args.length === 2 ? args[0] : undefined;
|
|
514
|
+
const modeArg = args[args.length - 1];
|
|
515
|
+
if (modeArg === ':line') {
|
|
516
|
+
replacement = wrapWithDeviceEvaluation(deviceArg, readLineLiteral(':eof'));
|
|
517
|
+
}
|
|
518
|
+
else if (modeArg === ':all') {
|
|
519
|
+
replacement = wrapWithDeviceEvaluation(deviceArg, readAllLiteral(':eof'));
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
const count = parseLiteralCount(modeArg);
|
|
523
|
+
if (count === null) {
|
|
524
|
+
throw new Error(`${callName} stdin bridge requires a literal non-negative count`);
|
|
525
|
+
}
|
|
526
|
+
replacement = wrapWithDeviceEvaluation(deviceArg, readCountLiteral(count, ':eof'));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else if (callName === 'IO.getn' && (args.length === 2 || args.length === 3)) {
|
|
530
|
+
const count = parseLiteralCount(args[args.length - 1]);
|
|
531
|
+
if (count === null) {
|
|
532
|
+
throw new Error('IO.getn stdin bridge requires a literal non-negative count');
|
|
533
|
+
}
|
|
534
|
+
replacement =
|
|
535
|
+
args.length === 3
|
|
536
|
+
? wrapWithPromptOutput(args[1], readCountLiteral(count, ':eof'), args[0])
|
|
537
|
+
: wrapWithPromptOutput(args[0], readCountLiteral(count, ':eof'));
|
|
538
|
+
}
|
|
539
|
+
else if (callName === ':io.get_line' && (args.length === 1 || args.length === 2)) {
|
|
540
|
+
replacement =
|
|
541
|
+
args.length === 2
|
|
542
|
+
? wrapWithPromptOutput(args[1], readLineLiteral(':eof'), args[0])
|
|
543
|
+
: wrapWithPromptOutput(args[0], readLineLiteral(':eof'));
|
|
544
|
+
}
|
|
545
|
+
else if (callName === ':io.get_chars' &&
|
|
546
|
+
(args.length === 2 || args.length === 3)) {
|
|
547
|
+
const count = parseLiteralCount(args[args.length - 1]);
|
|
548
|
+
if (count === null) {
|
|
549
|
+
throw new Error(':io.get_chars stdin bridge requires a literal non-negative count');
|
|
550
|
+
}
|
|
551
|
+
replacement =
|
|
552
|
+
args.length === 3
|
|
553
|
+
? wrapWithPromptOutput(args[1], readCountLiteral(count, ':eof'), args[0])
|
|
554
|
+
: wrapWithPromptOutput(args[0], readCountLiteral(count, ':eof'));
|
|
555
|
+
}
|
|
556
|
+
rewritten += replacement;
|
|
557
|
+
cursor = closeParenIndex + 1;
|
|
558
|
+
}
|
|
559
|
+
evalCode = rewritten;
|
|
286
560
|
}
|
|
287
561
|
const response = await runtime.module.call(runtime.process, ['eval_elixir', evalCode]);
|
|
288
562
|
const rendered = runtime.module.deserialize(response);
|
|
@@ -363,7 +363,7 @@ async function executeCompileResult(result, log = false) {
|
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
365
|
self.onmessage = async (event) => {
|
|
366
|
-
const { load, moduleUrl: nextModuleUrl, manifestUrl: nextManifestUrl, code, prepare, target = 'wasm', log = true, buffer } = event.data;
|
|
366
|
+
const { load, moduleUrl: nextModuleUrl, manifestUrl: nextManifestUrl, code, prepare, target = 'wasm', wasmBinaryenMode = 'fast', log = true, buffer } = event.data;
|
|
367
367
|
try {
|
|
368
368
|
if (load) {
|
|
369
369
|
moduleUrl = nextModuleUrl;
|
|
@@ -374,7 +374,7 @@ self.onmessage = async (event) => {
|
|
|
374
374
|
}
|
|
375
375
|
stdinBufferOcaml = buffer ? new Int32Array(buffer) : null;
|
|
376
376
|
if (log) {
|
|
377
|
-
console.log(`[wasm-idle:ocaml-worker] compile start prepare=${prepare} target=${target}`);
|
|
377
|
+
console.log(`[wasm-idle:ocaml-worker] compile start prepare=${prepare} target=${target} binaryen=${wasmBinaryenMode}`);
|
|
378
378
|
}
|
|
379
379
|
postMessage({ progress: { stage: 'compile-bootstrap', percent: 10 } });
|
|
380
380
|
const [compilerModule, manifest] = await Promise.all([
|
|
@@ -382,7 +382,7 @@ self.onmessage = async (event) => {
|
|
|
382
382
|
loadManifest(manifestUrl)
|
|
383
383
|
]);
|
|
384
384
|
postMessage({ progress: { stage: 'compile-ready', percent: 25 } });
|
|
385
|
-
const compileKey = `${target}\n${code}`;
|
|
385
|
+
const compileKey = `${target}\n${wasmBinaryenMode}\n${code}`;
|
|
386
386
|
let compiledFresh = false;
|
|
387
387
|
if (!compiledResult || compiledCacheKey !== compileKey) {
|
|
388
388
|
const result = await compilerModule.compile({
|
|
@@ -391,7 +391,8 @@ self.onmessage = async (event) => {
|
|
|
391
391
|
},
|
|
392
392
|
entry: 'main.ml',
|
|
393
393
|
target,
|
|
394
|
-
effectsMode: 'cps'
|
|
394
|
+
effectsMode: 'cps',
|
|
395
|
+
wasmBinaryenMode
|
|
395
396
|
}, {
|
|
396
397
|
system: compilerModule.createBrowserWorkerSystemDispatcher({
|
|
397
398
|
manifest
|
|
@@ -99,17 +99,20 @@ self.onmessage = async (event) => {
|
|
|
99
99
|
exitCode = wasiRuntime.start(instance);
|
|
100
100
|
}
|
|
101
101
|
else if (typeof exportsObject.memory === 'object' &&
|
|
102
|
-
typeof exportsObject.main === 'function'
|
|
102
|
+
(typeof exportsObject.main === 'function' ||
|
|
103
|
+
typeof exportsObject._initialize === 'function')) {
|
|
103
104
|
wasiRuntime.initialize(instance);
|
|
104
105
|
if (typeof exportsObject._initialize !== 'function' &&
|
|
105
106
|
typeof exportsObject.__wasm_call_ctors === 'function') {
|
|
106
107
|
exportsObject.__wasm_call_ctors();
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
if (typeof exportsObject.main === 'function') {
|
|
110
|
+
const mainResult = exportsObject.main();
|
|
111
|
+
exitCode = typeof mainResult === 'number' ? mainResult : 0;
|
|
112
|
+
}
|
|
110
113
|
}
|
|
111
114
|
else {
|
|
112
|
-
throw new Error('TinyGo worker expected a WASI artifact with _start or a reactor artifact with main');
|
|
115
|
+
throw new Error('TinyGo worker expected a WASI artifact with _start or a reactor artifact with _initialize or main');
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
catch (error) {
|
|
@@ -107,6 +107,10 @@
|
|
|
107
107
|
hasPythonLoader: !!currentRuntimeAssets?.python?.loader,
|
|
108
108
|
javaBaseUrl: currentRuntimeAssets?.java?.baseUrl || '',
|
|
109
109
|
hasJavaLoader: !!currentRuntimeAssets?.java?.loader,
|
|
110
|
+
clangBaseUrl: currentRuntimeAssets?.clang?.baseUrl || '',
|
|
111
|
+
hasClangLoader: !!currentRuntimeAssets?.clang?.loader,
|
|
112
|
+
clangdBaseUrl: currentRuntimeAssets?.clangd?.baseUrl || '',
|
|
113
|
+
hasClangdLoader: !!currentRuntimeAssets?.clangd?.loader,
|
|
110
114
|
rustCompilerUrl: currentRuntimeAssets?.rust?.compilerUrl || '',
|
|
111
115
|
goCompilerUrl: currentRuntimeAssets?.go?.compilerUrl || '',
|
|
112
116
|
elixirBundleUrl: currentRuntimeAssets?.elixir?.bundleUrl || '',
|