wasm-idle 0.3.2 → 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.
Files changed (46) hide show
  1. package/README.md +22 -2
  2. package/dist/clang/app.d.ts +2 -1
  3. package/dist/clang/app.js +166 -137
  4. package/dist/clang/index.d.ts +6 -0
  5. package/dist/clang/index.js +137 -21
  6. package/dist/debug/expression.js +135 -19
  7. package/dist/debug/index.d.ts +1 -1
  8. package/dist/debug/index.js +1 -1
  9. package/dist/debug/language/index.d.ts +1 -0
  10. package/dist/debug/language/index.js +1 -0
  11. package/dist/debug/language/python.d.ts +2 -0
  12. package/dist/debug/language/python.js +14 -0
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.js +1 -1
  15. package/dist/playground/assetBridge.d.ts +20 -0
  16. package/dist/playground/assetBridge.js +192 -0
  17. package/dist/playground/assets.d.ts +32 -0
  18. package/dist/playground/assets.js +97 -0
  19. package/dist/playground/clang.d.ts +11 -3
  20. package/dist/playground/clang.js +57 -20
  21. package/dist/playground/index.d.ts +2 -1
  22. package/dist/playground/index.js +10 -2
  23. package/dist/playground/java.d.ts +33 -0
  24. package/dist/playground/java.js +161 -0
  25. package/dist/playground/javaStdin.d.ts +8 -0
  26. package/dist/playground/javaStdin.js +323 -0
  27. package/dist/playground/options.d.ts +19 -0
  28. package/dist/playground/options.js +15 -1
  29. package/dist/playground/python.d.ts +21 -6
  30. package/dist/playground/python.js +135 -25
  31. package/dist/playground/sandbox.d.ts +5 -2
  32. package/dist/playground/stdinBuffer.d.ts +7 -0
  33. package/dist/playground/stdinBuffer.js +100 -0
  34. package/dist/playground/teavmConfig.d.ts +3 -0
  35. package/dist/playground/teavmConfig.js +14 -0
  36. package/dist/playground/worker/assets.d.ts +12 -0
  37. package/dist/playground/worker/assets.js +257 -0
  38. package/dist/playground/worker/clang.js +13 -23
  39. package/dist/playground/worker/java.d.ts +1 -0
  40. package/dist/playground/worker/java.js +228 -0
  41. package/dist/playground/worker/python.js +264 -58
  42. package/dist/terminal/Terminal.svelte +98 -18
  43. package/dist/terminal/Terminal.svelte.d.ts +4 -1
  44. package/dist/terminal/theme.js +2 -2
  45. package/dist/terminal/types.d.ts +4 -2
  46. package/package.json +11 -11
package/README.md CHANGED
@@ -2,8 +2,28 @@
2
2
 
3
3
  ![wasm-idle](static/image.jpeg)
4
4
 
5
- Executes C++, Python Code with working stdio.
5
+ Executes C++, Python, and Java code with working stdio.
6
6
 
7
7
  Refer to src/lib/clang.
8
8
 
9
- Powered by [wasm-clang](https://github.com/binji/wasm-clang) and Pyodide.
9
+ Java uses TeaVM's browser compiler/runtime. TeaVM compiler/runtime/classlib assets are bundled under `static/teavm/` by default, and the asset base URL can be overridden with `PUBLIC_TEAVM_BASE_URL`.
10
+
11
+ `Terminal` and `playground(...).load(...)` support either the legacy shared `path`/`rootUrl` or per-runtime asset config:
12
+
13
+ ```ts
14
+ import type { PlaygroundRuntimeAssets } from 'wasm-idle';
15
+
16
+ const runtimeAssets: PlaygroundRuntimeAssets = {
17
+ rootUrl: 'https://cdn.example.com/repl',
18
+ python: {
19
+ loader: async ({ asset }) => ({ url: `https://cdn.example.com/repl/pyodide/${asset}` })
20
+ },
21
+ java: {
22
+ baseUrl: 'https://cdn.example.com/repl/teavm/'
23
+ }
24
+ };
25
+ ```
26
+
27
+ Python custom loaders receive file names under the Pyodide asset root and can serve both core assets and package files. TeaVM custom loaders receive file names under the TeaVM asset root. Compressed TeaVM runtime assets are no longer unpacked inside the library; provide the final file URL or handle decompression in your own loader.
28
+
29
+ Powered by [wasm-clang](https://github.com/binji/wasm-clang), Pyodide, and TeaVM.
@@ -52,7 +52,8 @@ export default class App {
52
52
  constructor(module: WebAssembly.Module, memfs: MemFS, name: string, ...args: string[]);
53
53
  run(): Promise<boolean | undefined>;
54
54
  proc_exit(code: number): void;
55
- __wasm_idle_debug_enter(functionId: number): number;
55
+ private pauseDebugSession;
56
+ __wasm_idle_debug_enter(functionId: number, line: number): number;
56
57
  __wasm_idle_debug_leave(functionId: number): number;
57
58
  __wasm_idle_debug_value_num(functionId: number, slot: number, value: number): number;
58
59
  __wasm_idle_debug_value_bool(functionId: number, slot: number, value: number): number;
package/dist/clang/app.js CHANGED
@@ -68,139 +68,15 @@ export default class App {
68
68
  proc_exit(code) {
69
69
  throw new ProcExit(code);
70
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
- if (functionId === 0) {
107
- session.globalValues.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
108
- return ESUCCESS;
109
- }
110
- for (let index = session.frames.length - 1; index >= 0; index -= 1) {
111
- const frame = session.frames[index];
112
- if (frame?.functionId !== functionId)
113
- continue;
114
- frame.values.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
115
- break;
116
- }
117
- return ESUCCESS;
118
- }
119
- __wasm_idle_debug_value_bool(functionId, slot, value) {
120
- const session = this.debugSession;
121
- if (!session?.buffer)
122
- return ESUCCESS;
123
- if (functionId === 0) {
124
- session.globalValues.set(slot, value ? 'true' : 'false');
125
- return ESUCCESS;
126
- }
127
- for (let index = session.frames.length - 1; index >= 0; index -= 1) {
128
- const frame = session.frames[index];
129
- if (frame?.functionId !== functionId)
130
- continue;
131
- frame.values.set(slot, value ? 'true' : 'false');
132
- break;
133
- }
134
- return ESUCCESS;
135
- }
136
- __wasm_idle_debug_value_addr(functionId, slot, value) {
137
- const session = this.debugSession;
138
- if (!session?.buffer)
139
- return ESUCCESS;
140
- if (functionId === 0) {
141
- session.globalValues.set(slot, String(value >>> 0));
142
- return ESUCCESS;
143
- }
144
- for (let index = session.frames.length - 1; index >= 0; index -= 1) {
145
- const frame = session.frames[index];
146
- if (frame?.functionId !== functionId)
147
- continue;
148
- frame.values.set(slot, String(value >>> 0));
149
- break;
150
- }
151
- return ESUCCESS;
152
- }
153
- __wasm_idle_debug_value_text(functionId, slot, ptr, len) {
154
- const session = this.debugSession;
155
- if (!session?.buffer)
156
- return ESUCCESS;
157
- this.mem?.check?.();
158
- const text = this.mem?.readStr ? this.mem.readStr(ptr, len) : '?';
159
- if (functionId === 0) {
160
- session.globalValues.set(slot, text);
161
- return ESUCCESS;
162
- }
163
- for (let index = session.frames.length - 1; index >= 0; index -= 1) {
164
- const frame = session.frames[index];
165
- if (frame?.functionId !== functionId)
166
- continue;
167
- frame.values.set(slot, text);
168
- break;
169
- }
170
- return ESUCCESS;
171
- }
172
- __wasm_idle_debug_line(functionId, line) {
173
- const session = this.debugSession;
174
- if (!session?.buffer)
175
- return ESUCCESS;
176
- if (session.resumeSkipActive) {
177
- if (functionId === session.resumeSkipFunctionId && line === session.resumeSkipLine) {
178
- return ESUCCESS;
179
- }
180
- session.resumeSkipActive = false;
181
- session.resumeSkipFunctionId = 0;
182
- session.resumeSkipLine = 0;
183
- }
184
- let reason = '';
185
- if (session.pauseOnEntry)
186
- reason = 'entry';
187
- else if (session.breakpoints.has(line))
188
- reason = 'breakpoint';
189
- else if (session.stepArmed)
190
- reason = 'step';
191
- else if (session.nextLineArmed &&
192
- functionId === session.nextLineFunctionId &&
193
- line !== session.nextLineLine) {
194
- reason = 'nextLine';
195
- }
196
- else if (session.stepOutArmed && session.callDepth <= session.stepOutDepth) {
197
- reason = 'stepOut';
198
- }
199
- if (!reason)
71
+ pauseDebugSession(session, functionId, line, reason) {
72
+ const buffer = session.buffer;
73
+ if (!buffer)
200
74
  return ESUCCESS;
201
75
  session.currentFunctionId = functionId;
202
76
  session.currentLine = line;
203
- const frame = [...session.frames].reverse().find((candidate) => candidate.functionId === functionId);
77
+ const frame = [...session.frames]
78
+ .reverse()
79
+ .find((candidate) => candidate.functionId === functionId);
204
80
  if (frame)
205
81
  frame.line = line;
206
82
  session.pauseOnEntry = false;
@@ -219,7 +95,10 @@ export default class App {
219
95
  : variable.length
220
96
  ? [variable.length]
221
97
  : [];
222
- if (!Number.isFinite(address) || address <= 0 || !dimensions.length || !variable.elementKind) {
98
+ if (!Number.isFinite(address) ||
99
+ address <= 0 ||
100
+ !dimensions.length ||
101
+ !variable.elementKind) {
223
102
  return [{ name: variable.name, value: '?' }];
224
103
  }
225
104
  const elementStride = variable.elementKind === 'double'
@@ -241,7 +120,9 @@ export default class App {
241
120
  }
242
121
  if (variable.elementKind === 'char') {
243
122
  const charCode = this.mem.read8(offset);
244
- values.push(charCode >= 0x20 && charCode <= 0x7e ? `'${String.fromCharCode(charCode)}'` : `${charCode}`);
123
+ values.push(charCode >= 0x20 && charCode <= 0x7e
124
+ ? `'${String.fromCharCode(charCode)}'`
125
+ : `${charCode}`);
245
126
  continue;
246
127
  }
247
128
  if (variable.elementKind === 'float') {
@@ -273,7 +154,9 @@ export default class App {
273
154
  }
274
155
  if (variable.elementKind === 'char') {
275
156
  const charCode = this.mem.read8(offset);
276
- values.push(charCode >= 0x20 && charCode <= 0x7e ? `'${String.fromCharCode(charCode)}'` : `${charCode}`);
157
+ values.push(charCode >= 0x20 && charCode <= 0x7e
158
+ ? `'${String.fromCharCode(charCode)}'`
159
+ : `${charCode}`);
277
160
  continue;
278
161
  }
279
162
  if (variable.elementKind === 'float') {
@@ -312,16 +195,19 @@ export default class App {
312
195
  locals: [...locals, ...globals],
313
196
  callStack: [...session.frames]
314
197
  .reverse()
315
- .map((stackFrame) => ({ functionName: stackFrame.functionName, line: stackFrame.line }))
198
+ .map((stackFrame) => ({
199
+ functionName: stackFrame.functionName,
200
+ line: stackFrame.line
201
+ }))
316
202
  });
317
- const sequence = Atomics.load(session.buffer, 0);
203
+ const sequence = Atomics.load(buffer, 0);
318
204
  while (true) {
319
205
  if (session.interruptBuffer?.[0] === 2)
320
206
  throw new AbortError();
321
- Atomics.wait(session.buffer, 0, sequence, 100);
207
+ Atomics.wait(buffer, 0, sequence, 100);
322
208
  if (session.interruptBuffer?.[0] === 2)
323
209
  throw new AbortError();
324
- const command = Atomics.exchange(session.buffer, 1, 0);
210
+ const command = Atomics.exchange(buffer, 1, 0);
325
211
  if (command === 1) {
326
212
  session.resumeSkipActive = true;
327
213
  session.resumeSkipFunctionId = session.currentFunctionId;
@@ -354,6 +240,149 @@ export default class App {
354
240
  }
355
241
  }
356
242
  }
243
+ __wasm_idle_debug_enter(functionId, line) {
244
+ const session = this.debugSession;
245
+ if (!session?.buffer)
246
+ return ESUCCESS;
247
+ session.callDepth += 1;
248
+ session.currentFunctionId = functionId;
249
+ session.currentLine = line;
250
+ session.frames.push({
251
+ functionId,
252
+ functionName: session.functionMetadata[functionId] || `fn_${functionId}`,
253
+ line,
254
+ values: new Map()
255
+ });
256
+ this.trace(`enter(function=${functionId}, line=${line}, depth=${session.callDepth})`);
257
+ if (session.pauseOnEntry) {
258
+ return this.pauseDebugSession(session, functionId, line, 'entry');
259
+ }
260
+ if (session.stepArmed) {
261
+ return this.pauseDebugSession(session, functionId, line, 'step');
262
+ }
263
+ return ESUCCESS;
264
+ }
265
+ __wasm_idle_debug_leave(functionId) {
266
+ const session = this.debugSession;
267
+ if (!session?.buffer)
268
+ return ESUCCESS;
269
+ this.trace(`leave(function=${functionId}, depth=${session.callDepth})`);
270
+ if (session.nextLineArmed && functionId === session.nextLineFunctionId) {
271
+ session.nextLineArmed = false;
272
+ session.stepArmed = true;
273
+ }
274
+ session.callDepth = Math.max(0, session.callDepth - 1);
275
+ if (session.currentFunctionId === functionId)
276
+ session.currentFunctionId = 0;
277
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
278
+ if (session.frames[index]?.functionId === functionId) {
279
+ session.frames.splice(index, 1);
280
+ break;
281
+ }
282
+ }
283
+ return ESUCCESS;
284
+ }
285
+ __wasm_idle_debug_value_num(functionId, slot, value) {
286
+ const session = this.debugSession;
287
+ if (!session?.buffer)
288
+ return ESUCCESS;
289
+ if (functionId === 0) {
290
+ session.globalValues.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
291
+ return ESUCCESS;
292
+ }
293
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
294
+ const frame = session.frames[index];
295
+ if (frame?.functionId !== functionId)
296
+ continue;
297
+ frame.values.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
298
+ break;
299
+ }
300
+ return ESUCCESS;
301
+ }
302
+ __wasm_idle_debug_value_bool(functionId, slot, value) {
303
+ const session = this.debugSession;
304
+ if (!session?.buffer)
305
+ return ESUCCESS;
306
+ if (functionId === 0) {
307
+ session.globalValues.set(slot, value ? 'true' : 'false');
308
+ return ESUCCESS;
309
+ }
310
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
311
+ const frame = session.frames[index];
312
+ if (frame?.functionId !== functionId)
313
+ continue;
314
+ frame.values.set(slot, value ? 'true' : 'false');
315
+ break;
316
+ }
317
+ return ESUCCESS;
318
+ }
319
+ __wasm_idle_debug_value_addr(functionId, slot, value) {
320
+ const session = this.debugSession;
321
+ if (!session?.buffer)
322
+ return ESUCCESS;
323
+ if (functionId === 0) {
324
+ session.globalValues.set(slot, String(value >>> 0));
325
+ return ESUCCESS;
326
+ }
327
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
328
+ const frame = session.frames[index];
329
+ if (frame?.functionId !== functionId)
330
+ continue;
331
+ frame.values.set(slot, String(value >>> 0));
332
+ break;
333
+ }
334
+ return ESUCCESS;
335
+ }
336
+ __wasm_idle_debug_value_text(functionId, slot, ptr, len) {
337
+ const session = this.debugSession;
338
+ if (!session?.buffer)
339
+ return ESUCCESS;
340
+ this.mem?.check?.();
341
+ const text = this.mem?.readStr ? this.mem.readStr(ptr, len) : '?';
342
+ if (functionId === 0) {
343
+ session.globalValues.set(slot, text);
344
+ return ESUCCESS;
345
+ }
346
+ for (let index = session.frames.length - 1; index >= 0; index -= 1) {
347
+ const frame = session.frames[index];
348
+ if (frame?.functionId !== functionId)
349
+ continue;
350
+ frame.values.set(slot, text);
351
+ break;
352
+ }
353
+ return ESUCCESS;
354
+ }
355
+ __wasm_idle_debug_line(functionId, line) {
356
+ const session = this.debugSession;
357
+ if (!session?.buffer)
358
+ return ESUCCESS;
359
+ if (session.resumeSkipActive) {
360
+ if (functionId === session.resumeSkipFunctionId && line === session.resumeSkipLine) {
361
+ return ESUCCESS;
362
+ }
363
+ session.resumeSkipActive = false;
364
+ session.resumeSkipFunctionId = 0;
365
+ session.resumeSkipLine = 0;
366
+ }
367
+ let reason = '';
368
+ if (session.pauseOnEntry)
369
+ reason = 'entry';
370
+ else if (session.breakpoints.has(line))
371
+ reason = 'breakpoint';
372
+ else if (session.stepArmed)
373
+ reason = 'step';
374
+ else if (session.nextLineArmed &&
375
+ functionId === session.nextLineFunctionId &&
376
+ line !== session.nextLineLine) {
377
+ reason = 'nextLine';
378
+ }
379
+ else if (session.stepOutArmed && session.callDepth <= session.stepOutDepth) {
380
+ reason = 'stepOut';
381
+ }
382
+ if (!reason)
383
+ return ESUCCESS;
384
+ return this.pauseDebugSession(session, functionId, line, reason);
385
+ }
357
386
  environ_sizes_get(environ_count_out, environ_buf_size_out) {
358
387
  this.mem.check();
359
388
  let size = 0;
@@ -2,6 +2,7 @@ import type { DebugVariable, DebugVariableMetadata } from '../playground/options
2
2
  import App from './app';
3
3
  import { MemFS } from './memory';
4
4
  import { type Writable } from 'svelte/store';
5
+ type ClangSourceLanguage = 'C' | 'CPP';
5
6
  interface APIOption {
6
7
  stdin: () => string;
7
8
  stdout: (str: string) => void;
@@ -17,7 +18,12 @@ interface APIOption {
17
18
  path: string;
18
19
  }
19
20
  interface DebugRunOptions {
21
+ language?: ClangSourceLanguage;
20
22
  args?: string[];
23
+ compileArgs?: string[];
24
+ programArgs?: string[];
25
+ cppVersion?: string;
26
+ cVersion?: string;
21
27
  debug?: boolean;
22
28
  breakpoints?: number[];
23
29
  pauseOnEntry?: boolean;
@@ -20,7 +20,94 @@ const clangCommonArgs = [
20
20
  '80',
21
21
  '-fcolor-diagnostics'
22
22
  ];
23
- const clangLanguageStandardArg = '-std=gnu++2a';
23
+ const defaultCppStandardArg = '-std=gnu++2a';
24
+ const defaultCStandardArg = '-std=gnu11';
25
+ function normalizeStandardCode(value) {
26
+ return (value || '').trim().toUpperCase().replaceAll(/\s+/g, '');
27
+ }
28
+ function resolveCppStandardArg(version) {
29
+ switch (normalizeStandardCode(version)) {
30
+ case '03':
31
+ case 'CPP03':
32
+ case 'C++03':
33
+ case 'GNU++03':
34
+ case 'GNUC++03':
35
+ return '-std=gnu++03';
36
+ case '11':
37
+ case 'CPP11':
38
+ case 'C++11':
39
+ case 'GNU++11':
40
+ case 'GNUC++11':
41
+ return '-std=gnu++11';
42
+ case '14':
43
+ case 'CPP14':
44
+ case 'C++14':
45
+ case 'GNU++14':
46
+ case 'GNUC++14':
47
+ return '-std=gnu++14';
48
+ case '17':
49
+ case 'CPP17':
50
+ case 'C++17':
51
+ case 'GNU++17':
52
+ case 'GNUC++17':
53
+ return '-std=gnu++17';
54
+ case '20':
55
+ case '23':
56
+ case '26':
57
+ case 'CPP20':
58
+ case 'CPP23':
59
+ case 'CPP26':
60
+ case 'C++20':
61
+ case 'C++23':
62
+ case 'C++26':
63
+ case 'GNU++20':
64
+ case 'GNU++23':
65
+ case 'GNU++26':
66
+ case 'GNUC++20':
67
+ case 'GNUC++23':
68
+ case 'GNUC++26':
69
+ return defaultCppStandardArg;
70
+ default:
71
+ return defaultCppStandardArg;
72
+ }
73
+ }
74
+ function resolveCStandardArg(version) {
75
+ switch (normalizeStandardCode(version)) {
76
+ case '99':
77
+ case 'C99':
78
+ case 'GNU99':
79
+ case 'GNUC99':
80
+ return '-std=gnu99';
81
+ case '11':
82
+ case 'C11':
83
+ case 'GNU11':
84
+ case 'GNUC11':
85
+ return '-std=gnu11';
86
+ case '17':
87
+ case '18':
88
+ case 'C17':
89
+ case 'C18':
90
+ case 'GNU17':
91
+ case 'GNU18':
92
+ case 'GNUC17':
93
+ case 'GNUC18':
94
+ return '-std=gnu17';
95
+ default:
96
+ return defaultCStandardArg;
97
+ }
98
+ }
99
+ function resolveClangLanguageArgs(language, options) {
100
+ if (language === 'C') {
101
+ return {
102
+ languageArg: 'c',
103
+ standardArg: resolveCStandardArg(options.cVersion)
104
+ };
105
+ }
106
+ return {
107
+ languageArg: 'c++',
108
+ standardArg: resolveCppStandardArg(options.cppVersion)
109
+ };
110
+ }
24
111
  const toUtf8 = (text) => {
25
112
  const surrogate = encodeURIComponent(text);
26
113
  let result = '';
@@ -127,8 +214,11 @@ export default class Clang {
127
214
  const input = options.input;
128
215
  let source = options.code;
129
216
  const obj = options.obj;
217
+ const language = options.language === 'C' ? 'C' : 'CPP';
218
+ const compileArgs = options.compileArgs ?? options.args ?? [];
219
+ const { languageArg, standardArg } = resolveClangLanguageArgs(language, options);
130
220
  const debug = !!options.debug;
131
- const opt = debug ? '0' : (options.opt || '2');
221
+ const opt = debug ? '0' : options.opt || '2';
132
222
  if (debug) {
133
223
  const lines = source.split('\n');
134
224
  let braceDepth = 0;
@@ -151,7 +241,7 @@ export default class Clang {
151
241
  '#include <string>',
152
242
  '#include <type_traits>',
153
243
  '#include <vector>',
154
- 'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_enter"))) void __wasm_idle_debug_enter(int functionId);',
244
+ 'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_enter"))) void __wasm_idle_debug_enter(int functionId, int line);',
155
245
  'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_leave"))) void __wasm_idle_debug_leave(int functionId);',
156
246
  'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_value_num"))) void __wasm_idle_debug_value_num(int functionId, int slot, double value);',
157
247
  'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_value_bool"))) void __wasm_idle_debug_value_bool(int functionId, int slot, int value);',
@@ -231,7 +321,8 @@ export default class Clang {
231
321
  }
232
322
  else {
233
323
  analysisLine =
234
- analysisLine.slice(0, commentStart) + analysisLine.slice(commentEnd + 2);
324
+ analysisLine.slice(0, commentStart) +
325
+ analysisLine.slice(commentEnd + 2);
235
326
  }
236
327
  }
237
328
  const lineComment = analysisLine.indexOf('//');
@@ -343,7 +434,9 @@ export default class Clang {
343
434
  let declarationParenDepth = 0;
344
435
  let declarationBraceDepth = 0;
345
436
  for (const character of declarationMatch[2]) {
346
- if (character === ',' && declarationParenDepth === 0 && declarationBraceDepth === 0) {
437
+ if (character === ',' &&
438
+ declarationParenDepth === 0 &&
439
+ declarationBraceDepth === 0) {
347
440
  if (declarationBuffer.trim())
348
441
  declarations.push(declarationBuffer.trim());
349
442
  declarationBuffer = '';
@@ -491,7 +584,8 @@ export default class Clang {
491
584
  }
492
585
  }
493
586
  for (const [name, variable] of globalVariables) {
494
- if (currentFunctionVariables.has(name) || currentFunctionContainers.has(name))
587
+ if (currentFunctionVariables.has(name) ||
588
+ currentFunctionContainers.has(name))
495
589
  continue;
496
590
  const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
497
591
  if (new RegExp(`(?:^|[^\\w])(?:\\+\\+|--)\\s*${escapedName}\\b`).test(normalized) ||
@@ -529,7 +623,8 @@ export default class Clang {
529
623
  for (const [name, variable] of currentFunctionVariables) {
530
624
  const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
531
625
  const mutationPattern = new RegExp(`(?:^|[^\\w])(?:\\+\\+|--)\\s*${escapedName}\\b|\\b${escapedName}\\s*(?:[+\\-*/%]?=|\\+\\+|--)`);
532
- if (!initLooksLikeDeclaration && mutationPattern.test(initSource)) {
626
+ if (!initLooksLikeDeclaration &&
627
+ mutationPattern.test(initSource)) {
533
628
  initHooks.push(`${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(${currentFunctionId}, ${variable.slot}, ${name})`);
534
629
  }
535
630
  if (mutationPattern.test(updateSource)) {
@@ -584,7 +679,7 @@ export default class Clang {
584
679
  nextVariableSlot = 1;
585
680
  currentFunctionVariables = new Map();
586
681
  currentFunctionContainers = new Map();
587
- instrumented.push(`${indent} __wasm_idle_debug_enter(${currentFunctionId});`);
682
+ instrumented.push(`${indent} __wasm_idle_debug_enter(${currentFunctionId}, ${index + 1});`);
588
683
  const parameterSource = startsInlineFunctionBody
589
684
  ? normalized.slice(normalized.indexOf('(') + 1, normalized.lastIndexOf(')'))
590
685
  : pendingFunctionHeader?.parameters || '';
@@ -648,9 +743,11 @@ export default class Clang {
648
743
  if (!nameMatch)
649
744
  continue;
650
745
  const name = nameMatch[1];
651
- const kind = /\bbool\b/.test(cleaned) ? 'bool' : /\b(?:int|float|double|char|short|long)\b/.test(cleaned)
652
- ? 'number'
653
- : '';
746
+ const kind = /\bbool\b/.test(cleaned)
747
+ ? 'bool'
748
+ : /\b(?:int|float|double|char|short|long)\b/.test(cleaned)
749
+ ? 'number'
750
+ : '';
654
751
  if (!kind)
655
752
  continue;
656
753
  const slot = nextVariableSlot++;
@@ -721,11 +818,11 @@ export default class Clang {
721
818
  '-O' + opt,
722
819
  '-o',
723
820
  obj,
724
- clangLanguageStandardArg,
821
+ standardArg,
725
822
  '-x',
726
- 'c++',
823
+ languageArg,
727
824
  input,
728
- ...(options.args || [])
825
+ ...compileArgs
729
826
  ];
730
827
  this.trace(`compile ${input} -> ${obj}`);
731
828
  return await this.run(clang, true, 'clang', ...compilerArgs);
@@ -781,34 +878,53 @@ export default class Clang {
781
878
  return stillRunning ? app : null;
782
879
  }
783
880
  async compileLink(code, options = {}) {
784
- const { args = [], debug = false, breakpoints = [], pauseOnEntry = false, debugBuffer, interruptBuffer } = options;
785
- const input = `test.cc`, obj = `test.o`, wasm = `test.wasm`;
881
+ const { language = 'CPP', args = [], compileArgs = args, debug = false, breakpoints = [], pauseOnEntry = false, cppVersion, cVersion, debugBuffer, interruptBuffer } = options;
882
+ const input = language === 'C' ? `test.c` : `test.cc`, obj = `test.o`, wasm = `test.wasm`;
786
883
  this.beginTrace(debug);
787
884
  this.debugBreakpoints = new Set(debug ? breakpoints : []);
788
885
  this.debugPauseOnEntry = debug && pauseOnEntry;
789
886
  this.debugBuffer = debugBuffer;
790
887
  this.debugInterruptBuffer = interruptBuffer;
791
- const buildKey = JSON.stringify({ code, args, debug });
888
+ const buildKey = JSON.stringify({
889
+ code,
890
+ language,
891
+ compileArgs,
892
+ cppVersion,
893
+ cVersion,
894
+ debug
895
+ });
792
896
  if (this.lastBuildKey === buildKey) {
793
897
  this.trace(`reuse ${wasm}`);
794
898
  return this.wasm;
795
899
  }
796
- await this.compile({ input, code, obj, args, debug });
900
+ await this.compile({
901
+ input,
902
+ code,
903
+ obj,
904
+ language,
905
+ compileArgs,
906
+ cppVersion,
907
+ cVersion,
908
+ debug
909
+ });
797
910
  await this.link(obj, wasm, debug);
798
911
  this.lastBuildKey = buildKey;
799
912
  const wasmBytes = Uint8Array.from(this.memfs.getFileContents(wasm));
800
913
  return (this.wasm = await this.hostLogAsync(`Compiling ${wasm}`, WebAssembly.compile(wasmBytes)));
801
914
  }
802
915
  async compileLinkRun(code, options = {}) {
803
- const { args = [], debug = false, breakpoints = [], pauseOnEntry = false, debugBuffer, interruptBuffer } = options;
916
+ const { language = 'CPP', args = [], compileArgs = args, programArgs = [], debug = false, breakpoints = [], pauseOnEntry = false, cppVersion, cVersion, debugBuffer, interruptBuffer } = options;
804
917
  this.debug = debug;
805
918
  return await this.run(await this.compileLink(code, {
806
- args,
919
+ language,
920
+ compileArgs,
807
921
  debug,
808
922
  breakpoints,
809
923
  pauseOnEntry,
924
+ cppVersion,
925
+ cVersion,
810
926
  debugBuffer,
811
927
  interruptBuffer
812
- }), true, `test.wasm`);
928
+ }), true, `test.wasm`, ...programArgs);
813
929
  }
814
930
  }