wasm-idle 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -3
- package/dist/clang/app.d.ts +4 -0
- package/dist/clang/app.js +315 -12
- package/dist/clang/index.d.ts +4 -0
- package/dist/clang/index.js +151 -8
- package/dist/clangd/worker.js +21 -5
- package/dist/debug/controller.svelte.d.ts +43 -0
- package/dist/debug/controller.svelte.js +268 -0
- package/dist/debug/editor/index.d.ts +2 -1
- package/dist/debug/editor/index.js +1 -1
- package/dist/debug/editor/monaco.d.ts +8 -0
- package/dist/debug/editor/monaco.js +23 -0
- package/dist/debug/expression.d.ts +24 -0
- package/dist/debug/expression.js +307 -138
- package/dist/debug/index.d.ts +4 -1
- package/dist/debug/index.js +2 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -3
- package/dist/lsp/cpp/server.d.ts +2 -0
- package/dist/lsp/cpp/server.js +57 -0
- package/dist/lsp/index.d.ts +5 -0
- package/dist/lsp/index.js +4 -0
- package/dist/lsp/python/main.worker.d.ts +1 -0
- package/dist/lsp/python/main.worker.js +92 -0
- package/dist/lsp/python/package/arcturus_python_lsp/__init__.py +3 -0
- package/dist/lsp/python/package/arcturus_python_lsp/server.py +462 -0
- package/dist/lsp/python/server.d.ts +2 -0
- package/dist/lsp/python/server.js +54 -0
- package/dist/lsp/registry.d.ts +2 -0
- package/dist/lsp/registry.js +12 -0
- package/dist/lsp/runtime.d.ts +3 -0
- package/dist/lsp/runtime.js +30 -0
- package/dist/lsp/types.d.ts +15 -0
- package/dist/lsp/types.js +1 -0
- package/dist/playground/assets.d.ts +5 -0
- package/dist/playground/assets.js +8 -0
- package/dist/playground/clang.d.ts +5 -0
- package/dist/playground/clang.js +45 -2
- package/dist/playground/index.d.ts +5 -5
- package/dist/playground/index.js +45 -5
- package/dist/playground/options.d.ts +9 -0
- package/dist/playground/python.d.ts +1 -0
- package/dist/playground/python.js +12 -0
- package/dist/playground/rust.d.ts +32 -0
- package/dist/playground/rust.js +175 -0
- package/dist/playground/sandbox.d.ts +18 -4
- package/dist/playground/wasmRustVersion.d.ts +1 -0
- package/dist/playground/wasmRustVersion.js +1 -0
- package/dist/playground/worker/clang.js +12 -4
- package/dist/playground/worker/rust.d.ts +1 -0
- package/dist/playground/worker/rust.js +159 -0
- package/dist/terminal/Terminal.svelte +78 -25
- package/dist/terminal/Terminal.svelte.d.ts +2 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +3 -0
- package/package.json +15 -9
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getCppLanguageServer } from './cpp/server';
|
|
2
|
+
import { getPythonLanguageServer } from './python/server';
|
|
3
|
+
export async function getEditorLanguageServer(language, options) {
|
|
4
|
+
const normalized = language.toLowerCase();
|
|
5
|
+
if (normalized === 'c' || normalized === 'cpp') {
|
|
6
|
+
return getCppLanguageServer(options);
|
|
7
|
+
}
|
|
8
|
+
if (normalized === 'python') {
|
|
9
|
+
return getPythonLanguageServer(options);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { EditorLanguageServerRuntimeOptions } from './types';
|
|
2
|
+
export declare function resolveCppLanguageServerBaseUrl(options: string | EditorLanguageServerRuntimeOptions | undefined, currentUrl?: string): string;
|
|
3
|
+
export declare function resolvePythonLanguageServerBaseUrl(options: string | EditorLanguageServerRuntimeOptions | undefined, currentUrl?: string): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
|
|
2
|
+
const normalized = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
3
|
+
return currentUrl ? new URL(normalized, currentUrl).href : normalized;
|
|
4
|
+
};
|
|
5
|
+
const normalizeRootUrl = (rootUrl) => rootUrl.endsWith('/') ? rootUrl.slice(0, -1) : rootUrl;
|
|
6
|
+
const resolveRuntimeRootBaseUrl = (rootUrl, suffix, currentUrl = '') => normalizeBaseUrl(`${normalizeRootUrl(rootUrl) || ''}${suffix}`, currentUrl);
|
|
7
|
+
export function resolveCppLanguageServerBaseUrl(options, currentUrl = '') {
|
|
8
|
+
if (typeof options === 'string') {
|
|
9
|
+
return resolveRuntimeRootBaseUrl(options, '/clangd/', currentUrl);
|
|
10
|
+
}
|
|
11
|
+
if (options?.cpp?.baseUrl) {
|
|
12
|
+
return normalizeBaseUrl(options.cpp.baseUrl, currentUrl);
|
|
13
|
+
}
|
|
14
|
+
if (options?.rootUrl) {
|
|
15
|
+
return resolveRuntimeRootBaseUrl(options.rootUrl, '/clangd/', currentUrl);
|
|
16
|
+
}
|
|
17
|
+
return normalizeBaseUrl('/clangd/', currentUrl);
|
|
18
|
+
}
|
|
19
|
+
export function resolvePythonLanguageServerBaseUrl(options, currentUrl = '') {
|
|
20
|
+
if (typeof options === 'string') {
|
|
21
|
+
return resolveRuntimeRootBaseUrl(options, '/pyodide/', currentUrl);
|
|
22
|
+
}
|
|
23
|
+
if (options?.python?.baseUrl) {
|
|
24
|
+
return normalizeBaseUrl(options.python.baseUrl, currentUrl);
|
|
25
|
+
}
|
|
26
|
+
if (options?.rootUrl) {
|
|
27
|
+
return resolveRuntimeRootBaseUrl(options.rootUrl, '/pyodide/', currentUrl);
|
|
28
|
+
}
|
|
29
|
+
return normalizeBaseUrl('/pyodide/', currentUrl);
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MessageTransports } from '@hancomac/monaco-languageclient';
|
|
2
|
+
export interface EditorLanguageServerHandle {
|
|
3
|
+
transport: string | MessageTransports;
|
|
4
|
+
syncFile?: (path: string) => void;
|
|
5
|
+
dispose: () => void;
|
|
6
|
+
}
|
|
7
|
+
export interface EditorLanguageServerRuntimeOptions {
|
|
8
|
+
rootUrl?: string;
|
|
9
|
+
cpp?: {
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
};
|
|
12
|
+
python?: {
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,10 +17,14 @@ export interface RuntimeAssetConfig {
|
|
|
17
17
|
baseUrl?: string;
|
|
18
18
|
loader?: RuntimeAssetLoader;
|
|
19
19
|
}
|
|
20
|
+
export interface RustRuntimeAssetConfig {
|
|
21
|
+
compilerUrl?: string;
|
|
22
|
+
}
|
|
20
23
|
export interface PlaygroundRuntimeAssets {
|
|
21
24
|
rootUrl?: string;
|
|
22
25
|
python?: RuntimeAssetConfig;
|
|
23
26
|
java?: RuntimeAssetConfig;
|
|
27
|
+
rust?: RustRuntimeAssetConfig;
|
|
24
28
|
}
|
|
25
29
|
export interface ResolvedRuntimeAssetConfig {
|
|
26
30
|
baseUrl: string;
|
|
@@ -30,3 +34,4 @@ export interface ResolvedRuntimeAssetConfig {
|
|
|
30
34
|
export declare const PYTHON_RUNTIME_LOAD_ASSETS: readonly ["pyodide.asm.js", "pyodide-lock.json", "pyodide.asm.wasm", "python_stdlib.zip"];
|
|
31
35
|
export declare const JAVA_RUNTIME_LOAD_ASSETS: readonly ["compiler.wasm-runtime.js", "compiler.wasm", "compile-classlib-teavm.bin", "runtime-classlib-teavm.bin"];
|
|
32
36
|
export declare function resolveRuntimeAssetConfig(runtime: RuntimeAssetRuntime, options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): ResolvedRuntimeAssetConfig;
|
|
37
|
+
export declare function resolveRustCompilerUrl(options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): any;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { env as publicEnv } from '$env/dynamic/public';
|
|
1
2
|
import { normalizeTeaVmBaseUrl, resolveTeaVmBaseUrl } from './teavmConfig';
|
|
2
3
|
export const PYTHON_RUNTIME_LOAD_ASSETS = [
|
|
3
4
|
'pyodide.asm.js',
|
|
@@ -95,3 +96,10 @@ export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '') {
|
|
|
95
96
|
? resolvePythonRuntimeAssetConfig(options, currentUrl)
|
|
96
97
|
: resolveJavaRuntimeAssetConfig(options, currentUrl);
|
|
97
98
|
}
|
|
99
|
+
export function resolveRustCompilerUrl(options, currentUrl = '') {
|
|
100
|
+
const configuredCompilerUrl = (typeof options === 'object' && options?.rust?.compilerUrl) ||
|
|
101
|
+
(publicEnv.PUBLIC_WASM_RUST_COMPILER_URL || '').trim();
|
|
102
|
+
if (!configuredCompilerUrl)
|
|
103
|
+
return '';
|
|
104
|
+
return currentUrl ? new URL(configuredCompilerUrl, currentUrl).href : configuredCompilerUrl;
|
|
105
|
+
}
|
|
@@ -10,6 +10,8 @@ declare class Clang implements Sandbox {
|
|
|
10
10
|
worker?: Worker;
|
|
11
11
|
buffer: SharedArrayBuffer;
|
|
12
12
|
debugBuffer: SharedArrayBuffer;
|
|
13
|
+
watchBuffer: SharedArrayBuffer;
|
|
14
|
+
watchResultBuffer: SharedArrayBuffer;
|
|
13
15
|
interruptBuffer: SharedArrayBuffer;
|
|
14
16
|
pendingInput: string[];
|
|
15
17
|
begin: number;
|
|
@@ -19,6 +21,7 @@ declare class Clang implements Sandbox {
|
|
|
19
21
|
waitingForInput: boolean;
|
|
20
22
|
pendingEof: boolean;
|
|
21
23
|
exit: boolean;
|
|
24
|
+
activeReject: ((reason?: string) => void) | null;
|
|
22
25
|
constructor(language: 'C' | 'CPP');
|
|
23
26
|
load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions, _progress?: {
|
|
24
27
|
set?: (value: number) => void;
|
|
@@ -30,6 +33,8 @@ declare class Clang implements Sandbox {
|
|
|
30
33
|
set?: (value: number) => void;
|
|
31
34
|
}, args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
32
35
|
debugCommand(command: DebugCommand): void;
|
|
36
|
+
setBreakpoints(lines: number[]): void;
|
|
37
|
+
debugEvaluate(expression: string): Promise<string>;
|
|
33
38
|
kill(): void;
|
|
34
39
|
terminate(): void;
|
|
35
40
|
clear(): Promise<void>;
|
package/dist/playground/clang.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveSandboxExecutionArgs } from './options';
|
|
2
|
-
import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
|
|
2
|
+
import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
|
|
3
|
+
const debugBreakpointBufferInts = 1028;
|
|
3
4
|
class Clang {
|
|
4
5
|
language;
|
|
5
6
|
ts = Date.now();
|
|
@@ -7,7 +8,9 @@ class Clang {
|
|
|
7
8
|
ondebug;
|
|
8
9
|
worker = null;
|
|
9
10
|
buffer = new SharedArrayBuffer(1024);
|
|
10
|
-
debugBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT *
|
|
11
|
+
debugBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * debugBreakpointBufferInts);
|
|
12
|
+
watchBuffer = new SharedArrayBuffer(1024);
|
|
13
|
+
watchResultBuffer = new SharedArrayBuffer(1024);
|
|
11
14
|
interruptBuffer = new SharedArrayBuffer(1);
|
|
12
15
|
pendingInput = [];
|
|
13
16
|
begin = 0;
|
|
@@ -17,6 +20,7 @@ class Clang {
|
|
|
17
20
|
waitingForInput = false;
|
|
18
21
|
pendingEof = false;
|
|
19
22
|
exit = true;
|
|
23
|
+
activeReject = null;
|
|
20
24
|
constructor(language) {
|
|
21
25
|
this.language = language;
|
|
22
26
|
}
|
|
@@ -65,7 +69,9 @@ class Clang {
|
|
|
65
69
|
return new Promise(async (resolve, reject) => {
|
|
66
70
|
if (!this.worker)
|
|
67
71
|
return reject('Worker not loaded');
|
|
72
|
+
this.activeReject = reject;
|
|
68
73
|
const { compileArgs, programArgs } = resolveSandboxExecutionArgs(this.language, args, options);
|
|
74
|
+
this.setBreakpoints(options.debug ? [...(options.breakpoints || [])] : []);
|
|
69
75
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
70
76
|
const handler = (event) => {
|
|
71
77
|
if (!this.worker)
|
|
@@ -86,6 +92,7 @@ class Clang {
|
|
|
86
92
|
this.exit = true;
|
|
87
93
|
this.waitingForInput = false;
|
|
88
94
|
this.pendingEof = false;
|
|
95
|
+
this.activeReject = null;
|
|
89
96
|
this.ondebug?.({ type: 'stop' });
|
|
90
97
|
resolve(results);
|
|
91
98
|
}
|
|
@@ -95,6 +102,7 @@ class Clang {
|
|
|
95
102
|
this.elapse = Date.now() - this.begin;
|
|
96
103
|
this.waitingForInput = false;
|
|
97
104
|
this.pendingEof = false;
|
|
105
|
+
this.activeReject = null;
|
|
98
106
|
this.exit = true;
|
|
99
107
|
this.ondebug?.({ type: 'stop' });
|
|
100
108
|
reject(error);
|
|
@@ -110,6 +118,8 @@ class Clang {
|
|
|
110
118
|
prepare,
|
|
111
119
|
buffer: this.buffer,
|
|
112
120
|
debugBuffer: this.debugBuffer,
|
|
121
|
+
watchBuffer: this.watchBuffer,
|
|
122
|
+
watchResultBuffer: this.watchResultBuffer,
|
|
113
123
|
interrupt: this.interruptBuffer,
|
|
114
124
|
context: {},
|
|
115
125
|
log,
|
|
@@ -131,15 +141,46 @@ class Clang {
|
|
|
131
141
|
Atomics.notify(control, 0);
|
|
132
142
|
this.ondebug?.({ type: 'resume', command });
|
|
133
143
|
}
|
|
144
|
+
setBreakpoints(lines) {
|
|
145
|
+
const control = new Int32Array(this.debugBuffer);
|
|
146
|
+
const next = [...new Set(lines.filter((line) => Number.isInteger(line) && line > 0))]
|
|
147
|
+
.sort((left, right) => left - right)
|
|
148
|
+
.slice(0, Math.max(0, control.length - 4));
|
|
149
|
+
for (let index = 4; index < control.length; index += 1) {
|
|
150
|
+
Atomics.store(control, index, next[index - 4] || 0);
|
|
151
|
+
}
|
|
152
|
+
Atomics.store(control, 3, next.length);
|
|
153
|
+
Atomics.add(control, 2, 1);
|
|
154
|
+
}
|
|
155
|
+
async debugEvaluate(expression) {
|
|
156
|
+
if (!this.worker)
|
|
157
|
+
throw new Error('Worker not loaded');
|
|
158
|
+
resetBufferedStdin(this.watchResultBuffer);
|
|
159
|
+
const previousSequence = bufferedSequence(this.watchResultBuffer);
|
|
160
|
+
flushQueuedStdin([expression], this.watchBuffer);
|
|
161
|
+
const control = new Int32Array(this.debugBuffer);
|
|
162
|
+
Atomics.store(control, 1, 5);
|
|
163
|
+
Atomics.add(control, 0, 1);
|
|
164
|
+
Atomics.notify(control, 0);
|
|
165
|
+
return ((await waitForBufferedSequenceChange(this.watchResultBuffer, previousSequence, 5000)) ??
|
|
166
|
+
'?');
|
|
167
|
+
}
|
|
134
168
|
kill() {
|
|
135
169
|
this.terminate();
|
|
136
170
|
}
|
|
137
171
|
terminate() {
|
|
172
|
+
this.activeReject?.('Process terminated');
|
|
173
|
+
this.activeReject = null;
|
|
174
|
+
this.waitingForInput = false;
|
|
138
175
|
this.pendingEof = false;
|
|
176
|
+
this.uid += 1;
|
|
139
177
|
new Uint8Array(this.interruptBuffer)[0] = 2;
|
|
140
178
|
const control = new Int32Array(this.debugBuffer);
|
|
141
179
|
Atomics.add(control, 0, 1);
|
|
142
180
|
Atomics.notify(control, 0);
|
|
181
|
+
this.worker?.terminate?.();
|
|
182
|
+
delete this.worker;
|
|
183
|
+
this.exit = true;
|
|
143
184
|
}
|
|
144
185
|
async clear() {
|
|
145
186
|
this.terminate();
|
|
@@ -149,6 +190,8 @@ class Clang {
|
|
|
149
190
|
if (this.worker)
|
|
150
191
|
this.worker.onmessage = null;
|
|
151
192
|
resetBufferedStdin(this.buffer);
|
|
193
|
+
resetBufferedStdin(this.watchBuffer);
|
|
194
|
+
resetBufferedStdin(this.watchResultBuffer);
|
|
152
195
|
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
153
196
|
debugBuffer.fill(0);
|
|
154
197
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Java from './java';
|
|
3
|
-
import Python from './python';
|
|
4
|
-
import type { Sandbox } from './sandbox';
|
|
1
|
+
import type { BoundSandbox, PlaygroundBinding, Sandbox, SandboxRuntimeAssets } from './sandbox';
|
|
5
2
|
export declare const supportedLanguages: string[];
|
|
6
|
-
export
|
|
3
|
+
export declare function createPlaygroundBinding(runtimeAssets: SandboxRuntimeAssets): PlaygroundBinding;
|
|
4
|
+
declare function playground(language: string): Promise<Sandbox>;
|
|
5
|
+
declare function playground(language: string, runtimeAssets: SandboxRuntimeAssets): Promise<BoundSandbox>;
|
|
6
|
+
export default playground;
|
package/dist/playground/index.js
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
1
|
import Clang from './clang';
|
|
2
2
|
import Java from './java';
|
|
3
3
|
import Python from './python';
|
|
4
|
+
import Rust from './rust';
|
|
4
5
|
const sandboxCache = {};
|
|
5
|
-
export const supportedLanguages = ['PYTHON3', 'PYPY3', 'C', 'CPP', 'JAVA'];
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
export const supportedLanguages = ['PYTHON3', 'PYPY3', 'C', 'CPP', 'JAVA', 'RUST'];
|
|
7
|
+
function bindRuntimeAssets(sandbox, runtimeAssets) {
|
|
8
|
+
return new Proxy(sandbox, {
|
|
9
|
+
get(target, prop, receiver) {
|
|
10
|
+
if (prop === 'runtimeAssets')
|
|
11
|
+
return runtimeAssets;
|
|
12
|
+
if (prop === 'load') {
|
|
13
|
+
return (code = '', log = true, args = [], options = {}, progress) => target.load(runtimeAssets, code, log, args, options, progress);
|
|
14
|
+
}
|
|
15
|
+
const value = Reflect.get(target, prop, receiver);
|
|
16
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
17
|
+
},
|
|
18
|
+
set(target, prop, value, receiver) {
|
|
19
|
+
return Reflect.set(target, prop, value, receiver);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function createPlaygroundBinding(runtimeAssets) {
|
|
24
|
+
const binding = {
|
|
25
|
+
runtimeAssets,
|
|
26
|
+
terminalProps: {},
|
|
27
|
+
async load(language) {
|
|
28
|
+
return bindRuntimeAssets(await playground(language), runtimeAssets);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
binding.terminalProps = {
|
|
32
|
+
playground: binding,
|
|
33
|
+
runtimeAssets
|
|
34
|
+
};
|
|
35
|
+
return binding;
|
|
36
|
+
}
|
|
37
|
+
async function playground(language, runtimeAssets) {
|
|
38
|
+
if (sandboxCache[language]) {
|
|
39
|
+
return runtimeAssets
|
|
40
|
+
? bindRuntimeAssets(sandboxCache[language], runtimeAssets)
|
|
41
|
+
: sandboxCache[language];
|
|
42
|
+
}
|
|
9
43
|
let sandbox;
|
|
10
44
|
switch (language) {
|
|
11
45
|
case 'PYTHON3':
|
|
@@ -22,6 +56,9 @@ export default async function load(language) {
|
|
|
22
56
|
case 'JAVA':
|
|
23
57
|
sandbox = new Java();
|
|
24
58
|
break;
|
|
59
|
+
case 'RUST':
|
|
60
|
+
sandbox = new Rust();
|
|
61
|
+
break;
|
|
25
62
|
default:
|
|
26
63
|
throw new Error(`Unsupported language: ${language}`);
|
|
27
64
|
}
|
|
@@ -39,6 +76,9 @@ export default async function load(language) {
|
|
|
39
76
|
sandboxCache['CPP'] = sandbox;
|
|
40
77
|
if (language === 'JAVA')
|
|
41
78
|
sandboxCache['JAVA'] = sandbox;
|
|
79
|
+
if (language === 'RUST')
|
|
80
|
+
sandboxCache['RUST'] = sandbox;
|
|
42
81
|
}
|
|
43
|
-
return sandbox;
|
|
82
|
+
return runtimeAssets ? bindRuntimeAssets(sandbox, runtimeAssets) : sandbox;
|
|
44
83
|
}
|
|
84
|
+
export default playground;
|
|
@@ -13,8 +13,14 @@ export interface CompilerDiagnostic {
|
|
|
13
13
|
severity: CompilerDiagnosticSeverity;
|
|
14
14
|
message: string;
|
|
15
15
|
}
|
|
16
|
+
export type RustTargetTriple = 'wasm32-wasip1' | 'wasm32-wasip2' | 'wasm32-wasip3';
|
|
16
17
|
export type DebugVariableKind = 'number' | 'bool' | 'array' | 'text';
|
|
17
18
|
export type DebugArrayElementKind = 'int' | 'float' | 'double' | 'bool' | 'char';
|
|
19
|
+
export interface DebugStructFieldMetadata {
|
|
20
|
+
name: string;
|
|
21
|
+
kind: DebugArrayElementKind;
|
|
22
|
+
offset: number;
|
|
23
|
+
}
|
|
18
24
|
export interface DebugVariableMetadata {
|
|
19
25
|
slot: number;
|
|
20
26
|
name: string;
|
|
@@ -24,6 +30,8 @@ export interface DebugVariableMetadata {
|
|
|
24
30
|
elementKind?: DebugArrayElementKind;
|
|
25
31
|
length?: number;
|
|
26
32
|
dimensions?: number[];
|
|
33
|
+
structFields?: DebugStructFieldMetadata[];
|
|
34
|
+
structSize?: number;
|
|
27
35
|
}
|
|
28
36
|
export interface DebugFrame {
|
|
29
37
|
functionName: string;
|
|
@@ -50,6 +58,7 @@ export interface SandboxExecutionOptions {
|
|
|
50
58
|
programArgs?: string[];
|
|
51
59
|
cppVersion?: string;
|
|
52
60
|
cVersion?: string;
|
|
61
|
+
rustTargetTriple?: RustTargetTriple;
|
|
53
62
|
}
|
|
54
63
|
export interface ResolvedSandboxExecutionArgs {
|
|
55
64
|
compileArgs: string[];
|
|
@@ -25,6 +25,7 @@ declare class Python implements Sandbox {
|
|
|
25
25
|
pendingEof: boolean;
|
|
26
26
|
exit: boolean;
|
|
27
27
|
assetBridge: WorkerAssetBridge | null;
|
|
28
|
+
activeReject: ((reason?: string) => void) | null;
|
|
28
29
|
load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
|
|
29
30
|
set?: (value: number) => void;
|
|
30
31
|
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
@@ -20,6 +20,7 @@ class Python {
|
|
|
20
20
|
pendingEof = false;
|
|
21
21
|
exit = true;
|
|
22
22
|
assetBridge = null;
|
|
23
|
+
activeReject = null;
|
|
23
24
|
load(runtimeAssets = '', code = '', log = true, _args = [], _options = {}, progress) {
|
|
24
25
|
return new Promise(async (resolve, reject) => {
|
|
25
26
|
this.pendingInput = [];
|
|
@@ -89,6 +90,7 @@ class Python {
|
|
|
89
90
|
return new Promise(async (resolve, reject) => {
|
|
90
91
|
if (!this.worker)
|
|
91
92
|
return reject('Worker not loaded');
|
|
93
|
+
this.activeReject = reject;
|
|
92
94
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
93
95
|
const handler = (event) => {
|
|
94
96
|
if (this.assetBridge?.handleMessage(event))
|
|
@@ -113,6 +115,7 @@ class Python {
|
|
|
113
115
|
this.exit = true;
|
|
114
116
|
this.waitingForInput = false;
|
|
115
117
|
this.pendingEof = false;
|
|
118
|
+
this.activeReject = null;
|
|
116
119
|
this.ondebug?.({ type: 'stop' });
|
|
117
120
|
resolve(results);
|
|
118
121
|
}
|
|
@@ -123,6 +126,7 @@ class Python {
|
|
|
123
126
|
this.exit = true;
|
|
124
127
|
this.waitingForInput = false;
|
|
125
128
|
this.pendingEof = false;
|
|
129
|
+
this.activeReject = null;
|
|
126
130
|
this.ondebug?.({ type: 'stop' });
|
|
127
131
|
reject(error);
|
|
128
132
|
}
|
|
@@ -169,11 +173,19 @@ class Python {
|
|
|
169
173
|
this.terminate();
|
|
170
174
|
}
|
|
171
175
|
terminate() {
|
|
176
|
+
this.activeReject?.('Process terminated');
|
|
177
|
+
this.activeReject = null;
|
|
178
|
+
this.waitingForInput = false;
|
|
172
179
|
this.pendingEof = false;
|
|
180
|
+
this.uid += 1;
|
|
173
181
|
new Uint8Array(this.interruptBuffer)[0] = 2;
|
|
174
182
|
const control = new Int32Array(this.debugBuffer);
|
|
175
183
|
Atomics.add(control, 0, 1);
|
|
176
184
|
Atomics.notify(control, 0);
|
|
185
|
+
this.worker?.terminate?.();
|
|
186
|
+
delete this.worker;
|
|
187
|
+
this.assetBridge = null;
|
|
188
|
+
this.exit = true;
|
|
177
189
|
}
|
|
178
190
|
async clear() {
|
|
179
191
|
this.terminate();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type PlaygroundRuntimeAssets } from './assets';
|
|
2
|
+
import { type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
|
|
3
|
+
import type { Sandbox } from './sandbox';
|
|
4
|
+
declare class Rust implements Sandbox {
|
|
5
|
+
output: any;
|
|
6
|
+
worker?: Worker;
|
|
7
|
+
buffer: SharedArrayBuffer;
|
|
8
|
+
pendingInput: string[];
|
|
9
|
+
begin: number;
|
|
10
|
+
elapse: number;
|
|
11
|
+
uid: number;
|
|
12
|
+
exit: boolean;
|
|
13
|
+
compilerUrl: string;
|
|
14
|
+
assetPath: string;
|
|
15
|
+
activeReject: ((reason: string) => void) | null;
|
|
16
|
+
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
17
|
+
waitingForInput: boolean;
|
|
18
|
+
pendingEof: boolean;
|
|
19
|
+
load(runtimeAssets?: string | PlaygroundRuntimeAssets, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
|
|
20
|
+
set?: (value: number) => void;
|
|
21
|
+
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
22
|
+
write(input: string): void;
|
|
23
|
+
eof(): void;
|
|
24
|
+
private flushPendingInput;
|
|
25
|
+
run(code: string, prepare: boolean, _log?: boolean, _prog?: {
|
|
26
|
+
set?: (value: number) => void;
|
|
27
|
+
} | import('svelte/store').Writable<number>, args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
28
|
+
kill(): void;
|
|
29
|
+
terminate(): void;
|
|
30
|
+
clear(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export default Rust;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { resolveRustCompilerUrl } from './assets';
|
|
2
|
+
import { resolveSandboxExecutionArgs } from './options';
|
|
3
|
+
import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
|
|
4
|
+
class Rust {
|
|
5
|
+
output = null;
|
|
6
|
+
worker = null;
|
|
7
|
+
buffer = new SharedArrayBuffer(1024);
|
|
8
|
+
pendingInput = [];
|
|
9
|
+
begin = 0;
|
|
10
|
+
elapse = 0;
|
|
11
|
+
uid = 0;
|
|
12
|
+
exit = true;
|
|
13
|
+
compilerUrl = '';
|
|
14
|
+
assetPath = '';
|
|
15
|
+
activeReject = null;
|
|
16
|
+
oncompilerdiagnostic;
|
|
17
|
+
waitingForInput = false;
|
|
18
|
+
pendingEof = false;
|
|
19
|
+
load(runtimeAssets = '', _code = '', _log = true, _args = [], _options = {}, progress) {
|
|
20
|
+
return new Promise(async (resolve, reject) => {
|
|
21
|
+
this.pendingInput = [];
|
|
22
|
+
this.waitingForInput = false;
|
|
23
|
+
this.pendingEof = false;
|
|
24
|
+
const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
25
|
+
const nextCompilerUrl = resolveRustCompilerUrl(runtimeAssets, currentUrl);
|
|
26
|
+
const nextAssetPath = typeof runtimeAssets === 'string'
|
|
27
|
+
? runtimeAssets
|
|
28
|
+
: runtimeAssets?.rootUrl ||
|
|
29
|
+
(typeof window !== 'undefined'
|
|
30
|
+
? window.location.pathname.replace(/\/$/, '')
|
|
31
|
+
: '');
|
|
32
|
+
if (!nextCompilerUrl) {
|
|
33
|
+
return reject('Rust runtime is not configured. Set PUBLIC_WASM_RUST_COMPILER_URL or runtimeAssets.rust.compilerUrl.');
|
|
34
|
+
}
|
|
35
|
+
const needsWorkerReset = !this.worker || this.compilerUrl !== nextCompilerUrl || this.assetPath !== nextAssetPath;
|
|
36
|
+
this.compilerUrl = nextCompilerUrl;
|
|
37
|
+
this.assetPath = nextAssetPath;
|
|
38
|
+
if (needsWorkerReset && this.worker) {
|
|
39
|
+
this.worker.terminate();
|
|
40
|
+
delete this.worker;
|
|
41
|
+
}
|
|
42
|
+
if (!this.worker) {
|
|
43
|
+
this.worker = new (await import('./worker/rust?worker')).default();
|
|
44
|
+
this.worker.onerror = (event) => {
|
|
45
|
+
const location = event.filename && event.lineno
|
|
46
|
+
? ` (${event.filename}:${event.lineno}:${event.colno})`
|
|
47
|
+
: '';
|
|
48
|
+
reject(`Rust worker script error: ${event.message || 'unknown error'}${location}`);
|
|
49
|
+
};
|
|
50
|
+
this.worker.onmessageerror = () => {
|
|
51
|
+
reject('Rust worker message deserialization failed');
|
|
52
|
+
};
|
|
53
|
+
this.worker.onmessage = (event) => {
|
|
54
|
+
if (event.data?.load) {
|
|
55
|
+
progress?.set?.(1);
|
|
56
|
+
resolve();
|
|
57
|
+
}
|
|
58
|
+
if (event.data?.error)
|
|
59
|
+
reject(event.data.error);
|
|
60
|
+
};
|
|
61
|
+
this.worker.postMessage({
|
|
62
|
+
load: true,
|
|
63
|
+
compilerUrl: this.compilerUrl,
|
|
64
|
+
path: this.assetPath
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
progress?.set?.(1);
|
|
69
|
+
resolve();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
write(input) {
|
|
74
|
+
this.pendingInput.push(input);
|
|
75
|
+
this.pendingEof = false;
|
|
76
|
+
this.flushPendingInput();
|
|
77
|
+
}
|
|
78
|
+
eof() {
|
|
79
|
+
this.pendingEof = true;
|
|
80
|
+
this.flushPendingInput();
|
|
81
|
+
}
|
|
82
|
+
flushPendingInput() {
|
|
83
|
+
if (!this.waitingForInput)
|
|
84
|
+
return;
|
|
85
|
+
if (flushQueuedStdin(this.pendingInput, this.buffer)) {
|
|
86
|
+
this.waitingForInput = false;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (this.pendingEof) {
|
|
90
|
+
flushBufferedEof(this.buffer);
|
|
91
|
+
this.pendingEof = false;
|
|
92
|
+
this.waitingForInput = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
run(code, prepare, _log = true, _prog, args = [], options = {}) {
|
|
96
|
+
this.exit = false;
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (!this.worker)
|
|
99
|
+
return reject('Worker not loaded');
|
|
100
|
+
const { programArgs } = resolveSandboxExecutionArgs('RUST', args, options);
|
|
101
|
+
const targetTriple = options.rustTargetTriple || 'wasm32-wasip1';
|
|
102
|
+
const _uid = ++this.uid;
|
|
103
|
+
this.activeReject = reject;
|
|
104
|
+
const handler = (event) => {
|
|
105
|
+
if (!this.worker)
|
|
106
|
+
return reject('Worker not loaded');
|
|
107
|
+
if (_uid !== this.uid)
|
|
108
|
+
return (this.worker.onmessage = null);
|
|
109
|
+
const { output, results, error, buffer, diagnostic, progress } = event.data;
|
|
110
|
+
if (buffer) {
|
|
111
|
+
this.waitingForInput = true;
|
|
112
|
+
this.flushPendingInput();
|
|
113
|
+
}
|
|
114
|
+
if (progress && typeof progress.percent === 'number') {
|
|
115
|
+
_prog?.set?.(Math.max(0, Math.min(progress.percent / 100, 1)));
|
|
116
|
+
}
|
|
117
|
+
if (output)
|
|
118
|
+
this.output(output);
|
|
119
|
+
if (diagnostic)
|
|
120
|
+
this.oncompilerdiagnostic?.(diagnostic);
|
|
121
|
+
if (results) {
|
|
122
|
+
this.elapse = Date.now() - this.begin;
|
|
123
|
+
this.exit = true;
|
|
124
|
+
this.waitingForInput = false;
|
|
125
|
+
this.pendingEof = false;
|
|
126
|
+
this.activeReject = null;
|
|
127
|
+
resolve(results);
|
|
128
|
+
}
|
|
129
|
+
if (error) {
|
|
130
|
+
this.elapse = Date.now() - this.begin;
|
|
131
|
+
this.exit = true;
|
|
132
|
+
this.waitingForInput = false;
|
|
133
|
+
this.pendingEof = false;
|
|
134
|
+
this.activeReject = null;
|
|
135
|
+
reject(error);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
this.worker.onmessage = handler;
|
|
139
|
+
this.begin = Date.now();
|
|
140
|
+
this.worker.postMessage({
|
|
141
|
+
code,
|
|
142
|
+
prepare,
|
|
143
|
+
buffer: this.buffer,
|
|
144
|
+
args: programArgs,
|
|
145
|
+
targetTriple,
|
|
146
|
+
log: _log
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
kill() {
|
|
151
|
+
this.terminate();
|
|
152
|
+
}
|
|
153
|
+
terminate() {
|
|
154
|
+
this.activeReject?.('Process terminated');
|
|
155
|
+
this.activeReject = null;
|
|
156
|
+
this.waitingForInput = false;
|
|
157
|
+
this.pendingEof = false;
|
|
158
|
+
this.uid += 1;
|
|
159
|
+
this.worker?.terminate?.();
|
|
160
|
+
delete this.worker;
|
|
161
|
+
this.exit = true;
|
|
162
|
+
}
|
|
163
|
+
async clear() {
|
|
164
|
+
this.pendingInput = [];
|
|
165
|
+
this.waitingForInput = false;
|
|
166
|
+
this.pendingEof = false;
|
|
167
|
+
if (this.worker)
|
|
168
|
+
this.worker.onmessage = null;
|
|
169
|
+
resetBufferedStdin(this.buffer);
|
|
170
|
+
if (!this.exit) {
|
|
171
|
+
this.terminate();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export default Rust;
|