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
package/dist/debug/expression.js
CHANGED
|
@@ -8,6 +8,38 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
8
8
|
values.set(variable.name, variable.value);
|
|
9
9
|
const parsedValues = new Map();
|
|
10
10
|
const tokens = [];
|
|
11
|
+
const parseQuotedValue = (text, start) => {
|
|
12
|
+
const quote = text[start];
|
|
13
|
+
if (quote !== "'" && quote !== '"')
|
|
14
|
+
throw new Error('expected quoted string');
|
|
15
|
+
let index = start + 1;
|
|
16
|
+
let value = '';
|
|
17
|
+
while (index < text.length) {
|
|
18
|
+
const character = text[index];
|
|
19
|
+
if (!character)
|
|
20
|
+
break;
|
|
21
|
+
if (character === '\\') {
|
|
22
|
+
const escaped = text[index + 1];
|
|
23
|
+
if (!escaped)
|
|
24
|
+
throw new Error('unterminated string literal');
|
|
25
|
+
if (escaped === 'n')
|
|
26
|
+
value += '\n';
|
|
27
|
+
else if (escaped === 'r')
|
|
28
|
+
value += '\r';
|
|
29
|
+
else if (escaped === 't')
|
|
30
|
+
value += '\t';
|
|
31
|
+
else
|
|
32
|
+
value += escaped;
|
|
33
|
+
index += 2;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (character === quote)
|
|
37
|
+
return { value, next: index + 1 };
|
|
38
|
+
value += character;
|
|
39
|
+
index += 1;
|
|
40
|
+
}
|
|
41
|
+
throw new Error('unterminated string literal');
|
|
42
|
+
};
|
|
11
43
|
for (let index = 0; index < source.length;) {
|
|
12
44
|
const character = source[index];
|
|
13
45
|
if (!character)
|
|
@@ -32,6 +64,12 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
32
64
|
index += operator.length;
|
|
33
65
|
continue;
|
|
34
66
|
}
|
|
67
|
+
if (character === "'" || character === '"') {
|
|
68
|
+
const parsed = parseQuotedValue(source, index);
|
|
69
|
+
tokens.push({ type: 'string', value: parsed.value });
|
|
70
|
+
index = parsed.next;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
35
73
|
const number = source.slice(index).match(/^\d+(?:\.\d+)?/);
|
|
36
74
|
if (number?.[0]) {
|
|
37
75
|
tokens.push({ type: 'number', value: number[0] });
|
|
@@ -40,8 +78,26 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
40
78
|
}
|
|
41
79
|
const identifier = source.slice(index).match(/^[A-Za-z_]\w*/);
|
|
42
80
|
if (identifier?.[0]) {
|
|
43
|
-
if (identifier[0] === 'true' ||
|
|
44
|
-
|
|
81
|
+
if (identifier[0] === 'true' ||
|
|
82
|
+
identifier[0] === 'false' ||
|
|
83
|
+
identifier[0] === 'True' ||
|
|
84
|
+
identifier[0] === 'False') {
|
|
85
|
+
tokens.push({
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
value: identifier[0] === 'true' || identifier[0] === 'True'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (identifier[0] === 'null' || identifier[0] === 'None') {
|
|
91
|
+
tokens.push({ type: 'null' });
|
|
92
|
+
}
|
|
93
|
+
else if (identifier[0] === 'and') {
|
|
94
|
+
tokens.push({ type: 'operator', value: '&&' });
|
|
95
|
+
}
|
|
96
|
+
else if (identifier[0] === 'or') {
|
|
97
|
+
tokens.push({ type: 'operator', value: '||' });
|
|
98
|
+
}
|
|
99
|
+
else if (identifier[0] === 'not') {
|
|
100
|
+
tokens.push({ type: 'operator', value: '!' });
|
|
45
101
|
}
|
|
46
102
|
else {
|
|
47
103
|
tokens.push({ type: 'identifier', value: identifier[0] });
|
|
@@ -81,16 +137,44 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
81
137
|
throw new Error('unsupported array preview');
|
|
82
138
|
}
|
|
83
139
|
}
|
|
84
|
-
if (character ===
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
if (character === '(') {
|
|
141
|
+
index += 1;
|
|
142
|
+
const items = [];
|
|
143
|
+
while (true) {
|
|
144
|
+
while (/\s/.test(text[index] || ''))
|
|
145
|
+
index += 1;
|
|
146
|
+
if (text[index] === ')')
|
|
147
|
+
return { value: items, next: index + 1 };
|
|
148
|
+
if (text.startsWith('...', index))
|
|
149
|
+
throw new Error('unavailable');
|
|
150
|
+
const item = parsePreviewValue(text, index);
|
|
151
|
+
items.push(item.value);
|
|
152
|
+
index = item.next;
|
|
153
|
+
while (/\s/.test(text[index] || ''))
|
|
154
|
+
index += 1;
|
|
155
|
+
if (text[index] === ',') {
|
|
156
|
+
index += 1;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (text[index] === ')')
|
|
160
|
+
return { value: items, next: index + 1 };
|
|
161
|
+
throw new Error('unsupported tuple preview');
|
|
162
|
+
}
|
|
89
163
|
}
|
|
164
|
+
if (character === "'" || character === '"')
|
|
165
|
+
return parseQuotedValue(text, index);
|
|
90
166
|
if (text.startsWith('true', index))
|
|
91
167
|
return { value: true, next: index + 4 };
|
|
92
168
|
if (text.startsWith('false', index))
|
|
93
169
|
return { value: false, next: index + 5 };
|
|
170
|
+
if (text.startsWith('True', index))
|
|
171
|
+
return { value: true, next: index + 4 };
|
|
172
|
+
if (text.startsWith('False', index))
|
|
173
|
+
return { value: false, next: index + 5 };
|
|
174
|
+
if (text.startsWith('null', index))
|
|
175
|
+
return { value: null, next: index + 4 };
|
|
176
|
+
if (text.startsWith('None', index))
|
|
177
|
+
return { value: null, next: index + 4 };
|
|
94
178
|
const number = text.slice(index).match(/^-?\d+(?:\.\d+)?/);
|
|
95
179
|
if (number?.[0])
|
|
96
180
|
return { value: Number(number[0]), next: index + number[0].length };
|
|
@@ -102,17 +186,24 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
102
186
|
const value = values.get(name);
|
|
103
187
|
if (value == null || value === '?')
|
|
104
188
|
throw new Error('unavailable');
|
|
105
|
-
if (value === 'true' || value === 'false') {
|
|
106
|
-
const parsed = value === 'true';
|
|
189
|
+
if (value === 'true' || value === 'false' || value === 'True' || value === 'False') {
|
|
190
|
+
const parsed = value === 'true' || value === 'True';
|
|
107
191
|
parsedValues.set(name, parsed);
|
|
108
192
|
return parsed;
|
|
109
193
|
}
|
|
194
|
+
if (value === 'null' || value === 'None') {
|
|
195
|
+
parsedValues.set(name, null);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
110
198
|
const numeric = Number(value);
|
|
111
199
|
if (!Number.isNaN(numeric)) {
|
|
112
200
|
parsedValues.set(name, numeric);
|
|
113
201
|
return numeric;
|
|
114
202
|
}
|
|
115
|
-
if (value.startsWith('[')
|
|
203
|
+
if (value.startsWith('[') ||
|
|
204
|
+
value.startsWith('(') ||
|
|
205
|
+
value.startsWith("'") ||
|
|
206
|
+
value.startsWith('"')) {
|
|
116
207
|
const parsed = parsePreviewValue(value).value;
|
|
117
208
|
parsedValues.set(name, parsed);
|
|
118
209
|
return parsed;
|
|
@@ -131,10 +222,21 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
131
222
|
cursor += 1;
|
|
132
223
|
return token.value;
|
|
133
224
|
}
|
|
225
|
+
if (token.type === 'null') {
|
|
226
|
+
cursor += 1;
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (token.type === 'string') {
|
|
230
|
+
cursor += 1;
|
|
231
|
+
return token.value;
|
|
232
|
+
}
|
|
134
233
|
if (token.type === 'identifier') {
|
|
135
234
|
cursor += 1;
|
|
136
235
|
let resolved = resolveIdentifierValue(token.value);
|
|
137
|
-
while (
|
|
236
|
+
while (true) {
|
|
237
|
+
const bracket = tokens[cursor];
|
|
238
|
+
if (!bracket || bracket.type !== 'bracket' || bracket.value !== '[')
|
|
239
|
+
break;
|
|
138
240
|
cursor += 1;
|
|
139
241
|
const index = Number(parseOr());
|
|
140
242
|
const closing = tokens[cursor];
|
|
@@ -201,8 +303,14 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
201
303
|
return left;
|
|
202
304
|
cursor += 1;
|
|
203
305
|
const right = parseMul();
|
|
204
|
-
if (operator.value === '+')
|
|
205
|
-
|
|
306
|
+
if (operator.value === '+') {
|
|
307
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
308
|
+
left = `${left ?? 'null'}${right ?? 'null'}`;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
left = Number(left) + Number(right);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
206
314
|
if (operator.value === '-')
|
|
207
315
|
left = Number(left) - Number(right);
|
|
208
316
|
}
|
|
@@ -215,14 +323,16 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
215
323
|
return left;
|
|
216
324
|
cursor += 1;
|
|
217
325
|
const right = parseAdd();
|
|
326
|
+
const leftValue = typeof left === 'string' && typeof right === 'string' ? left : Number(left);
|
|
327
|
+
const rightValue = typeof left === 'string' && typeof right === 'string' ? right : Number(right);
|
|
218
328
|
if (operator.value === '<')
|
|
219
|
-
left =
|
|
329
|
+
left = leftValue < rightValue;
|
|
220
330
|
if (operator.value === '<=')
|
|
221
|
-
left =
|
|
331
|
+
left = leftValue <= rightValue;
|
|
222
332
|
if (operator.value === '>')
|
|
223
|
-
left =
|
|
333
|
+
left = leftValue > rightValue;
|
|
224
334
|
if (operator.value === '>=')
|
|
225
|
-
left =
|
|
335
|
+
left = leftValue >= rightValue;
|
|
226
336
|
}
|
|
227
337
|
};
|
|
228
338
|
const parseEquality = () => {
|
|
@@ -241,7 +351,10 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
241
351
|
};
|
|
242
352
|
const parseAnd = () => {
|
|
243
353
|
let left = parseEquality();
|
|
244
|
-
while (
|
|
354
|
+
while (true) {
|
|
355
|
+
const operator = tokens[cursor];
|
|
356
|
+
if (!operator || operator.type !== 'operator' || operator.value !== '&&')
|
|
357
|
+
break;
|
|
245
358
|
cursor += 1;
|
|
246
359
|
left = Boolean(left) && Boolean(parseEquality());
|
|
247
360
|
}
|
|
@@ -249,7 +362,10 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
249
362
|
};
|
|
250
363
|
const parseOr = () => {
|
|
251
364
|
let left = parseAnd();
|
|
252
|
-
while (
|
|
365
|
+
while (true) {
|
|
366
|
+
const operator = tokens[cursor];
|
|
367
|
+
if (!operator || operator.type !== 'operator' || operator.value !== '||')
|
|
368
|
+
break;
|
|
253
369
|
cursor += 1;
|
|
254
370
|
left = Boolean(left) || Boolean(parseAnd());
|
|
255
371
|
}
|
package/dist/debug/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { evaluateDebugExpression } from './expression';
|
|
2
2
|
export { selectInlineDebugLocals } from './locals';
|
|
3
|
-
export { cppDebugLanguageAdapter } from './language';
|
|
3
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter } from './language';
|
|
4
4
|
export { MonacoDebugView } from './editor';
|
|
5
5
|
export type { DebugLanguageAdapter } from './language';
|
package/dist/debug/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { evaluateDebugExpression } from './expression';
|
|
2
2
|
export { selectInlineDebugLocals } from './locals';
|
|
3
|
-
export { cppDebugLanguageAdapter } from './language';
|
|
3
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter } from './language';
|
|
4
4
|
export { MonacoDebugView } from './editor';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { evaluateDebugExpression } from '../expression';
|
|
2
|
+
const selectInlinePythonLocals = (lineText, locals) => {
|
|
3
|
+
const sanitized = lineText
|
|
4
|
+
.replace(/#.*$/g, ' ')
|
|
5
|
+
.replace(/"(?:\\.|[^"\\])*"/g, ' ')
|
|
6
|
+
.replace(/'(?:\\.|[^'\\])*'/g, ' ');
|
|
7
|
+
const identifiers = new Set(sanitized.match(/[A-Za-z_]\w*/g) || []);
|
|
8
|
+
return locals.filter((variable) => identifiers.has(variable.name));
|
|
9
|
+
};
|
|
10
|
+
export const pythonDebugLanguageAdapter = {
|
|
11
|
+
id: 'python',
|
|
12
|
+
evaluateExpression: evaluateDebugExpression,
|
|
13
|
+
selectInlineLocals: selectInlinePythonLocals
|
|
14
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
-
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
3
3
|
import playground from './playground';
|
|
4
4
|
export default Terminal;
|
|
5
5
|
export { playground };
|
|
6
6
|
export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions, TerminalControl } from './terminal';
|
|
7
|
+
export type { CompilerDiagnostic } from './playground/options';
|
|
7
8
|
export type { DebugLanguageAdapter } from './debug';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
-
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
3
3
|
import playground from './playground';
|
|
4
4
|
export default Terminal;
|
|
5
5
|
export { playground };
|
|
@@ -9,20 +9,23 @@ declare class Clang implements Sandbox {
|
|
|
9
9
|
buffer: SharedArrayBuffer;
|
|
10
10
|
debugBuffer: SharedArrayBuffer;
|
|
11
11
|
interruptBuffer: SharedArrayBuffer;
|
|
12
|
-
|
|
12
|
+
pendingInput: string[];
|
|
13
13
|
begin: number;
|
|
14
14
|
elapse: number;
|
|
15
15
|
uid: number;
|
|
16
16
|
log: boolean;
|
|
17
|
+
waitingForInput: boolean;
|
|
18
|
+
pendingEof: boolean;
|
|
17
19
|
exit: boolean;
|
|
18
20
|
load(path: string, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
|
|
19
21
|
write(input: string): void;
|
|
20
|
-
_write(input: string): void;
|
|
21
22
|
eof(): void;
|
|
23
|
+
private flushPendingInput;
|
|
22
24
|
run(code: string, prepare: boolean, log?: boolean, prog?: Writable<number> | {
|
|
23
25
|
set?: (value: number) => void;
|
|
24
26
|
}, args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
25
27
|
debugCommand(command: DebugCommand): void;
|
|
28
|
+
kill(): void;
|
|
26
29
|
terminate(): void;
|
|
27
30
|
clear(): Promise<void>;
|
|
28
31
|
}
|
package/dist/playground/clang.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
|
|
1
2
|
class Clang {
|
|
2
3
|
ts = Date.now();
|
|
3
4
|
output = null;
|
|
@@ -6,16 +7,20 @@ class Clang {
|
|
|
6
7
|
buffer = new SharedArrayBuffer(1024);
|
|
7
8
|
debugBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4);
|
|
8
9
|
interruptBuffer = new SharedArrayBuffer(1);
|
|
9
|
-
|
|
10
|
+
pendingInput = [];
|
|
10
11
|
begin = 0;
|
|
11
12
|
elapse = 0;
|
|
12
13
|
uid = 0;
|
|
13
14
|
log = true;
|
|
15
|
+
waitingForInput = false;
|
|
16
|
+
pendingEof = false;
|
|
14
17
|
exit = true;
|
|
15
18
|
load(path, code = '', log = true, args = [], _options = {}) {
|
|
16
19
|
return new Promise(async (resolve) => {
|
|
17
20
|
this.log = log;
|
|
18
|
-
this.
|
|
21
|
+
this.pendingInput = [];
|
|
22
|
+
this.waitingForInput = false;
|
|
23
|
+
this.pendingEof = false;
|
|
19
24
|
if (!this.worker) {
|
|
20
25
|
this.worker = new (await import('./worker/clang?worker')).default();
|
|
21
26
|
this.worker.onmessage = () => resolve();
|
|
@@ -28,19 +33,27 @@ class Clang {
|
|
|
28
33
|
});
|
|
29
34
|
}
|
|
30
35
|
write(input) {
|
|
31
|
-
this.
|
|
36
|
+
this.pendingInput.push(input);
|
|
37
|
+
this.pendingEof = false;
|
|
38
|
+
this.flushPendingInput();
|
|
32
39
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
eof() {
|
|
41
|
+
this.pendingEof = true;
|
|
42
|
+
this.flushPendingInput();
|
|
43
|
+
}
|
|
44
|
+
flushPendingInput() {
|
|
45
|
+
if (!this.waitingForInput)
|
|
46
|
+
return;
|
|
47
|
+
if (flushQueuedStdin(this.pendingInput, this.buffer)) {
|
|
48
|
+
this.waitingForInput = false;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (this.pendingEof) {
|
|
52
|
+
flushBufferedEof(this.buffer);
|
|
53
|
+
this.pendingEof = false;
|
|
54
|
+
this.waitingForInput = false;
|
|
55
|
+
}
|
|
42
56
|
}
|
|
43
|
-
eof() { }
|
|
44
57
|
run(code, prepare, log = this.log, prog, args = [], options = {}) {
|
|
45
58
|
this.exit = false;
|
|
46
59
|
return new Promise(async (resolve, reject) => {
|
|
@@ -53,8 +66,10 @@ class Clang {
|
|
|
53
66
|
if (_uid !== this.uid)
|
|
54
67
|
return (this.worker.onmessage = null);
|
|
55
68
|
const { id, output, results, log, error, buffer, progress, debugEvent } = event.data;
|
|
56
|
-
if (buffer
|
|
57
|
-
this.
|
|
69
|
+
if (buffer) {
|
|
70
|
+
this.waitingForInput = true;
|
|
71
|
+
this.flushPendingInput();
|
|
72
|
+
}
|
|
58
73
|
if (output)
|
|
59
74
|
this.output(output);
|
|
60
75
|
if (debugEvent)
|
|
@@ -62,6 +77,8 @@ class Clang {
|
|
|
62
77
|
if (results) {
|
|
63
78
|
this.elapse = Date.now() - this.begin;
|
|
64
79
|
this.exit = true;
|
|
80
|
+
this.waitingForInput = false;
|
|
81
|
+
this.pendingEof = false;
|
|
65
82
|
this.ondebug?.({ type: 'stop' });
|
|
66
83
|
resolve(results);
|
|
67
84
|
}
|
|
@@ -69,6 +86,10 @@ class Clang {
|
|
|
69
86
|
console.log(log);
|
|
70
87
|
if (error) {
|
|
71
88
|
this.elapse = Date.now() - this.begin;
|
|
89
|
+
this.waitingForInput = false;
|
|
90
|
+
this.pendingEof = false;
|
|
91
|
+
this.exit = true;
|
|
92
|
+
this.ondebug?.({ type: 'stop' });
|
|
72
93
|
reject(error);
|
|
73
94
|
}
|
|
74
95
|
if (progress)
|
|
@@ -99,7 +120,11 @@ class Clang {
|
|
|
99
120
|
Atomics.notify(control, 0);
|
|
100
121
|
this.ondebug?.({ type: 'resume', command });
|
|
101
122
|
}
|
|
123
|
+
kill() {
|
|
124
|
+
this.terminate();
|
|
125
|
+
}
|
|
102
126
|
terminate() {
|
|
127
|
+
this.pendingEof = false;
|
|
103
128
|
new Uint8Array(this.interruptBuffer)[0] = 2;
|
|
104
129
|
const control = new Int32Array(this.debugBuffer);
|
|
105
130
|
Atomics.add(control, 0, 1);
|
|
@@ -107,11 +132,12 @@ class Clang {
|
|
|
107
132
|
}
|
|
108
133
|
async clear() {
|
|
109
134
|
this.terminate();
|
|
110
|
-
this.
|
|
135
|
+
this.pendingInput = [];
|
|
136
|
+
this.waitingForInput = false;
|
|
137
|
+
this.pendingEof = false;
|
|
111
138
|
if (this.worker)
|
|
112
139
|
this.worker.onmessage = null;
|
|
113
|
-
|
|
114
|
-
buffer.fill(0);
|
|
140
|
+
resetBufferedStdin(this.buffer);
|
|
115
141
|
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
116
142
|
debugBuffer.fill(0);
|
|
117
143
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Clang from './clang';
|
|
2
|
+
import Java from './java';
|
|
2
3
|
import Python from './python';
|
|
3
4
|
import type { Sandbox } from './sandbox';
|
|
4
5
|
export declare const supportedLanguages: string[];
|
|
5
|
-
export default function load(language: string): Promise<Sandbox | Clang | Python>;
|
|
6
|
+
export default function load(language: string): Promise<Sandbox | Clang | Java | Python>;
|
package/dist/playground/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Clang from './clang';
|
|
2
|
+
import Java from './java';
|
|
2
3
|
import Python from './python';
|
|
3
4
|
const sandboxCache = {};
|
|
4
|
-
export const supportedLanguages = ['PYTHON3', 'PYPY3', 'C', 'CPP'];
|
|
5
|
+
export const supportedLanguages = ['PYTHON3', 'PYPY3', 'C', 'CPP', 'JAVA'];
|
|
5
6
|
export default async function load(language) {
|
|
6
7
|
if (sandboxCache[language])
|
|
7
8
|
return sandboxCache[language];
|
|
@@ -16,6 +17,9 @@ export default async function load(language) {
|
|
|
16
17
|
case 'CPP':
|
|
17
18
|
sandbox = new Clang();
|
|
18
19
|
break;
|
|
20
|
+
case 'JAVA':
|
|
21
|
+
sandbox = new Java();
|
|
22
|
+
break;
|
|
19
23
|
default:
|
|
20
24
|
throw new Error(`Unsupported language: ${language}`);
|
|
21
25
|
}
|
|
@@ -31,6 +35,8 @@ export default async function load(language) {
|
|
|
31
35
|
sandboxCache['C'] = sandbox;
|
|
32
36
|
if (language === 'CPP')
|
|
33
37
|
sandboxCache['CPP'] = sandbox;
|
|
38
|
+
if (language === 'JAVA')
|
|
39
|
+
sandboxCache['JAVA'] = sandbox;
|
|
34
40
|
}
|
|
35
41
|
return sandbox;
|
|
36
42
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CompilerDiagnostic, SandboxExecutionOptions } from './options';
|
|
2
|
+
import type { Sandbox } from './sandbox';
|
|
3
|
+
declare class Java implements Sandbox {
|
|
4
|
+
output: any;
|
|
5
|
+
worker?: Worker;
|
|
6
|
+
buffer: SharedArrayBuffer;
|
|
7
|
+
pendingInput: string[];
|
|
8
|
+
begin: number;
|
|
9
|
+
elapse: number;
|
|
10
|
+
uid: number;
|
|
11
|
+
exit: boolean;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
activeReject: ((reason: string) => void) | null;
|
|
14
|
+
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
15
|
+
waitingForInput: boolean;
|
|
16
|
+
pendingEof: boolean;
|
|
17
|
+
load(path: string, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
|
|
18
|
+
write(input: string): void;
|
|
19
|
+
eof(): void;
|
|
20
|
+
private flushPendingInput;
|
|
21
|
+
run(code: string, prepare: boolean, _log?: boolean, _prog?: {
|
|
22
|
+
set?: (value: number) => void;
|
|
23
|
+
} | import('svelte/store').Writable<number>, args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
24
|
+
kill(): void;
|
|
25
|
+
terminate(): void;
|
|
26
|
+
clear(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export default Java;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
|
|
2
|
+
import { resolveTeaVmBaseUrl } from './teavmConfig';
|
|
3
|
+
class Java {
|
|
4
|
+
output = null;
|
|
5
|
+
worker = null;
|
|
6
|
+
buffer = new SharedArrayBuffer(1024);
|
|
7
|
+
pendingInput = [];
|
|
8
|
+
begin = 0;
|
|
9
|
+
elapse = 0;
|
|
10
|
+
uid = 0;
|
|
11
|
+
exit = true;
|
|
12
|
+
baseUrl = '';
|
|
13
|
+
activeReject = null;
|
|
14
|
+
oncompilerdiagnostic;
|
|
15
|
+
waitingForInput = false;
|
|
16
|
+
pendingEof = false;
|
|
17
|
+
load(path, _code = '', _log = true, _args = [], _options = {}) {
|
|
18
|
+
return new Promise(async (resolve, reject) => {
|
|
19
|
+
this.pendingInput = [];
|
|
20
|
+
this.waitingForInput = false;
|
|
21
|
+
this.pendingEof = false;
|
|
22
|
+
this.baseUrl = resolveTeaVmBaseUrl(path, typeof window !== 'undefined' ? window.location.href : '');
|
|
23
|
+
if (!this.worker) {
|
|
24
|
+
this.worker = new (await import('./worker/java?worker')).default();
|
|
25
|
+
this.worker.onmessage = (event) => {
|
|
26
|
+
if (event.data?.load)
|
|
27
|
+
resolve();
|
|
28
|
+
if (event.data?.error)
|
|
29
|
+
reject(event.data.error);
|
|
30
|
+
};
|
|
31
|
+
this.worker.postMessage({ load: true, baseUrl: this.baseUrl });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
resolve();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
write(input) {
|
|
39
|
+
this.pendingInput.push(input);
|
|
40
|
+
this.pendingEof = false;
|
|
41
|
+
this.flushPendingInput();
|
|
42
|
+
}
|
|
43
|
+
eof() {
|
|
44
|
+
this.pendingEof = true;
|
|
45
|
+
this.flushPendingInput();
|
|
46
|
+
}
|
|
47
|
+
flushPendingInput() {
|
|
48
|
+
if (!this.waitingForInput)
|
|
49
|
+
return;
|
|
50
|
+
if (flushQueuedStdin(this.pendingInput, this.buffer)) {
|
|
51
|
+
this.waitingForInput = false;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (this.pendingEof) {
|
|
55
|
+
flushBufferedEof(this.buffer);
|
|
56
|
+
this.pendingEof = false;
|
|
57
|
+
this.waitingForInput = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
run(code, prepare, _log = true, _prog, args = [], options = {}) {
|
|
61
|
+
this.exit = false;
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
if (!this.worker)
|
|
64
|
+
return reject('Worker not loaded');
|
|
65
|
+
const _uid = ++this.uid;
|
|
66
|
+
this.activeReject = reject;
|
|
67
|
+
const handler = (event) => {
|
|
68
|
+
if (!this.worker)
|
|
69
|
+
return reject('Worker not loaded');
|
|
70
|
+
if (_uid !== this.uid)
|
|
71
|
+
return (this.worker.onmessage = null);
|
|
72
|
+
const { output, results, error, buffer, diagnostic } = event.data;
|
|
73
|
+
if (buffer) {
|
|
74
|
+
this.waitingForInput = true;
|
|
75
|
+
this.flushPendingInput();
|
|
76
|
+
}
|
|
77
|
+
if (output)
|
|
78
|
+
this.output(output);
|
|
79
|
+
if (diagnostic)
|
|
80
|
+
this.oncompilerdiagnostic?.(diagnostic);
|
|
81
|
+
if (results) {
|
|
82
|
+
this.elapse = Date.now() - this.begin;
|
|
83
|
+
this.exit = true;
|
|
84
|
+
this.waitingForInput = false;
|
|
85
|
+
this.pendingEof = false;
|
|
86
|
+
this.activeReject = null;
|
|
87
|
+
resolve(results);
|
|
88
|
+
}
|
|
89
|
+
if (error) {
|
|
90
|
+
this.elapse = Date.now() - this.begin;
|
|
91
|
+
this.exit = true;
|
|
92
|
+
this.waitingForInput = false;
|
|
93
|
+
this.pendingEof = false;
|
|
94
|
+
this.activeReject = null;
|
|
95
|
+
reject(error);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
this.worker.onmessage = handler;
|
|
99
|
+
this.begin = Date.now();
|
|
100
|
+
this.worker.postMessage({
|
|
101
|
+
code,
|
|
102
|
+
prepare,
|
|
103
|
+
buffer: this.buffer,
|
|
104
|
+
args,
|
|
105
|
+
stdin: options.stdin || '',
|
|
106
|
+
baseUrl: this.baseUrl
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
kill() {
|
|
111
|
+
this.terminate();
|
|
112
|
+
}
|
|
113
|
+
terminate() {
|
|
114
|
+
this.activeReject?.('Process terminated');
|
|
115
|
+
this.activeReject = null;
|
|
116
|
+
this.waitingForInput = false;
|
|
117
|
+
this.pendingEof = false;
|
|
118
|
+
this.uid += 1;
|
|
119
|
+
this.worker?.terminate?.();
|
|
120
|
+
delete this.worker;
|
|
121
|
+
this.exit = true;
|
|
122
|
+
}
|
|
123
|
+
async clear() {
|
|
124
|
+
this.pendingInput = [];
|
|
125
|
+
this.waitingForInput = false;
|
|
126
|
+
this.pendingEof = false;
|
|
127
|
+
if (this.worker)
|
|
128
|
+
this.worker.onmessage = null;
|
|
129
|
+
resetBufferedStdin(this.buffer);
|
|
130
|
+
if (!this.exit) {
|
|
131
|
+
this.terminate();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export default Java;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface PreparedJavaStdinInjection {
|
|
2
|
+
usesStdin: boolean;
|
|
3
|
+
stdinCacheKey: string;
|
|
4
|
+
transformedCode: string;
|
|
5
|
+
helperSourcePath: string | null;
|
|
6
|
+
helperSource: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare const prepareJavaStdinInjection: (code: string, stdin: string) => PreparedJavaStdinInjection;
|