warpo 3.1.3 → 3.2.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 (48) hide show
  1. package/dist/common/leb128.d.ts +5 -0
  2. package/dist/common/leb128.js +17 -0
  3. package/dist/common/leb128.js.map +1 -0
  4. package/dist/debug_server/debugSession.d.ts +30 -1
  5. package/dist/debug_server/debugSession.js +462 -93
  6. package/dist/debug_server/debugSession.js.map +1 -1
  7. package/dist/debug_server/debugTrace.d.ts +2 -0
  8. package/dist/debug_server/debugTrace.js +38 -0
  9. package/dist/debug_server/debugTrace.js.map +1 -0
  10. package/dist/debug_server/debugger.d.ts +5 -1
  11. package/dist/debug_server/debuggerWasmModule.d.ts +6 -0
  12. package/dist/debug_server/debuggerWasmModule.js +135 -2
  13. package/dist/debug_server/debuggerWasmModule.js.map +1 -1
  14. package/dist/debug_server/nodeDebugger.d.ts +12 -1
  15. package/dist/debug_server/nodeDebugger.js +74 -28
  16. package/dist/debug_server/nodeDebugger.js.map +1 -1
  17. package/dist/debug_server/tests/debugSession.test.js +460 -22
  18. package/dist/debug_server/tests/debugSession.test.js.map +1 -1
  19. package/dist/debug_server/tests/testModule/debugger_breakpoint_validation.d.ts +1 -0
  20. package/dist/debug_server/tests/testModule/debugger_breakpoint_validation.js +12 -0
  21. package/dist/debug_server/tests/testModule/debugger_breakpoint_validation.js.map +1 -0
  22. package/dist/debug_server/tests/testModule/debugger_function_closure.d.ts +1 -0
  23. package/dist/debug_server/tests/testModule/debugger_function_closure.js +24 -0
  24. package/dist/debug_server/tests/testModule/debugger_function_closure.js.map +1 -0
  25. package/dist/debug_server/tests/testModule/debugger_import.case.d.ts +1 -0
  26. package/dist/debug_server/tests/testModule/debugger_import.case.js +9 -0
  27. package/dist/debug_server/tests/testModule/debugger_import.case.js.map +1 -0
  28. package/dist/debug_server/tests/testModule/debugger_import.d.ts +1 -0
  29. package/dist/debug_server/tests/testModule/debugger_import.js +6 -0
  30. package/dist/debug_server/tests/testModule/debugger_import.js.map +1 -0
  31. package/dist/debug_server/tests/testModule/debugger_pause.d.ts +1 -0
  32. package/dist/debug_server/tests/testModule/debugger_pause.js +9 -0
  33. package/dist/debug_server/tests/testModule/debugger_pause.js.map +1 -0
  34. package/dist/debug_server/tests/testModule/debugger_stepping.d.ts +3 -0
  35. package/dist/debug_server/tests/testModule/debugger_stepping.js +28 -0
  36. package/dist/debug_server/tests/testModule/debugger_stepping.js.map +1 -0
  37. package/dist/debug_server/tests/testModule/debugger_typed_array.d.ts +1 -0
  38. package/dist/debug_server/tests/testModule/debugger_typed_array.js +52 -0
  39. package/dist/debug_server/tests/testModule/debugger_typed_array.js.map +1 -0
  40. package/dist/debug_server/tests/testModule/debugger_utf8_const_str.d.ts +1 -0
  41. package/dist/debug_server/tests/testModule/debugger_utf8_const_str.js +10 -0
  42. package/dist/debug_server/tests/testModule/debugger_utf8_const_str.js.map +1 -0
  43. package/dist/dwarf/dwarfParser.js +6 -13
  44. package/dist/dwarf/dwarfParser.js.map +1 -1
  45. package/dist/dwarf/functionDebugInfo.d.ts +3 -0
  46. package/dist/dwarf/functionDebugInfo.js +24 -0
  47. package/dist/dwarf/functionDebugInfo.js.map +1 -1
  48. package/package.json +1 -1
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { DebugClient } from "@vscode/debugadapter-testsupport";
4
4
  import * as assert from "node:assert/strict";
5
+ import * as fs from "node:fs";
5
6
  import * as path from "node:path";
6
7
  import { describe, it, beforeEach, afterEach } from "node:test";
7
8
  import { fileURLToPath } from "node:url";
@@ -73,8 +74,13 @@ function findVariable(variables, name) {
73
74
  async function launchAndWaitForBreakpoint(dc, launchArgs) {
74
75
  const stoppedPromise = waitForBreakpointStop(dc);
75
76
  await dc.launchRequest(launchArgs);
77
+ await dc.configurationDoneRequest({});
76
78
  await stoppedPromise;
77
79
  }
80
+ async function launchAndConfigure(dc, launchArgs) {
81
+ await dc.launchRequest(launchArgs);
82
+ await dc.configurationDoneRequest({});
83
+ }
78
84
  async function readFrameLocals(dc, frame) {
79
85
  const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
80
86
  const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
@@ -132,18 +138,49 @@ void describe("WarpoDebugSession", () => {
132
138
  await waitForExit(serverChild, 1000);
133
139
  }
134
140
  });
135
- void it("should accept breakpoints and return them verified", async () => {
141
+ void it("should return breakpoints unverified before a wasm module is loaded", async () => {
136
142
  await dc.initializeRequest();
137
143
  const response = await dc.setBreakpointsRequest({
138
144
  source: { path: "test.ts" },
139
145
  breakpoints: [{ line: 5 }, { line: 10 }],
140
146
  });
141
147
  assert.equal(response.body.breakpoints.length, 2);
142
- assert.equal(response.body.breakpoints[0].verified, true);
148
+ assert.equal(response.body.breakpoints[0].verified, false);
143
149
  assert.equal(response.body.breakpoints[0].line, 5);
144
- assert.equal(response.body.breakpoints[1].verified, true);
150
+ assert.equal(response.body.breakpoints[1].verified, false);
145
151
  assert.equal(response.body.breakpoints[1].line, 10);
146
152
  });
153
+ void it("should reject breakpoints without wasm bytecode mappings", { timeout: 10000 }, async () => {
154
+ const source = sourcePath("debugger_breakpoint_validation.ts");
155
+ const output = await buildModule(source);
156
+ const validLine = 7;
157
+ await dc.initializeRequest();
158
+ const initialResponse = await dc.setBreakpointsRequest({
159
+ source: { path: source },
160
+ breakpoints: [{ line: validLine }],
161
+ });
162
+ assert.equal(initialResponse.body.breakpoints[0].verified, false);
163
+ const breakpointChangedPromise = dc.waitForEvent("breakpoint");
164
+ const launchArgs = {
165
+ program: output,
166
+ launchType: "wasm file",
167
+ runtime: "node",
168
+ entryFunctionName: "_start",
169
+ };
170
+ await launchAndWaitForBreakpoint(dc, launchArgs);
171
+ const breakpointChangedEvent = await breakpointChangedPromise;
172
+ const breakpointChangedBody = breakpointChangedEvent.body;
173
+ assert.equal(breakpointChangedBody?.reason, "changed");
174
+ assert.equal(breakpointChangedBody?.breakpoint?.verified, true);
175
+ const response = await dc.setBreakpointsRequest({
176
+ source: { path: source },
177
+ breakpoints: [{ line: 5 }, { line: 6 }, { line: validLine }],
178
+ });
179
+ assert.equal(response.body.breakpoints[0].verified, false);
180
+ assert.equal(response.body.breakpoints[1].verified, false);
181
+ assert.equal(response.body.breakpoints[2].verified, true);
182
+ await dc.disconnectRequest({ terminateDebuggee: true });
183
+ });
147
184
  void it("should stop in a unit test after returning from main", { timeout: 15000 }, async () => {
148
185
  const testSource = sourcePath("unittest_debug.case.ts");
149
186
  const implementationSource = sourcePath("unittest_debug.ts");
@@ -173,33 +210,89 @@ void describe("WarpoDebugSession", () => {
173
210
  assert.equal(assertDefined(stackTraceResponse.body.stackFrames[0]).source?.path, normalizeDebugPath(testSource));
174
211
  assert.equal(stackTraceResponse.body.stackFrames[0].line, 9);
175
212
  });
213
+ void it("should step over a JavaScript import during step into", { timeout: 15000 }, async () => {
214
+ const implementationSource = sourcePath("debugger_import.ts");
215
+ await dc.initializeRequest();
216
+ await dc.setBreakpointsRequest({
217
+ source: { path: implementationSource },
218
+ breakpoints: [{ line: 7 }],
219
+ });
220
+ const launchArgs = {
221
+ launchType: "unittest",
222
+ runtime: "node",
223
+ cwd: TEST_MODULE_DIR,
224
+ warpoPath: WARPO_CLI,
225
+ };
226
+ await launchAndWaitForBreakpoint(dc, launchArgs);
227
+ const stoppedPromise = dc.waitForEvent("stopped");
228
+ await dc.stepInRequest({ threadId: 1 });
229
+ await stoppedPromise;
230
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
231
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
232
+ assert.equal(frame.source?.path, normalizeDebugPath(implementationSource));
233
+ assert.equal(frame.line, 8);
234
+ });
176
235
  void it("should report the built wasm as a loaded source on launch", { timeout: 5000 }, async () => {
177
236
  const source = sourcePath("debugger_basic.ts");
178
237
  const output = await buildModule(source);
179
238
  const breakpointLine = 13;
239
+ const traceFile = path.join(TEST_MODULE_DIR, "build", "test_debug_trace.log");
240
+ if (fs.existsSync(traceFile)) {
241
+ fs.unlinkSync(traceFile);
242
+ }
180
243
  await dc.initializeRequest();
181
244
  const breakpointResponse = await dc.setBreakpointsRequest({
182
245
  source: { path: source },
183
246
  breakpoints: [{ line: breakpointLine }],
184
247
  });
185
248
  assert.equal(breakpointResponse.body.breakpoints.length, 1);
186
- assert.equal(breakpointResponse.body.breakpoints[0].verified, true);
249
+ assert.equal(breakpointResponse.body.breakpoints[0].verified, false);
187
250
  assert.equal(breakpointResponse.body.breakpoints[0].line, breakpointLine);
188
251
  const launchArgs = {
189
252
  program: output,
190
253
  launchType: "wasm file",
191
254
  runtime: "node",
192
255
  entryFunctionName: "_start",
256
+ debugSessionLogFile: traceFile,
193
257
  };
194
258
  const loadedSourcePromise = waitForLoadedWasmSource(dc, output);
195
259
  const stoppedPromise = waitForBreakpointStop(dc);
196
- await dc.launchRequest(launchArgs);
260
+ await launchAndConfigure(dc, launchArgs);
197
261
  const loadedSourceEvent = await loadedSourcePromise;
198
262
  const loadedSourceBody = loadedSourceEvent.body;
199
263
  await stoppedPromise;
200
264
  assert.equal(loadedSourceBody?.reason, "new");
201
265
  assert.equal(loadedSourceBody?.source?.path, path.resolve(output));
202
266
  assert.equal(loadedSourceBody?.source?.name, path.basename(output));
267
+ assert.equal(fs.existsSync(traceFile), true, "debugSessionLogFile should be created on launch");
268
+ if (fs.existsSync(traceFile)) {
269
+ fs.unlinkSync(traceFile);
270
+ }
271
+ });
272
+ void it("should restart the debuggee and restore breakpoints", { timeout: 10000 }, async () => {
273
+ const source = sourcePath("debugger_basic.ts");
274
+ const output = await buildModule(source);
275
+ const breakpointLine = 13;
276
+ await dc.initializeRequest();
277
+ await dc.setBreakpointsRequest({
278
+ source: { path: source },
279
+ breakpoints: [{ line: breakpointLine }],
280
+ });
281
+ const launchArgs = {
282
+ program: output,
283
+ launchType: "wasm file",
284
+ runtime: "node",
285
+ entryFunctionName: "_start",
286
+ };
287
+ await launchAndWaitForBreakpoint(dc, launchArgs);
288
+ const restartedStop = waitForBreakpointStop(dc);
289
+ await dc.restartRequest({});
290
+ await restartedStop;
291
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
292
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
293
+ assert.equal(frame.source?.path, normalizeDebugPath(source));
294
+ assert.equal(frame.line, breakpointLine);
295
+ await dc.disconnectRequest({ terminateDebuggee: true });
203
296
  });
204
297
  void it("should reject a wasm launch when the file is missing", { timeout: 5000 }, async () => {
205
298
  await dc.initializeRequest();
@@ -211,7 +304,7 @@ void describe("WarpoDebugSession", () => {
211
304
  };
212
305
  const stderrPromise = waitForStderrOutput(dc);
213
306
  const terminatedPromise = dc.waitForEvent("terminated");
214
- await dc.launchRequest(launchArgs);
307
+ await launchAndConfigure(dc, launchArgs);
215
308
  const stderrEvent = await stderrPromise;
216
309
  const stderrBody = stderrEvent.body;
217
310
  assert.match(stderrBody?.output ?? "", /Wasm file not found/);
@@ -229,7 +322,7 @@ void describe("WarpoDebugSession", () => {
229
322
  };
230
323
  const stderrPromise = waitForStderrOutput(dc);
231
324
  const terminatedPromise = dc.waitForEvent("terminated");
232
- await dc.launchRequest(launchArgs);
325
+ await launchAndConfigure(dc, launchArgs);
233
326
  const stderrEvent = await stderrPromise;
234
327
  const stderrBody = stderrEvent.body;
235
328
  assert.match(stderrBody?.output ?? "", /Failed to load wasm module/);
@@ -342,7 +435,7 @@ void describe("WarpoDebugSession", () => {
342
435
  await dc.continueRequest({ threadId: 1 });
343
436
  await terminatedPromise;
344
437
  });
345
- void it("should add a breakpoint while the program is executing", { timeout: 10000 }, async () => {
438
+ void it("should add breakpoints after launch and while the program is executing", { timeout: 10000 }, async () => {
346
439
  const source = sourcePath("debugger_breakpoint_update.ts");
347
440
  const output = await buildModule(source);
348
441
  const initialBreakpointLine = 6;
@@ -358,7 +451,15 @@ void describe("WarpoDebugSession", () => {
358
451
  runtime: "node",
359
452
  entryFunctionName: "_start",
360
453
  };
361
- await launchAndWaitForBreakpoint(dc, launchArgs);
454
+ const firstStop = waitForBreakpointStop(dc);
455
+ await dc.launchRequest(launchArgs);
456
+ const initialBreakpointResponse = await dc.setBreakpointsRequest({
457
+ source: { path: source },
458
+ breakpoints: [{ line: initialBreakpointLine }],
459
+ });
460
+ assert.equal(initialBreakpointResponse.body.breakpoints[0].line, initialBreakpointLine);
461
+ await dc.configurationDoneRequest({});
462
+ await firstStop;
362
463
  const secondStop = waitForBreakpointStop(dc);
363
464
  await dc.continueRequest({ threadId: 1 });
364
465
  const breakpointResponse = await dc.setBreakpointsRequest({
@@ -402,6 +503,93 @@ void describe("WarpoDebugSession", () => {
402
503
  assert.equal(delta.type, "i32");
403
504
  assert.equal(delta.value, "2");
404
505
  });
506
+ void it("should expose a function object's name without its runtime fields", { timeout: 10000 }, async () => {
507
+ const source = sourcePath("debugger_function_closure.ts");
508
+ const output = await buildModule(source);
509
+ await dc.initializeRequest();
510
+ await dc.setBreakpointsRequest({
511
+ source: { path: source },
512
+ breakpoints: [{ line: 25 }],
513
+ });
514
+ const launchArgs = {
515
+ program: output,
516
+ launchType: "wasm file",
517
+ runtime: "node",
518
+ entryFunctionName: "_start",
519
+ };
520
+ await launchAndWaitForBreakpoint(dc, launchArgs);
521
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
522
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
523
+ const variablesResponse = await readFrameLocals(dc, frame);
524
+ const f1 = assertDefined(findVariable(variablesResponse.body.variables, "f1"));
525
+ assert.ok(f1.variablesReference > 0);
526
+ const f1FieldsResponse = await dc.variablesRequest({ variablesReference: f1.variablesReference });
527
+ assert.deepEqual(f1FieldsResponse.body.variables.map((variable) => variable.name), ["name", "[[scopes]]"]);
528
+ const f1Name = assertDefined(findVariable(f1FieldsResponse.body.variables, "name"));
529
+ assert.match(f1Name.value, /outer/);
530
+ assert.equal(f1Name.type, "string");
531
+ assert.equal(f1Name.variablesReference, 0);
532
+ const f1Scopes = assertDefined(findVariable(f1FieldsResponse.body.variables, "[[scopes]]"));
533
+ assert.equal(f1Scopes.value, "Scopes[2]");
534
+ assert.equal(f1Scopes.type, "Scopes");
535
+ assert.ok(f1Scopes.variablesReference > 0);
536
+ const f1ScopeArrayResponse = await dc.variablesRequest({ variablesReference: f1Scopes.variablesReference });
537
+ assert.deepEqual(f1ScopeArrayResponse.body.variables.map((variable) => variable.name), ["0", "1"]);
538
+ const f1MiddleScope = assertDefined(findVariable(f1ScopeArrayResponse.body.variables, "0"));
539
+ assert.equal(f1MiddleScope.value, "Closure(middle)");
540
+ assert.ok(f1MiddleScope.variablesReference > 0);
541
+ const f1MiddleScopeResponse = await dc.variablesRequest({ variablesReference: f1MiddleScope.variablesReference });
542
+ const f1CapturedB = assertDefined(findVariable(f1MiddleScopeResponse.body.variables, "b"));
543
+ assert.equal(f1CapturedB.value, "2");
544
+ assert.equal(f1CapturedB.type, "i32");
545
+ const f1OuterScope = assertDefined(findVariable(f1ScopeArrayResponse.body.variables, "1"));
546
+ assert.equal(f1OuterScope.value, "Closure(outer)");
547
+ assert.ok(f1OuterScope.variablesReference > 0);
548
+ const f1OuterScopeResponse = await dc.variablesRequest({ variablesReference: f1OuterScope.variablesReference });
549
+ const f1CapturedA = assertDefined(findVariable(f1OuterScopeResponse.body.variables, "a"));
550
+ assert.equal(f1CapturedA.value, "5");
551
+ assert.equal(f1CapturedA.type, "i32");
552
+ const f1CapturedC = assertDefined(findVariable(f1OuterScopeResponse.body.variables, "c"));
553
+ assert.ok(f1CapturedC.variablesReference > 0);
554
+ const f1CapturedCResponse = await dc.variablesRequest({ variablesReference: f1CapturedC.variablesReference });
555
+ const f1CapturedCValue = assertDefined(findVariable(f1CapturedCResponse.body.variables, "a"));
556
+ assert.equal(f1CapturedCValue.value, "10");
557
+ assert.equal(f1CapturedCValue.type, "i32");
558
+ const f2 = assertDefined(findVariable(variablesResponse.body.variables, "f2"));
559
+ assert.ok(f2.variablesReference > 0);
560
+ const f2FieldsResponse = await dc.variablesRequest({ variablesReference: f2.variablesReference });
561
+ assert.deepEqual(f2FieldsResponse.body.variables.map((variable) => variable.name), ["name", "[[scopes]]"]);
562
+ const f2Name = assertDefined(findVariable(f2FieldsResponse.body.variables, "name"));
563
+ assert.match(f2Name.value, /outer/);
564
+ assert.equal(f2Name.type, "string");
565
+ assert.equal(f2Name.variablesReference, 0);
566
+ const f2Scopes = assertDefined(findVariable(f2FieldsResponse.body.variables, "[[scopes]]"));
567
+ assert.equal(f2Scopes.value, "Scopes[2]");
568
+ assert.equal(f2Scopes.type, "Scopes");
569
+ assert.ok(f2Scopes.variablesReference > 0);
570
+ const f2ScopeArrayResponse = await dc.variablesRequest({ variablesReference: f2Scopes.variablesReference });
571
+ assert.deepEqual(f2ScopeArrayResponse.body.variables.map((variable) => variable.name), ["0", "1"]);
572
+ const f2MiddleScope = assertDefined(findVariable(f2ScopeArrayResponse.body.variables, "0"));
573
+ assert.equal(f2MiddleScope.value, "Closure(middle)");
574
+ assert.ok(f2MiddleScope.variablesReference > 0);
575
+ const f2MiddleScopeResponse = await dc.variablesRequest({ variablesReference: f2MiddleScope.variablesReference });
576
+ const f2CapturedB = assertDefined(findVariable(f2MiddleScopeResponse.body.variables, "b"));
577
+ assert.equal(f2CapturedB.value, "2");
578
+ assert.equal(f2CapturedB.type, "i32");
579
+ const f2OuterScope = assertDefined(findVariable(f2ScopeArrayResponse.body.variables, "1"));
580
+ assert.equal(f2OuterScope.value, "Closure(outer)");
581
+ assert.ok(f2OuterScope.variablesReference > 0);
582
+ const f2OuterScopeResponse = await dc.variablesRequest({ variablesReference: f2OuterScope.variablesReference });
583
+ const f2CapturedA = assertDefined(findVariable(f2OuterScopeResponse.body.variables, "a"));
584
+ assert.equal(f2CapturedA.value, "10");
585
+ assert.equal(f2CapturedA.type, "i32");
586
+ const f2CapturedC = assertDefined(findVariable(f2OuterScopeResponse.body.variables, "c"));
587
+ assert.ok(f2CapturedC.variablesReference > 0);
588
+ const f2CapturedCResponse = await dc.variablesRequest({ variablesReference: f2CapturedC.variablesReference });
589
+ const f2CapturedCValue = assertDefined(findVariable(f2CapturedCResponse.body.variables, "a"));
590
+ assert.equal(f2CapturedCValue.value, "15");
591
+ assert.equal(f2CapturedCValue.type, "i32");
592
+ });
405
593
  void it("should expose wasm call stack frames after hitting a breakpoint", { timeout: 5000 }, async () => {
406
594
  const source = sourcePath("debugger_basic.ts");
407
595
  const output = await buildModule(source);
@@ -467,7 +655,6 @@ void describe("WarpoDebugSession", () => {
467
655
  const calleeSource = sourcePath("debugger_callee.ts");
468
656
  const output = await buildModule(entrySource, [calleeSource]);
469
657
  const breakpointLine = 41;
470
- await dc.initializeRequest();
471
658
  await dc.setBreakpointsRequest({
472
659
  source: { path: calleeSource },
473
660
  breakpoints: [{ line: breakpointLine }],
@@ -495,6 +682,184 @@ void describe("WarpoDebugSession", () => {
495
682
  assert.equal(callerSeed.type, "i32");
496
683
  assert.equal(callerSeed.value, "23");
497
684
  });
685
+ void it("should step into a callee", { timeout: 10000 }, async () => {
686
+ const entrySource = sourcePath("debugger_stepping.ts");
687
+ const output = await buildModule(entrySource);
688
+ await dc.initializeRequest();
689
+ await dc.setBreakpointsRequest({
690
+ source: { path: entrySource },
691
+ breakpoints: [{ line: 5 }],
692
+ });
693
+ const launchArgs = {
694
+ program: output,
695
+ launchType: "wasm file",
696
+ runtime: "node",
697
+ entryFunctionName: "_start",
698
+ };
699
+ await launchAndWaitForBreakpoint(dc, launchArgs);
700
+ const stoppedPromise = dc.waitForEvent("stopped");
701
+ await dc.stepInRequest({ threadId: 1 });
702
+ await stoppedPromise;
703
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
704
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
705
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
706
+ assert.equal(frame.line, 11);
707
+ });
708
+ void it("should pause at a valid source line", { timeout: 10000 }, async () => {
709
+ const entrySource = sourcePath("debugger_pause.ts");
710
+ const output = await buildModule(entrySource);
711
+ await dc.initializeRequest();
712
+ await dc.setBreakpointsRequest({
713
+ source: { path: entrySource },
714
+ breakpoints: [{ line: 4 }],
715
+ });
716
+ const launchArgs = {
717
+ program: output,
718
+ launchType: "wasm file",
719
+ runtime: "node",
720
+ entryFunctionName: "_start",
721
+ };
722
+ await launchAndWaitForBreakpoint(dc, launchArgs);
723
+ const continuedPromise = dc.waitForEvent("continued");
724
+ await dc.continueRequest({ threadId: 1 });
725
+ await continuedPromise;
726
+ const stoppedPromise = dc.waitForEvent("stopped");
727
+ await dc.pauseRequest({ threadId: 1 });
728
+ const stoppedEvent = await stoppedPromise;
729
+ const stoppedBody = stoppedEvent.body;
730
+ assert.equal(stoppedBody?.reason, "pause");
731
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
732
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
733
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
734
+ assert.ok(frame.line > 0);
735
+ const terminatedPromise = dc.waitForEvent("terminated");
736
+ await dc.terminateRequest({ restart: false });
737
+ await terminatedPromise;
738
+ });
739
+ void it("should step out of a callee and stop at the caller", { timeout: 10000 }, async () => {
740
+ const entrySource = sourcePath("debugger_stepping.ts");
741
+ const output = await buildModule(entrySource);
742
+ await dc.initializeRequest();
743
+ await dc.setBreakpointsRequest({
744
+ source: { path: entrySource },
745
+ breakpoints: [{ line: 5 }],
746
+ });
747
+ const launchArgs = {
748
+ program: output,
749
+ launchType: "wasm file",
750
+ runtime: "node",
751
+ entryFunctionName: "_start",
752
+ };
753
+ await launchAndWaitForBreakpoint(dc, launchArgs);
754
+ const enteredCallee = dc.waitForEvent("stopped");
755
+ await dc.stepInRequest({ threadId: 1 });
756
+ await enteredCallee;
757
+ const returnedToCaller = dc.waitForEvent("stopped");
758
+ await dc.stepOutRequest({ threadId: 1 });
759
+ await returnedToCaller;
760
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
761
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
762
+ assert.match(frame.name, /_start/);
763
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
764
+ assert.equal(frame.line, 5);
765
+ });
766
+ void it("should step over a callee and stop at the caller's next line", { timeout: 10000 }, async () => {
767
+ const entrySource = sourcePath("debugger_stepping.ts");
768
+ const output = await buildModule(entrySource);
769
+ await dc.initializeRequest();
770
+ const breakpointResponse = await dc.setBreakpointsRequest({
771
+ source: { path: entrySource },
772
+ breakpoints: [{ line: 5 }],
773
+ });
774
+ assert.equal(breakpointResponse.body.breakpoints.length, 1);
775
+ const launchArgs = {
776
+ program: output,
777
+ launchType: "wasm file",
778
+ runtime: "node",
779
+ entryFunctionName: "_start",
780
+ };
781
+ await launchAndWaitForBreakpoint(dc, launchArgs);
782
+ const stoppedPromise = dc.waitForEvent("stopped");
783
+ await dc.nextRequest({ threadId: 1 });
784
+ await stoppedPromise;
785
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
786
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
787
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
788
+ assert.equal(frame.line, 6);
789
+ });
790
+ void it("should step over a function-object call and stop at the caller's next line", { timeout: 10000 }, async () => {
791
+ const entrySource = sourcePath("debugger_stepping.ts");
792
+ const output = await buildModule(entrySource);
793
+ await dc.initializeRequest();
794
+ const breakpointResponse = await dc.setBreakpointsRequest({
795
+ source: { path: entrySource },
796
+ breakpoints: [{ line: 18 }],
797
+ });
798
+ assert.equal(breakpointResponse.body.breakpoints.length, 1);
799
+ const launchArgs = {
800
+ program: output,
801
+ launchType: "wasm file",
802
+ runtime: "node",
803
+ entryFunctionName: "indirectStart",
804
+ };
805
+ await launchAndWaitForBreakpoint(dc, launchArgs);
806
+ const stoppedPromise = dc.waitForEvent("stopped");
807
+ await dc.nextRequest({ threadId: 1 });
808
+ await stoppedPromise;
809
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
810
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
811
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
812
+ assert.equal(frame.line, 19);
813
+ });
814
+ void it("should step into a function-object call", { timeout: 10000 }, async () => {
815
+ const entrySource = sourcePath("debugger_stepping.ts");
816
+ const output = await buildModule(entrySource);
817
+ await dc.initializeRequest();
818
+ const breakpointResponse = await dc.setBreakpointsRequest({
819
+ source: { path: entrySource },
820
+ breakpoints: [{ line: 18 }],
821
+ });
822
+ assert.equal(breakpointResponse.body.breakpoints.length, 1);
823
+ const launchArgs = {
824
+ program: output,
825
+ launchType: "wasm file",
826
+ runtime: "node",
827
+ entryFunctionName: "indirectStart",
828
+ };
829
+ await launchAndWaitForBreakpoint(dc, launchArgs);
830
+ const stoppedPromise = dc.waitForEvent("stopped");
831
+ await dc.stepInRequest({ threadId: 1 });
832
+ await stoppedPromise;
833
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
834
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
835
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
836
+ assert.equal(frame.line, 11);
837
+ });
838
+ void it("should step over a call that is the last instruction of a function", { timeout: 10000 }, async () => {
839
+ const entrySource = sourcePath("debugger_stepping.ts");
840
+ const output = await buildModule(entrySource);
841
+ await dc.initializeRequest();
842
+ const breakpointResponse = await dc.setBreakpointsRequest({
843
+ source: { path: entrySource },
844
+ breakpoints: [{ line: 24 }],
845
+ });
846
+ assert.equal(breakpointResponse.body.breakpoints.length, 1);
847
+ const launchArgs = {
848
+ program: output,
849
+ launchType: "wasm file",
850
+ runtime: "node",
851
+ entryFunctionName: "tailCallStart",
852
+ };
853
+ await launchAndWaitForBreakpoint(dc, launchArgs);
854
+ const stoppedPromise = dc.waitForEvent("stopped");
855
+ await dc.nextRequest({ threadId: 1 });
856
+ await stoppedPromise;
857
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
858
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
859
+ assert.match(frame.name, /tailCallStart/);
860
+ assert.equal(frame.source?.path, normalizeDebugPath(entrySource));
861
+ assert.equal(frame.line, 29);
862
+ });
498
863
  void it("should stop on a return breakpoint before evaluating its value", { timeout: 5000 }, async () => {
499
864
  const entrySource = sourcePath("debugger_caller.ts");
500
865
  const calleeSource = sourcePath("debugger_callee.ts");
@@ -559,7 +924,7 @@ void describe("WarpoDebugSession", () => {
559
924
  entryFunctionName: "_start",
560
925
  };
561
926
  const stoppedPromise = waitForBreakpointStop(dc);
562
- await dc.launchRequest(launchArgs);
927
+ await launchAndConfigure(dc, launchArgs);
563
928
  await stoppedPromise;
564
929
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
565
930
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -612,7 +977,7 @@ void describe("WarpoDebugSession", () => {
612
977
  entryFunctionName: "_start",
613
978
  };
614
979
  const stoppedPromise = waitForBreakpointStop(dc);
615
- await dc.launchRequest(launchArgs);
980
+ await launchAndConfigure(dc, launchArgs);
616
981
  await stoppedPromise;
617
982
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
618
983
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -650,7 +1015,7 @@ void describe("WarpoDebugSession", () => {
650
1015
  entryFunctionName: "_start",
651
1016
  };
652
1017
  const stoppedPromise = waitForBreakpointStop(dc);
653
- await dc.launchRequest(launchArgs);
1018
+ await launchAndConfigure(dc, launchArgs);
654
1019
  await stoppedPromise;
655
1020
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
656
1021
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -706,6 +1071,79 @@ void describe("WarpoDebugSession", () => {
706
1071
  { name: "2", type: "u8", value: "34", variablesReference: 0 },
707
1072
  ]);
708
1073
  });
1074
+ void it("should display utf8.ConstStr values", { timeout: 5000 }, async () => {
1075
+ const source = sourcePath("debugger_utf8_const_str.ts");
1076
+ const output = await buildModule(source);
1077
+ const breakpointLine = 11;
1078
+ await dc.initializeRequest();
1079
+ await dc.setBreakpointsRequest({
1080
+ source: { path: source },
1081
+ breakpoints: [{ line: breakpointLine }],
1082
+ });
1083
+ const launchArgs = {
1084
+ program: output,
1085
+ launchType: "wasm file",
1086
+ runtime: "node",
1087
+ entryFunctionName: "_start",
1088
+ };
1089
+ const { variablesResponse } = await launchAndReadTopFrameLocals(dc, launchArgs);
1090
+ const value = assertDefined(findVariable(variablesResponse.body.variables, "s"));
1091
+ assert.equal(value.type, "~lib/warpo/utf8/const_str/ConstStr");
1092
+ assert.equal(value.value, '"abcdef"');
1093
+ assert.equal(value.variablesReference, 0);
1094
+ });
1095
+ void it("should expand typed array values by index", { timeout: 5000 }, async () => {
1096
+ const source = sourcePath("debugger_typed_array.ts");
1097
+ const output = await buildModule(source);
1098
+ const breakpointLine = 41;
1099
+ await dc.initializeRequest();
1100
+ await dc.setBreakpointsRequest({
1101
+ source: { path: source },
1102
+ breakpoints: [{ line: breakpointLine }],
1103
+ });
1104
+ const launchArgs = {
1105
+ program: output,
1106
+ launchType: "wasm file",
1107
+ runtime: "node",
1108
+ entryFunctionName: "_start",
1109
+ };
1110
+ const { variablesResponse } = await launchAndReadTopFrameLocals(dc, launchArgs);
1111
+ const expected = [
1112
+ { name: "int8", type: "~lib/typedarray/Int8Array", elementType: "i8", values: ["-8", "8"] },
1113
+ { name: "uint8", type: "~lib/typedarray/Uint8Array", elementType: "u8", values: ["9", "19"] },
1114
+ {
1115
+ name: "uint8Clamped",
1116
+ type: "~lib/typedarray/Uint8ClampedArray",
1117
+ elementType: "u8",
1118
+ values: ["29", "39"],
1119
+ },
1120
+ { name: "int16", type: "~lib/typedarray/Int16Array", elementType: "i16", values: ["-49", "49"] },
1121
+ { name: "uint16", type: "~lib/typedarray/Uint16Array", elementType: "u16", values: ["59", "69"] },
1122
+ { name: "int32", type: "~lib/typedarray/Int32Array", elementType: "i32", values: ["-79", "79"] },
1123
+ { name: "uint32", type: "~lib/typedarray/Uint32Array", elementType: "u32", values: ["89", "99"] },
1124
+ { name: "int64", type: "~lib/typedarray/Int64Array", elementType: "i64", values: ["-109", "109"] },
1125
+ { name: "uint64", type: "~lib/typedarray/Uint64Array", elementType: "u64", values: ["119", "129"] },
1126
+ { name: "float32", type: "~lib/typedarray/Float32Array", elementType: "f32", values: ["1.5", "2.5"] },
1127
+ { name: "float64", type: "~lib/typedarray/Float64Array", elementType: "f64", values: ["3.5", "4.5"] },
1128
+ ];
1129
+ for (const expectedArray of expected) {
1130
+ const variable = assertDefined(findVariable(variablesResponse.body.variables, expectedArray.name));
1131
+ assert.equal(variable.type, expectedArray.type);
1132
+ assert.ok(variable.variablesReference > 0);
1133
+ const elementsResponse = await dc.variablesRequest({ variablesReference: variable.variablesReference });
1134
+ assert.deepEqual(elementsResponse.body.variables.map((element) => ({
1135
+ name: element.name,
1136
+ type: element.type,
1137
+ value: element.value,
1138
+ variablesReference: element.variablesReference,
1139
+ })), expectedArray.values.map((value, index) => ({
1140
+ name: index.toString(),
1141
+ type: expectedArray.elementType,
1142
+ value,
1143
+ variablesReference: 0,
1144
+ })));
1145
+ }
1146
+ });
709
1147
  void it("should expand class array elements as objects", { timeout: 5000 }, async () => {
710
1148
  const source = sourcePath("debugger_class_array.ts");
711
1149
  const output = await buildModule(source);
@@ -722,7 +1160,7 @@ void describe("WarpoDebugSession", () => {
722
1160
  entryFunctionName: "_start",
723
1161
  };
724
1162
  const stoppedPromise = waitForBreakpointStop(dc);
725
- await dc.launchRequest(launchArgs);
1163
+ await launchAndConfigure(dc, launchArgs);
726
1164
  await stoppedPromise;
727
1165
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
728
1166
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -768,7 +1206,7 @@ void describe("WarpoDebugSession", () => {
768
1206
  entryFunctionName: "_start",
769
1207
  };
770
1208
  const stoppedPromise = waitForBreakpointStop(dc);
771
- await dc.launchRequest(launchArgs);
1209
+ await launchAndConfigure(dc, launchArgs);
772
1210
  await stoppedPromise;
773
1211
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
774
1212
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -814,7 +1252,7 @@ void describe("WarpoDebugSession", () => {
814
1252
  entryFunctionName: "_start",
815
1253
  };
816
1254
  const stoppedPromise = waitForBreakpointStop(dc);
817
- await dc.launchRequest(launchArgs);
1255
+ await launchAndConfigure(dc, launchArgs);
818
1256
  await stoppedPromise;
819
1257
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
820
1258
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -876,7 +1314,7 @@ void describe("WarpoDebugSession", () => {
876
1314
  entryFunctionName: "_start",
877
1315
  };
878
1316
  const stoppedPromise = waitForBreakpointStop(dc);
879
- await dc.launchRequest(launchArgs);
1317
+ await launchAndConfigure(dc, launchArgs);
880
1318
  await stoppedPromise;
881
1319
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
882
1320
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -937,7 +1375,7 @@ void describe("WarpoDebugSession", () => {
937
1375
  entryFunctionName: "_start",
938
1376
  };
939
1377
  const stoppedPromise = waitForBreakpointStop(dc);
940
- await dc.launchRequest(launchArgs);
1378
+ await launchAndConfigure(dc, launchArgs);
941
1379
  await stoppedPromise;
942
1380
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
943
1381
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -1021,7 +1459,7 @@ void describe("WarpoDebugSession", () => {
1021
1459
  entryFunctionName: "_start",
1022
1460
  };
1023
1461
  const stoppedPromise = waitForBreakpointStop(dc);
1024
- await dc.launchRequest(launchArgs);
1462
+ await launchAndConfigure(dc, launchArgs);
1025
1463
  await stoppedPromise;
1026
1464
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1027
1465
  const frame = stackTraceResponse.body.stackFrames[0];
@@ -1097,7 +1535,7 @@ void describe("WarpoDebugSession", () => {
1097
1535
  entryFunctionName: "_start",
1098
1536
  };
1099
1537
  const stoppedPromise = waitForBreakpointStop(dc);
1100
- await dc.launchRequest(launchArgs);
1538
+ await launchAndConfigure(dc, launchArgs);
1101
1539
  await stoppedPromise;
1102
1540
  const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1103
1541
  const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
@@ -1142,7 +1580,7 @@ void describe("WarpoDebugSession", () => {
1142
1580
  entryFunctionName: "_start",
1143
1581
  };
1144
1582
  const firstStoppedPromise = waitForBreakpointStop(dc);
1145
- await dc.launchRequest(launchArgs);
1583
+ await launchAndConfigure(dc, launchArgs);
1146
1584
  await firstStoppedPromise;
1147
1585
  const firstStackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1148
1586
  const firstScopesResponse = await dc.scopesRequest({ frameId: firstStackTraceResponse.body.stackFrames[0].id });
@@ -1218,7 +1656,7 @@ void describe("WarpoDebugSession", () => {
1218
1656
  };
1219
1657
  const stoppedPromise = dc.waitForEvent("stopped");
1220
1658
  const terminatedPromise = dc.waitForEvent("terminated");
1221
- await dc.launchRequest(launchArgs);
1659
+ await launchAndConfigure(dc, launchArgs);
1222
1660
  const stoppedEvent = await stoppedPromise;
1223
1661
  const stoppedBody = stoppedEvent.body;
1224
1662
  assert.equal(stoppedBody?.reason, "exception");