wasm-idle 0.4.0 → 0.5.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 +111 -2
- package/dist/clang/app.d.ts +4 -0
- package/dist/clang/app.js +330 -15
- package/dist/clang/index.d.ts +10 -0
- package/dist/clang/index.js +284 -25
- 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 +6 -3
- package/dist/index.js +3 -3
- package/dist/playground/assetBridge.d.ts +20 -0
- package/dist/playground/assetBridge.js +192 -0
- package/dist/playground/assets.d.ts +37 -0
- package/dist/playground/assets.js +105 -0
- package/dist/playground/clang.d.ts +11 -1
- package/dist/playground/clang.js +58 -4
- package/dist/playground/index.d.ts +5 -5
- package/dist/playground/index.js +48 -6
- package/dist/playground/java.d.ts +7 -2
- package/dist/playground/java.js +31 -5
- package/dist/playground/javaStdin.js +235 -2
- package/dist/playground/options.d.ts +18 -0
- package/dist/playground/options.js +15 -1
- package/dist/playground/python.d.ts +7 -1
- package/dist/playground/python.js +50 -5
- package/dist/playground/rust.d.ts +32 -0
- package/dist/playground/rust.js +175 -0
- package/dist/playground/sandbox.d.ts +19 -4
- package/dist/playground/wasmRustVersion.d.ts +1 -0
- package/dist/playground/wasmRustVersion.js +1 -0
- package/dist/playground/worker/assets.d.ts +12 -0
- package/dist/playground/worker/assets.js +257 -0
- package/dist/playground/worker/clang.js +22 -6
- package/dist/playground/worker/java.js +15 -35
- package/dist/playground/worker/python.js +13 -33
- package/dist/playground/worker/rust.d.ts +1 -0
- package/dist/playground/worker/rust.js +159 -0
- package/dist/terminal/Terminal.svelte +121 -18
- package/dist/terminal/Terminal.svelte.d.ts +4 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +4 -0
- package/package.json +19 -13
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { JAVA_RUNTIME_LOAD_ASSETS, PYTHON_RUNTIME_LOAD_ASSETS } from './assets';
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const transferBuffer = (bytes) => bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength
|
|
4
|
+
? bytes.buffer
|
|
5
|
+
: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
6
|
+
const expectedAssetsForRuntime = (runtime) => new Set(runtime === 'python' ? [...PYTHON_RUNTIME_LOAD_ASSETS] : [...JAVA_RUNTIME_LOAD_ASSETS]);
|
|
7
|
+
class RuntimeLoadProgress {
|
|
8
|
+
fractions = new Map();
|
|
9
|
+
expectedAssets;
|
|
10
|
+
progress;
|
|
11
|
+
constructor(runtime) {
|
|
12
|
+
this.expectedAssets = expectedAssetsForRuntime(runtime);
|
|
13
|
+
this.reset();
|
|
14
|
+
}
|
|
15
|
+
reset(progress) {
|
|
16
|
+
this.progress = progress;
|
|
17
|
+
this.fractions.clear();
|
|
18
|
+
for (const asset of this.expectedAssets)
|
|
19
|
+
this.fractions.set(asset, 0);
|
|
20
|
+
this.emit();
|
|
21
|
+
}
|
|
22
|
+
update(asset, loaded, total) {
|
|
23
|
+
if (!this.expectedAssets.has(asset))
|
|
24
|
+
return;
|
|
25
|
+
const fraction = total && total > 0 ? Math.min(loaded / total, 1) : loaded > 0 ? 1 : 0;
|
|
26
|
+
this.fractions.set(asset, fraction);
|
|
27
|
+
this.emit();
|
|
28
|
+
}
|
|
29
|
+
emit() {
|
|
30
|
+
if (!this.progress)
|
|
31
|
+
return;
|
|
32
|
+
if (!this.fractions.size)
|
|
33
|
+
return this.progress.set?.(0);
|
|
34
|
+
let total = 0;
|
|
35
|
+
for (const fraction of this.fractions.values())
|
|
36
|
+
total += fraction;
|
|
37
|
+
this.progress.set?.(total / this.fractions.size);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class WorkerAssetBridge {
|
|
41
|
+
worker;
|
|
42
|
+
runtime;
|
|
43
|
+
config;
|
|
44
|
+
progress;
|
|
45
|
+
constructor(worker, runtime, config, progress) {
|
|
46
|
+
this.worker = worker;
|
|
47
|
+
this.runtime = runtime;
|
|
48
|
+
this.config = config;
|
|
49
|
+
this.progress = new RuntimeLoadProgress(runtime);
|
|
50
|
+
this.progress.reset(progress);
|
|
51
|
+
}
|
|
52
|
+
matches(config) {
|
|
53
|
+
return (this.config.baseUrl === config.baseUrl &&
|
|
54
|
+
this.config.loader === config.loader &&
|
|
55
|
+
this.config.useAssetBridge === config.useAssetBridge);
|
|
56
|
+
}
|
|
57
|
+
rebind(worker, config, progress) {
|
|
58
|
+
this.worker = worker;
|
|
59
|
+
this.config = config;
|
|
60
|
+
this.progress.reset(progress);
|
|
61
|
+
}
|
|
62
|
+
resetProgress(progress) {
|
|
63
|
+
this.progress.reset(progress);
|
|
64
|
+
}
|
|
65
|
+
handleMessage(event) {
|
|
66
|
+
const assetRequest = event.data?.assetRequest;
|
|
67
|
+
if (assetRequest) {
|
|
68
|
+
void this.respond(assetRequest);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const assetProgress = event.data?.assetProgress;
|
|
72
|
+
if (assetProgress) {
|
|
73
|
+
this.progress.update(assetProgress.asset, assetProgress.loaded, assetProgress.total);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
async respond(request) {
|
|
79
|
+
try {
|
|
80
|
+
const loaded = await this.loadAsset(request.asset);
|
|
81
|
+
const buffer = transferBuffer(loaded.bytes);
|
|
82
|
+
this.worker.postMessage({
|
|
83
|
+
assetResponse: {
|
|
84
|
+
id: request.id,
|
|
85
|
+
ok: true,
|
|
86
|
+
bytes: buffer,
|
|
87
|
+
mimeType: loaded.mimeType
|
|
88
|
+
}
|
|
89
|
+
}, [buffer]);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
this.worker.postMessage({
|
|
93
|
+
assetResponse: {
|
|
94
|
+
id: request.id,
|
|
95
|
+
ok: false,
|
|
96
|
+
error: error instanceof Error ? error.message : String(error)
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async loadAsset(asset) {
|
|
102
|
+
const reportProgress = (loaded, total) => this.progress.update(asset, loaded, total);
|
|
103
|
+
if (this.config.loader) {
|
|
104
|
+
const loaded = await this.normalizeLoaderResult(await this.config.loader({
|
|
105
|
+
runtime: this.runtime,
|
|
106
|
+
asset,
|
|
107
|
+
reportProgress
|
|
108
|
+
}), asset);
|
|
109
|
+
if (loaded)
|
|
110
|
+
return loaded;
|
|
111
|
+
}
|
|
112
|
+
return await this.fetchAsset(new URL(asset, this.config.baseUrl).href, asset);
|
|
113
|
+
}
|
|
114
|
+
async normalizeLoaderResult(result, asset) {
|
|
115
|
+
if (!result)
|
|
116
|
+
return null;
|
|
117
|
+
if (typeof result === 'string' || result instanceof URL) {
|
|
118
|
+
return await this.fetchAsset(String(result), asset);
|
|
119
|
+
}
|
|
120
|
+
if (result instanceof ArrayBuffer) {
|
|
121
|
+
const bytes = new Uint8Array(result);
|
|
122
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
123
|
+
return { bytes };
|
|
124
|
+
}
|
|
125
|
+
if (result instanceof Uint8Array) {
|
|
126
|
+
this.progress.update(asset, result.byteLength, result.byteLength);
|
|
127
|
+
return { bytes: result };
|
|
128
|
+
}
|
|
129
|
+
if (result instanceof Blob) {
|
|
130
|
+
const bytes = new Uint8Array(await result.arrayBuffer());
|
|
131
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
132
|
+
return { bytes, mimeType: result.type || undefined };
|
|
133
|
+
}
|
|
134
|
+
if ('url' in result && result.url) {
|
|
135
|
+
return await this.fetchAsset(String(result.url), asset);
|
|
136
|
+
}
|
|
137
|
+
if ('data' in result) {
|
|
138
|
+
if (typeof result.data === 'string') {
|
|
139
|
+
const bytes = encoder.encode(result.data);
|
|
140
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
141
|
+
return { bytes, mimeType: result.mimeType };
|
|
142
|
+
}
|
|
143
|
+
if (result.data instanceof ArrayBuffer) {
|
|
144
|
+
const bytes = new Uint8Array(result.data);
|
|
145
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
146
|
+
return { bytes, mimeType: result.mimeType };
|
|
147
|
+
}
|
|
148
|
+
if (result.data instanceof Uint8Array) {
|
|
149
|
+
this.progress.update(asset, result.data.byteLength, result.data.byteLength);
|
|
150
|
+
return { bytes: result.data, mimeType: result.mimeType };
|
|
151
|
+
}
|
|
152
|
+
const bytes = new Uint8Array(await result.data.arrayBuffer());
|
|
153
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
154
|
+
return { bytes, mimeType: result.mimeType || result.data.type || undefined };
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
async fetchAsset(url, asset) {
|
|
159
|
+
const response = await fetch(url);
|
|
160
|
+
if (!response.ok)
|
|
161
|
+
throw new Error(`Failed to load ${asset}: ${response.status}`);
|
|
162
|
+
const contentLength = Number(response.headers.get('content-length') || 0) || undefined;
|
|
163
|
+
const mimeType = response.headers.get('content-type') || undefined;
|
|
164
|
+
if (!response.body) {
|
|
165
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
166
|
+
this.progress.update(asset, bytes.byteLength, contentLength ?? bytes.byteLength);
|
|
167
|
+
return { bytes, mimeType };
|
|
168
|
+
}
|
|
169
|
+
const reader = response.body.getReader();
|
|
170
|
+
let receivedLength = 0;
|
|
171
|
+
const chunks = [];
|
|
172
|
+
while (true) {
|
|
173
|
+
const { done, value } = await reader.read();
|
|
174
|
+
if (done)
|
|
175
|
+
break;
|
|
176
|
+
if (!value)
|
|
177
|
+
continue;
|
|
178
|
+
const chunk = Uint8Array.from(value);
|
|
179
|
+
chunks.push(chunk);
|
|
180
|
+
receivedLength += chunk.byteLength;
|
|
181
|
+
this.progress.update(asset, receivedLength, contentLength);
|
|
182
|
+
}
|
|
183
|
+
const bytes = new Uint8Array(receivedLength);
|
|
184
|
+
let position = 0;
|
|
185
|
+
for (const chunk of chunks) {
|
|
186
|
+
bytes.set(chunk, position);
|
|
187
|
+
position += chunk.byteLength;
|
|
188
|
+
}
|
|
189
|
+
this.progress.update(asset, receivedLength, contentLength ?? receivedLength);
|
|
190
|
+
return { bytes, mimeType };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type RuntimeAssetRuntime = 'python' | 'java';
|
|
2
|
+
export interface RuntimeAssetLoadRequest {
|
|
3
|
+
runtime: RuntimeAssetRuntime;
|
|
4
|
+
asset: string;
|
|
5
|
+
reportProgress: (loaded: number, total?: number) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface RuntimeAssetDataResult {
|
|
8
|
+
data: string | ArrayBuffer | Uint8Array | Blob;
|
|
9
|
+
mimeType?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RuntimeAssetUrlResult {
|
|
12
|
+
url: string | URL;
|
|
13
|
+
}
|
|
14
|
+
export type RuntimeAssetLoaderResult = RuntimeAssetDataResult | RuntimeAssetUrlResult | string | URL | ArrayBuffer | Uint8Array | Blob | null | undefined;
|
|
15
|
+
export type RuntimeAssetLoader = (request: RuntimeAssetLoadRequest) => RuntimeAssetLoaderResult | Promise<RuntimeAssetLoaderResult>;
|
|
16
|
+
export interface RuntimeAssetConfig {
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
loader?: RuntimeAssetLoader;
|
|
19
|
+
}
|
|
20
|
+
export interface RustRuntimeAssetConfig {
|
|
21
|
+
compilerUrl?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PlaygroundRuntimeAssets {
|
|
24
|
+
rootUrl?: string;
|
|
25
|
+
python?: RuntimeAssetConfig;
|
|
26
|
+
java?: RuntimeAssetConfig;
|
|
27
|
+
rust?: RustRuntimeAssetConfig;
|
|
28
|
+
}
|
|
29
|
+
export interface ResolvedRuntimeAssetConfig {
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
loader?: RuntimeAssetLoader;
|
|
32
|
+
useAssetBridge: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const PYTHON_RUNTIME_LOAD_ASSETS: readonly ["pyodide.asm.js", "pyodide-lock.json", "pyodide.asm.wasm", "python_stdlib.zip"];
|
|
35
|
+
export declare const JAVA_RUNTIME_LOAD_ASSETS: readonly ["compiler.wasm-runtime.js", "compiler.wasm", "compile-classlib-teavm.bin", "runtime-classlib-teavm.bin"];
|
|
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;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { env as publicEnv } from '$env/dynamic/public';
|
|
2
|
+
import { normalizeTeaVmBaseUrl, resolveTeaVmBaseUrl } from './teavmConfig';
|
|
3
|
+
export const PYTHON_RUNTIME_LOAD_ASSETS = [
|
|
4
|
+
'pyodide.asm.js',
|
|
5
|
+
'pyodide-lock.json',
|
|
6
|
+
'pyodide.asm.wasm',
|
|
7
|
+
'python_stdlib.zip'
|
|
8
|
+
];
|
|
9
|
+
export const JAVA_RUNTIME_LOAD_ASSETS = [
|
|
10
|
+
'compiler.wasm-runtime.js',
|
|
11
|
+
'compiler.wasm',
|
|
12
|
+
'compile-classlib-teavm.bin',
|
|
13
|
+
'runtime-classlib-teavm.bin'
|
|
14
|
+
];
|
|
15
|
+
const PYTHON_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/python/';
|
|
16
|
+
const JAVA_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/java/';
|
|
17
|
+
const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
|
|
18
|
+
const normalized = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
19
|
+
return currentUrl ? new URL(normalized, currentUrl).href : normalized;
|
|
20
|
+
};
|
|
21
|
+
const normalizeRootUrl = (rootUrl) => rootUrl.endsWith('/') ? rootUrl.slice(0, -1) : rootUrl;
|
|
22
|
+
const resolvePythonBaseUrl = (rootUrl = '', currentUrl = '') => normalizeBaseUrl(`${normalizeRootUrl(rootUrl) || ''}/pyodide/`, currentUrl);
|
|
23
|
+
const resolvePythonRuntimeAssetConfig = (options, currentUrl = '') => {
|
|
24
|
+
if (typeof options === 'string') {
|
|
25
|
+
return {
|
|
26
|
+
baseUrl: resolvePythonBaseUrl(options, currentUrl),
|
|
27
|
+
useAssetBridge: false
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const runtimeConfig = options?.python;
|
|
31
|
+
if (runtimeConfig?.baseUrl) {
|
|
32
|
+
return {
|
|
33
|
+
baseUrl: normalizeBaseUrl(runtimeConfig.baseUrl, currentUrl),
|
|
34
|
+
loader: runtimeConfig.loader,
|
|
35
|
+
useAssetBridge: !!runtimeConfig.loader
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (options?.rootUrl) {
|
|
39
|
+
return {
|
|
40
|
+
baseUrl: resolvePythonBaseUrl(options.rootUrl, currentUrl),
|
|
41
|
+
loader: runtimeConfig?.loader,
|
|
42
|
+
useAssetBridge: !!runtimeConfig?.loader
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (runtimeConfig?.loader) {
|
|
46
|
+
return {
|
|
47
|
+
baseUrl: PYTHON_VIRTUAL_BASE_URL,
|
|
48
|
+
loader: runtimeConfig.loader,
|
|
49
|
+
useAssetBridge: true
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
baseUrl: resolvePythonBaseUrl('', currentUrl),
|
|
54
|
+
useAssetBridge: false
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const resolveJavaBaseUrl = (rootUrl = '', currentUrl = '') => resolveTeaVmBaseUrl(rootUrl, currentUrl);
|
|
58
|
+
const resolveJavaRuntimeAssetConfig = (options, currentUrl = '') => {
|
|
59
|
+
if (typeof options === 'string') {
|
|
60
|
+
return {
|
|
61
|
+
baseUrl: resolveJavaBaseUrl(options, currentUrl),
|
|
62
|
+
useAssetBridge: false
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const runtimeConfig = options?.java;
|
|
66
|
+
if (runtimeConfig?.baseUrl) {
|
|
67
|
+
return {
|
|
68
|
+
baseUrl: currentUrl
|
|
69
|
+
? new URL(normalizeTeaVmBaseUrl(runtimeConfig.baseUrl), currentUrl).href
|
|
70
|
+
: normalizeTeaVmBaseUrl(runtimeConfig.baseUrl),
|
|
71
|
+
loader: runtimeConfig.loader,
|
|
72
|
+
useAssetBridge: !!runtimeConfig.loader
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (options?.rootUrl) {
|
|
76
|
+
return {
|
|
77
|
+
baseUrl: resolveJavaBaseUrl(options.rootUrl, currentUrl),
|
|
78
|
+
loader: runtimeConfig?.loader,
|
|
79
|
+
useAssetBridge: !!runtimeConfig?.loader
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (runtimeConfig?.loader) {
|
|
83
|
+
return {
|
|
84
|
+
baseUrl: JAVA_VIRTUAL_BASE_URL,
|
|
85
|
+
loader: runtimeConfig.loader,
|
|
86
|
+
useAssetBridge: true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
baseUrl: resolveJavaBaseUrl('', currentUrl),
|
|
91
|
+
useAssetBridge: false
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '') {
|
|
95
|
+
return runtime === 'python'
|
|
96
|
+
? resolvePythonRuntimeAssetConfig(options, currentUrl)
|
|
97
|
+
: resolveJavaRuntimeAssetConfig(options, currentUrl);
|
|
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
|
+
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
|
+
import type { PlaygroundRuntimeAssets } from './assets';
|
|
2
3
|
import type { Sandbox } from './sandbox';
|
|
3
4
|
import type { Writable } from 'svelte/store';
|
|
4
5
|
declare class Clang implements Sandbox {
|
|
6
|
+
language: 'C' | 'CPP';
|
|
5
7
|
ts: number;
|
|
6
8
|
output: any;
|
|
7
9
|
ondebug?: (event: DebugSessionEvent) => void;
|
|
8
10
|
worker?: Worker;
|
|
9
11
|
buffer: SharedArrayBuffer;
|
|
10
12
|
debugBuffer: SharedArrayBuffer;
|
|
13
|
+
watchBuffer: SharedArrayBuffer;
|
|
14
|
+
watchResultBuffer: SharedArrayBuffer;
|
|
11
15
|
interruptBuffer: SharedArrayBuffer;
|
|
12
16
|
pendingInput: string[];
|
|
13
17
|
begin: number;
|
|
@@ -17,7 +21,11 @@ declare class Clang implements Sandbox {
|
|
|
17
21
|
waitingForInput: boolean;
|
|
18
22
|
pendingEof: boolean;
|
|
19
23
|
exit: boolean;
|
|
20
|
-
|
|
24
|
+
activeReject: ((reason?: string) => void) | null;
|
|
25
|
+
constructor(language: 'C' | 'CPP');
|
|
26
|
+
load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions, _progress?: {
|
|
27
|
+
set?: (value: number) => void;
|
|
28
|
+
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
21
29
|
write(input: string): void;
|
|
22
30
|
eof(): void;
|
|
23
31
|
private flushPendingInput;
|
|
@@ -25,6 +33,8 @@ declare class Clang implements Sandbox {
|
|
|
25
33
|
set?: (value: number) => void;
|
|
26
34
|
}, args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
27
35
|
debugCommand(command: DebugCommand): void;
|
|
36
|
+
setBreakpoints(lines: number[]): void;
|
|
37
|
+
debugEvaluate(expression: string): Promise<string>;
|
|
28
38
|
kill(): void;
|
|
29
39
|
terminate(): void;
|
|
30
40
|
clear(): Promise<void>;
|
package/dist/playground/clang.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveSandboxExecutionArgs } from './options';
|
|
2
|
+
import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
|
|
3
|
+
const debugBreakpointBufferInts = 1028;
|
|
2
4
|
class Clang {
|
|
5
|
+
language;
|
|
3
6
|
ts = Date.now();
|
|
4
7
|
output = null;
|
|
5
8
|
ondebug;
|
|
6
9
|
worker = null;
|
|
7
10
|
buffer = new SharedArrayBuffer(1024);
|
|
8
|
-
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);
|
|
9
14
|
interruptBuffer = new SharedArrayBuffer(1);
|
|
10
15
|
pendingInput = [];
|
|
11
16
|
begin = 0;
|
|
@@ -15,12 +20,17 @@ class Clang {
|
|
|
15
20
|
waitingForInput = false;
|
|
16
21
|
pendingEof = false;
|
|
17
22
|
exit = true;
|
|
18
|
-
|
|
23
|
+
activeReject = null;
|
|
24
|
+
constructor(language) {
|
|
25
|
+
this.language = language;
|
|
26
|
+
}
|
|
27
|
+
load(runtimeAssets = '', code = '', log = true, args = [], _options = {}, _progress) {
|
|
19
28
|
return new Promise(async (resolve) => {
|
|
20
29
|
this.log = log;
|
|
21
30
|
this.pendingInput = [];
|
|
22
31
|
this.waitingForInput = false;
|
|
23
32
|
this.pendingEof = false;
|
|
33
|
+
const path = typeof runtimeAssets === 'string' ? runtimeAssets : runtimeAssets?.rootUrl || '';
|
|
24
34
|
if (!this.worker) {
|
|
25
35
|
this.worker = new (await import('./worker/clang?worker')).default();
|
|
26
36
|
this.worker.onmessage = () => resolve();
|
|
@@ -59,6 +69,9 @@ class Clang {
|
|
|
59
69
|
return new Promise(async (resolve, reject) => {
|
|
60
70
|
if (!this.worker)
|
|
61
71
|
return reject('Worker not loaded');
|
|
72
|
+
this.activeReject = reject;
|
|
73
|
+
const { compileArgs, programArgs } = resolveSandboxExecutionArgs(this.language, args, options);
|
|
74
|
+
this.setBreakpoints(options.debug ? [...(options.breakpoints || [])] : []);
|
|
62
75
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
63
76
|
const handler = (event) => {
|
|
64
77
|
if (!this.worker)
|
|
@@ -79,6 +92,7 @@ class Clang {
|
|
|
79
92
|
this.exit = true;
|
|
80
93
|
this.waitingForInput = false;
|
|
81
94
|
this.pendingEof = false;
|
|
95
|
+
this.activeReject = null;
|
|
82
96
|
this.ondebug?.({ type: 'stop' });
|
|
83
97
|
resolve(results);
|
|
84
98
|
}
|
|
@@ -88,6 +102,7 @@ class Clang {
|
|
|
88
102
|
this.elapse = Date.now() - this.begin;
|
|
89
103
|
this.waitingForInput = false;
|
|
90
104
|
this.pendingEof = false;
|
|
105
|
+
this.activeReject = null;
|
|
91
106
|
this.exit = true;
|
|
92
107
|
this.ondebug?.({ type: 'stop' });
|
|
93
108
|
reject(error);
|
|
@@ -103,10 +118,16 @@ class Clang {
|
|
|
103
118
|
prepare,
|
|
104
119
|
buffer: this.buffer,
|
|
105
120
|
debugBuffer: this.debugBuffer,
|
|
121
|
+
watchBuffer: this.watchBuffer,
|
|
122
|
+
watchResultBuffer: this.watchResultBuffer,
|
|
106
123
|
interrupt: this.interruptBuffer,
|
|
107
124
|
context: {},
|
|
108
125
|
log,
|
|
109
|
-
|
|
126
|
+
language: this.language,
|
|
127
|
+
compileArgs,
|
|
128
|
+
programArgs,
|
|
129
|
+
cppVersion: options.cppVersion,
|
|
130
|
+
cVersion: options.cVersion,
|
|
110
131
|
debug: !!options.debug,
|
|
111
132
|
breakpoints: [...(options.breakpoints || [])],
|
|
112
133
|
pauseOnEntry: !!options.pauseOnEntry
|
|
@@ -120,15 +141,46 @@ class Clang {
|
|
|
120
141
|
Atomics.notify(control, 0);
|
|
121
142
|
this.ondebug?.({ type: 'resume', command });
|
|
122
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
|
+
}
|
|
123
168
|
kill() {
|
|
124
169
|
this.terminate();
|
|
125
170
|
}
|
|
126
171
|
terminate() {
|
|
172
|
+
this.activeReject?.('Process terminated');
|
|
173
|
+
this.activeReject = null;
|
|
174
|
+
this.waitingForInput = false;
|
|
127
175
|
this.pendingEof = false;
|
|
176
|
+
this.uid += 1;
|
|
128
177
|
new Uint8Array(this.interruptBuffer)[0] = 2;
|
|
129
178
|
const control = new Int32Array(this.debugBuffer);
|
|
130
179
|
Atomics.add(control, 0, 1);
|
|
131
180
|
Atomics.notify(control, 0);
|
|
181
|
+
this.worker?.terminate?.();
|
|
182
|
+
delete this.worker;
|
|
183
|
+
this.exit = true;
|
|
132
184
|
}
|
|
133
185
|
async clear() {
|
|
134
186
|
this.terminate();
|
|
@@ -138,6 +190,8 @@ class Clang {
|
|
|
138
190
|
if (this.worker)
|
|
139
191
|
this.worker.onmessage = null;
|
|
140
192
|
resetBufferedStdin(this.buffer);
|
|
193
|
+
resetBufferedStdin(this.watchBuffer);
|
|
194
|
+
resetBufferedStdin(this.watchResultBuffer);
|
|
141
195
|
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
142
196
|
debugBuffer.fill(0);
|
|
143
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':
|
|
@@ -14,12 +48,17 @@ export default async function load(language) {
|
|
|
14
48
|
sandbox = new Python();
|
|
15
49
|
break;
|
|
16
50
|
case 'C':
|
|
51
|
+
sandbox = new Clang('C');
|
|
52
|
+
break;
|
|
17
53
|
case 'CPP':
|
|
18
|
-
sandbox = new Clang();
|
|
54
|
+
sandbox = new Clang('CPP');
|
|
19
55
|
break;
|
|
20
56
|
case 'JAVA':
|
|
21
57
|
sandbox = new Java();
|
|
22
58
|
break;
|
|
59
|
+
case 'RUST':
|
|
60
|
+
sandbox = new Rust();
|
|
61
|
+
break;
|
|
23
62
|
default:
|
|
24
63
|
throw new Error(`Unsupported language: ${language}`);
|
|
25
64
|
}
|
|
@@ -37,6 +76,9 @@ export default async function load(language) {
|
|
|
37
76
|
sandboxCache['CPP'] = sandbox;
|
|
38
77
|
if (language === 'JAVA')
|
|
39
78
|
sandboxCache['JAVA'] = sandbox;
|
|
79
|
+
if (language === 'RUST')
|
|
80
|
+
sandboxCache['RUST'] = sandbox;
|
|
40
81
|
}
|
|
41
|
-
return sandbox;
|
|
82
|
+
return runtimeAssets ? bindRuntimeAssets(sandbox, runtimeAssets) : sandbox;
|
|
42
83
|
}
|
|
84
|
+
export default playground;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
|
|
2
|
+
import { WorkerAssetBridge } from './assetBridge';
|
|
3
|
+
import { type PlaygroundRuntimeAssets } from './assets';
|
|
2
4
|
import type { Sandbox } from './sandbox';
|
|
3
5
|
declare class Java implements Sandbox {
|
|
4
6
|
output: any;
|
|
@@ -14,7 +16,10 @@ declare class Java implements Sandbox {
|
|
|
14
16
|
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
15
17
|
waitingForInput: boolean;
|
|
16
18
|
pendingEof: boolean;
|
|
17
|
-
|
|
19
|
+
assetBridge: WorkerAssetBridge | null;
|
|
20
|
+
load(runtimeAssets?: string | PlaygroundRuntimeAssets, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
|
|
21
|
+
set?: (value: number) => void;
|
|
22
|
+
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
18
23
|
write(input: string): void;
|
|
19
24
|
eof(): void;
|
|
20
25
|
private flushPendingInput;
|