wasm-idle 0.2.5 → 0.3.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 (54) hide show
  1. package/dist/clang/app.d.ts +44 -0
  2. package/dist/clang/app.js +229 -2
  3. package/dist/clang/index.d.ts +40 -9
  4. package/dist/clang/index.js +591 -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 +182 -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 +38 -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,231 @@ 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
+ if (!Number.isFinite(address) || address <= 0 || !variable.length || !variable.elementKind) {
202
+ return [{ name: variable.name, value: '?' }];
203
+ }
204
+ const previewLength = Math.min(variable.length, 8);
205
+ const values = [];
206
+ for (let index = 0; index < previewLength; index += 1) {
207
+ const offset = address +
208
+ (variable.elementKind === 'double'
209
+ ? index * 8
210
+ : variable.elementKind === 'bool' || variable.elementKind === 'char'
211
+ ? index
212
+ : index * 4);
213
+ if (variable.elementKind === 'bool') {
214
+ values.push(this.mem.read8(offset) ? 'true' : 'false');
215
+ continue;
216
+ }
217
+ if (variable.elementKind === 'char') {
218
+ const charCode = this.mem.read8(offset);
219
+ values.push(charCode >= 0x20 && charCode <= 0x7e ? `'${String.fromCharCode(charCode)}'` : `${charCode}`);
220
+ continue;
221
+ }
222
+ if (variable.elementKind === 'float') {
223
+ values.push(`${this.mem.readFloat32(offset)}`);
224
+ continue;
225
+ }
226
+ if (variable.elementKind === 'double') {
227
+ values.push(`${this.mem.readFloat64(offset)}`);
228
+ continue;
229
+ }
230
+ values.push(`${this.mem.readInt32(offset)}`);
231
+ }
232
+ return [
233
+ {
234
+ name: variable.name,
235
+ value: `[${values.join(', ')}${variable.length > previewLength ? ', ...' : ''}]`
236
+ }
237
+ ];
238
+ }
239
+ const value = frame?.values.get(variable.slot) ?? '?';
240
+ return [{ name: variable.name, value }];
241
+ }) || [];
242
+ session.onPause?.({
243
+ type: 'pause',
244
+ line,
245
+ reason,
246
+ locals,
247
+ callStack: [...session.frames]
248
+ .reverse()
249
+ .map((stackFrame) => ({ functionName: stackFrame.functionName, line: stackFrame.line }))
250
+ });
251
+ const sequence = Atomics.load(session.buffer, 0);
252
+ while (true) {
253
+ if (session.interruptBuffer?.[0] === 2)
254
+ throw new AbortError();
255
+ Atomics.wait(session.buffer, 0, sequence, 100);
256
+ if (session.interruptBuffer?.[0] === 2)
257
+ throw new AbortError();
258
+ const command = Atomics.exchange(session.buffer, 1, 0);
259
+ if (command === 1) {
260
+ session.resumeSkipActive = true;
261
+ session.resumeSkipFunctionId = session.currentFunctionId;
262
+ session.resumeSkipLine = session.currentLine;
263
+ return ESUCCESS;
264
+ }
265
+ if (command === 2) {
266
+ session.stepArmed = true;
267
+ session.resumeSkipActive = true;
268
+ session.resumeSkipFunctionId = session.currentFunctionId;
269
+ session.resumeSkipLine = session.currentLine;
270
+ return ESUCCESS;
271
+ }
272
+ if (command === 3) {
273
+ session.nextLineArmed = true;
274
+ session.nextLineFunctionId = session.currentFunctionId;
275
+ session.nextLineLine = session.currentLine;
276
+ session.resumeSkipActive = true;
277
+ session.resumeSkipFunctionId = session.currentFunctionId;
278
+ session.resumeSkipLine = session.currentLine;
279
+ return ESUCCESS;
280
+ }
281
+ if (command === 4) {
282
+ session.stepOutArmed = true;
283
+ session.stepOutDepth = Math.max(0, session.callDepth - 1);
284
+ session.resumeSkipActive = true;
285
+ session.resumeSkipFunctionId = session.currentFunctionId;
286
+ session.resumeSkipLine = session.currentLine;
287
+ return ESUCCESS;
288
+ }
289
+ }
290
+ }
64
291
  environ_sizes_get(environ_count_out, environ_buf_size_out) {
65
292
  this.mem.check();
66
293
  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 {};