wasm-idle 0.3.0 → 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.
- package/dist/clang/app.d.ts +2 -0
- package/dist/clang/app.js +76 -10
- package/dist/clang/index.d.ts +1 -0
- package/dist/clang/index.js +100 -10
- package/dist/debug/expression.js +89 -9
- package/dist/playground/options.d.ts +1 -0
- package/package.json +1 -1
package/dist/clang/app.d.ts
CHANGED
|
@@ -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)
|
|
@@ -198,18 +214,59 @@ export default class App {
|
|
|
198
214
|
if (variable.kind === 'array') {
|
|
199
215
|
this.mem?.check?.();
|
|
200
216
|
const address = Number(frame?.values.get(variable.slot) ?? Number.NaN);
|
|
201
|
-
|
|
217
|
+
const dimensions = variable.dimensions?.length
|
|
218
|
+
? variable.dimensions
|
|
219
|
+
: variable.length
|
|
220
|
+
? [variable.length]
|
|
221
|
+
: [];
|
|
222
|
+
if (!Number.isFinite(address) || address <= 0 || !dimensions.length || !variable.elementKind) {
|
|
202
223
|
return [{ name: variable.name, value: '?' }];
|
|
203
224
|
}
|
|
204
|
-
const
|
|
225
|
+
const elementStride = variable.elementKind === 'double'
|
|
226
|
+
? 8
|
|
227
|
+
: variable.elementKind === 'bool' || variable.elementKind === 'char'
|
|
228
|
+
? 1
|
|
229
|
+
: 4;
|
|
230
|
+
if (dimensions.length === 2) {
|
|
231
|
+
const previewRows = Math.min(dimensions[0], 4);
|
|
232
|
+
const previewCols = Math.min(dimensions[1], 8);
|
|
233
|
+
const rows = [];
|
|
234
|
+
for (let row = 0; row < previewRows; row += 1) {
|
|
235
|
+
const values = [];
|
|
236
|
+
for (let col = 0; col < previewCols; col += 1) {
|
|
237
|
+
const offset = address + (row * dimensions[1] + col) * elementStride;
|
|
238
|
+
if (variable.elementKind === 'bool') {
|
|
239
|
+
values.push(this.mem.read8(offset) ? 'true' : 'false');
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (variable.elementKind === 'char') {
|
|
243
|
+
const charCode = this.mem.read8(offset);
|
|
244
|
+
values.push(charCode >= 0x20 && charCode <= 0x7e ? `'${String.fromCharCode(charCode)}'` : `${charCode}`);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (variable.elementKind === 'float') {
|
|
248
|
+
values.push(`${this.mem.readFloat32(offset)}`);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (variable.elementKind === 'double') {
|
|
252
|
+
values.push(`${this.mem.readFloat64(offset)}`);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
values.push(`${this.mem.readInt32(offset)}`);
|
|
256
|
+
}
|
|
257
|
+
rows.push(`[${values.join(', ')}${dimensions[1] > previewCols ? ', ...' : ''}]`);
|
|
258
|
+
}
|
|
259
|
+
return [
|
|
260
|
+
{
|
|
261
|
+
name: variable.name,
|
|
262
|
+
value: `[${rows.join(', ')}${dimensions[0] > previewRows ? ', ...' : ''}]`
|
|
263
|
+
}
|
|
264
|
+
];
|
|
265
|
+
}
|
|
266
|
+
const previewLength = Math.min(dimensions[0], 8);
|
|
205
267
|
const values = [];
|
|
206
268
|
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);
|
|
269
|
+
const offset = address + index * elementStride;
|
|
213
270
|
if (variable.elementKind === 'bool') {
|
|
214
271
|
values.push(this.mem.read8(offset) ? 'true' : 'false');
|
|
215
272
|
continue;
|
|
@@ -232,18 +289,27 @@ export default class App {
|
|
|
232
289
|
return [
|
|
233
290
|
{
|
|
234
291
|
name: variable.name,
|
|
235
|
-
value: `[${values.join(', ')}${
|
|
292
|
+
value: `[${values.join(', ')}${dimensions[0] > previewLength ? ', ...' : ''}]`
|
|
236
293
|
}
|
|
237
294
|
];
|
|
238
295
|
}
|
|
239
296
|
const value = frame?.values.get(variable.slot) ?? '?';
|
|
240
297
|
return [{ name: variable.name, value }];
|
|
241
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
|
+
}) || [];
|
|
242
308
|
session.onPause?.({
|
|
243
309
|
type: 'pause',
|
|
244
310
|
line,
|
|
245
311
|
reason,
|
|
246
|
-
locals,
|
|
312
|
+
locals: [...locals, ...globals],
|
|
247
313
|
callStack: [...session.frames]
|
|
248
314
|
.reverse()
|
|
249
315
|
.map((stackFrame) => ({ functionName: stackFrame.functionName, line: stackFrame.line }))
|
package/dist/clang/index.d.ts
CHANGED
package/dist/clang/index.js
CHANGED
|
@@ -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('#') &&
|
|
@@ -306,22 +364,27 @@ export default class Clang {
|
|
|
306
364
|
for (const declaration of declarations) {
|
|
307
365
|
const [left] = declaration.split('=');
|
|
308
366
|
const declarator = left?.trim() || '';
|
|
309
|
-
const
|
|
310
|
-
|
|
367
|
+
const arrayDimensions = [];
|
|
368
|
+
for (const match of declarator.matchAll(/\[(\d+)\]/g)) {
|
|
369
|
+
arrayDimensions.push(Number(match[1]));
|
|
370
|
+
}
|
|
371
|
+
const arrayName = declarator.match(/([A-Za-z_]\w*)\s*(?=\[\d+\])/);
|
|
372
|
+
if (arrayDimensions.length && arrayName) {
|
|
311
373
|
const slot = nextVariableSlot++;
|
|
312
374
|
this.debugVariableMetadata[currentFunctionId] = [
|
|
313
375
|
...(this.debugVariableMetadata[currentFunctionId] || []),
|
|
314
376
|
{
|
|
315
377
|
slot,
|
|
316
|
-
name:
|
|
378
|
+
name: arrayName[1],
|
|
317
379
|
kind: 'array',
|
|
318
380
|
elementKind: declarationMatch[1],
|
|
319
|
-
length:
|
|
381
|
+
length: arrayDimensions[0],
|
|
382
|
+
dimensions: arrayDimensions,
|
|
320
383
|
fromLine: index + 1,
|
|
321
384
|
toLine: Number.MAX_SAFE_INTEGER
|
|
322
385
|
}
|
|
323
386
|
];
|
|
324
|
-
trailingInstrumentation.push(`${indent}__wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${
|
|
387
|
+
trailingInstrumentation.push(`${indent}__wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${arrayName[1]})));`);
|
|
325
388
|
continue;
|
|
326
389
|
}
|
|
327
390
|
if (/[*&]/.test(declarator))
|
|
@@ -427,6 +490,16 @@ export default class Clang {
|
|
|
427
490
|
break;
|
|
428
491
|
}
|
|
429
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
|
+
}
|
|
430
503
|
}
|
|
431
504
|
if (closeIndex > openIndex) {
|
|
432
505
|
const conditionSource = line.slice(openIndex + 1, closeIndex);
|
|
@@ -544,22 +617,29 @@ export default class Clang {
|
|
|
544
617
|
instrumented.push(`${indent} __wasm_idle_debug_emit_${container}(${currentFunctionId}, ${slot}, ${name});`);
|
|
545
618
|
continue;
|
|
546
619
|
}
|
|
547
|
-
const
|
|
548
|
-
|
|
620
|
+
const parameterArrayDimensions = [];
|
|
621
|
+
for (const match of cleaned.matchAll(/\[(\d+)\]/g)) {
|
|
622
|
+
parameterArrayDimensions.push(Number(match[1]));
|
|
623
|
+
}
|
|
624
|
+
const parameterArrayName = cleaned.match(/([A-Za-z_]\w*)\s*(?=\[\d+\])/);
|
|
625
|
+
if (parameterArrayDimensions.length &&
|
|
626
|
+
parameterArrayName &&
|
|
627
|
+
/\b(int|float|double|bool|char)\b/.test(cleaned)) {
|
|
549
628
|
const slot = nextVariableSlot++;
|
|
550
629
|
this.debugVariableMetadata[currentFunctionId] = [
|
|
551
630
|
...(this.debugVariableMetadata[currentFunctionId] || []),
|
|
552
631
|
{
|
|
553
632
|
slot,
|
|
554
|
-
name:
|
|
633
|
+
name: parameterArrayName[1],
|
|
555
634
|
kind: 'array',
|
|
556
635
|
elementKind: (cleaned.match(/\b(int|float|double|bool|char)\b/)?.[1] || 'int'),
|
|
557
|
-
length:
|
|
636
|
+
length: parameterArrayDimensions[0],
|
|
637
|
+
dimensions: parameterArrayDimensions,
|
|
558
638
|
fromLine: index + 1,
|
|
559
639
|
toLine: Number.MAX_SAFE_INTEGER
|
|
560
640
|
}
|
|
561
641
|
];
|
|
562
|
-
instrumented.push(`${indent} __wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${
|
|
642
|
+
instrumented.push(`${indent} __wasm_idle_debug_value_addr(${currentFunctionId}, ${slot}, (int)((unsigned long long)(${parameterArrayName[1]})));`);
|
|
563
643
|
continue;
|
|
564
644
|
}
|
|
565
645
|
if (/[*&\[]/.test(cleaned))
|
|
@@ -617,10 +697,18 @@ export default class Clang {
|
|
|
617
697
|
currentFunctionContainers = new Map();
|
|
618
698
|
}
|
|
619
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
|
+
}
|
|
620
707
|
source = instrumented.join('\n');
|
|
621
708
|
}
|
|
622
709
|
else {
|
|
623
710
|
this.debugVariableMetadata = {};
|
|
711
|
+
this.debugGlobalMetadata = [];
|
|
624
712
|
}
|
|
625
713
|
const code = toUtf8(source);
|
|
626
714
|
await this.ready;
|
|
@@ -677,8 +765,10 @@ export default class Clang {
|
|
|
677
765
|
nextLineFunctionId: 0,
|
|
678
766
|
nextLineLine: 0,
|
|
679
767
|
variableMetadata: this.debugVariableMetadata,
|
|
768
|
+
globalVariableMetadata: this.debugGlobalMetadata,
|
|
680
769
|
functionMetadata: this.debugFunctionMetadata,
|
|
681
770
|
frames: [],
|
|
771
|
+
globalValues: new Map(),
|
|
682
772
|
onPause: (event) => this.onDebugEvent?.(event)
|
|
683
773
|
};
|
|
684
774
|
const instantiate = +new Date();
|
package/dist/debug/expression.js
CHANGED
|
@@ -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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|