wasm-idle 0.3.1 → 0.3.2

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.
@@ -18,6 +18,7 @@ interface DebugSession {
18
18
  nextLineFunctionId: number;
19
19
  nextLineLine: number;
20
20
  variableMetadata: Record<number, DebugVariableMetadata[]>;
21
+ globalVariableMetadata: DebugVariableMetadata[];
21
22
  functionMetadata: Record<number, string>;
22
23
  frames: Array<{
23
24
  functionId: number;
@@ -25,6 +26,7 @@ interface DebugSession {
25
26
  line: number;
26
27
  values: Map<number, string>;
27
28
  }>;
29
+ globalValues: Map<number, string>;
28
30
  onPause?: (event: {
29
31
  type: 'pause';
30
32
  line: number;
package/dist/clang/app.js CHANGED
@@ -103,6 +103,10 @@ export default class App {
103
103
  const session = this.debugSession;
104
104
  if (!session?.buffer)
105
105
  return ESUCCESS;
106
+ if (functionId === 0) {
107
+ session.globalValues.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
108
+ return ESUCCESS;
109
+ }
106
110
  for (let index = session.frames.length - 1; index >= 0; index -= 1) {
107
111
  const frame = session.frames[index];
108
112
  if (frame?.functionId !== functionId)
@@ -116,6 +120,10 @@ export default class App {
116
120
  const session = this.debugSession;
117
121
  if (!session?.buffer)
118
122
  return ESUCCESS;
123
+ if (functionId === 0) {
124
+ session.globalValues.set(slot, value ? 'true' : 'false');
125
+ return ESUCCESS;
126
+ }
119
127
  for (let index = session.frames.length - 1; index >= 0; index -= 1) {
120
128
  const frame = session.frames[index];
121
129
  if (frame?.functionId !== functionId)
@@ -129,6 +137,10 @@ export default class App {
129
137
  const session = this.debugSession;
130
138
  if (!session?.buffer)
131
139
  return ESUCCESS;
140
+ if (functionId === 0) {
141
+ session.globalValues.set(slot, String(value >>> 0));
142
+ return ESUCCESS;
143
+ }
132
144
  for (let index = session.frames.length - 1; index >= 0; index -= 1) {
133
145
  const frame = session.frames[index];
134
146
  if (frame?.functionId !== functionId)
@@ -144,6 +156,10 @@ export default class App {
144
156
  return ESUCCESS;
145
157
  this.mem?.check?.();
146
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
+ }
147
163
  for (let index = session.frames.length - 1; index >= 0; index -= 1) {
148
164
  const frame = session.frames[index];
149
165
  if (frame?.functionId !== functionId)
@@ -280,11 +296,20 @@ export default class App {
280
296
  const value = frame?.values.get(variable.slot) ?? '?';
281
297
  return [{ name: variable.name, value }];
282
298
  }) || [];
299
+ const localNames = new Set(locals.map((variable) => variable.name));
300
+ const globals = (session.globalVariableMetadata || []).flatMap((variable) => {
301
+ if (localNames.has(variable.name))
302
+ return [];
303
+ if (line < variable.fromLine || line > variable.toLine)
304
+ return [];
305
+ const value = session.globalValues?.get(variable.slot) ?? '?';
306
+ return [{ name: variable.name, value }];
307
+ }) || [];
283
308
  session.onPause?.({
284
309
  type: 'pause',
285
310
  line,
286
311
  reason,
287
- locals,
312
+ locals: [...locals, ...globals],
288
313
  callStack: [...session.frames]
289
314
  .reverse()
290
315
  .map((stackFrame) => ({ functionName: stackFrame.functionName, line: stackFrame.line }))
@@ -49,6 +49,7 @@ export default class Clang {
49
49
  }[];
50
50
  }) => void;
51
51
  debugVariableMetadata: Record<number, DebugVariableMetadata[]>;
52
+ debugGlobalMetadata: DebugVariableMetadata[];
52
53
  debugFunctionMetadata: Record<number, string>;
53
54
  lastBuildKey: string;
54
55
  path: string;
@@ -52,6 +52,7 @@ export default class Clang {
52
52
  debugInterruptBuffer;
53
53
  onDebugEvent;
54
54
  debugVariableMetadata = {};
55
+ debugGlobalMetadata = [];
55
56
  debugFunctionMetadata = {};
56
57
  lastBuildKey = '';
57
58
  path;
@@ -136,11 +137,14 @@ export default class Clang {
136
137
  let nextFunctionId = 1;
137
138
  let nextVariableSlot = 1;
138
139
  let currentFunctionVariables = new Map();
140
+ let globalVariables = new Map();
139
141
  let currentFunctionContainers = new Map();
140
142
  let inBlockComment = false;
141
143
  let pendingFunctionHeader;
142
144
  this.debugVariableMetadata = {};
145
+ this.debugGlobalMetadata = [];
143
146
  this.debugFunctionMetadata = {};
147
+ const globalInitialization = [];
144
148
  const instrumented = [
145
149
  '#include <map>',
146
150
  '#include <set>',
@@ -235,10 +239,64 @@ export default class Clang {
235
239
  analysisLine = analysisLine.slice(0, lineComment);
236
240
  const normalized = analysisLine.trim();
237
241
  const inFunctionBody = functionDepth > 0 && braceDepth >= functionDepth;
242
+ const isTopLevelDeclarationContext = functionDepth === 0 &&
243
+ braceDepth === 0 &&
244
+ !normalized.includes('(') &&
245
+ !normalized.startsWith('#');
238
246
  const controlHeaderWithoutInlineBlock = /^(while|if|for)\s*\(/.test(normalized) && !normalized.includes('{');
239
247
  const leadingInstrumentation = [];
240
248
  const trailingInstrumentation = [];
241
249
  const declaredContainerNames = new Set();
250
+ const globalDeclarationMatch = isTopLevelDeclarationContext &&
251
+ normalized.match(/^(?:const\s+)?(?:(?:unsigned|signed)\s+)?(?:(?:short|long long|long)\s+)?(int|float|double|bool|char)\s+(.+);$/);
252
+ if (globalDeclarationMatch) {
253
+ const kind = globalDeclarationMatch[1] === 'bool' ? 'bool' : 'number';
254
+ const declarations = [];
255
+ let declarationBuffer = '';
256
+ let declarationBraceDepth = 0;
257
+ for (const character of globalDeclarationMatch[2]) {
258
+ if (character === ',' && declarationBraceDepth === 0) {
259
+ if (declarationBuffer.trim())
260
+ declarations.push(declarationBuffer.trim());
261
+ declarationBuffer = '';
262
+ continue;
263
+ }
264
+ if (character === '{')
265
+ declarationBraceDepth += 1;
266
+ if (character === '}')
267
+ declarationBraceDepth = Math.max(0, declarationBraceDepth - 1);
268
+ declarationBuffer += character;
269
+ }
270
+ if (declarationBuffer.trim())
271
+ declarations.push(declarationBuffer.trim());
272
+ for (const declaration of declarations) {
273
+ const [left] = declaration.split('=');
274
+ const declarator = left?.trim() || '';
275
+ if (/[*&\[]/.test(declarator))
276
+ continue;
277
+ const name = declarator.match(/([A-Za-z_]\w*)\s*$/)?.[1];
278
+ if (!name)
279
+ continue;
280
+ const slot = nextVariableSlot++;
281
+ globalVariables.set(name, {
282
+ slot,
283
+ kind,
284
+ fromLine: index + 1,
285
+ toLine: Number.MAX_SAFE_INTEGER
286
+ });
287
+ this.debugGlobalMetadata = [
288
+ ...this.debugGlobalMetadata,
289
+ {
290
+ slot,
291
+ name,
292
+ kind,
293
+ fromLine: index + 1,
294
+ toLine: Number.MAX_SAFE_INTEGER
295
+ }
296
+ ];
297
+ globalInitialization.push(`${kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(0, ${slot}, ${name});`);
298
+ }
299
+ }
242
300
  if (inFunctionBody &&
243
301
  normalized &&
244
302
  !normalized.startsWith('#') &&
@@ -432,6 +490,16 @@ export default class Clang {
432
490
  break;
433
491
  }
434
492
  }
493
+ for (const [name, variable] of globalVariables) {
494
+ if (currentFunctionVariables.has(name) || currentFunctionContainers.has(name))
495
+ continue;
496
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
497
+ if (new RegExp(`(?:^|[^\\w])(?:\\+\\+|--)\\s*${escapedName}\\b`).test(normalized) ||
498
+ new RegExp(`\\b${escapedName}\\s*(?:[+\\-*/%]?=|\\+\\+|--)`).test(normalized) ||
499
+ new RegExp(`&\\s*${escapedName}\\b`).test(normalized)) {
500
+ trailingInstrumentation.push(`${indent}${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(0, ${variable.slot}, ${name});`);
501
+ }
502
+ }
435
503
  }
436
504
  if (closeIndex > openIndex) {
437
505
  const conditionSource = line.slice(openIndex + 1, closeIndex);
@@ -629,10 +697,18 @@ export default class Clang {
629
697
  currentFunctionContainers = new Map();
630
698
  }
631
699
  }
700
+ if (globalInitialization.length) {
701
+ instrumented.push('struct __wasm_idle_debug_globals_init {');
702
+ instrumented.push(' __wasm_idle_debug_globals_init() {');
703
+ instrumented.push(...globalInitialization.map((line) => ` ${line}`));
704
+ instrumented.push(' }');
705
+ instrumented.push('} __wasm_idle_debug_globals_init_instance;');
706
+ }
632
707
  source = instrumented.join('\n');
633
708
  }
634
709
  else {
635
710
  this.debugVariableMetadata = {};
711
+ this.debugGlobalMetadata = [];
636
712
  }
637
713
  const code = toUtf8(source);
638
714
  await this.ready;
@@ -689,8 +765,10 @@ export default class Clang {
689
765
  nextLineFunctionId: 0,
690
766
  nextLineLine: 0,
691
767
  variableMetadata: this.debugVariableMetadata,
768
+ globalVariableMetadata: this.debugGlobalMetadata,
692
769
  functionMetadata: this.debugFunctionMetadata,
693
770
  frames: [],
771
+ globalValues: new Map(),
694
772
  onPause: (event) => this.onDebugEvent?.(event)
695
773
  };
696
774
  const instantiate = +new Date();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wasm-idle",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "A simple web app to run C++/Python code in the browser",
5
5
  "license": "MIT",
6
6
  "type": "module",