warpo 3.1.1 → 3.1.3

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 (189) hide show
  1. package/asconfig.schema.json +10 -2
  2. package/dist/debug_server/debugPath.d.ts +1 -0
  3. package/dist/debug_server/debugPath.js +10 -0
  4. package/dist/debug_server/debugPath.js.map +1 -0
  5. package/dist/debug_server/debugSession.d.ts +61 -3
  6. package/dist/debug_server/debugSession.js +919 -80
  7. package/dist/debug_server/debugSession.js.map +1 -1
  8. package/dist/debug_server/debugger.d.ts +30 -2
  9. package/dist/debug_server/debuggerWasmModule.d.ts +59 -3
  10. package/dist/debug_server/debuggerWasmModule.js +234 -14
  11. package/dist/debug_server/debuggerWasmModule.js.map +1 -1
  12. package/dist/debug_server/nodeDebugger.d.ts +42 -3
  13. package/dist/debug_server/nodeDebugger.js +648 -59
  14. package/dist/debug_server/nodeDebugger.js.map +1 -1
  15. package/dist/debug_server/tests/debugSession.test.js +1154 -26
  16. package/dist/debug_server/tests/debugSession.test.js.map +1 -1
  17. package/dist/debug_server/tests/launcher.js +1 -0
  18. package/dist/debug_server/tests/launcher.js.map +1 -1
  19. package/dist/debug_server/tests/testModule/debugger_array.d.ts +1 -0
  20. package/dist/debug_server/tests/testModule/debugger_array.js +9 -0
  21. package/dist/debug_server/tests/testModule/debugger_array.js.map +1 -0
  22. package/dist/debug_server/tests/testModule/debugger_array_buffer.d.ts +1 -0
  23. package/dist/debug_server/tests/testModule/debugger_array_buffer.js +12 -0
  24. package/dist/debug_server/tests/testModule/debugger_array_buffer.js.map +1 -0
  25. package/dist/debug_server/tests/testModule/debugger_basic.d.ts +1 -0
  26. package/dist/debug_server/tests/testModule/debugger_basic.js +9 -1
  27. package/dist/debug_server/tests/testModule/debugger_basic.js.map +1 -1
  28. package/dist/debug_server/tests/testModule/debugger_breakpoint_update.d.ts +1 -0
  29. package/dist/debug_server/tests/testModule/debugger_breakpoint_update.js +10 -0
  30. package/dist/debug_server/tests/testModule/debugger_breakpoint_update.js.map +1 -0
  31. package/dist/debug_server/tests/testModule/debugger_callee.d.ts +10 -0
  32. package/dist/debug_server/tests/testModule/debugger_callee.js +106 -0
  33. package/dist/debug_server/tests/testModule/debugger_callee.js.map +1 -0
  34. package/dist/debug_server/tests/testModule/debugger_caller.d.ts +1 -0
  35. package/dist/debug_server/tests/testModule/debugger_caller.js +7 -0
  36. package/dist/debug_server/tests/testModule/debugger_caller.js.map +1 -0
  37. package/dist/debug_server/tests/testModule/debugger_class.d.ts +1 -0
  38. package/dist/debug_server/tests/testModule/debugger_class.js +39 -0
  39. package/dist/debug_server/tests/testModule/debugger_class.js.map +1 -0
  40. package/dist/debug_server/tests/testModule/debugger_class_array.d.ts +1 -0
  41. package/dist/debug_server/tests/testModule/debugger_class_array.js +15 -0
  42. package/dist/debug_server/tests/testModule/debugger_class_array.js.map +1 -0
  43. package/dist/debug_server/tests/testModule/debugger_closure.d.ts +1 -0
  44. package/dist/debug_server/tests/testModule/debugger_closure.js +48 -0
  45. package/dist/debug_server/tests/testModule/debugger_closure.js.map +1 -0
  46. package/dist/debug_server/tests/testModule/debugger_closure_if.d.ts +1 -0
  47. package/dist/debug_server/tests/testModule/debugger_closure_if.js +16 -0
  48. package/dist/debug_server/tests/testModule/debugger_closure_if.js.map +1 -0
  49. package/dist/debug_server/tests/testModule/debugger_generic_function.d.ts +1 -0
  50. package/dist/debug_server/tests/testModule/debugger_generic_function.js +10 -0
  51. package/dist/debug_server/tests/testModule/debugger_generic_function.js.map +1 -0
  52. package/dist/debug_server/tests/testModule/debugger_global.d.ts +1 -0
  53. package/dist/debug_server/tests/testModule/debugger_global.js +11 -0
  54. package/dist/debug_server/tests/testModule/debugger_global.js.map +1 -0
  55. package/dist/debug_server/tests/testModule/debugger_map.d.ts +1 -0
  56. package/dist/debug_server/tests/testModule/debugger_map.js +22 -0
  57. package/dist/debug_server/tests/testModule/debugger_map.js.map +1 -0
  58. package/dist/debug_server/tests/testModule/debugger_member_function.d.ts +1 -0
  59. package/dist/debug_server/tests/testModule/debugger_member_function.js +16 -0
  60. package/dist/debug_server/tests/testModule/debugger_member_function.js.map +1 -0
  61. package/dist/debug_server/tests/testModule/debugger_scopes.d.ts +1 -0
  62. package/dist/debug_server/tests/testModule/debugger_scopes.js +22 -0
  63. package/dist/debug_server/tests/testModule/debugger_scopes.js.map +1 -0
  64. package/dist/debug_server/tests/testModule/debugger_set.d.ts +1 -0
  65. package/dist/debug_server/tests/testModule/debugger_set.js +23 -0
  66. package/dist/debug_server/tests/testModule/debugger_set.js.map +1 -0
  67. package/dist/debug_server/tests/testModule/debugger_static_array.d.ts +1 -0
  68. package/dist/debug_server/tests/testModule/debugger_static_array.js +21 -0
  69. package/dist/debug_server/tests/testModule/debugger_static_array.js.map +1 -0
  70. package/dist/debug_server/tests/testModule/debugger_string.d.ts +1 -0
  71. package/dist/debug_server/tests/testModule/debugger_string.js +16 -0
  72. package/dist/debug_server/tests/testModule/debugger_string.js.map +1 -0
  73. package/dist/debug_server/tests/testModule/debugger_trap.d.ts +1 -0
  74. package/dist/debug_server/tests/testModule/debugger_trap.js +9 -0
  75. package/dist/debug_server/tests/testModule/debugger_trap.js.map +1 -0
  76. package/dist/debug_server/tests/testModule/debugger_tuple.d.ts +1 -0
  77. package/dist/debug_server/tests/testModule/debugger_tuple.js +15 -0
  78. package/dist/debug_server/tests/testModule/debugger_tuple.js.map +1 -0
  79. package/dist/debug_server/tests/testModule/import_failure.d.ts +1 -0
  80. package/dist/debug_server/tests/testModule/import_failure.js +4 -0
  81. package/dist/debug_server/tests/testModule/import_failure.js.map +1 -0
  82. package/dist/debug_server/tests/testModule/unittest_debug.case.js +10 -0
  83. package/dist/debug_server/tests/testModule/unittest_debug.case.js.map +1 -0
  84. package/dist/debug_server/tests/testModule/unittest_debug.d.ts +1 -0
  85. package/dist/debug_server/tests/testModule/unittest_debug.js +6 -0
  86. package/dist/debug_server/tests/testModule/unittest_debug.js.map +1 -0
  87. package/dist/debug_server/wasmEntry.js +32 -12
  88. package/dist/debug_server/wasmEntry.js.map +1 -1
  89. package/dist/dwarf/classDebugInfo.d.ts +45 -0
  90. package/dist/dwarf/classDebugInfo.js +292 -0
  91. package/dist/dwarf/classDebugInfo.js.map +1 -0
  92. package/dist/{heap_analyzer/src → dwarf}/dwarfParser.js +2 -0
  93. package/dist/dwarf/dwarfParser.js.map +1 -0
  94. package/dist/dwarf/functionDebugInfo.d.ts +63 -0
  95. package/dist/dwarf/functionDebugInfo.js +377 -0
  96. package/dist/dwarf/functionDebugInfo.js.map +1 -0
  97. package/dist/dwarf/tests/fixtures/functionDebugInfo.d.ts +3 -0
  98. package/dist/dwarf/tests/fixtures/functionDebugInfo.js +24 -0
  99. package/dist/dwarf/tests/fixtures/functionDebugInfo.js.map +1 -0
  100. package/dist/dwarf/tests/functionDebugInfo.test.d.ts +1 -0
  101. package/dist/dwarf/tests/functionDebugInfo.test.js +102 -0
  102. package/dist/dwarf/tests/functionDebugInfo.test.js.map +1 -0
  103. package/dist/heap_analyzer/cli.js +1 -1
  104. package/dist/heap_analyzer/cli.js.map +1 -1
  105. package/dist/heap_analyzer/src/constants.js +2 -1
  106. package/dist/heap_analyzer/src/constants.js.map +1 -1
  107. package/dist/heap_analyzer/src/debugInfoResolver.d.ts +4 -15
  108. package/dist/heap_analyzer/src/debugInfoResolver.js +20 -231
  109. package/dist/heap_analyzer/src/debugInfoResolver.js.map +1 -1
  110. package/dist/heap_analyzer/src/dominator.js +4 -1
  111. package/dist/heap_analyzer/src/dominator.js.map +1 -1
  112. package/dist/heap_analyzer/src/referenceScanner.js +7 -7
  113. package/dist/heap_analyzer/src/referenceScanner.js.map +1 -1
  114. package/dist/heap_analyzer/src/retainedSize.js +2 -0
  115. package/dist/heap_analyzer/src/retainedSize.js.map +1 -1
  116. package/dist/heap_analyzer/src/snapshot.js +1 -1
  117. package/dist/heap_analyzer/src/snapshot.js.map +1 -1
  118. package/dist/heap_analyzer/src/{tlsf.js → tlsfBlockWalker.js} +4 -3
  119. package/dist/heap_analyzer/src/tlsfBlockWalker.js.map +1 -0
  120. package/dist/heap_analyzer/src/types.d.ts +3 -32
  121. package/dist/heap_analyzer/src/types.js +1 -8
  122. package/dist/heap_analyzer/src/types.js.map +1 -1
  123. package/dist/heap_analyzer/tests/debugInfoResolver.test.js +1 -1
  124. package/dist/heap_analyzer/tests/debugInfoResolver.test.js.map +1 -1
  125. package/dist/heap_analyzer/tests/dwarfParser.test.js +57 -40
  126. package/dist/heap_analyzer/tests/dwarfParser.test.js.map +1 -1
  127. package/dist/heap_analyzer/tests/roots.test.js +1 -1
  128. package/dist/heap_analyzer/tests/roots.test.js.map +1 -1
  129. package/dist/heap_analyzer/tests/snapshot.test.js +4 -4
  130. package/dist/heap_analyzer/tests/testHelper.js +4 -7
  131. package/dist/heap_analyzer/tests/testHelper.js.map +1 -1
  132. package/dist/heap_analyzer/tests/tlsfBlockWalker.test.d.ts +1 -0
  133. package/dist/heap_analyzer/tests/{tlsf.test.js → tlsfBlockWalker.test.js} +2 -2
  134. package/dist/heap_analyzer/tests/tlsfBlockWalker.test.js.map +1 -0
  135. package/dist/heap_analyzer/tests/wasmExecutor.d.ts +2 -2
  136. package/dist/heap_analyzer/tests/wasmExecutor.js +5 -5
  137. package/dist/heap_analyzer/tests/wasmExecutor.js.map +1 -1
  138. package/dist/runtime/objectLayout.d.ts +10 -0
  139. package/dist/runtime/objectLayout.js +13 -0
  140. package/dist/runtime/objectLayout.js.map +1 -0
  141. package/dist/scripts/lib.d.ts +1 -0
  142. package/dist/scripts/lib.js +86 -22
  143. package/dist/scripts/lib.js.map +1 -1
  144. package/dist/test_runner/cli.d.ts +0 -1
  145. package/dist/test_runner/cli.js +37 -19
  146. package/dist/test_runner/cli.js.map +1 -1
  147. package/dist/test_runner/config.d.ts +3 -0
  148. package/dist/test_runner/config.js +8 -0
  149. package/dist/test_runner/config.js.map +1 -0
  150. package/dist/test_runner/core/analyze.js +12 -3
  151. package/dist/test_runner/core/analyze.js.map +1 -1
  152. package/dist/test_runner/core/compile.js +1 -4
  153. package/dist/test_runner/core/compile.js.map +1 -1
  154. package/dist/test_runner/core/compiler.js +2 -0
  155. package/dist/test_runner/core/compiler.js.map +1 -1
  156. package/dist/test_runner/core/execute.js +20 -3
  157. package/dist/test_runner/core/execute.js.map +1 -1
  158. package/dist/test_runner/executionResult.js +10 -8
  159. package/dist/test_runner/executionResult.js.map +1 -1
  160. package/dist/test_runner/generator/table-generator/index.js +4 -2
  161. package/dist/test_runner/generator/table-generator/index.js.map +1 -1
  162. package/dist/test_runner/parser/index.js +6 -5
  163. package/dist/test_runner/parser/index.js.map +1 -1
  164. package/dist/test_runner/parser/singleFileAnalysis.js +7 -4
  165. package/dist/test_runner/parser/singleFileAnalysis.js.map +1 -1
  166. package/dist/test_runner/parser/singleFunctionAnalysis.js +1 -1
  167. package/dist/test_runner/parser/singleFunctionAnalysis.js.map +1 -1
  168. package/dist/test_runner/utils/errorTraceHandler.js +2 -1
  169. package/dist/test_runner/utils/errorTraceHandler.js.map +1 -1
  170. package/dist/test_runner/utils/index.js +9 -2
  171. package/dist/test_runner/utils/index.js.map +1 -1
  172. package/dist/test_runner/utils/interface.js +2 -1
  173. package/dist/test_runner/utils/interface.js.map +1 -1
  174. package/dist/test_runner/utils/pathResolver.js +9 -7
  175. package/dist/test_runner/utils/pathResolver.js.map +1 -1
  176. package/dist/test_runner/utils/wasm.js +16 -8
  177. package/dist/test_runner/utils/wasm.js.map +1 -1
  178. package/dist/trace_visualizer.d.ts +2 -0
  179. package/dist/trace_visualizer.js +18 -0
  180. package/dist/trace_visualizer.js.map +1 -0
  181. package/dist/warpo.js +2 -1
  182. package/dist/warpo.js.map +1 -1
  183. package/package.json +9 -5
  184. package/dist/heap_analyzer/src/dwarfParser.js.map +0 -1
  185. package/dist/heap_analyzer/src/tlsf.js.map +0 -1
  186. package/dist/heap_analyzer/tests/tlsf.test.js.map +0 -1
  187. /package/dist/{heap_analyzer/tests/tlsf.test.d.ts → debug_server/tests/testModule/unittest_debug.case.d.ts} +0 -0
  188. /package/dist/{heap_analyzer/src → dwarf}/dwarfParser.d.ts +0 -0
  189. /package/dist/heap_analyzer/src/{tlsf.d.ts → tlsfBlockWalker.d.ts} +0 -0
@@ -3,15 +3,98 @@
3
3
  import { DebugClient } from "@vscode/debugadapter-testsupport";
4
4
  import * as assert from "node:assert/strict";
5
5
  import * as path from "node:path";
6
- import { describe, it, before, beforeEach, afterEach } from "node:test";
6
+ import { describe, it, beforeEach, afterEach } from "node:test";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { launchDapServer } from "./launcher.js";
9
9
  import { build } from "../../scripts/lib.js";
10
+ import { normalizeDebugPath } from "../debugPath.js";
10
11
  const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
11
12
  const DAP_SERVER = path.resolve(DIRNAME, "..", "..", "..", "dist", "debug_server", "dapServer.js");
13
+ const WARPO_CLI = path.resolve(DIRNAME, "..", "..", "..", "dist", "warpo.js");
12
14
  const TEST_MODULE_DIR = path.resolve(DIRNAME, "testModule");
13
- const TEST_MODULE_SOURCE = path.join(TEST_MODULE_DIR, "debugger_basic.ts");
14
- const TEST_MODULE_OUTPUT = path.join(TEST_MODULE_DIR, "build/debugger_basic.wasm");
15
+ function sourcePath(name) {
16
+ return path.join(TEST_MODULE_DIR, name);
17
+ }
18
+ function outputPath(source) {
19
+ return path.join(TEST_MODULE_DIR, "build", `${path.basename(source, ".ts")}.wasm`);
20
+ }
21
+ async function buildModule(source, extraSources = []) {
22
+ const output = outputPath(source);
23
+ const name = path.basename(source, ".ts");
24
+ let buildOutput = "";
25
+ const exitCode = await build({
26
+ argv: [source, ...extraSources, "-o", output, "--debug", "--enable-feature", "tail-call", "--use", "abort="],
27
+ cwd: TEST_MODULE_DIR,
28
+ onStdout: (chunk) => {
29
+ buildOutput += chunk;
30
+ },
31
+ });
32
+ if (exitCode !== 0) {
33
+ throw new Error(`failed to build ${name} test module: ${buildOutput}`);
34
+ }
35
+ return output;
36
+ }
37
+ async function waitForLoadedWasmSource(dc, outputPath) {
38
+ while (true) {
39
+ const event = await dc.waitForEvent("loadedSource");
40
+ const body = event.body;
41
+ if (body?.source?.path === path.resolve(outputPath)) {
42
+ return event;
43
+ }
44
+ }
45
+ }
46
+ async function waitForBreakpointStop(dc) {
47
+ while (true) {
48
+ const event = await dc.waitForEvent("stopped");
49
+ const body = event.body;
50
+ if (body?.reason === "breakpoint") {
51
+ return event;
52
+ }
53
+ }
54
+ }
55
+ async function waitForStderrOutput(dc) {
56
+ while (true) {
57
+ const event = await dc.waitForEvent("output");
58
+ const body = event.body;
59
+ if (body?.category === "stderr") {
60
+ return event;
61
+ }
62
+ }
63
+ }
64
+ function assertDefined(value, message) {
65
+ if (value === undefined) {
66
+ assert.fail(message ?? "Expected value to be defined");
67
+ }
68
+ return value;
69
+ }
70
+ function findVariable(variables, name) {
71
+ return variables.find((candidate) => candidate.name === name || candidate.name.startsWith(`${name}: `));
72
+ }
73
+ async function launchAndWaitForBreakpoint(dc, launchArgs) {
74
+ const stoppedPromise = waitForBreakpointStop(dc);
75
+ await dc.launchRequest(launchArgs);
76
+ await stoppedPromise;
77
+ }
78
+ async function readFrameLocals(dc, frame) {
79
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
80
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
81
+ return dc.variablesRequest({ variablesReference: localsScope.variablesReference });
82
+ }
83
+ async function readFrameScopeByName(dc, frame, name) {
84
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
85
+ const scope = scopesResponse.body.scopes.find((candidate) => candidate.name === name);
86
+ if (!scope) {
87
+ return undefined;
88
+ }
89
+ return dc.variablesRequest({ variablesReference: scope.variablesReference });
90
+ }
91
+ async function launchAndReadTopFrameLocals(dc, launchArgs) {
92
+ await launchAndWaitForBreakpoint(dc, launchArgs);
93
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
94
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
95
+ const variablesResponse = await readFrameLocals(dc, frame);
96
+ return { frame, variablesResponse };
97
+ }
15
98
  const waitForExit = (child, timeoutMs) => new Promise((resolve) => {
16
99
  if (child.exitCode !== null) {
17
100
  resolve(true);
@@ -27,19 +110,6 @@ const waitForExit = (child, timeoutMs) => new Promise((resolve) => {
27
110
  };
28
111
  child.once("exit", onExit);
29
112
  });
30
- before(async () => {
31
- let buildOutput = "";
32
- const exitCode = await build({
33
- argv: [TEST_MODULE_SOURCE, "-o", TEST_MODULE_OUTPUT, "--debug"],
34
- cwd: TEST_MODULE_DIR,
35
- onStdout: (chunk) => {
36
- buildOutput += chunk;
37
- },
38
- });
39
- if (exitCode !== 0) {
40
- throw new Error(`failed to build debugger test module: ${buildOutput}`);
41
- }
42
- });
43
113
  void describe("WarpoDebugSession", () => {
44
114
  let dc;
45
115
  let serverChild;
@@ -74,32 +144,1090 @@ void describe("WarpoDebugSession", () => {
74
144
  assert.equal(response.body.breakpoints[1].verified, true);
75
145
  assert.equal(response.body.breakpoints[1].line, 10);
76
146
  });
147
+ void it("should stop in a unit test after returning from main", { timeout: 15000 }, async () => {
148
+ const testSource = sourcePath("unittest_debug.case.ts");
149
+ const implementationSource = sourcePath("unittest_debug.ts");
150
+ await dc.initializeRequest();
151
+ await dc.setBreakpointsRequest({
152
+ source: { path: testSource },
153
+ breakpoints: [{ line: 9 }],
154
+ });
155
+ await dc.setBreakpointsRequest({
156
+ source: { path: implementationSource },
157
+ breakpoints: [{ line: 5 }],
158
+ });
159
+ const launchArgs = {
160
+ launchType: "unittest",
161
+ runtime: "node",
162
+ cwd: TEST_MODULE_DIR,
163
+ warpoPath: WARPO_CLI,
164
+ };
165
+ await launchAndWaitForBreakpoint(dc, launchArgs);
166
+ let stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
167
+ assert.equal(assertDefined(stackTraceResponse.body.stackFrames[0]).source?.path, normalizeDebugPath(implementationSource));
168
+ assert.equal(stackTraceResponse.body.stackFrames[0].line, 5);
169
+ const nextStop = waitForBreakpointStop(dc);
170
+ await dc.continueRequest({ threadId: 1 });
171
+ await nextStop;
172
+ stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
173
+ assert.equal(assertDefined(stackTraceResponse.body.stackFrames[0]).source?.path, normalizeDebugPath(testSource));
174
+ assert.equal(stackTraceResponse.body.stackFrames[0].line, 9);
175
+ });
77
176
  void it("should report the built wasm as a loaded source on launch", { timeout: 5000 }, async () => {
177
+ const source = sourcePath("debugger_basic.ts");
178
+ const output = await buildModule(source);
179
+ const breakpointLine = 13;
78
180
  await dc.initializeRequest();
79
181
  const breakpointResponse = await dc.setBreakpointsRequest({
80
- source: { path: TEST_MODULE_SOURCE },
81
- breakpoints: [{ line: 3 }],
182
+ source: { path: source },
183
+ breakpoints: [{ line: breakpointLine }],
82
184
  });
83
185
  assert.equal(breakpointResponse.body.breakpoints.length, 1);
84
186
  assert.equal(breakpointResponse.body.breakpoints[0].verified, true);
85
- assert.equal(breakpointResponse.body.breakpoints[0].line, 3);
187
+ assert.equal(breakpointResponse.body.breakpoints[0].line, breakpointLine);
86
188
  const launchArgs = {
87
- program: TEST_MODULE_OUTPUT,
189
+ program: output,
88
190
  launchType: "wasm file",
89
191
  runtime: "node",
90
192
  entryFunctionName: "_start",
91
193
  };
92
- const loadedSourcePromise = dc.waitForEvent("loadedSource");
93
- const stoppedPromise = dc.waitForEvent("stopped");
194
+ const loadedSourcePromise = waitForLoadedWasmSource(dc, output);
195
+ const stoppedPromise = waitForBreakpointStop(dc);
94
196
  await dc.launchRequest(launchArgs);
95
197
  const loadedSourceEvent = await loadedSourcePromise;
96
198
  const loadedSourceBody = loadedSourceEvent.body;
199
+ await stoppedPromise;
200
+ assert.equal(loadedSourceBody?.reason, "new");
201
+ assert.equal(loadedSourceBody?.source?.path, path.resolve(output));
202
+ assert.equal(loadedSourceBody?.source?.name, path.basename(output));
203
+ });
204
+ void it("should reject a wasm launch when the file is missing", { timeout: 5000 }, async () => {
205
+ await dc.initializeRequest();
206
+ const launchArgs = {
207
+ program: path.join(TEST_MODULE_DIR, "missing.wasm"),
208
+ launchType: "wasm file",
209
+ runtime: "node",
210
+ entryFunctionName: "_start",
211
+ };
212
+ const stderrPromise = waitForStderrOutput(dc);
213
+ const terminatedPromise = dc.waitForEvent("terminated");
214
+ await dc.launchRequest(launchArgs);
215
+ const stderrEvent = await stderrPromise;
216
+ const stderrBody = stderrEvent.body;
217
+ assert.match(stderrBody?.output ?? "", /Wasm file not found/);
218
+ await terminatedPromise;
219
+ });
220
+ void it("should terminate when the runtime exits after wasm instantiation fails", { timeout: 5000 }, async () => {
221
+ const source = sourcePath("import_failure.ts");
222
+ const output = await buildModule(source);
223
+ await dc.initializeRequest();
224
+ const launchArgs = {
225
+ program: output,
226
+ launchType: "wasm file",
227
+ runtime: "node",
228
+ entryFunctionName: "_start",
229
+ };
230
+ const stderrPromise = waitForStderrOutput(dc);
231
+ const terminatedPromise = dc.waitForEvent("terminated");
232
+ await dc.launchRequest(launchArgs);
233
+ const stderrEvent = await stderrPromise;
234
+ const stderrBody = stderrEvent.body;
235
+ assert.match(stderrBody?.output ?? "", /Failed to load wasm module/);
236
+ await terminatedPromise;
237
+ });
238
+ void it("should expose local variables after hitting a breakpoint", { timeout: 5000 }, async () => {
239
+ const source = sourcePath("debugger_basic.ts");
240
+ const output = await buildModule(source);
241
+ const breakpointLine = 13;
242
+ await dc.initializeRequest();
243
+ await dc.setBreakpointsRequest({
244
+ source: { path: source },
245
+ breakpoints: [{ line: breakpointLine }],
246
+ });
247
+ const launchArgs = {
248
+ program: output,
249
+ launchType: "wasm file",
250
+ runtime: "node",
251
+ entryFunctionName: "_start",
252
+ };
253
+ const { frame, variablesResponse } = await launchAndReadTopFrameLocals(dc, launchArgs);
254
+ assert.equal(frame.source?.path, normalizeDebugPath(source));
255
+ assert.equal(frame.line, breakpointLine);
256
+ const a = assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "a"));
257
+ assert.equal(a.type, "i32");
258
+ assert.equal(a.value, "1");
259
+ const b = assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "b"));
260
+ assert.equal(b.type, "f32");
261
+ const c = assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "c"));
262
+ assert.equal(c.type, "i64");
263
+ const d = assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "d"));
264
+ assert.equal(d.type, "f64");
265
+ });
266
+ void it("should stop in every instantiated generic function", { timeout: 10000 }, async () => {
267
+ const source = sourcePath("debugger_generic_function.ts");
268
+ const output = await buildModule(source);
269
+ const breakpointLine = 4;
270
+ await dc.initializeRequest();
271
+ await dc.setBreakpointsRequest({
272
+ source: { path: source },
273
+ breakpoints: [{ line: breakpointLine }],
274
+ });
275
+ const launchArgs = {
276
+ program: output,
277
+ launchType: "wasm file",
278
+ runtime: "node",
279
+ entryFunctionName: "main",
280
+ };
281
+ await launchAndWaitForBreakpoint(dc, launchArgs);
282
+ const firstStackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
283
+ const firstFrame = assertDefined(firstStackTraceResponse.body.stackFrames[0]);
284
+ const firstVariablesResponse = await readFrameLocals(dc, firstFrame);
285
+ const firstValue = assertDefined(findVariable(firstVariablesResponse.body.variables, "value"));
286
+ const secondStop = waitForBreakpointStop(dc);
287
+ await dc.continueRequest({ threadId: 1 });
288
+ await secondStop;
289
+ const secondStackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
290
+ const secondFrame = assertDefined(secondStackTraceResponse.body.stackFrames[0]);
291
+ const secondVariablesResponse = await readFrameLocals(dc, secondFrame);
292
+ const secondValue = assertDefined(findVariable(secondVariablesResponse.body.variables, "value"));
293
+ assert.deepEqual([firstValue.type, secondValue.type].toSorted((left, right) => (left ?? "").localeCompare(right ?? "")), ["f64", "i32"]);
294
+ assert.match(firstFrame.name, /identity</);
295
+ assert.match(secondFrame.name, /identity</);
296
+ });
297
+ void it("should remove a breakpoint from a concrete function", { timeout: 5000 }, async () => {
298
+ const source = sourcePath("debugger_basic.ts");
299
+ const output = await buildModule(source);
300
+ const breakpointLine = 13;
301
+ await dc.initializeRequest();
302
+ await dc.setBreakpointsRequest({
303
+ source: { path: source },
304
+ breakpoints: [{ line: breakpointLine }],
305
+ });
306
+ const launchArgs = {
307
+ program: output,
308
+ launchType: "wasm file",
309
+ runtime: "node",
310
+ entryFunctionName: "_start",
311
+ };
312
+ await launchAndWaitForBreakpoint(dc, launchArgs);
313
+ const terminatedPromise = dc.waitForEvent("terminated");
314
+ await dc.setBreakpointsRequest({
315
+ source: { path: source },
316
+ breakpoints: [],
317
+ });
318
+ await dc.continueRequest({ threadId: 1 });
319
+ await terminatedPromise;
320
+ });
321
+ void it("should remove breakpoints from every instantiated generic function", { timeout: 10000 }, async () => {
322
+ const source = sourcePath("debugger_generic_function.ts");
323
+ const output = await buildModule(source);
324
+ const breakpointLine = 4;
325
+ await dc.initializeRequest();
326
+ await dc.setBreakpointsRequest({
327
+ source: { path: source },
328
+ breakpoints: [{ line: breakpointLine }],
329
+ });
330
+ const launchArgs = {
331
+ program: output,
332
+ launchType: "wasm file",
333
+ runtime: "node",
334
+ entryFunctionName: "main",
335
+ };
336
+ await launchAndWaitForBreakpoint(dc, launchArgs);
337
+ const terminatedPromise = dc.waitForEvent("terminated");
338
+ await dc.setBreakpointsRequest({
339
+ source: { path: source },
340
+ breakpoints: [],
341
+ });
342
+ await dc.continueRequest({ threadId: 1 });
343
+ await terminatedPromise;
344
+ });
345
+ void it("should add a breakpoint while the program is executing", { timeout: 10000 }, async () => {
346
+ const source = sourcePath("debugger_breakpoint_update.ts");
347
+ const output = await buildModule(source);
348
+ const initialBreakpointLine = 6;
349
+ const addedBreakpointLine = 8;
350
+ await dc.initializeRequest();
351
+ await dc.setBreakpointsRequest({
352
+ source: { path: source },
353
+ breakpoints: [{ line: initialBreakpointLine }],
354
+ });
355
+ const launchArgs = {
356
+ program: output,
357
+ launchType: "wasm file",
358
+ runtime: "node",
359
+ entryFunctionName: "_start",
360
+ };
361
+ await launchAndWaitForBreakpoint(dc, launchArgs);
362
+ const secondStop = waitForBreakpointStop(dc);
363
+ await dc.continueRequest({ threadId: 1 });
364
+ const breakpointResponse = await dc.setBreakpointsRequest({
365
+ source: { path: source },
366
+ breakpoints: [{ line: addedBreakpointLine }],
367
+ });
368
+ assert.equal(breakpointResponse.body.breakpoints[0].line, addedBreakpointLine);
369
+ await secondStop;
370
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
371
+ assert.equal(assertDefined(stackTraceResponse.body.stackFrames[0]).line, addedBreakpointLine);
372
+ await dc.disconnectRequest({ terminateDebuggee: true });
373
+ });
374
+ void it("should expose this in a class member function", { timeout: 5000 }, async () => {
375
+ const source = sourcePath("debugger_member_function.ts");
376
+ const output = await buildModule(source);
377
+ const breakpointLine = 12;
378
+ await dc.initializeRequest();
379
+ await dc.setBreakpointsRequest({
380
+ source: { path: source },
381
+ breakpoints: [{ line: breakpointLine }],
382
+ });
383
+ const launchArgs = {
384
+ program: output,
385
+ launchType: "wasm file",
386
+ runtime: "node",
387
+ entryFunctionName: "_start",
388
+ };
389
+ await launchAndWaitForBreakpoint(dc, launchArgs);
390
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
391
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
392
+ assert.match(frame.name, /Counter#add/);
393
+ const variablesResponse = await readFrameLocals(dc, frame);
394
+ const thisVariable = assertDefined(findVariable(variablesResponse.body.variables, "this"));
395
+ assert.ok(thisVariable.type?.endsWith("Counter"));
396
+ assert.ok(thisVariable.variablesReference > 0);
397
+ const thisFieldsResponse = await dc.variablesRequest({ variablesReference: thisVariable.variablesReference });
398
+ const value = assertDefined(thisFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
399
+ assert.equal(value.type, "i32");
400
+ assert.equal(value.value, "40");
401
+ const delta = assertDefined(findVariable(variablesResponse.body.variables, "delta"));
402
+ assert.equal(delta.type, "i32");
403
+ assert.equal(delta.value, "2");
404
+ });
405
+ void it("should expose wasm call stack frames after hitting a breakpoint", { timeout: 5000 }, async () => {
406
+ const source = sourcePath("debugger_basic.ts");
407
+ const output = await buildModule(source);
408
+ const breakpointLine = 13;
409
+ await dc.initializeRequest();
410
+ await dc.setBreakpointsRequest({
411
+ source: { path: source },
412
+ breakpoints: [{ line: breakpointLine }],
413
+ });
414
+ const launchArgs = {
415
+ program: output,
416
+ launchType: "wasm file",
417
+ runtime: "node",
418
+ entryFunctionName: "_start",
419
+ };
420
+ await launchAndWaitForBreakpoint(dc, launchArgs);
421
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 20 });
422
+ assert.ok(stackTraceResponse.body.stackFrames.length >= 2);
423
+ const topFrame = stackTraceResponse.body.stackFrames[0];
424
+ const callerFrame = stackTraceResponse.body.stackFrames[1];
425
+ assert.match(topFrame.name, /calculate/);
426
+ assert.equal(topFrame.source?.path, normalizeDebugPath(source));
427
+ assert.equal(topFrame.line, breakpointLine);
428
+ assert.match(callerFrame.name, /_start/);
429
+ assert.equal(callerFrame.source?.path, normalizeDebugPath(source));
430
+ const variablesResponse = await readFrameLocals(dc, topFrame);
431
+ assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "a"));
432
+ const callerVariablesResponse = await readFrameLocals(dc, callerFrame);
433
+ const callerSeed = assertDefined(callerVariablesResponse.body.variables.find((candidate) => candidate.name === "callerSeed"));
434
+ assert.equal(callerSeed.type, "i32");
435
+ assert.equal(callerSeed.value, "23");
436
+ });
437
+ void it("should expose local variables in separate scopes when shadowed", { timeout: 5000 }, async () => {
438
+ const source = sourcePath("debugger_scopes.ts");
439
+ const output = await buildModule(source);
440
+ const breakpointLine = 12;
441
+ await dc.initializeRequest();
442
+ await dc.setBreakpointsRequest({
443
+ source: { path: source },
444
+ breakpoints: [{ line: breakpointLine }],
445
+ });
446
+ const launchArgs = {
447
+ program: output,
448
+ launchType: "wasm file",
449
+ runtime: "node",
450
+ entryFunctionName: "_start",
451
+ };
452
+ await launchAndWaitForBreakpoint(dc, launchArgs);
453
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
454
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
455
+ const localScope = assertDefined(await readFrameScopeByName(dc, frame, "Local: nestedScopes"));
456
+ const localA = assertDefined(findVariable(localScope.body.variables, "a"));
457
+ assert.equal(localA.value, "1");
458
+ const localB = assertDefined(findVariable(localScope.body.variables, "b"));
459
+ assert.equal(localB.value, "2");
460
+ const blockScope = assertDefined(await readFrameScopeByName(dc, frame, "Block: nestedScopes"));
461
+ const blockA = assertDefined(findVariable(blockScope.body.variables, "a"));
462
+ assert.equal(blockA.value, "10");
463
+ assert.equal(blockScope.body.variables.length, 1);
464
+ });
465
+ void it("should resolve call stack frames across multiple source files", { timeout: 5000 }, async () => {
466
+ const entrySource = sourcePath("debugger_caller.ts");
467
+ const calleeSource = sourcePath("debugger_callee.ts");
468
+ const output = await buildModule(entrySource, [calleeSource]);
469
+ const breakpointLine = 41;
470
+ await dc.initializeRequest();
471
+ await dc.setBreakpointsRequest({
472
+ source: { path: calleeSource },
473
+ breakpoints: [{ line: breakpointLine }],
474
+ });
475
+ const launchArgs = {
476
+ program: output,
477
+ launchType: "wasm file",
478
+ runtime: "node",
479
+ entryFunctionName: "_start",
480
+ };
481
+ await launchAndWaitForBreakpoint(dc, launchArgs);
482
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 20 });
483
+ assert.ok(stackTraceResponse.body.stackFrames.length >= 2);
484
+ const topFrame = stackTraceResponse.body.stackFrames[0];
485
+ const callerFrame = stackTraceResponse.body.stackFrames[1];
486
+ assert.match(topFrame.name, /calculate/);
487
+ assert.equal(topFrame.source?.path, normalizeDebugPath(calleeSource));
488
+ assert.equal(topFrame.line, breakpointLine);
489
+ assert.match(callerFrame.name, /_start/);
490
+ assert.equal(callerFrame.source?.path, normalizeDebugPath(entrySource));
491
+ const variablesResponse = await readFrameLocals(dc, topFrame);
492
+ assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "a"));
493
+ const callerVariablesResponse = await readFrameLocals(dc, callerFrame);
494
+ const callerSeed = assertDefined(callerVariablesResponse.body.variables.find((candidate) => candidate.name === "callerSeed"));
495
+ assert.equal(callerSeed.type, "i32");
496
+ assert.equal(callerSeed.value, "23");
497
+ });
498
+ void it("should stop on a return breakpoint before evaluating its value", { timeout: 5000 }, async () => {
499
+ const entrySource = sourcePath("debugger_caller.ts");
500
+ const calleeSource = sourcePath("debugger_callee.ts");
501
+ const output = await buildModule(entrySource, [calleeSource]);
502
+ await dc.initializeRequest();
503
+ await dc.setBreakpointsRequest({
504
+ source: { path: entrySource },
505
+ breakpoints: [{ line: 6 }],
506
+ });
507
+ await dc.setBreakpointsRequest({
508
+ source: { path: calleeSource },
509
+ breakpoints: [{ line: 41 }],
510
+ });
511
+ const launchArgs = {
512
+ program: output,
513
+ launchType: "wasm file",
514
+ runtime: "node",
515
+ entryFunctionName: "_start",
516
+ };
517
+ await launchAndWaitForBreakpoint(dc, launchArgs);
518
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
519
+ const topFrame = assertDefined(stackTraceResponse.body.stackFrames[0]);
520
+ assert.match(topFrame.name, /_start/);
521
+ assert.equal(topFrame.source?.path, normalizeDebugPath(entrySource));
522
+ assert.equal(topFrame.line, 6);
523
+ });
524
+ void it("should terminate when the entry function returns", { timeout: 5000 }, async () => {
525
+ const source = sourcePath("debugger_class.ts");
526
+ const output = await buildModule(source);
527
+ const breakpointLine = 45;
528
+ await dc.initializeRequest();
529
+ await dc.setBreakpointsRequest({
530
+ source: { path: source },
531
+ breakpoints: [{ line: breakpointLine }],
532
+ });
533
+ const launchArgs = {
534
+ program: output,
535
+ launchType: "wasm file",
536
+ runtime: "node",
537
+ entryFunctionName: "_start",
538
+ };
539
+ const { variablesResponse } = await launchAndReadTopFrameLocals(dc, launchArgs);
540
+ const holder = assertDefined(findVariable(variablesResponse.body.variables, "holder"));
541
+ await dc.variablesRequest({ variablesReference: holder.variablesReference });
542
+ const terminatedPromise = dc.waitForEvent("terminated");
543
+ await dc.continueRequest({ threadId: 1 });
544
+ await terminatedPromise;
545
+ });
546
+ void it("should expand class local variables using DWARF layout", { timeout: 5000 }, async () => {
547
+ const source = sourcePath("debugger_class.ts");
548
+ const output = await buildModule(source);
549
+ const breakpointLine = 45;
550
+ await dc.initializeRequest();
551
+ await dc.setBreakpointsRequest({
552
+ source: { path: source },
553
+ breakpoints: [{ line: breakpointLine }],
554
+ });
555
+ const launchArgs = {
556
+ program: output,
557
+ launchType: "wasm file",
558
+ runtime: "node",
559
+ entryFunctionName: "_start",
560
+ };
561
+ const stoppedPromise = waitForBreakpointStop(dc);
562
+ await dc.launchRequest(launchArgs);
563
+ await stoppedPromise;
564
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
565
+ const frame = stackTraceResponse.body.stackFrames[0];
566
+ assert.notStrictEqual(frame, undefined);
567
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
568
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
569
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
570
+ const holder = assertDefined(findVariable(variablesResponse.body.variables, "holder"));
571
+ assert.ok(holder.variablesReference > 0);
572
+ assert.match(holder.name, /^holder: .+Holder$/);
573
+ assert.ok(holder.type?.endsWith("Holder"));
574
+ assert.equal(holder.value, "");
575
+ const holderFieldsResponse = await dc.variablesRequest({ variablesReference: holder.variablesReference });
576
+ const count = assertDefined(holderFieldsResponse.body.variables.find((candidate) => candidate.name === "count"));
577
+ assert.equal(count.type, "i32");
578
+ assert.equal(count.value, "7");
579
+ const child = assertDefined(findVariable(holderFieldsResponse.body.variables, "child"));
580
+ assert.ok(child.type?.endsWith("Child"));
581
+ assert.ok(child.variablesReference > 0);
582
+ assert.equal(child.value, "");
583
+ const childFieldsResponse = await dc.variablesRequest({ variablesReference: child.variablesReference });
584
+ const value = assertDefined(childFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
585
+ assert.equal(value.type, "i32");
586
+ assert.equal(value.value, "11");
587
+ const box = assertDefined(findVariable(variablesResponse.body.variables, "box"));
588
+ assert.ok(box.type?.endsWith("BaseBox"));
589
+ assert.ok(box.variablesReference > 0);
590
+ assert.equal(box.value, "");
591
+ const boxFieldsResponse = await dc.variablesRequest({ variablesReference: box.variablesReference });
592
+ const base = assertDefined(boxFieldsResponse.body.variables.find((candidate) => candidate.name === "base"));
593
+ assert.equal(base.type, "i32");
594
+ assert.equal(base.value, "3");
595
+ const extra = assertDefined(boxFieldsResponse.body.variables.find((candidate) => candidate.name === "extra"));
596
+ assert.equal(extra.type, "i32");
597
+ assert.equal(extra.value, "17");
598
+ });
599
+ void it("should display string objects without expanding them", { timeout: 5000 }, async () => {
600
+ const source = sourcePath("debugger_string.ts");
601
+ const output = await buildModule(source);
602
+ const breakpointLine = 18;
603
+ await dc.initializeRequest();
604
+ await dc.setBreakpointsRequest({
605
+ source: { path: source },
606
+ breakpoints: [{ line: breakpointLine }],
607
+ });
608
+ const launchArgs = {
609
+ program: output,
610
+ launchType: "wasm file",
611
+ runtime: "node",
612
+ entryFunctionName: "_start",
613
+ };
614
+ const stoppedPromise = waitForBreakpointStop(dc);
615
+ await dc.launchRequest(launchArgs);
616
+ await stoppedPromise;
617
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
618
+ const frame = stackTraceResponse.body.stackFrames[0];
619
+ assert.notStrictEqual(frame, undefined);
620
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
621
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
622
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
623
+ const message = assertDefined(variablesResponse.body.variables.find((candidate) => candidate.name === "message"));
624
+ assert.equal(message.type, "~lib/string/String");
625
+ assert.equal(message.value, '"hello debugger"');
626
+ assert.equal(message.variablesReference, 0);
627
+ const holder = assertDefined(findVariable(variablesResponse.body.variables, "holder"));
628
+ assert.ok(holder.type?.endsWith("StringHolder"));
629
+ assert.ok(holder.variablesReference > 0);
630
+ assert.equal(holder.value, "");
631
+ const holderFieldsResponse = await dc.variablesRequest({ variablesReference: holder.variablesReference });
632
+ const label = assertDefined(holderFieldsResponse.body.variables.find((candidate) => candidate.name === "label"));
633
+ assert.equal(label.type, "~lib/string/String");
634
+ assert.equal(label.value, '"first holder"');
635
+ assert.equal(label.variablesReference, 0);
636
+ });
637
+ void it("should expand array elements by index", { timeout: 5000 }, async () => {
638
+ const source = sourcePath("debugger_array.ts");
639
+ const output = await buildModule(source);
640
+ const breakpointLine = 9;
641
+ await dc.initializeRequest();
642
+ await dc.setBreakpointsRequest({
643
+ source: { path: source },
644
+ breakpoints: [{ line: breakpointLine }],
645
+ });
646
+ const launchArgs = {
647
+ program: output,
648
+ launchType: "wasm file",
649
+ runtime: "node",
650
+ entryFunctionName: "_start",
651
+ };
652
+ const stoppedPromise = waitForBreakpointStop(dc);
653
+ await dc.launchRequest(launchArgs);
654
+ await stoppedPromise;
655
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
656
+ const frame = stackTraceResponse.body.stackFrames[0];
657
+ assert.notStrictEqual(frame, undefined);
658
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
659
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
660
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
661
+ const values = assertDefined(findVariable(variablesResponse.body.variables, "values"));
662
+ assert.ok(values.type?.startsWith("~lib/array/Array<"));
663
+ assert.ok(values.variablesReference > 0);
664
+ assert.equal(values.value, "");
665
+ const elementsResponse = await dc.variablesRequest({ variablesReference: values.variablesReference });
666
+ assert.deepEqual(elementsResponse.body.variables.map((variable) => ({
667
+ name: variable.name,
668
+ type: variable.type,
669
+ value: variable.value,
670
+ variablesReference: variable.variablesReference,
671
+ })), [
672
+ { name: "0", type: "i32", value: "3", variablesReference: 0 },
673
+ { name: "1", type: "i32", value: "5", variablesReference: 0 },
674
+ { name: "2", type: "i32", value: "8", variablesReference: 0 },
675
+ ]);
676
+ });
677
+ void it("should expand ArrayBuffer values by index", { timeout: 5000 }, async () => {
678
+ const source = sourcePath("debugger_array_buffer.ts");
679
+ const output = await buildModule(source);
680
+ const breakpointLine = 12;
681
+ await dc.initializeRequest();
682
+ await dc.setBreakpointsRequest({
683
+ source: { path: source },
684
+ breakpoints: [{ line: breakpointLine }],
685
+ });
686
+ const launchArgs = {
687
+ program: output,
688
+ launchType: "wasm file",
689
+ runtime: "node",
690
+ entryFunctionName: "_start",
691
+ };
692
+ const { variablesResponse } = await launchAndReadTopFrameLocals(dc, launchArgs);
693
+ const buffer = assertDefined(findVariable(variablesResponse.body.variables, "buffer"));
694
+ assert.equal(buffer.type, "~lib/arraybuffer/ArrayBuffer");
695
+ assert.ok(buffer.variablesReference > 0);
696
+ assert.equal(buffer.value, "");
697
+ const bufferElementsResponse = await dc.variablesRequest({ variablesReference: buffer.variablesReference });
698
+ assert.deepEqual(bufferElementsResponse.body.variables.map((variable) => ({
699
+ name: variable.name,
700
+ type: variable.type,
701
+ value: variable.value,
702
+ variablesReference: variable.variablesReference,
703
+ })), [
704
+ { name: "0", type: "u8", value: "13", variablesReference: 0 },
705
+ { name: "1", type: "u8", value: "21", variablesReference: 0 },
706
+ { name: "2", type: "u8", value: "34", variablesReference: 0 },
707
+ ]);
708
+ });
709
+ void it("should expand class array elements as objects", { timeout: 5000 }, async () => {
710
+ const source = sourcePath("debugger_class_array.ts");
711
+ const output = await buildModule(source);
712
+ const breakpointLine = 17;
713
+ await dc.initializeRequest();
714
+ await dc.setBreakpointsRequest({
715
+ source: { path: source },
716
+ breakpoints: [{ line: breakpointLine }],
717
+ });
718
+ const launchArgs = {
719
+ program: output,
720
+ launchType: "wasm file",
721
+ runtime: "node",
722
+ entryFunctionName: "_start",
723
+ };
724
+ const stoppedPromise = waitForBreakpointStop(dc);
725
+ await dc.launchRequest(launchArgs);
726
+ await stoppedPromise;
727
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
728
+ const frame = stackTraceResponse.body.stackFrames[0];
729
+ assert.notStrictEqual(frame, undefined);
730
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
731
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
732
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
733
+ const children = assertDefined(findVariable(variablesResponse.body.variables, "children"));
734
+ assert.ok(children.type?.startsWith("~lib/array/Array<"));
735
+ assert.ok(children.variablesReference > 0);
736
+ assert.equal(children.value, "");
737
+ const elementsResponse = await dc.variablesRequest({ variablesReference: children.variablesReference });
738
+ const firstChild = assertDefined(findVariable(elementsResponse.body.variables, "0"));
739
+ assert.ok(firstChild.type?.endsWith("Child"));
740
+ assert.ok(firstChild.variablesReference > 0);
741
+ assert.equal(firstChild.value, "");
742
+ const secondChild = assertDefined(findVariable(elementsResponse.body.variables, "1"));
743
+ assert.ok(secondChild.type?.endsWith("Child"));
744
+ assert.ok(secondChild.variablesReference > 0);
745
+ assert.equal(secondChild.value, "");
746
+ const firstChildFieldsResponse = await dc.variablesRequest({ variablesReference: firstChild.variablesReference });
747
+ const firstValue = assertDefined(firstChildFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
748
+ assert.equal(firstValue.type, "i32");
749
+ assert.equal(firstValue.value, "21");
750
+ const secondChildFieldsResponse = await dc.variablesRequest({ variablesReference: secondChild.variablesReference });
751
+ const secondValue = assertDefined(secondChildFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
752
+ assert.equal(secondValue.type, "i32");
753
+ assert.equal(secondValue.value, "34");
754
+ });
755
+ void it("should expand tuple elements by index", { timeout: 5000 }, async () => {
756
+ const source = sourcePath("debugger_tuple.ts");
757
+ const output = await buildModule(source);
758
+ const breakpointLine = 17;
759
+ await dc.initializeRequest();
760
+ await dc.setBreakpointsRequest({
761
+ source: { path: source },
762
+ breakpoints: [{ line: breakpointLine }],
763
+ });
764
+ const launchArgs = {
765
+ program: output,
766
+ launchType: "wasm file",
767
+ runtime: "node",
768
+ entryFunctionName: "_start",
769
+ };
770
+ const stoppedPromise = waitForBreakpointStop(dc);
771
+ await dc.launchRequest(launchArgs);
772
+ await stoppedPromise;
773
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
774
+ const frame = stackTraceResponse.body.stackFrames[0];
775
+ assert.notStrictEqual(frame, undefined);
776
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
777
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
778
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
779
+ const values = assertDefined(findVariable(variablesResponse.body.variables, "values"));
780
+ assert.equal(values.type, "~lib/tuple/SmallTuple");
781
+ assert.ok(values.variablesReference > 0);
782
+ assert.equal(values.value, "");
783
+ const elementsResponse = await dc.variablesRequest({ variablesReference: values.variablesReference });
784
+ const first = assertDefined(elementsResponse.body.variables.find((candidate) => candidate.name === "0"));
785
+ assert.equal(first.type, "usize");
786
+ assert.equal(first.value, "89");
787
+ assert.equal(first.variablesReference, 0);
788
+ const second = assertDefined(elementsResponse.body.variables.find((candidate) => candidate.name === "1"));
789
+ assert.equal(second.type, "~lib/string/String");
790
+ assert.equal(second.value, '"tuple value"');
791
+ assert.equal(second.variablesReference, 0);
792
+ const third = assertDefined(findVariable(elementsResponse.body.variables, "2"));
793
+ assert.ok(third.type?.endsWith("Child"));
794
+ assert.ok(third.variablesReference > 0);
795
+ assert.equal(third.value, "");
796
+ const childFieldsResponse = await dc.variablesRequest({ variablesReference: third.variablesReference });
797
+ const value = assertDefined(childFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
798
+ assert.equal(value.type, "i32");
799
+ assert.equal(value.value, "55");
800
+ });
801
+ void it("should expand static array elements by index", { timeout: 5000 }, async () => {
802
+ const source = sourcePath("debugger_static_array.ts");
803
+ const output = await buildModule(source);
804
+ const breakpointLine = 23;
805
+ await dc.initializeRequest();
806
+ await dc.setBreakpointsRequest({
807
+ source: { path: source },
808
+ breakpoints: [{ line: breakpointLine }],
809
+ });
810
+ const launchArgs = {
811
+ program: output,
812
+ launchType: "wasm file",
813
+ runtime: "node",
814
+ entryFunctionName: "_start",
815
+ };
816
+ const stoppedPromise = waitForBreakpointStop(dc);
817
+ await dc.launchRequest(launchArgs);
818
+ await stoppedPromise;
819
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
820
+ const frame = stackTraceResponse.body.stackFrames[0];
821
+ assert.notStrictEqual(frame, undefined);
822
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
823
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
824
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
825
+ const values = assertDefined(findVariable(variablesResponse.body.variables, "values"));
826
+ assert.ok(values.type?.startsWith("~lib/staticarray/StaticArray<"));
827
+ assert.ok(values.variablesReference > 0);
828
+ assert.equal(values.value, "");
829
+ const valueElementsResponse = await dc.variablesRequest({ variablesReference: values.variablesReference });
830
+ assert.deepEqual(valueElementsResponse.body.variables.map((variable) => variable.name), ["0", "1", "2"]);
831
+ assert.deepEqual(valueElementsResponse.body.variables.map((variable) => ({
832
+ name: variable.name,
833
+ type: variable.type,
834
+ value: variable.value,
835
+ variablesReference: variable.variablesReference,
836
+ })), [
837
+ { name: "0", type: "i32", value: "13", variablesReference: 0 },
838
+ { name: "1", type: "i32", value: "21", variablesReference: 0 },
839
+ { name: "2", type: "i32", value: "34", variablesReference: 0 },
840
+ ]);
841
+ const children = assertDefined(findVariable(variablesResponse.body.variables, "children"));
842
+ assert.ok(children.type?.startsWith("~lib/staticarray/StaticArray<"));
843
+ assert.ok(children.variablesReference > 0);
844
+ assert.equal(children.value, "");
845
+ const childElementsResponse = await dc.variablesRequest({ variablesReference: children.variablesReference });
846
+ const firstChild = assertDefined(findVariable(childElementsResponse.body.variables, "0"));
847
+ assert.ok(firstChild.type?.endsWith("Child"));
848
+ assert.ok(firstChild.variablesReference > 0);
849
+ assert.equal(firstChild.value, "");
850
+ const secondChild = assertDefined(findVariable(childElementsResponse.body.variables, "1"));
851
+ assert.ok(secondChild.type?.endsWith("Child"));
852
+ assert.ok(secondChild.variablesReference > 0);
853
+ assert.equal(secondChild.value, "");
854
+ const firstChildFieldsResponse = await dc.variablesRequest({ variablesReference: firstChild.variablesReference });
855
+ const firstValue = assertDefined(firstChildFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
856
+ assert.equal(firstValue.type, "i32");
857
+ assert.equal(firstValue.value, "44");
858
+ const secondChildFieldsResponse = await dc.variablesRequest({ variablesReference: secondChild.variablesReference });
859
+ const secondValue = assertDefined(secondChildFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
860
+ assert.equal(secondValue.type, "i32");
861
+ assert.equal(secondValue.value, "55");
862
+ });
863
+ void it("should expand set elements by index", { timeout: 5000 }, async () => {
864
+ const source = sourcePath("debugger_set.ts");
865
+ const output = await buildModule(source);
866
+ const breakpointLine = 25;
867
+ await dc.initializeRequest();
868
+ await dc.setBreakpointsRequest({
869
+ source: { path: source },
870
+ breakpoints: [{ line: breakpointLine }],
871
+ });
872
+ const launchArgs = {
873
+ program: output,
874
+ launchType: "wasm file",
875
+ runtime: "node",
876
+ entryFunctionName: "_start",
877
+ };
878
+ const stoppedPromise = waitForBreakpointStop(dc);
879
+ await dc.launchRequest(launchArgs);
880
+ await stoppedPromise;
881
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
882
+ const frame = stackTraceResponse.body.stackFrames[0];
883
+ assert.notStrictEqual(frame, undefined);
884
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
885
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
886
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
887
+ const values = assertDefined(findVariable(variablesResponse.body.variables, "values"));
888
+ assert.ok(values.type?.startsWith("~lib/set/Set<"));
889
+ assert.ok(values.variablesReference > 0);
890
+ assert.equal(values.value, "");
891
+ const valueElementsResponse = await dc.variablesRequest({ variablesReference: values.variablesReference });
892
+ assert.deepEqual(valueElementsResponse.body.variables.map((variable) => ({
893
+ name: variable.name,
894
+ type: variable.type,
895
+ value: variable.value,
896
+ variablesReference: variable.variablesReference,
897
+ })), [
898
+ { name: "0", type: "i32", value: "5", variablesReference: 0 },
899
+ { name: "1", type: "i32", value: "8", variablesReference: 0 },
900
+ { name: "2", type: "i32", value: "13", variablesReference: 0 },
901
+ ]);
902
+ const children = assertDefined(findVariable(variablesResponse.body.variables, "children"));
903
+ assert.ok(children.type?.startsWith("~lib/set/Set<"));
904
+ assert.ok(children.variablesReference > 0);
905
+ assert.equal(children.value, "");
906
+ const childElementsResponse = await dc.variablesRequest({ variablesReference: children.variablesReference });
907
+ const firstChild = assertDefined(findVariable(childElementsResponse.body.variables, "0"));
908
+ assert.ok(firstChild.type?.endsWith("Child"));
909
+ assert.ok(firstChild.variablesReference > 0);
910
+ assert.equal(firstChild.value, "");
911
+ const secondChild = assertDefined(findVariable(childElementsResponse.body.variables, "1"));
912
+ assert.ok(secondChild.type?.endsWith("Child"));
913
+ assert.ok(secondChild.variablesReference > 0);
914
+ assert.equal(secondChild.value, "");
915
+ const firstChildFieldsResponse = await dc.variablesRequest({ variablesReference: firstChild.variablesReference });
916
+ const firstValue = assertDefined(firstChildFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
917
+ assert.equal(firstValue.type, "i32");
918
+ assert.equal(firstValue.value, "61");
919
+ const secondChildFieldsResponse = await dc.variablesRequest({ variablesReference: secondChild.variablesReference });
920
+ const secondValue = assertDefined(secondChildFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
921
+ assert.equal(secondValue.type, "i32");
922
+ assert.equal(secondValue.value, "89");
923
+ });
924
+ void it("should expand map entries by index", { timeout: 5000 }, async () => {
925
+ const source = sourcePath("debugger_map.ts");
926
+ const output = await buildModule(source);
927
+ const breakpointLine = 26;
928
+ await dc.initializeRequest();
929
+ await dc.setBreakpointsRequest({
930
+ source: { path: source },
931
+ breakpoints: [{ line: breakpointLine }],
932
+ });
933
+ const launchArgs = {
934
+ program: output,
935
+ launchType: "wasm file",
936
+ runtime: "node",
937
+ entryFunctionName: "_start",
938
+ };
939
+ const stoppedPromise = waitForBreakpointStop(dc);
940
+ await dc.launchRequest(launchArgs);
941
+ await stoppedPromise;
942
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
943
+ const frame = stackTraceResponse.body.stackFrames[0];
944
+ assert.notStrictEqual(frame, undefined);
945
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
946
+ const localsScope = assertDefined(scopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
947
+ const variablesResponse = await dc.variablesRequest({ variablesReference: localsScope.variablesReference });
948
+ const values = assertDefined(findVariable(variablesResponse.body.variables, "values"));
949
+ assert.ok(values.type?.startsWith("~lib/map/Map<"));
950
+ assert.ok(values.variablesReference > 0);
951
+ assert.equal(values.value, "");
952
+ const entriesResponse = await dc.variablesRequest({ variablesReference: values.variablesReference });
953
+ assert.deepEqual(entriesResponse.body.variables.map((variable) => variable.name), ["0", "1"]);
954
+ const firstEntry = assertDefined(entriesResponse.body.variables.find((candidate) => candidate.name === "0"));
955
+ assert.equal(firstEntry.value, "");
956
+ assert.ok(firstEntry.variablesReference > 0);
957
+ const secondEntry = assertDefined(entriesResponse.body.variables.find((candidate) => candidate.name === "1"));
958
+ assert.equal(secondEntry.value, "");
959
+ assert.ok(secondEntry.variablesReference > 0);
960
+ const firstEntryFieldsResponse = await dc.variablesRequest({ variablesReference: firstEntry.variablesReference });
961
+ assert.deepEqual(firstEntryFieldsResponse.body.variables.map((variable) => variable.name), ["key", "value: debugger_map/Child"]);
962
+ const firstKey = assertDefined(firstEntryFieldsResponse.body.variables.find((candidate) => candidate.name === "key"));
963
+ assert.equal(firstKey.type, "i32");
964
+ assert.equal(firstKey.value, "5");
965
+ assert.equal(firstKey.variablesReference, 0);
966
+ const firstValue = assertDefined(findVariable(firstEntryFieldsResponse.body.variables, "value"));
967
+ assert.match(firstValue.name, /^value: .+Child$/);
968
+ assert.ok(firstValue.type?.endsWith("Child"));
969
+ assert.ok(firstValue.variablesReference > 0);
970
+ assert.equal(firstValue.value, "");
971
+ const secondEntryFieldsResponse = await dc.variablesRequest({ variablesReference: secondEntry.variablesReference });
972
+ assert.deepEqual(secondEntryFieldsResponse.body.variables.map((variable) => variable.name), ["key", "value: debugger_map/Child"]);
973
+ const secondKey = assertDefined(secondEntryFieldsResponse.body.variables.find((candidate) => candidate.name === "key"));
974
+ assert.equal(secondKey.type, "i32");
975
+ assert.equal(secondKey.value, "8");
976
+ assert.equal(secondKey.variablesReference, 0);
977
+ const secondValue = assertDefined(findVariable(secondEntryFieldsResponse.body.variables, "value"));
978
+ assert.match(secondValue.name, /^value: .+Child$/);
979
+ assert.ok(secondValue.type?.endsWith("Child"));
980
+ assert.ok(secondValue.variablesReference > 0);
981
+ assert.equal(secondValue.value, "");
982
+ const firstValueFieldsResponse = await dc.variablesRequest({ variablesReference: firstValue.variablesReference });
983
+ const firstChildValue = assertDefined(firstValueFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
984
+ assert.equal(firstChildValue.type, "i32");
985
+ assert.equal(firstChildValue.value, "144");
986
+ const secondValueFieldsResponse = await dc.variablesRequest({ variablesReference: secondValue.variablesReference });
987
+ const secondChildValue = assertDefined(secondValueFieldsResponse.body.variables.find((candidate) => candidate.name === "value"));
988
+ assert.equal(secondChildValue.type, "i32");
989
+ assert.equal(secondChildValue.value, "233");
990
+ const numericValues = assertDefined(findVariable(variablesResponse.body.variables, "numericValues"));
991
+ assert.ok(numericValues.type?.startsWith("~lib/map/Map<"));
992
+ assert.ok(numericValues.variablesReference > 0);
993
+ assert.equal(numericValues.value, "");
994
+ const numericEntriesResponse = await dc.variablesRequest({ variablesReference: numericValues.variablesReference });
995
+ assert.deepEqual(numericEntriesResponse.body.variables.map((variable) => variable.name), ["0", "1"]);
996
+ const numericEntryFields = await Promise.all(numericEntriesResponse.body.variables.map((entry) => dc.variablesRequest({ variablesReference: entry.variablesReference })));
997
+ assert.deepEqual(numericEntryFields.map((response) => response.body.variables.map((variable) => ({ name: variable.name, type: variable.type, value: variable.value }))), [
998
+ [
999
+ { name: "key", type: "f64", value: "1" },
1000
+ { name: "value", type: "f64", value: "100" },
1001
+ ],
1002
+ [
1003
+ { name: "key", type: "f64", value: "2" },
1004
+ { name: "value", type: "f64", value: "200" },
1005
+ ],
1006
+ ]);
1007
+ });
1008
+ void it("should expose closure locals by source name", { timeout: 5000 }, async () => {
1009
+ const source = sourcePath("debugger_closure.ts");
1010
+ const output = await buildModule(source);
1011
+ const breakpointLine = 36;
1012
+ await dc.initializeRequest();
1013
+ await dc.setBreakpointsRequest({
1014
+ source: { path: source },
1015
+ breakpoints: [{ line: breakpointLine }],
1016
+ });
1017
+ const launchArgs = {
1018
+ program: output,
1019
+ launchType: "wasm file",
1020
+ runtime: "node",
1021
+ entryFunctionName: "_start",
1022
+ };
1023
+ const stoppedPromise = waitForBreakpointStop(dc);
1024
+ await dc.launchRequest(launchArgs);
1025
+ await stoppedPromise;
1026
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1027
+ const frame = stackTraceResponse.body.stackFrames[0];
1028
+ assert.notStrictEqual(frame, undefined);
1029
+ const scopesResponse = await dc.scopesRequest({ frameId: frame.id });
1030
+ const scopes = scopesResponse.body.scopes;
1031
+ const scopeNames = scopes.map((scope) => scope.name);
1032
+ assert.deepEqual(scopeNames, [
1033
+ "Local: run~middle~inner~anonymous|0",
1034
+ "Scope",
1035
+ "Scope",
1036
+ "Closure(inner)",
1037
+ "Closure(middle)",
1038
+ "Closure(run)",
1039
+ ]);
1040
+ const localScope = assertDefined(scopes.find((scope) => scope.name === "Local: run~middle~inner~anonymous|0"));
1041
+ const lexicalScopes = scopes.filter((scope) => scope.name === "Scope");
1042
+ assert.equal(lexicalScopes.length, 2);
1043
+ const innerScope = assertDefined(scopes.find((scope) => scope.name === "Closure(inner)"));
1044
+ const middleScope = assertDefined(scopes.find((scope) => scope.name === "Closure(middle)"));
1045
+ const runScope = assertDefined(scopes.find((scope) => scope.name === "Closure(run)"));
1046
+ const [localVariablesResponse, ifScopeResponse, loopScopeResponse, innerVariablesResponse, middleVariablesResponse, runVariablesResponse,] = await Promise.all([
1047
+ dc.variablesRequest({ variablesReference: localScope.variablesReference }),
1048
+ dc.variablesRequest({ variablesReference: lexicalScopes[0].variablesReference }),
1049
+ dc.variablesRequest({ variablesReference: lexicalScopes[1].variablesReference }),
1050
+ dc.variablesRequest({ variablesReference: innerScope.variablesReference }),
1051
+ dc.variablesRequest({ variablesReference: middleScope.variablesReference }),
1052
+ dc.variablesRequest({ variablesReference: runScope.variablesReference }),
1053
+ ]);
1054
+ const delta = assertDefined(findVariable(localVariablesResponse.body.variables, "delta"));
1055
+ assert.equal(delta.type, "i32");
1056
+ assert.equal(delta.value, "5");
1057
+ const inside = assertDefined(findVariable(localVariablesResponse.body.variables, "inside"));
1058
+ assert.equal(inside.type, "i32");
1059
+ assert.equal(inside.value, "11193");
1060
+ const ifA = assertDefined(findVariable(ifScopeResponse.body.variables, "ifA"));
1061
+ assert.equal(ifA.type, "i32");
1062
+ assert.equal(ifA.value, "10000");
1063
+ const loopA = assertDefined(findVariable(loopScopeResponse.body.variables, "loopA"));
1064
+ assert.equal(loopA.type, "i32");
1065
+ assert.equal(loopA.value, "1000");
1066
+ const innerA = assertDefined(findVariable(innerVariablesResponse.body.variables, "innerA"));
1067
+ assert.equal(innerA.type, "i32");
1068
+ assert.equal(innerA.value, "100");
1069
+ const middleA = assertDefined(findVariable(middleVariablesResponse.body.variables, "middleA"));
1070
+ assert.equal(middleA.type, "i32");
1071
+ assert.equal(middleA.value, "10");
1072
+ assert.equal(findVariable(middleVariablesResponse.body.variables, "middleNotCaptured"), undefined);
1073
+ const outerA = assertDefined(findVariable(runVariablesResponse.body.variables, "outerA"));
1074
+ assert.equal(outerA.type, "i32");
1075
+ assert.equal(outerA.value, "1");
1076
+ assert.equal(findVariable(runVariablesResponse.body.variables, "runNotCaptured"), undefined);
1077
+ const child = assertDefined(findVariable(runVariablesResponse.body.variables, "child"));
1078
+ assert.ok(child.type?.endsWith("Child"));
1079
+ assert.ok(child.variablesReference > 0);
1080
+ const childFieldsResponse = await dc.variablesRequest({ variablesReference: child.variablesReference });
1081
+ const childValue = assertDefined(findVariable(childFieldsResponse.body.variables, "value"));
1082
+ assert.equal(childValue.type, "i32");
1083
+ assert.equal(childValue.value, "77");
1084
+ });
1085
+ void it("should expose and decode global variables", { timeout: 5000 }, async () => {
1086
+ const source = sourcePath("debugger_global.ts");
1087
+ const output = await buildModule(source);
1088
+ await dc.initializeRequest();
1089
+ await dc.setBreakpointsRequest({
1090
+ source: { path: source },
1091
+ breakpoints: [{ line: 11 }],
1092
+ });
1093
+ const launchArgs = {
1094
+ program: output,
1095
+ launchType: "wasm file",
1096
+ runtime: "node",
1097
+ entryFunctionName: "_start",
1098
+ };
1099
+ const stoppedPromise = waitForBreakpointStop(dc);
1100
+ await dc.launchRequest(launchArgs);
1101
+ await stoppedPromise;
1102
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1103
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
1104
+ const globalScope = assertDefined(await readFrameScopeByName(dc, frame, "Global"));
1105
+ const globalCounter = assertDefined(globalScope.body.variables.find((variable) => variable.name.endsWith("/globalCounter")));
1106
+ assert.equal(globalCounter.type, "i32");
1107
+ assert.equal(globalCounter.value, "24");
1108
+ const message = assertDefined(globalScope.body.variables.find((variable) => variable.name.endsWith("/globalMessage")));
1109
+ assert.equal(message.type, "~lib/string/String");
1110
+ assert.equal(message.value, '"global debugger"');
1111
+ assert.equal(message.variablesReference, 0);
1112
+ const values = assertDefined(findVariable(globalScope.body.variables, "debugger_global/globalValues"));
1113
+ assert.ok(values.type?.startsWith("~lib/array/Array<"));
1114
+ const valueElements = await dc.variablesRequest({ variablesReference: values.variablesReference });
1115
+ assert.deepEqual(valueElements.body.variables.map((variable) => variable.value), ["13", "21"]);
1116
+ const entries = assertDefined(findVariable(globalScope.body.variables, "debugger_global/globalEntries"));
1117
+ assert.ok(entries.type?.startsWith("~lib/map/Map<"));
1118
+ const mapEntries = await dc.variablesRequest({ variablesReference: entries.variablesReference });
1119
+ assert.equal(mapEntries.body.variables.length, 1);
1120
+ const entryFields = await dc.variablesRequest({
1121
+ variablesReference: mapEntries.body.variables[0].variablesReference,
1122
+ });
1123
+ assert.deepEqual(entryFields.body.variables.map((variable) => ({ name: variable.name, value: variable.value })), [
1124
+ { name: "key", value: "5" },
1125
+ { name: "value", value: "34" },
1126
+ ]);
1127
+ });
1128
+ void it("should refresh variable references across multiple pauses", { timeout: 5000 }, async () => {
1129
+ const source = sourcePath("debugger_class.ts");
1130
+ const output = await buildModule(source);
1131
+ const firstBreakpointLine = 45;
1132
+ const secondBreakpointLine = 47;
1133
+ await dc.initializeRequest();
1134
+ await dc.setBreakpointsRequest({
1135
+ source: { path: source },
1136
+ breakpoints: [{ line: firstBreakpointLine }, { line: secondBreakpointLine }],
1137
+ });
1138
+ const launchArgs = {
1139
+ program: output,
1140
+ launchType: "wasm file",
1141
+ runtime: "node",
1142
+ entryFunctionName: "_start",
1143
+ };
1144
+ const firstStoppedPromise = waitForBreakpointStop(dc);
1145
+ await dc.launchRequest(launchArgs);
1146
+ await firstStoppedPromise;
1147
+ const firstStackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1148
+ const firstScopesResponse = await dc.scopesRequest({ frameId: firstStackTraceResponse.body.stackFrames[0].id });
1149
+ const firstLocalsScope = assertDefined(firstScopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
1150
+ const firstVariablesResponse = await dc.variablesRequest({
1151
+ variablesReference: firstLocalsScope.variablesReference,
1152
+ });
1153
+ const firstHolder = assertDefined(findVariable(firstVariablesResponse.body.variables, "holder"));
1154
+ assert.ok(firstHolder.variablesReference > 0);
1155
+ assert.ok(firstHolder.type?.endsWith("Holder"));
1156
+ assert.equal(firstHolder.value, "");
1157
+ const firstHolderFieldsResponse = await dc.variablesRequest({ variablesReference: firstHolder.variablesReference });
1158
+ const firstCount = assertDefined(firstHolderFieldsResponse.body.variables.find((candidate) => candidate.name === "count"));
1159
+ assert.equal(firstCount.value, "7");
1160
+ const secondStoppedPromise = waitForBreakpointStop(dc);
1161
+ await dc.continueRequest({ threadId: 1 });
1162
+ await secondStoppedPromise;
1163
+ const staleHolderFieldsResponse = await dc.variablesRequest({ variablesReference: firstHolder.variablesReference });
1164
+ assert.deepEqual(staleHolderFieldsResponse.body.variables, []);
1165
+ const secondStackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1166
+ const secondScopesResponse = await dc.scopesRequest({ frameId: secondStackTraceResponse.body.stackFrames[0].id });
1167
+ const secondLocalsScope = assertDefined(secondScopesResponse.body.scopes.find((scope) => scope.name.startsWith("Local:")));
1168
+ const secondVariablesResponse = await dc.variablesRequest({
1169
+ variablesReference: secondLocalsScope.variablesReference,
1170
+ });
1171
+ const secondHolder = assertDefined(findVariable(secondVariablesResponse.body.variables, "holder"));
1172
+ assert.ok(secondHolder.variablesReference > 0);
1173
+ assert.notEqual(secondHolder.variablesReference, firstHolder.variablesReference);
1174
+ assert.ok(secondHolder.type?.endsWith("Holder"));
1175
+ assert.equal(secondHolder.value, "");
1176
+ const secondHolderFieldsResponse = await dc.variablesRequest({
1177
+ variablesReference: secondHolder.variablesReference,
1178
+ });
1179
+ const secondCount = assertDefined(secondHolderFieldsResponse.body.variables.find((candidate) => candidate.name === "count"));
1180
+ assert.equal(secondCount.value, "9");
1181
+ });
1182
+ void it("should expose closure locals captured in an if scope", { timeout: 5000 }, async () => {
1183
+ const source = sourcePath("debugger_closure_if.ts");
1184
+ const output = await buildModule(source);
1185
+ const breakpointLine = 14;
1186
+ await dc.initializeRequest();
1187
+ await dc.setBreakpointsRequest({
1188
+ source: { path: source },
1189
+ breakpoints: [{ line: breakpointLine }],
1190
+ });
1191
+ const launchArgs = {
1192
+ program: output,
1193
+ launchType: "wasm file",
1194
+ runtime: "node",
1195
+ entryFunctionName: "_start",
1196
+ };
1197
+ await launchAndWaitForBreakpoint(dc, launchArgs);
1198
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1199
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
1200
+ const localScope = assertDefined(await readFrameScopeByName(dc, frame, "Local: foo"));
1201
+ const a = assertDefined(findVariable(localScope.body.variables, "a"));
1202
+ assert.equal(a.type, "i32");
1203
+ assert.equal(a.value, "1");
1204
+ const blockScope = assertDefined(await readFrameScopeByName(dc, frame, "Block: foo"));
1205
+ const b = assertDefined(findVariable(blockScope.body.variables, "b"));
1206
+ assert.equal(b.type, "i32");
1207
+ assert.equal(b.value, "2");
1208
+ });
1209
+ void it("should stop at a wasm trap and terminate on resume", { timeout: 5000 }, async () => {
1210
+ const source = sourcePath("debugger_trap.ts");
1211
+ const output = await buildModule(source);
1212
+ await dc.initializeRequest();
1213
+ const launchArgs = {
1214
+ program: output,
1215
+ launchType: "wasm file",
1216
+ runtime: "node",
1217
+ entryFunctionName: "_start",
1218
+ };
1219
+ const stoppedPromise = dc.waitForEvent("stopped");
1220
+ const terminatedPromise = dc.waitForEvent("terminated");
1221
+ await dc.launchRequest(launchArgs);
97
1222
  const stoppedEvent = await stoppedPromise;
98
1223
  const stoppedBody = stoppedEvent.body;
99
- assert.equal(loadedSourceBody?.reason, "new");
100
- assert.equal(loadedSourceBody?.source?.path, path.resolve(TEST_MODULE_OUTPUT));
101
- assert.equal(loadedSourceBody?.source?.name, path.basename(TEST_MODULE_OUTPUT));
102
- assert.equal(stoppedBody?.reason, "breakpoint");
1224
+ assert.equal(stoppedBody?.reason, "exception");
1225
+ const stackTraceResponse = await dc.stackTraceRequest({ threadId: 1, startFrame: 0, levels: 1 });
1226
+ const frame = assertDefined(stackTraceResponse.body.stackFrames[0]);
1227
+ assert.equal(frame.source?.path, normalizeDebugPath(source));
1228
+ assert.equal(frame.line, 9);
1229
+ await dc.continueRequest({ threadId: 1 });
1230
+ await terminatedPromise;
103
1231
  });
104
1232
  });
105
1233
  //# sourceMappingURL=debugSession.test.js.map