wasm-idle 0.3.0 → 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.
package/dist/clang/app.js CHANGED
@@ -198,18 +198,59 @@ export default class App {
198
198
  if (variable.kind === 'array') {
199
199
  this.mem?.check?.();
200
200
  const address = Number(frame?.values.get(variable.slot) ?? Number.NaN);
201
- if (!Number.isFinite(address) || address <= 0 || !variable.length || !variable.elementKind) {
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) {
202
207
  return [{ name: variable.name, value: '?' }];
203
208
  }
204
- const previewLength = Math.min(variable.length, 8);
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);
205
251
  const values = [];
206
252
  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);
253
+ const offset = address + index * elementStride;
213
254
  if (variable.elementKind === 'bool') {
214
255
  values.push(this.mem.read8(offset) ? 'true' : 'false');
215
256
  continue;
@@ -232,7 +273,7 @@ export default class App {
232
273
  return [
233
274
  {
234
275
  name: variable.name,
235
- value: `[${values.join(', ')}${variable.length > previewLength ? ', ...' : ''}]`
276
+ value: `[${values.join(', ')}${dimensions[0] > previewLength ? ', ...' : ''}]`
236
277
  }
237
278
  ];
238
279
  }
@@ -306,22 +306,27 @@ export default class Clang {
306
306
  for (const declaration of declarations) {
307
307
  const [left] = declaration.split('=');
308
308
  const declarator = left?.trim() || '';
309
- const arrayMatch = declarator.match(/([A-Za-z_]\w*)\s*\[(\d+)\]\s*$/);
310
- if (arrayMatch) {
309
+ const arrayDimensions = [];
310
+ for (const match of declarator.matchAll(/\[(\d+)\]/g)) {
311
+ arrayDimensions.push(Number(match[1]));
312
+ }
313
+ const arrayName = declarator.match(/([A-Za-z_]\w*)\s*(?=\[\d+\])/);
314
+ if (arrayDimensions.length && arrayName) {
311
315
  const slot = nextVariableSlot++;
312
316
  this.debugVariableMetadata[currentFunctionId] = [
313
317
  ...(this.debugVariableMetadata[currentFunctionId] || []),
314
318
  {
315
319
  slot,
316
- name: arrayMatch[1],
320
+ name: arrayName[1],
317
321
  kind: 'array',
318
322
  elementKind: declarationMatch[1],
319
- length: Number(arrayMatch[2]),
323
+ length: arrayDimensions[0],
324
+ dimensions: arrayDimensions,
320
325
  fromLine: index + 1,
321
326
  toLine: Number.MAX_SAFE_INTEGER
322
327
  }
323
328
  ];
324
- trailingInstrumentation.push(`${indent}__wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${arrayMatch[1]})));`);
329
+ trailingInstrumentation.push(`${indent}__wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${arrayName[1]})));`);
325
330
  continue;
326
331
  }
327
332
  if (/[*&]/.test(declarator))
@@ -544,22 +549,29 @@ export default class Clang {
544
549
  instrumented.push(`${indent} __wasm_idle_debug_emit_${container}(${currentFunctionId}, ${slot}, ${name});`);
545
550
  continue;
546
551
  }
547
- const parameterArrayMatch = cleaned.match(/([A-Za-z_]\w*)\s*\[(\d+)\]\s*$/);
548
- if (parameterArrayMatch && /\b(int|float|double|bool|char)\b/.test(cleaned)) {
552
+ const parameterArrayDimensions = [];
553
+ for (const match of cleaned.matchAll(/\[(\d+)\]/g)) {
554
+ parameterArrayDimensions.push(Number(match[1]));
555
+ }
556
+ const parameterArrayName = cleaned.match(/([A-Za-z_]\w*)\s*(?=\[\d+\])/);
557
+ if (parameterArrayDimensions.length &&
558
+ parameterArrayName &&
559
+ /\b(int|float|double|bool|char)\b/.test(cleaned)) {
549
560
  const slot = nextVariableSlot++;
550
561
  this.debugVariableMetadata[currentFunctionId] = [
551
562
  ...(this.debugVariableMetadata[currentFunctionId] || []),
552
563
  {
553
564
  slot,
554
- name: parameterArrayMatch[1],
565
+ name: parameterArrayName[1],
555
566
  kind: 'array',
556
567
  elementKind: (cleaned.match(/\b(int|float|double|bool|char)\b/)?.[1] || 'int'),
557
- length: Number(parameterArrayMatch[2]),
568
+ length: parameterArrayDimensions[0],
569
+ dimensions: parameterArrayDimensions,
558
570
  fromLine: index + 1,
559
571
  toLine: Number.MAX_SAFE_INTEGER
560
572
  }
561
573
  ];
562
- instrumented.push(`${indent} __wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${parameterArrayMatch[1]})));`);
574
+ instrumented.push(`${indent} __wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${parameterArrayName[1]})));`);
563
575
  continue;
564
576
  }
565
577
  if (/[*&\[]/.test(cleaned))
@@ -6,6 +6,7 @@ export function evaluateDebugExpression(expression, variables) {
6
6
  const values = new Map();
7
7
  for (const variable of variables)
8
8
  values.set(variable.name, variable.value);
9
+ const parsedValues = new Map();
9
10
  const tokens = [];
10
11
  for (let index = 0; index < source.length;) {
11
12
  const character = source[index];
@@ -20,6 +21,11 @@ export function evaluateDebugExpression(expression, variables) {
20
21
  index += 1;
21
22
  continue;
22
23
  }
24
+ if (character === '[' || character === ']') {
25
+ tokens.push({ type: 'bracket', value: character });
26
+ index += 1;
27
+ continue;
28
+ }
23
29
  const operator = operators.find((candidate) => source.startsWith(candidate, index));
24
30
  if (operator) {
25
31
  tokens.push({ type: 'operator', value: operator });
@@ -46,6 +52,73 @@ export function evaluateDebugExpression(expression, variables) {
46
52
  throw new Error(`unsupported token near "${source.slice(index)}"`);
47
53
  }
48
54
  let cursor = 0;
55
+ const parsePreviewValue = (text, start = 0) => {
56
+ let index = start;
57
+ while (/\s/.test(text[index] || ''))
58
+ index += 1;
59
+ const character = text[index];
60
+ if (character === '[') {
61
+ index += 1;
62
+ const items = [];
63
+ while (true) {
64
+ while (/\s/.test(text[index] || ''))
65
+ index += 1;
66
+ if (text[index] === ']')
67
+ return { value: items, next: index + 1 };
68
+ if (text.startsWith('...', index))
69
+ throw new Error('unavailable');
70
+ const item = parsePreviewValue(text, index);
71
+ items.push(item.value);
72
+ index = item.next;
73
+ while (/\s/.test(text[index] || ''))
74
+ index += 1;
75
+ if (text[index] === ',') {
76
+ index += 1;
77
+ continue;
78
+ }
79
+ if (text[index] === ']')
80
+ return { value: items, next: index + 1 };
81
+ throw new Error('unsupported array preview');
82
+ }
83
+ }
84
+ if (character === "'") {
85
+ const end = text.indexOf("'", index + 1);
86
+ if (end === -1)
87
+ throw new Error('unsupported char preview');
88
+ return { value: text.slice(index + 1, end), next: end + 1 };
89
+ }
90
+ if (text.startsWith('true', index))
91
+ return { value: true, next: index + 4 };
92
+ if (text.startsWith('false', index))
93
+ return { value: false, next: index + 5 };
94
+ const number = text.slice(index).match(/^-?\d+(?:\.\d+)?/);
95
+ if (number?.[0])
96
+ return { value: Number(number[0]), next: index + number[0].length };
97
+ throw new Error('unsupported preview');
98
+ };
99
+ const resolveIdentifierValue = (name) => {
100
+ if (parsedValues.has(name))
101
+ return parsedValues.get(name);
102
+ const value = values.get(name);
103
+ if (value == null || value === '?')
104
+ throw new Error('unavailable');
105
+ if (value === 'true' || value === 'false') {
106
+ const parsed = value === 'true';
107
+ parsedValues.set(name, parsed);
108
+ return parsed;
109
+ }
110
+ const numeric = Number(value);
111
+ if (!Number.isNaN(numeric)) {
112
+ parsedValues.set(name, numeric);
113
+ return numeric;
114
+ }
115
+ if (value.startsWith('[')) {
116
+ const parsed = parsePreviewValue(value).value;
117
+ parsedValues.set(name, parsed);
118
+ return parsed;
119
+ }
120
+ throw new Error(`unsupported value for ${name}`);
121
+ };
49
122
  const parsePrimary = () => {
50
123
  const token = tokens[cursor];
51
124
  if (!token)
@@ -60,15 +133,22 @@ export function evaluateDebugExpression(expression, variables) {
60
133
  }
61
134
  if (token.type === 'identifier') {
62
135
  cursor += 1;
63
- const value = values.get(token.value);
64
- if (value == null || value === '?')
65
- throw new Error('unavailable');
66
- if (value === 'true' || value === 'false')
67
- return value === 'true';
68
- const parsed = Number(value);
69
- if (!Number.isNaN(parsed))
70
- return parsed;
71
- throw new Error(`unsupported value for ${token.value}`);
136
+ let resolved = resolveIdentifierValue(token.value);
137
+ while (tokens[cursor]?.type === 'bracket' && tokens[cursor]?.value === '[') {
138
+ cursor += 1;
139
+ const index = Number(parseOr());
140
+ const closing = tokens[cursor];
141
+ if (!closing || closing.type !== 'bracket' || closing.value !== ']')
142
+ throw new Error('missing closing bracket');
143
+ cursor += 1;
144
+ if (!Array.isArray(resolved) || !Number.isInteger(index))
145
+ throw new Error('unsupported index access');
146
+ const next = resolved[index];
147
+ if (next == null)
148
+ throw new Error('unavailable');
149
+ resolved = next;
150
+ }
151
+ return resolved;
72
152
  }
73
153
  if (token.type === 'paren' && token.value === '(') {
74
154
  cursor += 1;
@@ -14,6 +14,7 @@ export interface DebugVariableMetadata {
14
14
  toLine: number;
15
15
  elementKind?: DebugArrayElementKind;
16
16
  length?: number;
17
+ dimensions?: number[];
17
18
  }
18
19
  export interface DebugFrame {
19
20
  functionName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wasm-idle",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "A simple web app to run C++/Python code in the browser",
5
5
  "license": "MIT",
6
6
  "type": "module",