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.
Files changed (49) hide show
  1. package/README.md +111 -2
  2. package/dist/clang/app.d.ts +4 -0
  3. package/dist/clang/app.js +330 -15
  4. package/dist/clang/index.d.ts +10 -0
  5. package/dist/clang/index.js +284 -25
  6. package/dist/debug/controller.svelte.d.ts +43 -0
  7. package/dist/debug/controller.svelte.js +268 -0
  8. package/dist/debug/editor/index.d.ts +2 -1
  9. package/dist/debug/editor/index.js +1 -1
  10. package/dist/debug/editor/monaco.d.ts +8 -0
  11. package/dist/debug/editor/monaco.js +23 -0
  12. package/dist/debug/expression.d.ts +24 -0
  13. package/dist/debug/expression.js +307 -138
  14. package/dist/debug/index.d.ts +4 -1
  15. package/dist/debug/index.js +2 -1
  16. package/dist/index.d.ts +6 -3
  17. package/dist/index.js +3 -3
  18. package/dist/playground/assetBridge.d.ts +20 -0
  19. package/dist/playground/assetBridge.js +192 -0
  20. package/dist/playground/assets.d.ts +37 -0
  21. package/dist/playground/assets.js +105 -0
  22. package/dist/playground/clang.d.ts +11 -1
  23. package/dist/playground/clang.js +58 -4
  24. package/dist/playground/index.d.ts +5 -5
  25. package/dist/playground/index.js +48 -6
  26. package/dist/playground/java.d.ts +7 -2
  27. package/dist/playground/java.js +31 -5
  28. package/dist/playground/javaStdin.js +235 -2
  29. package/dist/playground/options.d.ts +18 -0
  30. package/dist/playground/options.js +15 -1
  31. package/dist/playground/python.d.ts +7 -1
  32. package/dist/playground/python.js +50 -5
  33. package/dist/playground/rust.d.ts +32 -0
  34. package/dist/playground/rust.js +175 -0
  35. package/dist/playground/sandbox.d.ts +19 -4
  36. package/dist/playground/wasmRustVersion.d.ts +1 -0
  37. package/dist/playground/wasmRustVersion.js +1 -0
  38. package/dist/playground/worker/assets.d.ts +12 -0
  39. package/dist/playground/worker/assets.js +257 -0
  40. package/dist/playground/worker/clang.js +22 -6
  41. package/dist/playground/worker/java.js +15 -35
  42. package/dist/playground/worker/python.js +13 -33
  43. package/dist/playground/worker/rust.d.ts +1 -0
  44. package/dist/playground/worker/rust.js +159 -0
  45. package/dist/terminal/Terminal.svelte +121 -18
  46. package/dist/terminal/Terminal.svelte.d.ts +4 -0
  47. package/dist/terminal/theme.js +2 -2
  48. package/dist/terminal/types.d.ts +4 -0
  49. 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
- load(path: string, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
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>;
@@ -1,11 +1,16 @@
1
- import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
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 * 4);
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
- load(path, code = '', log = true, args = [], _options = {}) {
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
- args,
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 Clang from './clang';
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 default function load(language: string): Promise<Sandbox | Clang | Java | Python>;
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;
@@ -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
- export default async function load(language) {
7
- if (sandboxCache[language])
8
- return sandboxCache[language];
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 { CompilerDiagnostic, SandboxExecutionOptions } from './options';
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
- load(path: string, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
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;