wasm-idle 0.4.0 → 0.4.1
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 +18 -0
- package/dist/clang/app.js +17 -5
- package/dist/clang/index.d.ts +6 -0
- package/dist/clang/index.js +135 -19
- package/dist/index.d.ts +1 -0
- package/dist/playground/assetBridge.d.ts +20 -0
- package/dist/playground/assetBridge.js +192 -0
- package/dist/playground/assets.d.ts +32 -0
- package/dist/playground/assets.js +97 -0
- package/dist/playground/clang.d.ts +6 -1
- package/dist/playground/clang.js +13 -2
- package/dist/playground/index.js +3 -1
- 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 +9 -0
- package/dist/playground/options.js +15 -1
- package/dist/playground/python.d.ts +6 -1
- package/dist/playground/python.js +38 -5
- package/dist/playground/sandbox.d.ts +2 -1
- package/dist/playground/worker/assets.d.ts +12 -0
- package/dist/playground/worker/assets.js +257 -0
- package/dist/playground/worker/clang.js +11 -3
- package/dist/playground/worker/java.js +15 -35
- package/dist/playground/worker/python.js +13 -33
- package/dist/terminal/Terminal.svelte +57 -7
- package/dist/terminal/Terminal.svelte.d.ts +2 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +1 -0
- package/package.json +11 -11
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { normalizeTeaVmBaseUrl, resolveTeaVmBaseUrl } from './teavmConfig';
|
|
2
|
+
export const PYTHON_RUNTIME_LOAD_ASSETS = [
|
|
3
|
+
'pyodide.asm.js',
|
|
4
|
+
'pyodide-lock.json',
|
|
5
|
+
'pyodide.asm.wasm',
|
|
6
|
+
'python_stdlib.zip'
|
|
7
|
+
];
|
|
8
|
+
export const JAVA_RUNTIME_LOAD_ASSETS = [
|
|
9
|
+
'compiler.wasm-runtime.js',
|
|
10
|
+
'compiler.wasm',
|
|
11
|
+
'compile-classlib-teavm.bin',
|
|
12
|
+
'runtime-classlib-teavm.bin'
|
|
13
|
+
];
|
|
14
|
+
const PYTHON_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/python/';
|
|
15
|
+
const JAVA_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/java/';
|
|
16
|
+
const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
|
|
17
|
+
const normalized = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
18
|
+
return currentUrl ? new URL(normalized, currentUrl).href : normalized;
|
|
19
|
+
};
|
|
20
|
+
const normalizeRootUrl = (rootUrl) => rootUrl.endsWith('/') ? rootUrl.slice(0, -1) : rootUrl;
|
|
21
|
+
const resolvePythonBaseUrl = (rootUrl = '', currentUrl = '') => normalizeBaseUrl(`${normalizeRootUrl(rootUrl) || ''}/pyodide/`, currentUrl);
|
|
22
|
+
const resolvePythonRuntimeAssetConfig = (options, currentUrl = '') => {
|
|
23
|
+
if (typeof options === 'string') {
|
|
24
|
+
return {
|
|
25
|
+
baseUrl: resolvePythonBaseUrl(options, currentUrl),
|
|
26
|
+
useAssetBridge: false
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const runtimeConfig = options?.python;
|
|
30
|
+
if (runtimeConfig?.baseUrl) {
|
|
31
|
+
return {
|
|
32
|
+
baseUrl: normalizeBaseUrl(runtimeConfig.baseUrl, currentUrl),
|
|
33
|
+
loader: runtimeConfig.loader,
|
|
34
|
+
useAssetBridge: !!runtimeConfig.loader
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (options?.rootUrl) {
|
|
38
|
+
return {
|
|
39
|
+
baseUrl: resolvePythonBaseUrl(options.rootUrl, currentUrl),
|
|
40
|
+
loader: runtimeConfig?.loader,
|
|
41
|
+
useAssetBridge: !!runtimeConfig?.loader
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (runtimeConfig?.loader) {
|
|
45
|
+
return {
|
|
46
|
+
baseUrl: PYTHON_VIRTUAL_BASE_URL,
|
|
47
|
+
loader: runtimeConfig.loader,
|
|
48
|
+
useAssetBridge: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
baseUrl: resolvePythonBaseUrl('', currentUrl),
|
|
53
|
+
useAssetBridge: false
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const resolveJavaBaseUrl = (rootUrl = '', currentUrl = '') => resolveTeaVmBaseUrl(rootUrl, currentUrl);
|
|
57
|
+
const resolveJavaRuntimeAssetConfig = (options, currentUrl = '') => {
|
|
58
|
+
if (typeof options === 'string') {
|
|
59
|
+
return {
|
|
60
|
+
baseUrl: resolveJavaBaseUrl(options, currentUrl),
|
|
61
|
+
useAssetBridge: false
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const runtimeConfig = options?.java;
|
|
65
|
+
if (runtimeConfig?.baseUrl) {
|
|
66
|
+
return {
|
|
67
|
+
baseUrl: currentUrl
|
|
68
|
+
? new URL(normalizeTeaVmBaseUrl(runtimeConfig.baseUrl), currentUrl).href
|
|
69
|
+
: normalizeTeaVmBaseUrl(runtimeConfig.baseUrl),
|
|
70
|
+
loader: runtimeConfig.loader,
|
|
71
|
+
useAssetBridge: !!runtimeConfig.loader
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (options?.rootUrl) {
|
|
75
|
+
return {
|
|
76
|
+
baseUrl: resolveJavaBaseUrl(options.rootUrl, currentUrl),
|
|
77
|
+
loader: runtimeConfig?.loader,
|
|
78
|
+
useAssetBridge: !!runtimeConfig?.loader
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (runtimeConfig?.loader) {
|
|
82
|
+
return {
|
|
83
|
+
baseUrl: JAVA_VIRTUAL_BASE_URL,
|
|
84
|
+
loader: runtimeConfig.loader,
|
|
85
|
+
useAssetBridge: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
baseUrl: resolveJavaBaseUrl('', currentUrl),
|
|
90
|
+
useAssetBridge: false
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '') {
|
|
94
|
+
return runtime === 'python'
|
|
95
|
+
? resolvePythonRuntimeAssetConfig(options, currentUrl)
|
|
96
|
+
: resolveJavaRuntimeAssetConfig(options, currentUrl);
|
|
97
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
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;
|
|
@@ -17,7 +19,10 @@ declare class Clang implements Sandbox {
|
|
|
17
19
|
waitingForInput: boolean;
|
|
18
20
|
pendingEof: boolean;
|
|
19
21
|
exit: boolean;
|
|
20
|
-
|
|
22
|
+
constructor(language: 'C' | 'CPP');
|
|
23
|
+
load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions, _progress?: {
|
|
24
|
+
set?: (value: number) => void;
|
|
25
|
+
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
21
26
|
write(input: string): void;
|
|
22
27
|
eof(): void;
|
|
23
28
|
private flushPendingInput;
|
package/dist/playground/clang.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { resolveSandboxExecutionArgs } from './options';
|
|
1
2
|
import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
|
|
2
3
|
class Clang {
|
|
4
|
+
language;
|
|
3
5
|
ts = Date.now();
|
|
4
6
|
output = null;
|
|
5
7
|
ondebug;
|
|
@@ -15,12 +17,16 @@ class Clang {
|
|
|
15
17
|
waitingForInput = false;
|
|
16
18
|
pendingEof = false;
|
|
17
19
|
exit = true;
|
|
18
|
-
|
|
20
|
+
constructor(language) {
|
|
21
|
+
this.language = language;
|
|
22
|
+
}
|
|
23
|
+
load(runtimeAssets = '', code = '', log = true, args = [], _options = {}, _progress) {
|
|
19
24
|
return new Promise(async (resolve) => {
|
|
20
25
|
this.log = log;
|
|
21
26
|
this.pendingInput = [];
|
|
22
27
|
this.waitingForInput = false;
|
|
23
28
|
this.pendingEof = false;
|
|
29
|
+
const path = typeof runtimeAssets === 'string' ? runtimeAssets : runtimeAssets?.rootUrl || '';
|
|
24
30
|
if (!this.worker) {
|
|
25
31
|
this.worker = new (await import('./worker/clang?worker')).default();
|
|
26
32
|
this.worker.onmessage = () => resolve();
|
|
@@ -59,6 +65,7 @@ class Clang {
|
|
|
59
65
|
return new Promise(async (resolve, reject) => {
|
|
60
66
|
if (!this.worker)
|
|
61
67
|
return reject('Worker not loaded');
|
|
68
|
+
const { compileArgs, programArgs } = resolveSandboxExecutionArgs(this.language, args, options);
|
|
62
69
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
63
70
|
const handler = (event) => {
|
|
64
71
|
if (!this.worker)
|
|
@@ -106,7 +113,11 @@ class Clang {
|
|
|
106
113
|
interrupt: this.interruptBuffer,
|
|
107
114
|
context: {},
|
|
108
115
|
log,
|
|
109
|
-
|
|
116
|
+
language: this.language,
|
|
117
|
+
compileArgs,
|
|
118
|
+
programArgs,
|
|
119
|
+
cppVersion: options.cppVersion,
|
|
120
|
+
cVersion: options.cVersion,
|
|
110
121
|
debug: !!options.debug,
|
|
111
122
|
breakpoints: [...(options.breakpoints || [])],
|
|
112
123
|
pauseOnEntry: !!options.pauseOnEntry
|
package/dist/playground/index.js
CHANGED
|
@@ -14,8 +14,10 @@ export default async function load(language) {
|
|
|
14
14
|
sandbox = new Python();
|
|
15
15
|
break;
|
|
16
16
|
case 'C':
|
|
17
|
+
sandbox = new Clang('C');
|
|
18
|
+
break;
|
|
17
19
|
case 'CPP':
|
|
18
|
-
sandbox = new Clang();
|
|
20
|
+
sandbox = new Clang('CPP');
|
|
19
21
|
break;
|
|
20
22
|
case 'JAVA':
|
|
21
23
|
sandbox = new Java();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
|
|
2
|
+
import { WorkerAssetBridge } from './assetBridge';
|
|
3
|
+
import { type PlaygroundRuntimeAssets } from './assets';
|
|
2
4
|
import type { Sandbox } from './sandbox';
|
|
3
5
|
declare class Java implements Sandbox {
|
|
4
6
|
output: any;
|
|
@@ -14,7 +16,10 @@ declare class Java implements Sandbox {
|
|
|
14
16
|
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
15
17
|
waitingForInput: boolean;
|
|
16
18
|
pendingEof: boolean;
|
|
17
|
-
|
|
19
|
+
assetBridge: WorkerAssetBridge | null;
|
|
20
|
+
load(runtimeAssets?: string | PlaygroundRuntimeAssets, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
|
|
21
|
+
set?: (value: number) => void;
|
|
22
|
+
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
18
23
|
write(input: string): void;
|
|
19
24
|
eof(): void;
|
|
20
25
|
private flushPendingInput;
|
package/dist/playground/java.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { resolveSandboxExecutionArgs } from './options';
|
|
2
|
+
import { WorkerAssetBridge } from './assetBridge';
|
|
3
|
+
import { resolveRuntimeAssetConfig } from './assets';
|
|
1
4
|
import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
|
|
2
|
-
import { resolveTeaVmBaseUrl } from './teavmConfig';
|
|
3
5
|
class Java {
|
|
4
6
|
output = null;
|
|
5
7
|
worker = null;
|
|
@@ -14,23 +16,43 @@ class Java {
|
|
|
14
16
|
oncompilerdiagnostic;
|
|
15
17
|
waitingForInput = false;
|
|
16
18
|
pendingEof = false;
|
|
17
|
-
|
|
19
|
+
assetBridge = null;
|
|
20
|
+
load(runtimeAssets = '', _code = '', _log = true, _args = [], _options = {}, progress) {
|
|
18
21
|
return new Promise(async (resolve, reject) => {
|
|
19
22
|
this.pendingInput = [];
|
|
20
23
|
this.waitingForInput = false;
|
|
21
24
|
this.pendingEof = false;
|
|
22
|
-
|
|
25
|
+
const assetConfig = resolveRuntimeAssetConfig('java', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
26
|
+
this.baseUrl = assetConfig.baseUrl;
|
|
27
|
+
const needsWorkerReset = !this.worker || !this.assetBridge || !this.assetBridge.matches(assetConfig);
|
|
28
|
+
if (needsWorkerReset && this.worker) {
|
|
29
|
+
this.worker.terminate();
|
|
30
|
+
delete this.worker;
|
|
31
|
+
this.assetBridge = null;
|
|
32
|
+
}
|
|
23
33
|
if (!this.worker) {
|
|
24
34
|
this.worker = new (await import('./worker/java?worker')).default();
|
|
35
|
+
this.assetBridge = new WorkerAssetBridge(this.worker, 'java', assetConfig, progress);
|
|
25
36
|
this.worker.onmessage = (event) => {
|
|
37
|
+
if (this.assetBridge?.handleMessage(event))
|
|
38
|
+
return;
|
|
26
39
|
if (event.data?.load)
|
|
27
40
|
resolve();
|
|
28
41
|
if (event.data?.error)
|
|
29
42
|
reject(event.data.error);
|
|
30
43
|
};
|
|
31
|
-
this.worker.postMessage({
|
|
44
|
+
this.worker.postMessage({
|
|
45
|
+
load: true,
|
|
46
|
+
assets: {
|
|
47
|
+
baseUrl: assetConfig.baseUrl,
|
|
48
|
+
useAssetBridge: assetConfig.useAssetBridge
|
|
49
|
+
}
|
|
50
|
+
});
|
|
32
51
|
}
|
|
33
52
|
else {
|
|
53
|
+
if (!this.assetBridge)
|
|
54
|
+
return reject('Worker asset bridge unavailable');
|
|
55
|
+
this.assetBridge.rebind(this.worker, assetConfig, progress);
|
|
34
56
|
resolve();
|
|
35
57
|
}
|
|
36
58
|
});
|
|
@@ -62,9 +84,12 @@ class Java {
|
|
|
62
84
|
return new Promise((resolve, reject) => {
|
|
63
85
|
if (!this.worker)
|
|
64
86
|
return reject('Worker not loaded');
|
|
87
|
+
const { programArgs } = resolveSandboxExecutionArgs('JAVA', args, options);
|
|
65
88
|
const _uid = ++this.uid;
|
|
66
89
|
this.activeReject = reject;
|
|
67
90
|
const handler = (event) => {
|
|
91
|
+
if (this.assetBridge?.handleMessage(event))
|
|
92
|
+
return;
|
|
68
93
|
if (!this.worker)
|
|
69
94
|
return reject('Worker not loaded');
|
|
70
95
|
if (_uid !== this.uid)
|
|
@@ -101,7 +126,7 @@ class Java {
|
|
|
101
126
|
code,
|
|
102
127
|
prepare,
|
|
103
128
|
buffer: this.buffer,
|
|
104
|
-
args,
|
|
129
|
+
args: programArgs,
|
|
105
130
|
stdin: options.stdin || '',
|
|
106
131
|
baseUrl: this.baseUrl
|
|
107
132
|
});
|
|
@@ -118,6 +143,7 @@ class Java {
|
|
|
118
143
|
this.uid += 1;
|
|
119
144
|
this.worker?.terminate?.();
|
|
120
145
|
delete this.worker;
|
|
146
|
+
this.assetBridge = null;
|
|
121
147
|
this.exit = true;
|
|
122
148
|
}
|
|
123
149
|
async clear() {
|
|
@@ -1,11 +1,238 @@
|
|
|
1
1
|
const JAVA_STDIN_HELPER_CLASS = 'WasmIdleStdin';
|
|
2
|
+
const JAVA_SCANNER_COMPAT_SENTINEL = '// wasm-idle Scanner compatibility shim';
|
|
3
|
+
const JAVA_SCANNER_COMPAT_SHIM = `${JAVA_SCANNER_COMPAT_SENTINEL}
|
|
4
|
+
final class Scanner implements AutoCloseable {
|
|
5
|
+
private final java.io.InputStream input;
|
|
6
|
+
private int bufferedChar = Integer.MIN_VALUE;
|
|
7
|
+
private String bufferedToken = null;
|
|
8
|
+
private boolean closed = false;
|
|
9
|
+
|
|
10
|
+
Scanner(java.io.InputStream input) {
|
|
11
|
+
this.input = input;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Scanner(String source) {
|
|
15
|
+
this(
|
|
16
|
+
new java.io.ByteArrayInputStream(
|
|
17
|
+
source != null ? source.getBytes() : new byte[0]
|
|
18
|
+
)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private void ensureOpen() {
|
|
23
|
+
if (closed) {
|
|
24
|
+
throw new IllegalStateException("Scanner closed");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private int readByteInternal() {
|
|
29
|
+
try {
|
|
30
|
+
return input.read();
|
|
31
|
+
} catch (java.io.IOException error) {
|
|
32
|
+
throw new RuntimeException(error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private int readByte() {
|
|
37
|
+
ensureOpen();
|
|
38
|
+
if (bufferedChar != Integer.MIN_VALUE) {
|
|
39
|
+
int value = bufferedChar;
|
|
40
|
+
bufferedChar = Integer.MIN_VALUE;
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
return readByteInternal();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private int peekByte() {
|
|
47
|
+
ensureOpen();
|
|
48
|
+
if (bufferedChar == Integer.MIN_VALUE) {
|
|
49
|
+
bufferedChar = readByteInternal();
|
|
50
|
+
}
|
|
51
|
+
return bufferedChar;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private boolean isWhitespace(int value) {
|
|
55
|
+
return value == ' ' || value == '\\n' || value == '\\r' || value == '\\t' || value == '\\f';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private boolean skipWhitespace() {
|
|
59
|
+
int value = peekByte();
|
|
60
|
+
while (value != -1 && isWhitespace(value)) {
|
|
61
|
+
readByte();
|
|
62
|
+
value = peekByte();
|
|
63
|
+
}
|
|
64
|
+
return value != -1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private String readTokenValue() {
|
|
68
|
+
java.lang.StringBuilder token = new java.lang.StringBuilder();
|
|
69
|
+
int value = peekByte();
|
|
70
|
+
while (value != -1 && !isWhitespace(value)) {
|
|
71
|
+
token.append((char) readByte());
|
|
72
|
+
value = peekByte();
|
|
73
|
+
}
|
|
74
|
+
return token.toString();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public boolean hasNext() {
|
|
78
|
+
ensureOpen();
|
|
79
|
+
if (bufferedToken != null) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (!skipWhitespace()) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
bufferedToken = readTokenValue();
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public String next() {
|
|
90
|
+
ensureOpen();
|
|
91
|
+
if (bufferedToken != null) {
|
|
92
|
+
String token = bufferedToken;
|
|
93
|
+
bufferedToken = null;
|
|
94
|
+
return token;
|
|
95
|
+
}
|
|
96
|
+
if (!skipWhitespace()) {
|
|
97
|
+
throw new RuntimeException("No more tokens");
|
|
98
|
+
}
|
|
99
|
+
return readTokenValue();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public String nextLine() {
|
|
103
|
+
ensureOpen();
|
|
104
|
+
java.lang.StringBuilder line = new java.lang.StringBuilder();
|
|
105
|
+
if (bufferedToken != null) {
|
|
106
|
+
line.append(bufferedToken);
|
|
107
|
+
bufferedToken = null;
|
|
108
|
+
}
|
|
109
|
+
int value = peekByte();
|
|
110
|
+
if (line.length() == 0 && value == -1) {
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
if (line.length() == 0 && value == '\\r') {
|
|
114
|
+
readByte();
|
|
115
|
+
if (peekByte() == '\\n') {
|
|
116
|
+
readByte();
|
|
117
|
+
}
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
if (line.length() == 0 && value == '\\n') {
|
|
121
|
+
readByte();
|
|
122
|
+
return "";
|
|
123
|
+
}
|
|
124
|
+
while (true) {
|
|
125
|
+
value = readByte();
|
|
126
|
+
if (value == -1 || value == '\\n' || value == '\\r') {
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
line.append((char) value);
|
|
130
|
+
}
|
|
131
|
+
if (value == '\\r' && peekByte() == '\\n') {
|
|
132
|
+
readByte();
|
|
133
|
+
}
|
|
134
|
+
return line.toString();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public int nextInt() {
|
|
138
|
+
return Integer.parseInt(next());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public long nextLong() {
|
|
142
|
+
return Long.parseLong(next());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public float nextFloat() {
|
|
146
|
+
return Float.parseFloat(next());
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public double nextDouble() {
|
|
150
|
+
return Double.parseDouble(next());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public boolean hasNextInt() {
|
|
154
|
+
if (!hasNext()) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
Integer.parseInt(bufferedToken);
|
|
159
|
+
return true;
|
|
160
|
+
} catch (RuntimeException error) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public boolean hasNextLong() {
|
|
166
|
+
if (!hasNext()) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
Long.parseLong(bufferedToken);
|
|
171
|
+
return true;
|
|
172
|
+
} catch (RuntimeException error) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public boolean hasNextFloat() {
|
|
178
|
+
if (!hasNext()) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
Float.parseFloat(bufferedToken);
|
|
183
|
+
return true;
|
|
184
|
+
} catch (RuntimeException error) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public boolean hasNextDouble() {
|
|
190
|
+
if (!hasNext()) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
Double.parseDouble(bufferedToken);
|
|
195
|
+
return true;
|
|
196
|
+
} catch (RuntimeException error) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public void close() {
|
|
202
|
+
if (closed) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
closed = true;
|
|
206
|
+
try {
|
|
207
|
+
input.close();
|
|
208
|
+
} catch (java.io.IOException error) {
|
|
209
|
+
throw new RuntimeException(error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}`;
|
|
2
213
|
export const prepareJavaStdinInjection = (code, stdin) => {
|
|
3
214
|
const usesStdin = code.includes('System.in');
|
|
215
|
+
const usesScanner = /\bScanner\b/.test(code) &&
|
|
216
|
+
!code.includes(JAVA_SCANNER_COMPAT_SENTINEL) &&
|
|
217
|
+
!/\b(?:class|interface|enum|record)\s+Scanner\b/.test(code) &&
|
|
218
|
+
!/^[ \t]*import[ \t]+(?!java\.util\.Scanner\b)(?!static\b)[\w.]+\.Scanner[ \t]*;[ \t]*$/m.test(code);
|
|
4
219
|
if (!usesStdin) {
|
|
220
|
+
if (!usesScanner) {
|
|
221
|
+
return {
|
|
222
|
+
usesStdin: false,
|
|
223
|
+
stdinCacheKey: '',
|
|
224
|
+
transformedCode: code,
|
|
225
|
+
helperSourcePath: null,
|
|
226
|
+
helperSource: null
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const transformedCode = code
|
|
230
|
+
.replace(/^[ \t]*import[ \t]+java\.util\.Scanner[ \t]*;[ \t]*$/gm, (match) => match.replace(/[^\r\n]/g, ' '))
|
|
231
|
+
.replaceAll(/\bjava\.util\.Scanner\b/g, 'Scanner');
|
|
5
232
|
return {
|
|
6
233
|
usesStdin: false,
|
|
7
234
|
stdinCacheKey: '',
|
|
8
|
-
transformedCode:
|
|
235
|
+
transformedCode: `${transformedCode.trimEnd()}\n\n${JAVA_SCANNER_COMPAT_SHIM}\n`,
|
|
9
236
|
helperSourcePath: null,
|
|
10
237
|
helperSource: null
|
|
11
238
|
};
|
|
@@ -15,7 +242,13 @@ export const prepareJavaStdinInjection = (code, stdin) => {
|
|
|
15
242
|
const helperSourcePath = packageName
|
|
16
243
|
? `${packageName.replaceAll('.', '/')}/${JAVA_STDIN_HELPER_CLASS}.java`
|
|
17
244
|
: `${JAVA_STDIN_HELPER_CLASS}.java`;
|
|
18
|
-
|
|
245
|
+
let transformedCode = code.replaceAll('System.in', `${JAVA_STDIN_HELPER_CLASS}.open()`);
|
|
246
|
+
if (usesScanner) {
|
|
247
|
+
transformedCode = transformedCode
|
|
248
|
+
.replace(/^[ \t]*import[ \t]+java\.util\.Scanner[ \t]*;[ \t]*$/gm, (match) => match.replace(/[^\r\n]/g, ' '))
|
|
249
|
+
.replaceAll(/\bjava\.util\.Scanner\b/g, 'Scanner');
|
|
250
|
+
transformedCode = `${transformedCode.trimEnd()}\n\n${JAVA_SCANNER_COMPAT_SHIM}\n`;
|
|
251
|
+
}
|
|
19
252
|
const encodedBytes = [...new TextEncoder().encode(stdin)].join(', ');
|
|
20
253
|
return {
|
|
21
254
|
usesStdin: true,
|
|
@@ -46,4 +46,13 @@ export interface SandboxExecutionOptions {
|
|
|
46
46
|
breakpoints?: number[];
|
|
47
47
|
pauseOnEntry?: boolean;
|
|
48
48
|
stdin?: string;
|
|
49
|
+
compileArgs?: string[];
|
|
50
|
+
programArgs?: string[];
|
|
51
|
+
cppVersion?: string;
|
|
52
|
+
cVersion?: string;
|
|
49
53
|
}
|
|
54
|
+
export interface ResolvedSandboxExecutionArgs {
|
|
55
|
+
compileArgs: string[];
|
|
56
|
+
programArgs: string[];
|
|
57
|
+
}
|
|
58
|
+
export declare function resolveSandboxExecutionArgs(language: string, args?: string[], options?: SandboxExecutionOptions): ResolvedSandboxExecutionArgs;
|
|
@@ -1 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
function cloneArgs(value) {
|
|
2
|
+
return Array.isArray(value) ? [...value] : [];
|
|
3
|
+
}
|
|
4
|
+
export function resolveSandboxExecutionArgs(language, args = [], options = {}) {
|
|
5
|
+
if (language === 'C' || language === 'CPP') {
|
|
6
|
+
return {
|
|
7
|
+
compileArgs: cloneArgs(options.compileArgs ?? args),
|
|
8
|
+
programArgs: cloneArgs(options.programArgs)
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
compileArgs: cloneArgs(options.compileArgs),
|
|
13
|
+
programArgs: cloneArgs(options.programArgs ?? args)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DebugCommand, DebugSessionEvent, 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 Python implements Sandbox {
|
|
4
6
|
ts: number;
|
|
@@ -22,7 +24,10 @@ declare class Python implements Sandbox {
|
|
|
22
24
|
waitingForInput: boolean;
|
|
23
25
|
pendingEof: boolean;
|
|
24
26
|
exit: boolean;
|
|
25
|
-
|
|
27
|
+
assetBridge: WorkerAssetBridge | null;
|
|
28
|
+
load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
|
|
29
|
+
set?: (value: number) => void;
|
|
30
|
+
} | import('svelte/store').Writable<number>): Promise<void>;
|
|
26
31
|
write(input: string): void;
|
|
27
32
|
eof(): void;
|
|
28
33
|
private flushPendingInput;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { WorkerAssetBridge } from './assetBridge';
|
|
2
|
+
import { resolveRuntimeAssetConfig } from './assets';
|
|
1
3
|
import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
|
|
2
4
|
class Python {
|
|
3
5
|
ts = Date.now();
|
|
@@ -17,17 +19,44 @@ class Python {
|
|
|
17
19
|
waitingForInput = false;
|
|
18
20
|
pendingEof = false;
|
|
19
21
|
exit = true;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
assetBridge = null;
|
|
23
|
+
load(runtimeAssets = '', code = '', log = true, _args = [], _options = {}, progress) {
|
|
24
|
+
return new Promise(async (resolve, reject) => {
|
|
22
25
|
this.pendingInput = [];
|
|
23
26
|
this.waitingForInput = false;
|
|
24
27
|
this.pendingEof = false;
|
|
28
|
+
const assetConfig = resolveRuntimeAssetConfig('python', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
29
|
+
const needsWorkerReset = !this.worker || !this.assetBridge || !this.assetBridge.matches(assetConfig);
|
|
30
|
+
if (needsWorkerReset && this.worker) {
|
|
31
|
+
this.worker.terminate();
|
|
32
|
+
delete this.worker;
|
|
33
|
+
this.assetBridge = null;
|
|
34
|
+
}
|
|
25
35
|
if (!this.worker) {
|
|
26
36
|
this.worker = new (await import('./worker/python?worker')).default();
|
|
27
|
-
this.
|
|
28
|
-
this.worker.
|
|
37
|
+
this.assetBridge = new WorkerAssetBridge(this.worker, 'python', assetConfig, progress);
|
|
38
|
+
this.worker.onmessage = (event) => {
|
|
39
|
+
if (this.assetBridge?.handleMessage(event))
|
|
40
|
+
return;
|
|
41
|
+
if (event.data?.load)
|
|
42
|
+
resolve();
|
|
43
|
+
if (event.data?.error)
|
|
44
|
+
reject(event.data.error);
|
|
45
|
+
};
|
|
46
|
+
this.worker.postMessage({
|
|
47
|
+
load: true,
|
|
48
|
+
log,
|
|
49
|
+
code,
|
|
50
|
+
assets: {
|
|
51
|
+
baseUrl: assetConfig.baseUrl,
|
|
52
|
+
useAssetBridge: assetConfig.useAssetBridge
|
|
53
|
+
}
|
|
54
|
+
});
|
|
29
55
|
}
|
|
30
56
|
else {
|
|
57
|
+
if (!this.assetBridge)
|
|
58
|
+
return reject('Worker asset bridge unavailable');
|
|
59
|
+
this.assetBridge.rebind(this.worker, assetConfig, progress);
|
|
31
60
|
this.worker.postMessage({ log });
|
|
32
61
|
resolve();
|
|
33
62
|
}
|
|
@@ -62,6 +91,8 @@ class Python {
|
|
|
62
91
|
return reject('Worker not loaded');
|
|
63
92
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
64
93
|
const handler = (event) => {
|
|
94
|
+
if (this.assetBridge?.handleMessage(event))
|
|
95
|
+
return;
|
|
65
96
|
if (!this.worker)
|
|
66
97
|
return reject('Worker not loaded');
|
|
67
98
|
if (_uid !== this.uid)
|
|
@@ -131,7 +162,8 @@ class Python {
|
|
|
131
162
|
Atomics.store(control, 1, 5);
|
|
132
163
|
Atomics.add(control, 0, 1);
|
|
133
164
|
Atomics.notify(control, 0);
|
|
134
|
-
return (await waitForBufferedSequenceChange(this.watchResultBuffer, previousSequence, 5000)) ??
|
|
165
|
+
return ((await waitForBufferedSequenceChange(this.watchResultBuffer, previousSequence, 5000)) ??
|
|
166
|
+
'?');
|
|
135
167
|
}
|
|
136
168
|
kill() {
|
|
137
169
|
this.terminate();
|
|
@@ -159,6 +191,7 @@ class Python {
|
|
|
159
191
|
if (!this.exit) {
|
|
160
192
|
this.worker?.terminate?.();
|
|
161
193
|
delete this.worker;
|
|
194
|
+
this.assetBridge = null;
|
|
162
195
|
this.exit = true;
|
|
163
196
|
}
|
|
164
197
|
}
|