wasm-idle 0.3.1 → 0.4.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 +4 -2
- package/dist/clang/app.d.ts +4 -1
- package/dist/clang/app.js +159 -117
- package/dist/clang/index.d.ts +1 -0
- package/dist/clang/index.js +80 -2
- package/dist/debug/expression.js +135 -19
- package/dist/debug/index.d.ts +1 -1
- package/dist/debug/index.js +1 -1
- package/dist/debug/language/index.d.ts +1 -0
- package/dist/debug/language/index.js +1 -0
- package/dist/debug/language/python.d.ts +2 -0
- package/dist/debug/language/python.js +14 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/playground/clang.d.ts +5 -2
- package/dist/playground/clang.js +44 -18
- package/dist/playground/index.d.ts +2 -1
- package/dist/playground/index.js +7 -1
- package/dist/playground/java.d.ts +28 -0
- package/dist/playground/java.js +135 -0
- package/dist/playground/javaStdin.d.ts +8 -0
- package/dist/playground/javaStdin.js +90 -0
- package/dist/playground/options.d.ts +10 -0
- package/dist/playground/python.d.ts +15 -5
- package/dist/playground/python.js +98 -21
- package/dist/playground/sandbox.d.ts +3 -1
- package/dist/playground/stdinBuffer.d.ts +7 -0
- package/dist/playground/stdinBuffer.js +100 -0
- package/dist/playground/teavmConfig.d.ts +3 -0
- package/dist/playground/teavmConfig.js +14 -0
- package/dist/playground/worker/clang.js +2 -20
- package/dist/playground/worker/java.d.ts +1 -0
- package/dist/playground/worker/java.js +248 -0
- package/dist/playground/worker/python.js +253 -27
- package/dist/terminal/Terminal.svelte +41 -11
- package/dist/terminal/Terminal.svelte.d.ts +2 -1
- package/dist/terminal/types.d.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const JAVA_STDIN_HELPER_CLASS = 'WasmIdleStdin';
|
|
2
|
+
export const prepareJavaStdinInjection = (code, stdin) => {
|
|
3
|
+
const usesStdin = code.includes('System.in');
|
|
4
|
+
if (!usesStdin) {
|
|
5
|
+
return {
|
|
6
|
+
usesStdin: false,
|
|
7
|
+
stdinCacheKey: '',
|
|
8
|
+
transformedCode: code,
|
|
9
|
+
helperSourcePath: null,
|
|
10
|
+
helperSource: null
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const packageMatch = code.match(/^\s*package\s+([A-Za-z_][\w.]*)\s*;/m);
|
|
14
|
+
const packageName = packageMatch?.[1] || '';
|
|
15
|
+
const helperSourcePath = packageName
|
|
16
|
+
? `${packageName.replaceAll('.', '/')}/${JAVA_STDIN_HELPER_CLASS}.java`
|
|
17
|
+
: `${JAVA_STDIN_HELPER_CLASS}.java`;
|
|
18
|
+
const transformedCode = code.replaceAll('System.in', `${JAVA_STDIN_HELPER_CLASS}.open()`);
|
|
19
|
+
const encodedBytes = [...new TextEncoder().encode(stdin)].join(', ');
|
|
20
|
+
return {
|
|
21
|
+
usesStdin: true,
|
|
22
|
+
stdinCacheKey: stdin,
|
|
23
|
+
transformedCode,
|
|
24
|
+
helperSourcePath,
|
|
25
|
+
helperSource: `${packageName ? `package ${packageName};\n\n` : ''}import java.io.InputStream;
|
|
26
|
+
import org.teavm.jso.JSObject;
|
|
27
|
+
import org.teavm.jso.browser.Window;
|
|
28
|
+
import org.teavm.jso.core.JSFunction;
|
|
29
|
+
import org.teavm.jso.core.JSMapLike;
|
|
30
|
+
|
|
31
|
+
final class ${JAVA_STDIN_HELPER_CLASS} extends InputStream {
|
|
32
|
+
private static final byte[] INITIAL_DATA = new byte[] { ${encodedBytes} };
|
|
33
|
+
private static final ${JAVA_STDIN_HELPER_CLASS} INSTANCE = new ${JAVA_STDIN_HELPER_CLASS}();
|
|
34
|
+
private int position = 0;
|
|
35
|
+
|
|
36
|
+
private ${JAVA_STDIN_HELPER_CLASS}() {
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static InputStream open() {
|
|
40
|
+
return INSTANCE;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private int readFromHost() {
|
|
44
|
+
Window current = Window.current();
|
|
45
|
+
if (current == null) {
|
|
46
|
+
return -1;
|
|
47
|
+
}
|
|
48
|
+
JSMapLike<JSObject> globals = current.cast();
|
|
49
|
+
JSObject stdin = globals.get("wasmIdleJavaStdin");
|
|
50
|
+
if (stdin == null) {
|
|
51
|
+
return -1;
|
|
52
|
+
}
|
|
53
|
+
JSFunction readByte = stdin.<JSMapLike<JSObject>>cast().get("readByte").cast();
|
|
54
|
+
Object value = readByte.call(stdin);
|
|
55
|
+
return value != null ? Integer.parseInt(value.toString()) : -1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Override
|
|
59
|
+
public int read(byte[] b, int off, int len) {
|
|
60
|
+
if (b == null) {
|
|
61
|
+
throw new NullPointerException();
|
|
62
|
+
}
|
|
63
|
+
if (off < 0 || len < 0 || len > b.length - off) {
|
|
64
|
+
throw new IndexOutOfBoundsException();
|
|
65
|
+
}
|
|
66
|
+
if (len == 0) {
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
if (position < INITIAL_DATA.length) {
|
|
70
|
+
int count = Math.min(len, INITIAL_DATA.length - position);
|
|
71
|
+
System.arraycopy(INITIAL_DATA, position, b, off, count);
|
|
72
|
+
position += count;
|
|
73
|
+
return count;
|
|
74
|
+
}
|
|
75
|
+
int next = readFromHost();
|
|
76
|
+
if (next == -1) {
|
|
77
|
+
return -1;
|
|
78
|
+
}
|
|
79
|
+
b[off] = (byte) next;
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Override
|
|
84
|
+
public int read() {
|
|
85
|
+
return position < INITIAL_DATA.length ? INITIAL_DATA[position++] & 0xff : readFromHost();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
`
|
|
89
|
+
};
|
|
90
|
+
};
|
|
@@ -4,6 +4,15 @@ export interface DebugVariable {
|
|
|
4
4
|
name: string;
|
|
5
5
|
value: string;
|
|
6
6
|
}
|
|
7
|
+
export type CompilerDiagnosticSeverity = 'error' | 'warning' | 'other';
|
|
8
|
+
export interface CompilerDiagnostic {
|
|
9
|
+
fileName?: string | null;
|
|
10
|
+
lineNumber: number;
|
|
11
|
+
columnNumber?: number;
|
|
12
|
+
endColumnNumber?: number;
|
|
13
|
+
severity: CompilerDiagnosticSeverity;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
7
16
|
export type DebugVariableKind = 'number' | 'bool' | 'array' | 'text';
|
|
8
17
|
export type DebugArrayElementKind = 'int' | 'float' | 'double' | 'bool' | 'char';
|
|
9
18
|
export interface DebugVariableMetadata {
|
|
@@ -36,4 +45,5 @@ export interface SandboxExecutionOptions {
|
|
|
36
45
|
debug?: boolean;
|
|
37
46
|
breakpoints?: number[];
|
|
38
47
|
pauseOnEntry?: boolean;
|
|
48
|
+
stdin?: string;
|
|
39
49
|
}
|
|
@@ -1,27 +1,37 @@
|
|
|
1
|
+
import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
1
2
|
import type { Sandbox } from './sandbox';
|
|
2
|
-
import type { SandboxExecutionOptions } from './options';
|
|
3
3
|
declare class Python implements Sandbox {
|
|
4
4
|
ts: number;
|
|
5
5
|
output: any;
|
|
6
|
+
ondebug?: (event: DebugSessionEvent) => void;
|
|
6
7
|
image?: (data: {
|
|
7
8
|
mime: string;
|
|
8
9
|
b64: string;
|
|
9
10
|
ts?: number;
|
|
10
11
|
}) => void;
|
|
11
|
-
worker
|
|
12
|
+
worker?: Worker;
|
|
12
13
|
buffer: SharedArrayBuffer;
|
|
14
|
+
debugBuffer: SharedArrayBuffer;
|
|
15
|
+
watchBuffer: SharedArrayBuffer;
|
|
16
|
+
watchResultBuffer: SharedArrayBuffer;
|
|
13
17
|
interruptBuffer: SharedArrayBuffer;
|
|
14
|
-
|
|
18
|
+
pendingInput: string[];
|
|
15
19
|
begin: number;
|
|
16
20
|
elapse: number;
|
|
17
21
|
uid: number;
|
|
22
|
+
waitingForInput: boolean;
|
|
23
|
+
pendingEof: boolean;
|
|
24
|
+
exit: boolean;
|
|
18
25
|
load(path: string, code?: string, log?: boolean, _args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
|
|
19
26
|
write(input: string): void;
|
|
20
|
-
_write(input: string): void;
|
|
21
27
|
eof(): void;
|
|
28
|
+
private flushPendingInput;
|
|
22
29
|
run(code: string, prepare: boolean, _log?: boolean, _prog?: {
|
|
23
30
|
set?: (value: number) => void;
|
|
24
|
-
} | import('svelte/store').Writable<number>, _args?: string[],
|
|
31
|
+
} | import('svelte/store').Writable<number>, _args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
32
|
+
debugCommand(command: DebugCommand): void;
|
|
33
|
+
debugEvaluate(expression: string): Promise<string>;
|
|
34
|
+
kill(): void;
|
|
25
35
|
terminate(): void;
|
|
26
36
|
clear(): Promise<void>;
|
|
27
37
|
}
|
|
@@ -1,17 +1,27 @@
|
|
|
1
|
+
import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
|
|
1
2
|
class Python {
|
|
2
3
|
ts = Date.now();
|
|
3
4
|
output = null;
|
|
5
|
+
ondebug;
|
|
4
6
|
image;
|
|
5
7
|
worker = null;
|
|
6
8
|
buffer = new SharedArrayBuffer(1024);
|
|
9
|
+
debugBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4);
|
|
10
|
+
watchBuffer = new SharedArrayBuffer(1024);
|
|
11
|
+
watchResultBuffer = new SharedArrayBuffer(1024);
|
|
7
12
|
interruptBuffer = new SharedArrayBuffer(1);
|
|
8
|
-
|
|
13
|
+
pendingInput = [];
|
|
9
14
|
begin = 0;
|
|
10
15
|
elapse = 0;
|
|
11
16
|
uid = 0;
|
|
17
|
+
waitingForInput = false;
|
|
18
|
+
pendingEof = false;
|
|
19
|
+
exit = true;
|
|
12
20
|
load(path, code = '', log = true, _args = [], _options = {}) {
|
|
13
21
|
return new Promise(async (resolve) => {
|
|
14
|
-
this.
|
|
22
|
+
this.pendingInput = [];
|
|
23
|
+
this.waitingForInput = false;
|
|
24
|
+
this.pendingEof = false;
|
|
15
25
|
if (!this.worker) {
|
|
16
26
|
this.worker = new (await import('./worker/python?worker')).default();
|
|
17
27
|
this.worker.onmessage = () => resolve();
|
|
@@ -24,40 +34,65 @@ class Python {
|
|
|
24
34
|
});
|
|
25
35
|
}
|
|
26
36
|
write(input) {
|
|
27
|
-
this.
|
|
37
|
+
this.pendingInput.push(input);
|
|
38
|
+
this.pendingEof = false;
|
|
39
|
+
this.flushPendingInput();
|
|
28
40
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
strInfo += ' ';
|
|
33
|
-
strInfo += padding;
|
|
34
|
-
const buffer = new Int32Array(this.buffer);
|
|
35
|
-
const enc = new TextEncoder();
|
|
36
|
-
const data = enc.encode(strInfo);
|
|
37
|
-
buffer.set(new Int32Array(data.buffer.slice(data.byteOffset), 0));
|
|
41
|
+
eof() {
|
|
42
|
+
this.pendingEof = true;
|
|
43
|
+
this.flushPendingInput();
|
|
38
44
|
}
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
flushPendingInput() {
|
|
46
|
+
if (!this.waitingForInput)
|
|
47
|
+
return;
|
|
48
|
+
if (flushQueuedStdin(this.pendingInput, this.buffer)) {
|
|
49
|
+
this.waitingForInput = false;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (this.pendingEof) {
|
|
53
|
+
flushBufferedEof(this.buffer);
|
|
54
|
+
this.pendingEof = false;
|
|
55
|
+
this.waitingForInput = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
run(code, prepare, _log = true, _prog, _args = [], options = {}) {
|
|
59
|
+
this.exit = false;
|
|
41
60
|
return new Promise(async (resolve, reject) => {
|
|
61
|
+
if (!this.worker)
|
|
62
|
+
return reject('Worker not loaded');
|
|
42
63
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
43
64
|
const handler = (event) => {
|
|
65
|
+
if (!this.worker)
|
|
66
|
+
return reject('Worker not loaded');
|
|
44
67
|
if (_uid !== this.uid)
|
|
45
68
|
return (this.worker.onmessage = null);
|
|
46
|
-
const {
|
|
47
|
-
if (buffer
|
|
48
|
-
this.
|
|
69
|
+
const { output, results, log, error, buffer, type, data: payload, debugEvent } = event.data;
|
|
70
|
+
if (buffer) {
|
|
71
|
+
this.waitingForInput = true;
|
|
72
|
+
this.flushPendingInput();
|
|
73
|
+
}
|
|
49
74
|
if (type === 'img' && payload)
|
|
50
75
|
this.image?.(payload);
|
|
51
76
|
if (output)
|
|
52
77
|
this.output(output);
|
|
78
|
+
if (debugEvent)
|
|
79
|
+
this.ondebug?.(debugEvent);
|
|
53
80
|
if (results) {
|
|
54
81
|
this.elapse = Date.now() - this.begin;
|
|
82
|
+
this.exit = true;
|
|
83
|
+
this.waitingForInput = false;
|
|
84
|
+
this.pendingEof = false;
|
|
85
|
+
this.ondebug?.({ type: 'stop' });
|
|
55
86
|
resolve(results);
|
|
56
87
|
}
|
|
57
88
|
if (log)
|
|
58
89
|
console.log(log);
|
|
59
90
|
if (error) {
|
|
60
91
|
this.elapse = Date.now() - this.begin;
|
|
92
|
+
this.exit = true;
|
|
93
|
+
this.waitingForInput = false;
|
|
94
|
+
this.pendingEof = false;
|
|
95
|
+
this.ondebug?.({ type: 'stop' });
|
|
61
96
|
reject(error);
|
|
62
97
|
}
|
|
63
98
|
};
|
|
@@ -68,22 +103,64 @@ class Python {
|
|
|
68
103
|
code,
|
|
69
104
|
prepare,
|
|
70
105
|
buffer: this.buffer,
|
|
106
|
+
debugBuffer: this.debugBuffer,
|
|
107
|
+
watchBuffer: this.watchBuffer,
|
|
108
|
+
watchResultBuffer: this.watchResultBuffer,
|
|
71
109
|
interrupt: this.interruptBuffer,
|
|
72
|
-
context: {}
|
|
110
|
+
context: {},
|
|
111
|
+
debug: !!options.debug,
|
|
112
|
+
breakpoints: [...(options.breakpoints || [])],
|
|
113
|
+
pauseOnEntry: !!options.pauseOnEntry
|
|
73
114
|
});
|
|
74
115
|
});
|
|
75
116
|
}
|
|
117
|
+
debugCommand(command) {
|
|
118
|
+
const control = new Int32Array(this.debugBuffer);
|
|
119
|
+
Atomics.store(control, 1, command === 'stepInto' ? 2 : command === 'nextLine' ? 3 : command === 'stepOut' ? 4 : 1);
|
|
120
|
+
Atomics.add(control, 0, 1);
|
|
121
|
+
Atomics.notify(control, 0);
|
|
122
|
+
this.ondebug?.({ type: 'resume', command });
|
|
123
|
+
}
|
|
124
|
+
async debugEvaluate(expression) {
|
|
125
|
+
if (!this.worker)
|
|
126
|
+
throw new Error('Worker not loaded');
|
|
127
|
+
resetBufferedStdin(this.watchResultBuffer);
|
|
128
|
+
const previousSequence = bufferedSequence(this.watchResultBuffer);
|
|
129
|
+
flushQueuedStdin([expression], this.watchBuffer);
|
|
130
|
+
const control = new Int32Array(this.debugBuffer);
|
|
131
|
+
Atomics.store(control, 1, 5);
|
|
132
|
+
Atomics.add(control, 0, 1);
|
|
133
|
+
Atomics.notify(control, 0);
|
|
134
|
+
return (await waitForBufferedSequenceChange(this.watchResultBuffer, previousSequence, 5000)) ?? '?';
|
|
135
|
+
}
|
|
136
|
+
kill() {
|
|
137
|
+
this.terminate();
|
|
138
|
+
}
|
|
76
139
|
terminate() {
|
|
140
|
+
this.pendingEof = false;
|
|
77
141
|
new Uint8Array(this.interruptBuffer)[0] = 2;
|
|
142
|
+
const control = new Int32Array(this.debugBuffer);
|
|
143
|
+
Atomics.add(control, 0, 1);
|
|
144
|
+
Atomics.notify(control, 0);
|
|
78
145
|
}
|
|
79
146
|
async clear() {
|
|
80
147
|
this.terminate();
|
|
81
|
-
this.
|
|
148
|
+
this.pendingInput = [];
|
|
149
|
+
this.waitingForInput = false;
|
|
150
|
+
this.pendingEof = false;
|
|
82
151
|
if (this.worker)
|
|
83
152
|
this.worker.onmessage = null;
|
|
84
|
-
|
|
85
|
-
|
|
153
|
+
resetBufferedStdin(this.buffer);
|
|
154
|
+
resetBufferedStdin(this.watchBuffer);
|
|
155
|
+
resetBufferedStdin(this.watchResultBuffer);
|
|
156
|
+
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
157
|
+
debugBuffer.fill(0);
|
|
86
158
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
159
|
+
if (!this.exit) {
|
|
160
|
+
this.worker?.terminate?.();
|
|
161
|
+
delete this.worker;
|
|
162
|
+
this.exit = true;
|
|
163
|
+
}
|
|
87
164
|
}
|
|
88
165
|
}
|
|
89
166
|
export default Python;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
1
|
+
import type { CompilerDiagnostic, DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
2
|
import type { Writable } from 'svelte/store';
|
|
3
3
|
type ProgressLike = Writable<number> | {
|
|
4
4
|
set?: (value: number) => void;
|
|
@@ -14,7 +14,9 @@ export interface Sandbox {
|
|
|
14
14
|
write?: (data: string) => void;
|
|
15
15
|
output?: (data: string) => void;
|
|
16
16
|
ondebug?: (event: DebugSessionEvent) => void;
|
|
17
|
+
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
17
18
|
debugCommand?: (command: DebugCommand) => void;
|
|
19
|
+
debugEvaluate?: (expression: string) => Promise<string>;
|
|
18
20
|
image?: (data: {
|
|
19
21
|
mime: string;
|
|
20
22
|
b64: string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const flushQueuedStdin: (queue: string[], buffer: SharedArrayBuffer | Int32Array) => boolean;
|
|
2
|
+
export declare const readBufferedStdin: (buffer: SharedArrayBuffer | Int32Array) => string | null;
|
|
3
|
+
export declare const bufferedSequence: (buffer: SharedArrayBuffer | Int32Array) => number;
|
|
4
|
+
export declare const waitForBufferedSequenceChange: (buffer: SharedArrayBuffer | Int32Array, sequence: number, timeoutMs?: number) => Promise<string | null>;
|
|
5
|
+
export declare const flushBufferedEof: (buffer: SharedArrayBuffer | Int32Array) => void;
|
|
6
|
+
export declare const waitForBufferedStdin: (buffer: Int32Array, requestInput: () => void) => string | null;
|
|
7
|
+
export declare const resetBufferedStdin: (buffer: SharedArrayBuffer | Int32Array) => void;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const SEQUENCE_INDEX = 0;
|
|
2
|
+
const LENGTH_INDEX = 1;
|
|
3
|
+
const HEADER_BYTES = Int32Array.BYTES_PER_ELEMENT * 2;
|
|
4
|
+
const EOF_LENGTH = -1;
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
const decoder = new TextDecoder();
|
|
7
|
+
const controlViewOf = (buffer) => buffer instanceof Int32Array ? buffer : new Int32Array(buffer);
|
|
8
|
+
const payloadViewOf = (control) => new Uint8Array(control.buffer, control.byteOffset + HEADER_BYTES, control.byteLength - HEADER_BYTES);
|
|
9
|
+
const splitChunk = (input, maxBytes) => {
|
|
10
|
+
const encoded = encoder.encode(input);
|
|
11
|
+
if (encoded.length <= maxBytes) {
|
|
12
|
+
return { chunk: input, bytes: encoded, rest: '' };
|
|
13
|
+
}
|
|
14
|
+
let left = 0;
|
|
15
|
+
let right = input.length;
|
|
16
|
+
while (left < right) {
|
|
17
|
+
const middle = Math.ceil((left + right) / 2);
|
|
18
|
+
if (encoder.encode(input.slice(0, middle)).length <= maxBytes) {
|
|
19
|
+
left = middle;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
right = middle - 1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const chunk = input.slice(0, left);
|
|
26
|
+
return {
|
|
27
|
+
chunk,
|
|
28
|
+
bytes: encoder.encode(chunk),
|
|
29
|
+
rest: input.slice(left)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export const flushQueuedStdin = (queue, buffer) => {
|
|
33
|
+
if (!queue.length)
|
|
34
|
+
return false;
|
|
35
|
+
const control = controlViewOf(buffer);
|
|
36
|
+
const payload = payloadViewOf(control);
|
|
37
|
+
const next = queue[0];
|
|
38
|
+
const { bytes, rest } = splitChunk(next, payload.length);
|
|
39
|
+
payload.fill(0);
|
|
40
|
+
payload.set(bytes);
|
|
41
|
+
Atomics.store(control, LENGTH_INDEX, bytes.length);
|
|
42
|
+
Atomics.add(control, SEQUENCE_INDEX, 1);
|
|
43
|
+
Atomics.notify(control, SEQUENCE_INDEX);
|
|
44
|
+
if (rest) {
|
|
45
|
+
queue[0] = rest;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
queue.shift();
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
export const readBufferedStdin = (buffer) => {
|
|
53
|
+
const control = controlViewOf(buffer);
|
|
54
|
+
const length = Atomics.load(control, LENGTH_INDEX);
|
|
55
|
+
if (length === EOF_LENGTH)
|
|
56
|
+
return null;
|
|
57
|
+
const payload = payloadViewOf(control);
|
|
58
|
+
return decoder.decode(payload.slice(0, length));
|
|
59
|
+
};
|
|
60
|
+
export const bufferedSequence = (buffer) => Atomics.load(controlViewOf(buffer), SEQUENCE_INDEX);
|
|
61
|
+
export const waitForBufferedSequenceChange = (buffer, sequence, timeoutMs = 5000) => new Promise((resolve, reject) => {
|
|
62
|
+
const startedAt = Date.now();
|
|
63
|
+
const poll = () => {
|
|
64
|
+
if (bufferedSequence(buffer) !== sequence) {
|
|
65
|
+
resolve(readBufferedStdin(buffer));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
69
|
+
reject(new Error('Timed out waiting for buffered value'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
setTimeout(poll, 10);
|
|
73
|
+
};
|
|
74
|
+
poll();
|
|
75
|
+
});
|
|
76
|
+
export const flushBufferedEof = (buffer) => {
|
|
77
|
+
const control = controlViewOf(buffer);
|
|
78
|
+
const payload = payloadViewOf(control);
|
|
79
|
+
payload.fill(0);
|
|
80
|
+
Atomics.store(control, LENGTH_INDEX, EOF_LENGTH);
|
|
81
|
+
Atomics.add(control, SEQUENCE_INDEX, 1);
|
|
82
|
+
Atomics.notify(control, SEQUENCE_INDEX);
|
|
83
|
+
};
|
|
84
|
+
export const waitForBufferedStdin = (buffer, requestInput) => {
|
|
85
|
+
const sequence = Atomics.load(buffer, SEQUENCE_INDEX);
|
|
86
|
+
requestInput();
|
|
87
|
+
while (true) {
|
|
88
|
+
const result = Atomics.wait(buffer, SEQUENCE_INDEX, sequence, 100);
|
|
89
|
+
if (result === 'not-equal') {
|
|
90
|
+
return readBufferedStdin(buffer);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
export const resetBufferedStdin = (buffer) => {
|
|
95
|
+
const control = controlViewOf(buffer);
|
|
96
|
+
const payload = payloadViewOf(control);
|
|
97
|
+
payload.fill(0);
|
|
98
|
+
Atomics.store(control, SEQUENCE_INDEX, 0);
|
|
99
|
+
Atomics.store(control, LENGTH_INDEX, 0);
|
|
100
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const normalizeTeaVmBaseUrl = (baseUrl) => baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
2
|
+
const normalizePathPrefix = (pathPrefix) => {
|
|
3
|
+
if (!pathPrefix)
|
|
4
|
+
return '';
|
|
5
|
+
return pathPrefix.endsWith('/') ? pathPrefix.slice(0, -1) : pathPrefix;
|
|
6
|
+
};
|
|
7
|
+
export const resolveTeaVmBaseUrl = (pathPrefix = '', currentUrl = '') => {
|
|
8
|
+
const configuredBaseUrl = (import.meta.env.PUBLIC_TEAVM_BASE_URL || '').trim();
|
|
9
|
+
const baseUrl = configuredBaseUrl
|
|
10
|
+
? normalizeTeaVmBaseUrl(configuredBaseUrl)
|
|
11
|
+
: `${normalizePathPrefix(pathPrefix) || ''}/teavm/`;
|
|
12
|
+
return currentUrl ? new URL(baseUrl, currentUrl).href : baseUrl;
|
|
13
|
+
};
|
|
14
|
+
export const resolveTeaVmAssetUrl = (baseUrl, assetPath) => new URL(assetPath, normalizeTeaVmBaseUrl(baseUrl)).href;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { waitForBufferedStdin } from '../stdinBuffer';
|
|
1
2
|
self.document = {
|
|
2
3
|
querySelectorAll() {
|
|
3
4
|
return [];
|
|
@@ -9,25 +10,7 @@ async function loadClang(path, log) {
|
|
|
9
10
|
clang = new Clang({
|
|
10
11
|
stdout: (output) => postMessage({ output }),
|
|
11
12
|
onDebugEvent: (debugEvent) => postMessage({ debugEvent }),
|
|
12
|
-
stdin: () => {
|
|
13
|
-
while (true) {
|
|
14
|
-
postMessage({ buffer: true });
|
|
15
|
-
const res = Atomics.wait(stdinBufferClang, 0, 0, 100);
|
|
16
|
-
if (res === 'not-equal') {
|
|
17
|
-
try {
|
|
18
|
-
const cpy = new Int32Array(stdinBufferClang.byteLength);
|
|
19
|
-
cpy.set(stdinBufferClang);
|
|
20
|
-
stdinBufferClang.fill(0);
|
|
21
|
-
const dec = new TextDecoder();
|
|
22
|
-
const strInfo = dec.decode(cpy).replace(/\x00/g, ''), padding = parseInt(strInfo.slice(-1));
|
|
23
|
-
return strInfo.slice(0, -padding);
|
|
24
|
-
}
|
|
25
|
-
catch (e) {
|
|
26
|
-
postMessage({ log: { e } });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
},
|
|
13
|
+
stdin: () => waitForBufferedStdin(stdinBufferClang, () => postMessage({ buffer: true })) ?? '',
|
|
31
14
|
progress: (value) => postMessage({ progress: value }),
|
|
32
15
|
log,
|
|
33
16
|
path
|
|
@@ -79,4 +62,3 @@ self.onmessage = async (event) => {
|
|
|
79
62
|
}
|
|
80
63
|
}
|
|
81
64
|
};
|
|
82
|
-
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|