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,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;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { CompilerDiagnostic, DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
|
+
import type { PlaygroundRuntimeAssets } from './assets';
|
|
2
3
|
import type { Writable } from 'svelte/store';
|
|
3
|
-
type
|
|
4
|
+
export type SandboxRuntimeAssets = string | PlaygroundRuntimeAssets;
|
|
5
|
+
export type SandboxProgress = Writable<number> | {
|
|
4
6
|
set?: (value: number) => void;
|
|
5
7
|
};
|
|
6
8
|
export interface Sandbox {
|
|
7
9
|
constructor: any;
|
|
8
10
|
eof: () => void;
|
|
9
|
-
load: (
|
|
10
|
-
run: (code: string, prepare: boolean, log?: boolean, prog?:
|
|
11
|
+
load: (runtimeAssets?: SandboxRuntimeAssets, code?: string, log?: boolean, args?: string[], options?: SandboxExecutionOptions, progress?: SandboxProgress) => Promise<void>;
|
|
12
|
+
run: (code: string, prepare: boolean, log?: boolean, prog?: SandboxProgress, args?: string[], options?: SandboxExecutionOptions) => Promise<boolean | string>;
|
|
11
13
|
terminate: () => void;
|
|
12
14
|
clear: () => Promise<void>;
|
|
13
15
|
kill?: () => void;
|
|
@@ -16,6 +18,7 @@ export interface Sandbox {
|
|
|
16
18
|
ondebug?: (event: DebugSessionEvent) => void;
|
|
17
19
|
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
18
20
|
debugCommand?: (command: DebugCommand) => void;
|
|
21
|
+
setBreakpoints?: (lines: number[]) => void;
|
|
19
22
|
debugEvaluate?: (expression: string) => Promise<string>;
|
|
20
23
|
image?: (data: {
|
|
21
24
|
mime: string;
|
|
@@ -24,4 +27,16 @@ export interface Sandbox {
|
|
|
24
27
|
}) => void;
|
|
25
28
|
elapse?: number;
|
|
26
29
|
}
|
|
27
|
-
export {
|
|
30
|
+
export interface BoundSandbox extends Omit<Sandbox, 'load'> {
|
|
31
|
+
load: (code?: string, log?: boolean, args?: string[], options?: SandboxExecutionOptions, progress?: SandboxProgress) => Promise<void>;
|
|
32
|
+
runtimeAssets: SandboxRuntimeAssets;
|
|
33
|
+
}
|
|
34
|
+
export interface PlaygroundTerminalProps {
|
|
35
|
+
playground: PlaygroundBinding;
|
|
36
|
+
runtimeAssets: SandboxRuntimeAssets;
|
|
37
|
+
}
|
|
38
|
+
export interface PlaygroundBinding {
|
|
39
|
+
runtimeAssets: SandboxRuntimeAssets;
|
|
40
|
+
terminalProps: PlaygroundTerminalProps;
|
|
41
|
+
load: (language: string) => Promise<BoundSandbox>;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const WASM_RUST_ASSET_VERSION = "2d36c8a3cd2cbbfd";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const WASM_RUST_ASSET_VERSION = "2d36c8a3cd2cbbfd";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface WorkerRuntimeAssetConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
useAssetBridge: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface LoadedWorkerAsset {
|
|
6
|
+
bytes: Uint8Array;
|
|
7
|
+
mimeType?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function configureWorkerRuntimeAssets(config: WorkerRuntimeAssetConfig | null): void;
|
|
10
|
+
export declare function handleWorkerAssetMessage(data: any): boolean;
|
|
11
|
+
export declare function loadWorkerRuntimeAsset(asset: string): Promise<LoadedWorkerAsset>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const decoder = new TextDecoder();
|
|
2
|
+
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
3
|
+
const NativeXMLHttpRequest = globalThis.XMLHttpRequest;
|
|
4
|
+
let activeConfig = null;
|
|
5
|
+
let interceptorsInstalled = false;
|
|
6
|
+
let nextAssetRequestId = 0;
|
|
7
|
+
const pendingAssetRequests = new Map();
|
|
8
|
+
const responseBuffer = (bytes) => {
|
|
9
|
+
const buffer = bytes.buffer;
|
|
10
|
+
return bytes.byteOffset === 0 && bytes.byteLength === buffer.byteLength
|
|
11
|
+
? buffer
|
|
12
|
+
: buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
13
|
+
};
|
|
14
|
+
const trackedAssetUrl = (input) => {
|
|
15
|
+
if (!activeConfig)
|
|
16
|
+
return null;
|
|
17
|
+
if (typeof input === 'string')
|
|
18
|
+
return new URL(input, activeConfig.baseUrl).href;
|
|
19
|
+
if (input instanceof URL)
|
|
20
|
+
return input.href;
|
|
21
|
+
return input.url;
|
|
22
|
+
};
|
|
23
|
+
const trackedAssetName = (url) => {
|
|
24
|
+
if (!activeConfig || !url.startsWith(activeConfig.baseUrl))
|
|
25
|
+
return null;
|
|
26
|
+
return url.slice(activeConfig.baseUrl.length);
|
|
27
|
+
};
|
|
28
|
+
const isTrackedAssetUrl = (url) => trackedAssetName(url) !== null;
|
|
29
|
+
const loadAssetFromBridge = async (asset) => {
|
|
30
|
+
const id = ++nextAssetRequestId;
|
|
31
|
+
return await new Promise((resolve, reject) => {
|
|
32
|
+
pendingAssetRequests.set(id, { resolve, reject });
|
|
33
|
+
self.postMessage({
|
|
34
|
+
assetRequest: {
|
|
35
|
+
id,
|
|
36
|
+
asset
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const loadAssetFromUrl = async (url, asset) => {
|
|
42
|
+
const response = await originalFetch(url);
|
|
43
|
+
if (!response.ok)
|
|
44
|
+
throw new Error(`Failed to load ${asset}: ${response.status}`);
|
|
45
|
+
const total = Number(response.headers.get('content-length') || 0) || undefined;
|
|
46
|
+
const mimeType = response.headers.get('content-type') || undefined;
|
|
47
|
+
if (!response.body) {
|
|
48
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
49
|
+
self.postMessage({
|
|
50
|
+
assetProgress: {
|
|
51
|
+
asset,
|
|
52
|
+
loaded: bytes.byteLength,
|
|
53
|
+
total: total ?? bytes.byteLength
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return { bytes, mimeType };
|
|
57
|
+
}
|
|
58
|
+
const reader = response.body.getReader();
|
|
59
|
+
let receivedLength = 0;
|
|
60
|
+
const chunks = [];
|
|
61
|
+
while (true) {
|
|
62
|
+
const { done, value } = await reader.read();
|
|
63
|
+
if (done)
|
|
64
|
+
break;
|
|
65
|
+
if (!value)
|
|
66
|
+
continue;
|
|
67
|
+
const chunk = Uint8Array.from(value);
|
|
68
|
+
chunks.push(chunk);
|
|
69
|
+
receivedLength += chunk.byteLength;
|
|
70
|
+
self.postMessage({
|
|
71
|
+
assetProgress: {
|
|
72
|
+
asset,
|
|
73
|
+
loaded: receivedLength,
|
|
74
|
+
total
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const bytes = new Uint8Array(receivedLength);
|
|
79
|
+
let position = 0;
|
|
80
|
+
for (const chunk of chunks) {
|
|
81
|
+
bytes.set(chunk, position);
|
|
82
|
+
position += chunk.byteLength;
|
|
83
|
+
}
|
|
84
|
+
self.postMessage({
|
|
85
|
+
assetProgress: {
|
|
86
|
+
asset,
|
|
87
|
+
loaded: receivedLength,
|
|
88
|
+
total: total ?? receivedLength
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return { bytes, mimeType };
|
|
92
|
+
};
|
|
93
|
+
async function loadTrackedAsset(url) {
|
|
94
|
+
const asset = trackedAssetName(url);
|
|
95
|
+
if (!asset || !activeConfig)
|
|
96
|
+
throw new Error('Untracked runtime asset request');
|
|
97
|
+
return activeConfig.useAssetBridge
|
|
98
|
+
? await loadAssetFromBridge(asset)
|
|
99
|
+
: await loadAssetFromUrl(url, asset);
|
|
100
|
+
}
|
|
101
|
+
function createTrackedResponse(asset) {
|
|
102
|
+
return new Response(responseBuffer(asset.bytes), {
|
|
103
|
+
status: 200,
|
|
104
|
+
headers: asset.mimeType ? { 'Content-Type': asset.mimeType } : undefined
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function installTrackedFetch() {
|
|
108
|
+
if (typeof NativeXMLHttpRequest === 'undefined')
|
|
109
|
+
return;
|
|
110
|
+
class RuntimeAssetXMLHttpRequest {
|
|
111
|
+
responseType = '';
|
|
112
|
+
response = null;
|
|
113
|
+
responseText = '';
|
|
114
|
+
readyState = 0;
|
|
115
|
+
status = 0;
|
|
116
|
+
statusText = '';
|
|
117
|
+
timeout = 0;
|
|
118
|
+
withCredentials = false;
|
|
119
|
+
onload = null;
|
|
120
|
+
onerror = null;
|
|
121
|
+
onprogress = null;
|
|
122
|
+
onreadystatechange = null;
|
|
123
|
+
native = null;
|
|
124
|
+
url = '';
|
|
125
|
+
open(method, url) {
|
|
126
|
+
const resolvedUrl = trackedAssetUrl(url);
|
|
127
|
+
if (!resolvedUrl || !isTrackedAssetUrl(resolvedUrl)) {
|
|
128
|
+
const nativeUrl = resolvedUrl || (url instanceof URL ? url.href : String(url));
|
|
129
|
+
this.native = new NativeXMLHttpRequest();
|
|
130
|
+
this.native.responseType = this.responseType;
|
|
131
|
+
this.native.timeout = this.timeout;
|
|
132
|
+
this.native.withCredentials = this.withCredentials;
|
|
133
|
+
this.native.onload = (event) => {
|
|
134
|
+
this.response = this.native?.response;
|
|
135
|
+
this.responseText = this.native?.responseText || '';
|
|
136
|
+
this.readyState = this.native?.readyState || 0;
|
|
137
|
+
this.status = this.native?.status || 0;
|
|
138
|
+
this.statusText = this.native?.statusText || '';
|
|
139
|
+
this.onreadystatechange?.call(this, event);
|
|
140
|
+
this.onload?.call(this, event);
|
|
141
|
+
};
|
|
142
|
+
this.native.onerror = (event) => {
|
|
143
|
+
this.readyState = this.native?.readyState || 4;
|
|
144
|
+
this.status = this.native?.status || 0;
|
|
145
|
+
this.statusText = this.native?.statusText || '';
|
|
146
|
+
this.onreadystatechange?.call(this, event);
|
|
147
|
+
this.onerror?.call(this, event);
|
|
148
|
+
};
|
|
149
|
+
this.native.onprogress = (event) => {
|
|
150
|
+
this.onprogress?.call(this, event);
|
|
151
|
+
};
|
|
152
|
+
this.native.onreadystatechange = (event) => {
|
|
153
|
+
this.readyState = this.native?.readyState || 0;
|
|
154
|
+
this.onreadystatechange?.call(this, event);
|
|
155
|
+
};
|
|
156
|
+
this.native.open(method, nativeUrl);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.url = resolvedUrl;
|
|
160
|
+
this.readyState = 1;
|
|
161
|
+
this.onreadystatechange?.call(this, new ProgressEvent('readystatechange'));
|
|
162
|
+
}
|
|
163
|
+
setRequestHeader(name, value) {
|
|
164
|
+
this.native?.setRequestHeader(name, value);
|
|
165
|
+
}
|
|
166
|
+
async send(body) {
|
|
167
|
+
if (this.native) {
|
|
168
|
+
this.native.send(body);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const loaded = await loadTrackedAsset(this.url);
|
|
173
|
+
const buffer = responseBuffer(loaded.bytes);
|
|
174
|
+
this.status = 200;
|
|
175
|
+
this.statusText = 'OK';
|
|
176
|
+
this.readyState = 4;
|
|
177
|
+
if (this.responseType === 'arraybuffer') {
|
|
178
|
+
this.response = buffer;
|
|
179
|
+
}
|
|
180
|
+
else if (this.responseType === 'blob') {
|
|
181
|
+
this.response = new Blob([buffer], {
|
|
182
|
+
type: loaded.mimeType || 'application/octet-stream'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const text = decoder.decode(loaded.bytes);
|
|
187
|
+
this.responseText = text;
|
|
188
|
+
this.response = text;
|
|
189
|
+
}
|
|
190
|
+
const progressEvent = new ProgressEvent('progress', {
|
|
191
|
+
lengthComputable: true,
|
|
192
|
+
loaded: loaded.bytes.byteLength,
|
|
193
|
+
total: loaded.bytes.byteLength
|
|
194
|
+
});
|
|
195
|
+
this.onprogress?.call(this, progressEvent);
|
|
196
|
+
this.onreadystatechange?.call(this, new ProgressEvent('readystatechange'));
|
|
197
|
+
this.onload?.call(this, new ProgressEvent('load'));
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.readyState = 4;
|
|
201
|
+
this.status = 0;
|
|
202
|
+
this.statusText = error instanceof Error ? error.message : String(error);
|
|
203
|
+
this.onreadystatechange?.call(this, new ProgressEvent('readystatechange'));
|
|
204
|
+
this.onerror?.call(this, new ProgressEvent('error'));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
abort() {
|
|
208
|
+
this.native?.abort();
|
|
209
|
+
}
|
|
210
|
+
getAllResponseHeaders() {
|
|
211
|
+
return this.native?.getAllResponseHeaders() || '';
|
|
212
|
+
}
|
|
213
|
+
getResponseHeader(name) {
|
|
214
|
+
return this.native?.getResponseHeader(name) || null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
globalThis.XMLHttpRequest = RuntimeAssetXMLHttpRequest;
|
|
218
|
+
}
|
|
219
|
+
function installRuntimeAssetInterceptors() {
|
|
220
|
+
if (interceptorsInstalled)
|
|
221
|
+
return;
|
|
222
|
+
interceptorsInstalled = true;
|
|
223
|
+
globalThis.fetch = (async (input, init) => {
|
|
224
|
+
const resolvedUrl = trackedAssetUrl(input);
|
|
225
|
+
if (!resolvedUrl || !isTrackedAssetUrl(resolvedUrl))
|
|
226
|
+
return originalFetch(input, init);
|
|
227
|
+
return createTrackedResponse(await loadTrackedAsset(resolvedUrl));
|
|
228
|
+
});
|
|
229
|
+
installTrackedFetch();
|
|
230
|
+
}
|
|
231
|
+
export function configureWorkerRuntimeAssets(config) {
|
|
232
|
+
activeConfig = config;
|
|
233
|
+
installRuntimeAssetInterceptors();
|
|
234
|
+
}
|
|
235
|
+
export function handleWorkerAssetMessage(data) {
|
|
236
|
+
const response = data?.assetResponse;
|
|
237
|
+
if (!response)
|
|
238
|
+
return false;
|
|
239
|
+
const pending = pendingAssetRequests.get(response.id);
|
|
240
|
+
if (!pending)
|
|
241
|
+
return true;
|
|
242
|
+
pendingAssetRequests.delete(response.id);
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
pending.reject(new Error(response.error || 'Runtime asset request failed'));
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
pending.resolve({
|
|
248
|
+
bytes: new Uint8Array(response.bytes),
|
|
249
|
+
mimeType: response.mimeType || undefined
|
|
250
|
+
});
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
export async function loadWorkerRuntimeAsset(asset) {
|
|
254
|
+
if (!activeConfig)
|
|
255
|
+
throw new Error('Runtime asset config unavailable');
|
|
256
|
+
return await loadTrackedAsset(new URL(asset, activeConfig.baseUrl).href);
|
|
257
|
+
}
|
|
@@ -4,7 +4,7 @@ self.document = {
|
|
|
4
4
|
return [];
|
|
5
5
|
}
|
|
6
6
|
};
|
|
7
|
-
let stdinBufferClang, debugBufferClang, interruptBufferClang, clang;
|
|
7
|
+
let stdinBufferClang, debugBufferClang, watchBufferClang, watchResultBufferClang, interruptBufferClang, clang;
|
|
8
8
|
async function loadClang(path, log) {
|
|
9
9
|
const { default: Clang } = await import('../../clang');
|
|
10
10
|
clang = new Clang({
|
|
@@ -17,7 +17,7 @@ async function loadClang(path, log) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
self.onmessage = async (event) => {
|
|
20
|
-
const { code, buffer, debugBuffer, load, interrupt, log, path, prepare,
|
|
20
|
+
const { code, buffer, debugBuffer, watchBuffer, watchResultBuffer, load, interrupt, log, path, prepare, language, compileArgs, programArgs, cppVersion, cVersion, debug, breakpoints, pauseOnEntry } = event.data;
|
|
21
21
|
if (load) {
|
|
22
22
|
await loadClang(path, log);
|
|
23
23
|
postMessage({ load: true });
|
|
@@ -25,15 +25,23 @@ self.onmessage = async (event) => {
|
|
|
25
25
|
else if (prepare) {
|
|
26
26
|
stdinBufferClang = new Int32Array(buffer);
|
|
27
27
|
debugBufferClang = new Int32Array(debugBuffer);
|
|
28
|
+
watchBufferClang = new Int32Array(watchBuffer);
|
|
29
|
+
watchResultBufferClang = new Int32Array(watchResultBuffer);
|
|
28
30
|
interruptBufferClang = new Uint8Array(interrupt);
|
|
29
31
|
try {
|
|
30
32
|
await clang.compileLink(code, {
|
|
31
|
-
|
|
33
|
+
language,
|
|
34
|
+
compileArgs,
|
|
35
|
+
programArgs,
|
|
36
|
+
cppVersion,
|
|
37
|
+
cVersion,
|
|
32
38
|
debug,
|
|
33
39
|
breakpoints,
|
|
34
40
|
pauseOnEntry,
|
|
35
41
|
debugBuffer: debugBufferClang,
|
|
36
|
-
interruptBuffer: interruptBufferClang
|
|
42
|
+
interruptBuffer: interruptBufferClang,
|
|
43
|
+
watchBuffer: watchBufferClang,
|
|
44
|
+
watchResultBuffer: watchResultBufferClang
|
|
37
45
|
});
|
|
38
46
|
self.postMessage({ results: true });
|
|
39
47
|
}
|
|
@@ -45,15 +53,23 @@ self.onmessage = async (event) => {
|
|
|
45
53
|
clang.log = log;
|
|
46
54
|
stdinBufferClang = new Int32Array(buffer);
|
|
47
55
|
debugBufferClang = new Int32Array(debugBuffer);
|
|
56
|
+
watchBufferClang = new Int32Array(watchBuffer);
|
|
57
|
+
watchResultBufferClang = new Int32Array(watchResultBuffer);
|
|
48
58
|
interruptBufferClang = new Uint8Array(interrupt);
|
|
49
59
|
try {
|
|
50
60
|
await clang.compileLinkRun(code, {
|
|
51
|
-
|
|
61
|
+
language,
|
|
62
|
+
compileArgs,
|
|
63
|
+
programArgs,
|
|
64
|
+
cppVersion,
|
|
65
|
+
cVersion,
|
|
52
66
|
debug,
|
|
53
67
|
breakpoints,
|
|
54
68
|
pauseOnEntry,
|
|
55
69
|
debugBuffer: debugBufferClang,
|
|
56
|
-
interruptBuffer: interruptBufferClang
|
|
70
|
+
interruptBuffer: interruptBufferClang,
|
|
71
|
+
watchBuffer: watchBufferClang,
|
|
72
|
+
watchResultBuffer: watchResultBufferClang
|
|
57
73
|
});
|
|
58
74
|
self.postMessage({ results: true });
|
|
59
75
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { prepareJavaStdinInjection } from '../javaStdin';
|
|
2
2
|
import { waitForBufferedStdin } from '../stdinBuffer';
|
|
3
|
-
import {
|
|
3
|
+
import { configureWorkerRuntimeAssets, handleWorkerAssetMessage, loadWorkerRuntimeAsset } from './assets';
|
|
4
4
|
let compilerLib = null;
|
|
5
5
|
let compiler = null;
|
|
6
6
|
let runtimeLoad = null;
|
|
@@ -15,18 +15,8 @@ let compiledStdin = '';
|
|
|
15
15
|
let compiledMainClass = '';
|
|
16
16
|
let compiledWasm = null;
|
|
17
17
|
let currentSourcePath = '';
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
if (!isGzip(bytes))
|
|
21
|
-
return bytes;
|
|
22
|
-
if (typeof DecompressionStream === 'undefined') {
|
|
23
|
-
throw new Error('Generated WebAssembly is gzip-compressed, but DecompressionStream is unavailable');
|
|
24
|
-
}
|
|
25
|
-
const stream = new Blob([Uint8Array.from(bytes)])
|
|
26
|
-
.stream()
|
|
27
|
-
.pipeThrough(new DecompressionStream('gzip'));
|
|
28
|
-
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29
|
-
};
|
|
18
|
+
const decoder = new TextDecoder();
|
|
19
|
+
const toInt8Array = (bytes) => new Int8Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
30
20
|
const flushStdout = () => {
|
|
31
21
|
if (!stdoutBuffer)
|
|
32
22
|
return;
|
|
@@ -50,41 +40,31 @@ const pushStderr = (charCode) => {
|
|
|
50
40
|
flushStderr();
|
|
51
41
|
};
|
|
52
42
|
self.addEventListener('message', async (event) => {
|
|
53
|
-
|
|
43
|
+
if (handleWorkerAssetMessage(event.data))
|
|
44
|
+
return;
|
|
45
|
+
const { load, assets, buffer, code, prepare, args = [], stdin = '' } = event.data;
|
|
54
46
|
try {
|
|
55
47
|
if (load) {
|
|
48
|
+
const runtimeAssets = assets;
|
|
49
|
+
configureWorkerRuntimeAssets(runtimeAssets || null);
|
|
50
|
+
const baseUrl = runtimeAssets?.baseUrl || '';
|
|
56
51
|
if (!compiler || loadedBaseUrl !== baseUrl) {
|
|
57
52
|
loadedBaseUrl = baseUrl;
|
|
58
|
-
const runtimeSource = await
|
|
59
|
-
if (!response.ok)
|
|
60
|
-
throw new Error(`Failed to load TeaVM compiler runtime: ${response.status}`);
|
|
61
|
-
return response.text();
|
|
62
|
-
});
|
|
53
|
+
const runtimeSource = decoder.decode((await loadWorkerRuntimeAsset('compiler.wasm-runtime.js')).bytes);
|
|
63
54
|
const runtimeUrl = URL.createObjectURL(new Blob([runtimeSource], { type: 'text/javascript;charset=utf-8' }));
|
|
64
55
|
const runtimeModule = await import(/* @vite-ignore */ runtimeUrl);
|
|
56
|
+
URL.revokeObjectURL(runtimeUrl);
|
|
65
57
|
const runtimeLoadFn = runtimeModule.load;
|
|
66
58
|
runtimeLoad = runtimeLoadFn;
|
|
67
|
-
const compilerWasm = await
|
|
68
|
-
if (!response.ok)
|
|
69
|
-
throw new Error(`Failed to load TeaVM compiler wasm: ${response.status}`);
|
|
70
|
-
return await maybeDecompressWasm(new Uint8Array(await response.arrayBuffer()));
|
|
71
|
-
});
|
|
59
|
+
const compilerWasm = (await loadWorkerRuntimeAsset('compiler.wasm')).bytes;
|
|
72
60
|
const compilerModule = await runtimeLoadFn(compilerWasm, {
|
|
73
61
|
stackDeobfuscator: { enabled: false }
|
|
74
62
|
});
|
|
75
63
|
compilerLib = compilerModule.exports;
|
|
76
64
|
compiler = compilerLib.createCompiler();
|
|
77
65
|
const [sdk, runtimeClasslib] = await Promise.all([
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
throw new Error(`Failed to load TeaVM SDK: ${response.status}`);
|
|
81
|
-
return new Int8Array(await response.arrayBuffer());
|
|
82
|
-
}),
|
|
83
|
-
fetch(resolveTeaVmAssetUrl(baseUrl, 'runtime-classlib-teavm.bin')).then(async (response) => {
|
|
84
|
-
if (!response.ok)
|
|
85
|
-
throw new Error(`Failed to load TeaVM runtime classlib: ${response.status}`);
|
|
86
|
-
return new Int8Array(await response.arrayBuffer());
|
|
87
|
-
})
|
|
66
|
+
loadWorkerRuntimeAsset('compile-classlib-teavm.bin').then(({ bytes }) => toInt8Array(bytes)),
|
|
67
|
+
loadWorkerRuntimeAsset('runtime-classlib-teavm.bin').then(({ bytes }) => toInt8Array(bytes))
|
|
88
68
|
]);
|
|
89
69
|
compiler.setSdk(sdk);
|
|
90
70
|
compiler.setTeaVMClasslib(runtimeClasslib);
|
|
@@ -185,7 +165,7 @@ self.addEventListener('message', async (event) => {
|
|
|
185
165
|
compiledCode = code;
|
|
186
166
|
compiledStdin = stdinInjection.stdinCacheKey;
|
|
187
167
|
compiledMainClass = mainClass;
|
|
188
|
-
compiledWasm =
|
|
168
|
+
compiledWasm = new Uint8Array(compiler.getWebAssemblyOutputFile('app.wasm'));
|
|
189
169
|
}
|
|
190
170
|
if (prepare) {
|
|
191
171
|
self.postMessage({ results: true });
|