wasm-idle 0.3.2 → 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.
Files changed (37) hide show
  1. package/README.md +4 -2
  2. package/dist/clang/app.d.ts +2 -1
  3. package/dist/clang/app.js +149 -132
  4. package/dist/clang/index.js +2 -2
  5. package/dist/debug/expression.js +135 -19
  6. package/dist/debug/index.d.ts +1 -1
  7. package/dist/debug/index.js +1 -1
  8. package/dist/debug/language/index.d.ts +1 -0
  9. package/dist/debug/language/index.js +1 -0
  10. package/dist/debug/language/python.d.ts +2 -0
  11. package/dist/debug/language/python.js +14 -0
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.js +1 -1
  14. package/dist/playground/clang.d.ts +5 -2
  15. package/dist/playground/clang.js +44 -18
  16. package/dist/playground/index.d.ts +2 -1
  17. package/dist/playground/index.js +7 -1
  18. package/dist/playground/java.d.ts +28 -0
  19. package/dist/playground/java.js +135 -0
  20. package/dist/playground/javaStdin.d.ts +8 -0
  21. package/dist/playground/javaStdin.js +90 -0
  22. package/dist/playground/options.d.ts +10 -0
  23. package/dist/playground/python.d.ts +15 -5
  24. package/dist/playground/python.js +98 -21
  25. package/dist/playground/sandbox.d.ts +3 -1
  26. package/dist/playground/stdinBuffer.d.ts +7 -0
  27. package/dist/playground/stdinBuffer.js +100 -0
  28. package/dist/playground/teavmConfig.d.ts +3 -0
  29. package/dist/playground/teavmConfig.js +14 -0
  30. package/dist/playground/worker/clang.js +2 -20
  31. package/dist/playground/worker/java.d.ts +1 -0
  32. package/dist/playground/worker/java.js +248 -0
  33. package/dist/playground/worker/python.js +253 -27
  34. package/dist/terminal/Terminal.svelte +41 -11
  35. package/dist/terminal/Terminal.svelte.d.ts +2 -1
  36. package/dist/terminal/types.d.ts +3 -2
  37. package/package.json +1 -1
@@ -9,20 +9,23 @@ declare class Clang implements Sandbox {
9
9
  buffer: SharedArrayBuffer;
10
10
  debugBuffer: SharedArrayBuffer;
11
11
  interruptBuffer: SharedArrayBuffer;
12
- internalBuffer: string[];
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
  }
@@ -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
- internalBuffer = [];
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.internalBuffer = [];
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.internalBuffer.push(input);
36
+ this.pendingInput.push(input);
37
+ this.pendingEof = false;
38
+ this.flushPendingInput();
32
39
  }
33
- _write(input) {
34
- let strInfo = input, padding = 4 - (strInfo.length % 4);
35
- while (strInfo.length % 4 !== 3)
36
- strInfo += ' ';
37
- strInfo += padding;
38
- const buffer = new Int32Array(this.buffer);
39
- const enc = new TextEncoder();
40
- const data = enc.encode(strInfo);
41
- buffer.set(new Int32Array(data.buffer.slice(data.byteOffset), 0));
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 && this.internalBuffer.length)
57
- this._write(this.internalBuffer.splice(0, 1)[0]);
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.internalBuffer = [];
135
+ this.pendingInput = [];
136
+ this.waitingForInput = false;
137
+ this.pendingEof = false;
111
138
  if (this.worker)
112
139
  this.worker.onmessage = null;
113
- const buffer = new Int32Array(this.buffer);
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>;
@@ -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;
@@ -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: Worker;
12
+ worker?: Worker;
12
13
  buffer: SharedArrayBuffer;
14
+ debugBuffer: SharedArrayBuffer;
15
+ watchBuffer: SharedArrayBuffer;
16
+ watchResultBuffer: SharedArrayBuffer;
13
17
  interruptBuffer: SharedArrayBuffer;
14
- internalBuffer: string[];
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[], _options?: SandboxExecutionOptions): Promise<boolean | 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
  }