wasm-idle 0.2.5 → 0.3.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.
Files changed (54) hide show
  1. package/dist/clang/app.d.ts +44 -0
  2. package/dist/clang/app.js +270 -2
  3. package/dist/clang/index.d.ts +40 -9
  4. package/dist/clang/index.js +603 -15
  5. package/dist/clang/memory/memFs.d.ts +3 -1
  6. package/dist/clang/memory/memFs.js +12 -1
  7. package/dist/clang/memory/memory.d.ts +8 -4
  8. package/dist/clang/memory/memory.js +14 -0
  9. package/dist/clang/memory/tar.d.ts +1 -1
  10. package/dist/clang/memory/tar.js +1 -1
  11. package/dist/clang/wasm.d.ts +1 -1
  12. package/dist/clang/wasm.js +13 -4
  13. package/dist/clangd/config.d.ts +18 -0
  14. package/dist/clangd/config.js +14 -0
  15. package/dist/clangd/jsonStream.d.ts +9 -0
  16. package/dist/clangd/jsonStream.js +48 -0
  17. package/dist/clangd/session.d.ts +14 -0
  18. package/dist/clangd/session.js +94 -0
  19. package/dist/clangd/worker.d.ts +1 -0
  20. package/dist/clangd/worker.js +130 -0
  21. package/dist/debug/editor/index.d.ts +1 -0
  22. package/dist/debug/editor/index.js +1 -0
  23. package/dist/debug/editor/monaco.d.ts +18 -0
  24. package/dist/debug/editor/monaco.js +105 -0
  25. package/dist/debug/expression.d.ts +2 -0
  26. package/dist/debug/expression.js +262 -0
  27. package/dist/debug/index.d.ts +5 -0
  28. package/dist/debug/index.js +4 -0
  29. package/dist/debug/language/cpp.d.ts +2 -0
  30. package/dist/debug/language/cpp.js +7 -0
  31. package/dist/debug/language/index.d.ts +2 -0
  32. package/dist/debug/language/index.js +1 -0
  33. package/dist/debug/language/types.d.ts +6 -0
  34. package/dist/debug/language/types.js +1 -0
  35. package/dist/debug/locals.d.ts +2 -0
  36. package/dist/debug/locals.js +8 -0
  37. package/dist/index.d.ts +3 -0
  38. package/dist/index.js +1 -0
  39. package/dist/playground/clang.d.ts +8 -2
  40. package/dist/playground/clang.js +25 -4
  41. package/dist/playground/options.d.ts +39 -0
  42. package/dist/playground/options.js +1 -0
  43. package/dist/playground/python.d.ts +10 -2
  44. package/dist/playground/python.js +6 -3
  45. package/dist/playground/sandbox.d.ts +15 -2
  46. package/dist/playground/worker/clang.js +21 -4
  47. package/dist/playground/worker/python.js +196 -3
  48. package/dist/terminal/Terminal.svelte +35 -11
  49. package/dist/terminal/Terminal.svelte.d.ts +12 -9
  50. package/dist/terminal/index.d.ts +2 -0
  51. package/dist/terminal/theme.js +2 -2
  52. package/dist/terminal/types.d.ts +13 -0
  53. package/dist/terminal/types.js +1 -0
  54. package/package.json +42 -33
@@ -1,10 +1,46 @@
1
+ import type { DebugFrame, DebugVariable, DebugVariableMetadata } from '../playground/options';
1
2
  import { Memory, type MemFS } from './memory';
3
+ interface DebugSession {
4
+ buffer?: Int32Array;
5
+ interruptBuffer?: Uint8Array;
6
+ breakpoints: Set<number>;
7
+ pauseOnEntry: boolean;
8
+ stepArmed: boolean;
9
+ nextLineArmed: boolean;
10
+ stepOutArmed: boolean;
11
+ callDepth: number;
12
+ stepOutDepth: number;
13
+ currentFunctionId: number;
14
+ currentLine: number;
15
+ resumeSkipActive: boolean;
16
+ resumeSkipFunctionId: number;
17
+ resumeSkipLine: number;
18
+ nextLineFunctionId: number;
19
+ nextLineLine: number;
20
+ variableMetadata: Record<number, DebugVariableMetadata[]>;
21
+ functionMetadata: Record<number, string>;
22
+ frames: Array<{
23
+ functionId: number;
24
+ functionName: string;
25
+ line: number;
26
+ values: Map<number, string>;
27
+ }>;
28
+ onPause?: (event: {
29
+ type: 'pause';
30
+ line: number;
31
+ reason: string;
32
+ locals: DebugVariable[];
33
+ callStack: DebugFrame[];
34
+ }) => void;
35
+ }
2
36
  export default class App {
3
37
  ready: Promise<void>;
4
38
  mem: Memory;
5
39
  memfs: MemFS;
6
40
  instance: WebAssembly.Instance;
7
41
  exports: any;
42
+ trace: (message: string) => void;
43
+ debugSession?: DebugSession;
8
44
  argv: string[];
9
45
  environ: {
10
46
  [key: string]: string;
@@ -14,6 +50,13 @@ export default class App {
14
50
  constructor(module: WebAssembly.Module, memfs: MemFS, name: string, ...args: string[]);
15
51
  run(): Promise<boolean | undefined>;
16
52
  proc_exit(code: number): void;
53
+ __wasm_idle_debug_enter(functionId: number): number;
54
+ __wasm_idle_debug_leave(functionId: number): number;
55
+ __wasm_idle_debug_value_num(functionId: number, slot: number, value: number): number;
56
+ __wasm_idle_debug_value_bool(functionId: number, slot: number, value: number): number;
57
+ __wasm_idle_debug_value_addr(functionId: number, slot: number, value: number): number;
58
+ __wasm_idle_debug_value_text(functionId: number, slot: number, ptr: number, len: number): number;
59
+ __wasm_idle_debug_line(functionId: number, line: number): number;
17
60
  environ_sizes_get(environ_count_out: number, environ_buf_size_out: number): number;
18
61
  environ_get(environ_ptrs: number, environ_buf: number): number;
19
62
  args_sizes_get(argc_out: number, argv_buf_size_out: number): number;
@@ -22,3 +65,4 @@ export default class App {
22
65
  clock_time_get(): void;
23
66
  poll_oneoff(): void;
24
67
  }
68
+ export {};
package/dist/clang/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { bindNew } from './apply';
2
2
  import { Memory } from './memory';
3
3
  import { getInstance } from './wasm';
4
- import { NotImplemented, ProcExit } from './error';
4
+ import { AbortError, NotImplemented, ProcExit } from './error';
5
5
  const ESUCCESS = 0;
6
6
  const RAF_PROC_EXIT_CODE = 0xc0c0a;
7
7
  export default class App {
@@ -10,6 +10,8 @@ export default class App {
10
10
  memfs;
11
11
  instance = null;
12
12
  exports;
13
+ trace = () => { };
14
+ debugSession;
13
15
  argv;
14
16
  environ;
15
17
  handles = new Map();
@@ -18,7 +20,7 @@ export default class App {
18
20
  this.argv = [name, ...args];
19
21
  this.environ = { USER: 'jungol' };
20
22
  this.memfs = memfs;
21
- const env = bindNew(this);
23
+ const env = bindNew(this, '__wasm_idle_debug_enter', '__wasm_idle_debug_leave', '__wasm_idle_debug_line', '__wasm_idle_debug_value_num', '__wasm_idle_debug_value_bool', '__wasm_idle_debug_value_addr', '__wasm_idle_debug_value_text');
22
24
  const wasi_unstable = {
23
25
  ...bindNew(this, 'proc_exit', 'environ_sizes_get', 'environ_get', 'args_sizes_get', 'args_get', 'random_get', 'clock_time_get', 'poll_oneoff'),
24
26
  ...this.memfs.exports
@@ -32,12 +34,14 @@ export default class App {
32
34
  }
33
35
  async run() {
34
36
  await this.ready;
37
+ this.trace(`start(argv=${JSON.stringify(this.argv)}, exports=${JSON.stringify(Object.keys(this.exports || {}))})`);
35
38
  try {
36
39
  this.exports._start();
37
40
  }
38
41
  catch (exn) {
39
42
  let writeStack = true;
40
43
  if (exn instanceof ProcExit) {
44
+ this.trace(`proc_exit(code=${exn.code})`);
41
45
  if (exn.code === RAF_PROC_EXIT_CODE) {
42
46
  console.log('Allowing rAF after exit.');
43
47
  return true;
@@ -48,6 +52,8 @@ export default class App {
48
52
  return false;
49
53
  writeStack = false;
50
54
  }
55
+ if (exn instanceof NotImplemented)
56
+ this.trace(`not_implemented(${exn.message})`);
51
57
  // Write error message.
52
58
  let msg = `\x1b[91mError: ${exn.message}`;
53
59
  if (writeStack)
@@ -57,10 +63,272 @@ export default class App {
57
63
  // Propagate error.
58
64
  throw exn;
59
65
  }
66
+ this.trace('start() returned without proc_exit');
60
67
  }
61
68
  proc_exit(code) {
62
69
  throw new ProcExit(code);
63
70
  }
71
+ __wasm_idle_debug_enter(functionId) {
72
+ const session = this.debugSession;
73
+ if (!session?.buffer)
74
+ return ESUCCESS;
75
+ session.callDepth += 1;
76
+ session.currentFunctionId = functionId;
77
+ session.frames.push({
78
+ functionId,
79
+ functionName: session.functionMetadata[functionId] || `fn_${functionId}`,
80
+ line: 0,
81
+ values: new Map()
82
+ });
83
+ this.trace(`enter(function=${functionId}, depth=${session.callDepth})`);
84
+ return ESUCCESS;
85
+ }
86
+ __wasm_idle_debug_leave(functionId) {
87
+ const session = this.debugSession;
88
+ if (!session?.buffer)
89
+ return ESUCCESS;
90
+ this.trace(`leave(function=${functionId}, depth=${session.callDepth})`);
91
+ session.callDepth = Math.max(0, session.callDepth - 1);
92
+ if (session.currentFunctionId === functionId)
93
+ session.currentFunctionId = 0;
94
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
95
+ if (session.frames[index]?.functionId === functionId) {
96
+ session.frames.splice(index, 1);
97
+ break;
98
+ }
99
+ }
100
+ return ESUCCESS;
101
+ }
102
+ __wasm_idle_debug_value_num(functionId, slot, value) {
103
+ const session = this.debugSession;
104
+ if (!session?.buffer)
105
+ return ESUCCESS;
106
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
107
+ const frame = session.frames[index];
108
+ if (frame?.functionId !== functionId)
109
+ continue;
110
+ frame.values.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
111
+ break;
112
+ }
113
+ return ESUCCESS;
114
+ }
115
+ __wasm_idle_debug_value_bool(functionId, slot, value) {
116
+ const session = this.debugSession;
117
+ if (!session?.buffer)
118
+ return ESUCCESS;
119
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
120
+ const frame = session.frames[index];
121
+ if (frame?.functionId !== functionId)
122
+ continue;
123
+ frame.values.set(slot, value ? 'true' : 'false');
124
+ break;
125
+ }
126
+ return ESUCCESS;
127
+ }
128
+ __wasm_idle_debug_value_addr(functionId, slot, value) {
129
+ const session = this.debugSession;
130
+ if (!session?.buffer)
131
+ return ESUCCESS;
132
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
133
+ const frame = session.frames[index];
134
+ if (frame?.functionId !== functionId)
135
+ continue;
136
+ frame.values.set(slot, String(value >>> 0));
137
+ break;
138
+ }
139
+ return ESUCCESS;
140
+ }
141
+ __wasm_idle_debug_value_text(functionId, slot, ptr, len) {
142
+ const session = this.debugSession;
143
+ if (!session?.buffer)
144
+ return ESUCCESS;
145
+ this.mem?.check?.();
146
+ const text = this.mem?.readStr ? this.mem.readStr(ptr, len) : '?';
147
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
148
+ const frame = session.frames[index];
149
+ if (frame?.functionId !== functionId)
150
+ continue;
151
+ frame.values.set(slot, text);
152
+ break;
153
+ }
154
+ return ESUCCESS;
155
+ }
156
+ __wasm_idle_debug_line(functionId, line) {
157
+ const session = this.debugSession;
158
+ if (!session?.buffer)
159
+ return ESUCCESS;
160
+ if (session.resumeSkipActive) {
161
+ if (functionId === session.resumeSkipFunctionId && line === session.resumeSkipLine) {
162
+ return ESUCCESS;
163
+ }
164
+ session.resumeSkipActive = false;
165
+ session.resumeSkipFunctionId = 0;
166
+ session.resumeSkipLine = 0;
167
+ }
168
+ let reason = '';
169
+ if (session.pauseOnEntry)
170
+ reason = 'entry';
171
+ else if (session.breakpoints.has(line))
172
+ reason = 'breakpoint';
173
+ else if (session.stepArmed)
174
+ reason = 'step';
175
+ else if (session.nextLineArmed &&
176
+ functionId === session.nextLineFunctionId &&
177
+ line !== session.nextLineLine) {
178
+ reason = 'nextLine';
179
+ }
180
+ else if (session.stepOutArmed && session.callDepth <= session.stepOutDepth) {
181
+ reason = 'stepOut';
182
+ }
183
+ if (!reason)
184
+ return ESUCCESS;
185
+ session.currentFunctionId = functionId;
186
+ session.currentLine = line;
187
+ const frame = [...session.frames].reverse().find((candidate) => candidate.functionId === functionId);
188
+ if (frame)
189
+ frame.line = line;
190
+ session.pauseOnEntry = false;
191
+ session.stepArmed = false;
192
+ session.nextLineArmed = false;
193
+ session.stepOutArmed = false;
194
+ this.trace(`pause(function=${functionId}, line=${line}, reason=${reason})`);
195
+ const locals = session.variableMetadata[functionId]?.flatMap((variable) => {
196
+ if (line < variable.fromLine || line > variable.toLine)
197
+ return [];
198
+ if (variable.kind === 'array') {
199
+ this.mem?.check?.();
200
+ const address = Number(frame?.values.get(variable.slot) ?? Number.NaN);
201
+ const dimensions = variable.dimensions?.length
202
+ ? variable.dimensions
203
+ : variable.length
204
+ ? [variable.length]
205
+ : [];
206
+ if (!Number.isFinite(address) || address <= 0 || !dimensions.length || !variable.elementKind) {
207
+ return [{ name: variable.name, value: '?' }];
208
+ }
209
+ const elementStride = variable.elementKind === 'double'
210
+ ? 8
211
+ : variable.elementKind === 'bool' || variable.elementKind === 'char'
212
+ ? 1
213
+ : 4;
214
+ if (dimensions.length === 2) {
215
+ const previewRows = Math.min(dimensions[0], 4);
216
+ const previewCols = Math.min(dimensions[1], 8);
217
+ const rows = [];
218
+ for (let row = 0; row < previewRows; row += 1) {
219
+ const values = [];
220
+ for (let col = 0; col < previewCols; col += 1) {
221
+ const offset = address + (row * dimensions[1] + col) * elementStride;
222
+ if (variable.elementKind === 'bool') {
223
+ values.push(this.mem.read8(offset) ? 'true' : 'false');
224
+ continue;
225
+ }
226
+ if (variable.elementKind === 'char') {
227
+ const charCode = this.mem.read8(offset);
228
+ values.push(charCode >= 0x20 && charCode <= 0x7e ? `'${String.fromCharCode(charCode)}'` : `${charCode}`);
229
+ continue;
230
+ }
231
+ if (variable.elementKind === 'float') {
232
+ values.push(`${this.mem.readFloat32(offset)}`);
233
+ continue;
234
+ }
235
+ if (variable.elementKind === 'double') {
236
+ values.push(`${this.mem.readFloat64(offset)}`);
237
+ continue;
238
+ }
239
+ values.push(`${this.mem.readInt32(offset)}`);
240
+ }
241
+ rows.push(`[${values.join(', ')}${dimensions[1] > previewCols ? ', ...' : ''}]`);
242
+ }
243
+ return [
244
+ {
245
+ name: variable.name,
246
+ value: `[${rows.join(', ')}${dimensions[0] > previewRows ? ', ...' : ''}]`
247
+ }
248
+ ];
249
+ }
250
+ const previewLength = Math.min(dimensions[0], 8);
251
+ const values = [];
252
+ for (let index = 0; index < previewLength; index += 1) {
253
+ const offset = address + index * elementStride;
254
+ if (variable.elementKind === 'bool') {
255
+ values.push(this.mem.read8(offset) ? 'true' : 'false');
256
+ continue;
257
+ }
258
+ if (variable.elementKind === 'char') {
259
+ const charCode = this.mem.read8(offset);
260
+ values.push(charCode >= 0x20 && charCode <= 0x7e ? `'${String.fromCharCode(charCode)}'` : `${charCode}`);
261
+ continue;
262
+ }
263
+ if (variable.elementKind === 'float') {
264
+ values.push(`${this.mem.readFloat32(offset)}`);
265
+ continue;
266
+ }
267
+ if (variable.elementKind === 'double') {
268
+ values.push(`${this.mem.readFloat64(offset)}`);
269
+ continue;
270
+ }
271
+ values.push(`${this.mem.readInt32(offset)}`);
272
+ }
273
+ return [
274
+ {
275
+ name: variable.name,
276
+ value: `[${values.join(', ')}${dimensions[0] > previewLength ? ', ...' : ''}]`
277
+ }
278
+ ];
279
+ }
280
+ const value = frame?.values.get(variable.slot) ?? '?';
281
+ return [{ name: variable.name, value }];
282
+ }) || [];
283
+ session.onPause?.({
284
+ type: 'pause',
285
+ line,
286
+ reason,
287
+ locals,
288
+ callStack: [...session.frames]
289
+ .reverse()
290
+ .map((stackFrame) => ({ functionName: stackFrame.functionName, line: stackFrame.line }))
291
+ });
292
+ const sequence = Atomics.load(session.buffer, 0);
293
+ while (true) {
294
+ if (session.interruptBuffer?.[0] === 2)
295
+ throw new AbortError();
296
+ Atomics.wait(session.buffer, 0, sequence, 100);
297
+ if (session.interruptBuffer?.[0] === 2)
298
+ throw new AbortError();
299
+ const command = Atomics.exchange(session.buffer, 1, 0);
300
+ if (command === 1) {
301
+ session.resumeSkipActive = true;
302
+ session.resumeSkipFunctionId = session.currentFunctionId;
303
+ session.resumeSkipLine = session.currentLine;
304
+ return ESUCCESS;
305
+ }
306
+ if (command === 2) {
307
+ session.stepArmed = true;
308
+ session.resumeSkipActive = true;
309
+ session.resumeSkipFunctionId = session.currentFunctionId;
310
+ session.resumeSkipLine = session.currentLine;
311
+ return ESUCCESS;
312
+ }
313
+ if (command === 3) {
314
+ session.nextLineArmed = true;
315
+ session.nextLineFunctionId = session.currentFunctionId;
316
+ session.nextLineLine = session.currentLine;
317
+ session.resumeSkipActive = true;
318
+ session.resumeSkipFunctionId = session.currentFunctionId;
319
+ session.resumeSkipLine = session.currentLine;
320
+ return ESUCCESS;
321
+ }
322
+ if (command === 4) {
323
+ session.stepOutArmed = true;
324
+ session.stepOutDepth = Math.max(0, session.callDepth - 1);
325
+ session.resumeSkipActive = true;
326
+ session.resumeSkipFunctionId = session.currentFunctionId;
327
+ session.resumeSkipLine = session.currentLine;
328
+ return ESUCCESS;
329
+ }
330
+ }
331
+ }
64
332
  environ_sizes_get(environ_count_out, environ_buf_size_out) {
65
333
  this.mem.check();
66
334
  let size = 0;
@@ -1,3 +1,4 @@
1
+ import type { DebugVariable, DebugVariableMetadata } from '../playground/options';
1
2
  import App from './app';
2
3
  import { MemFS } from './memory';
3
4
  import { type Writable } from 'svelte/store';
@@ -5,10 +6,24 @@ interface APIOption {
5
6
  stdin: () => string;
6
7
  stdout: (str: string) => void;
7
8
  progress: (value: number) => void;
9
+ onDebugEvent?: (event: {
10
+ type: 'pause';
11
+ line: number;
12
+ reason: string;
13
+ locals: DebugVariable[];
14
+ }) => void;
8
15
  log?: boolean;
9
16
  showTiming?: boolean;
10
17
  path: string;
11
18
  }
19
+ interface DebugRunOptions {
20
+ args?: string[];
21
+ debug?: boolean;
22
+ breakpoints?: number[];
23
+ pauseOnEntry?: boolean;
24
+ debugBuffer?: Int32Array;
25
+ interruptBuffer?: Uint8Array;
26
+ }
12
27
  export default class Clang {
13
28
  ready: Promise<void>;
14
29
  memfs: MemFS;
@@ -18,9 +33,27 @@ export default class Clang {
18
33
  };
19
34
  showTiming: boolean;
20
35
  log: boolean;
21
- lastCode: string;
36
+ debug: boolean;
37
+ debugBreakpoints: Set<number>;
38
+ debugPauseOnEntry: boolean;
39
+ debugBuffer?: Int32Array;
40
+ debugInterruptBuffer?: Uint8Array;
41
+ onDebugEvent?: (event: {
42
+ type: 'pause';
43
+ line: number;
44
+ reason: string;
45
+ locals: DebugVariable[];
46
+ callStack: {
47
+ functionName: string;
48
+ line: number;
49
+ }[];
50
+ }) => void;
51
+ debugVariableMetadata: Record<number, DebugVariableMetadata[]>;
52
+ debugFunctionMetadata: Record<number, string>;
53
+ lastBuildKey: string;
22
54
  path: string;
23
- wasm?: WebAssembly.Instance;
55
+ wasm?: WebAssembly.Module;
56
+ traceStartedAt: number;
24
57
  progress: {
25
58
  clang: Writable<number>;
26
59
  lld: Writable<number>;
@@ -28,16 +61,14 @@ export default class Clang {
28
61
  };
29
62
  constructor(options: APIOption);
30
63
  hostLog(message: string): void;
64
+ beginTrace(debug: boolean): void;
65
+ trace(message: string): void;
31
66
  hostLogAsync(message: string, promise: Promise<any>): Promise<any>;
32
67
  getModule(name: string, progress?: Writable<number>): Promise<any>;
33
68
  compile(options: any): Promise<App | null>;
34
- link(obj: string, wasm: string): Promise<App | null>;
69
+ link(obj: string, wasm: string, debug?: boolean): Promise<App | null>;
35
70
  run(module: WebAssembly.Module, out: boolean, ...args: string[]): Promise<App | null>;
36
- compileLink(code: string, { args }?: {
37
- args?: never[] | undefined;
38
- }): Promise<any>;
39
- compileLinkRun(code: string, { args }?: {
40
- args?: never[] | undefined;
41
- }): Promise<App | null>;
71
+ compileLink(code: string, options?: DebugRunOptions): Promise<any>;
72
+ compileLinkRun(code: string, options?: DebugRunOptions): Promise<App | null>;
42
73
  }
43
74
  export {};