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.
- package/asconfig.schema.json +10 -2
- package/dist/debug_server/debugPath.d.ts +1 -0
- package/dist/debug_server/debugPath.js +10 -0
- package/dist/debug_server/debugPath.js.map +1 -0
- package/dist/debug_server/debugSession.d.ts +61 -3
- package/dist/debug_server/debugSession.js +919 -80
- package/dist/debug_server/debugSession.js.map +1 -1
- package/dist/debug_server/debugger.d.ts +30 -2
- package/dist/debug_server/debuggerWasmModule.d.ts +59 -3
- package/dist/debug_server/debuggerWasmModule.js +234 -14
- package/dist/debug_server/debuggerWasmModule.js.map +1 -1
- package/dist/debug_server/nodeDebugger.d.ts +42 -3
- package/dist/debug_server/nodeDebugger.js +648 -59
- package/dist/debug_server/nodeDebugger.js.map +1 -1
- package/dist/debug_server/tests/debugSession.test.js +1154 -26
- package/dist/debug_server/tests/debugSession.test.js.map +1 -1
- package/dist/debug_server/tests/launcher.js +1 -0
- package/dist/debug_server/tests/launcher.js.map +1 -1
- package/dist/debug_server/tests/testModule/debugger_array.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_array.js +9 -0
- package/dist/debug_server/tests/testModule/debugger_array.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_array_buffer.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_array_buffer.js +12 -0
- package/dist/debug_server/tests/testModule/debugger_array_buffer.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_basic.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_basic.js +9 -1
- package/dist/debug_server/tests/testModule/debugger_basic.js.map +1 -1
- package/dist/debug_server/tests/testModule/debugger_breakpoint_update.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_breakpoint_update.js +10 -0
- package/dist/debug_server/tests/testModule/debugger_breakpoint_update.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_callee.d.ts +10 -0
- package/dist/debug_server/tests/testModule/debugger_callee.js +106 -0
- package/dist/debug_server/tests/testModule/debugger_callee.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_caller.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_caller.js +7 -0
- package/dist/debug_server/tests/testModule/debugger_caller.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_class.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_class.js +39 -0
- package/dist/debug_server/tests/testModule/debugger_class.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_class_array.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_class_array.js +15 -0
- package/dist/debug_server/tests/testModule/debugger_class_array.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_closure.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_closure.js +48 -0
- package/dist/debug_server/tests/testModule/debugger_closure.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_closure_if.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_closure_if.js +16 -0
- package/dist/debug_server/tests/testModule/debugger_closure_if.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_generic_function.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_generic_function.js +10 -0
- package/dist/debug_server/tests/testModule/debugger_generic_function.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_global.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_global.js +11 -0
- package/dist/debug_server/tests/testModule/debugger_global.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_map.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_map.js +22 -0
- package/dist/debug_server/tests/testModule/debugger_map.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_member_function.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_member_function.js +16 -0
- package/dist/debug_server/tests/testModule/debugger_member_function.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_scopes.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_scopes.js +22 -0
- package/dist/debug_server/tests/testModule/debugger_scopes.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_set.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_set.js +23 -0
- package/dist/debug_server/tests/testModule/debugger_set.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_static_array.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_static_array.js +21 -0
- package/dist/debug_server/tests/testModule/debugger_static_array.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_string.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_string.js +16 -0
- package/dist/debug_server/tests/testModule/debugger_string.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_trap.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_trap.js +9 -0
- package/dist/debug_server/tests/testModule/debugger_trap.js.map +1 -0
- package/dist/debug_server/tests/testModule/debugger_tuple.d.ts +1 -0
- package/dist/debug_server/tests/testModule/debugger_tuple.js +15 -0
- package/dist/debug_server/tests/testModule/debugger_tuple.js.map +1 -0
- package/dist/debug_server/tests/testModule/import_failure.d.ts +1 -0
- package/dist/debug_server/tests/testModule/import_failure.js +4 -0
- package/dist/debug_server/tests/testModule/import_failure.js.map +1 -0
- package/dist/debug_server/tests/testModule/unittest_debug.case.js +10 -0
- package/dist/debug_server/tests/testModule/unittest_debug.case.js.map +1 -0
- package/dist/debug_server/tests/testModule/unittest_debug.d.ts +1 -0
- package/dist/debug_server/tests/testModule/unittest_debug.js +6 -0
- package/dist/debug_server/tests/testModule/unittest_debug.js.map +1 -0
- package/dist/debug_server/wasmEntry.js +32 -12
- package/dist/debug_server/wasmEntry.js.map +1 -1
- package/dist/dwarf/classDebugInfo.d.ts +45 -0
- package/dist/dwarf/classDebugInfo.js +292 -0
- package/dist/dwarf/classDebugInfo.js.map +1 -0
- package/dist/{heap_analyzer/src → dwarf}/dwarfParser.js +2 -0
- package/dist/dwarf/dwarfParser.js.map +1 -0
- package/dist/dwarf/functionDebugInfo.d.ts +63 -0
- package/dist/dwarf/functionDebugInfo.js +377 -0
- package/dist/dwarf/functionDebugInfo.js.map +1 -0
- package/dist/dwarf/tests/fixtures/functionDebugInfo.d.ts +3 -0
- package/dist/dwarf/tests/fixtures/functionDebugInfo.js +24 -0
- package/dist/dwarf/tests/fixtures/functionDebugInfo.js.map +1 -0
- package/dist/dwarf/tests/functionDebugInfo.test.d.ts +1 -0
- package/dist/dwarf/tests/functionDebugInfo.test.js +102 -0
- package/dist/dwarf/tests/functionDebugInfo.test.js.map +1 -0
- package/dist/heap_analyzer/cli.js +1 -1
- package/dist/heap_analyzer/cli.js.map +1 -1
- package/dist/heap_analyzer/src/constants.js +2 -1
- package/dist/heap_analyzer/src/constants.js.map +1 -1
- package/dist/heap_analyzer/src/debugInfoResolver.d.ts +4 -15
- package/dist/heap_analyzer/src/debugInfoResolver.js +20 -231
- package/dist/heap_analyzer/src/debugInfoResolver.js.map +1 -1
- package/dist/heap_analyzer/src/dominator.js +4 -1
- package/dist/heap_analyzer/src/dominator.js.map +1 -1
- package/dist/heap_analyzer/src/referenceScanner.js +7 -7
- package/dist/heap_analyzer/src/referenceScanner.js.map +1 -1
- package/dist/heap_analyzer/src/retainedSize.js +2 -0
- package/dist/heap_analyzer/src/retainedSize.js.map +1 -1
- package/dist/heap_analyzer/src/snapshot.js +1 -1
- package/dist/heap_analyzer/src/snapshot.js.map +1 -1
- package/dist/heap_analyzer/src/{tlsf.js → tlsfBlockWalker.js} +4 -3
- package/dist/heap_analyzer/src/tlsfBlockWalker.js.map +1 -0
- package/dist/heap_analyzer/src/types.d.ts +3 -32
- package/dist/heap_analyzer/src/types.js +1 -8
- package/dist/heap_analyzer/src/types.js.map +1 -1
- package/dist/heap_analyzer/tests/debugInfoResolver.test.js +1 -1
- package/dist/heap_analyzer/tests/debugInfoResolver.test.js.map +1 -1
- package/dist/heap_analyzer/tests/dwarfParser.test.js +57 -40
- package/dist/heap_analyzer/tests/dwarfParser.test.js.map +1 -1
- package/dist/heap_analyzer/tests/roots.test.js +1 -1
- package/dist/heap_analyzer/tests/roots.test.js.map +1 -1
- package/dist/heap_analyzer/tests/snapshot.test.js +4 -4
- package/dist/heap_analyzer/tests/testHelper.js +4 -7
- package/dist/heap_analyzer/tests/testHelper.js.map +1 -1
- package/dist/heap_analyzer/tests/tlsfBlockWalker.test.d.ts +1 -0
- package/dist/heap_analyzer/tests/{tlsf.test.js → tlsfBlockWalker.test.js} +2 -2
- package/dist/heap_analyzer/tests/tlsfBlockWalker.test.js.map +1 -0
- package/dist/heap_analyzer/tests/wasmExecutor.d.ts +2 -2
- package/dist/heap_analyzer/tests/wasmExecutor.js +5 -5
- package/dist/heap_analyzer/tests/wasmExecutor.js.map +1 -1
- package/dist/runtime/objectLayout.d.ts +10 -0
- package/dist/runtime/objectLayout.js +13 -0
- package/dist/runtime/objectLayout.js.map +1 -0
- package/dist/scripts/lib.d.ts +1 -0
- package/dist/scripts/lib.js +86 -22
- package/dist/scripts/lib.js.map +1 -1
- package/dist/test_runner/cli.d.ts +0 -1
- package/dist/test_runner/cli.js +37 -19
- package/dist/test_runner/cli.js.map +1 -1
- package/dist/test_runner/config.d.ts +3 -0
- package/dist/test_runner/config.js +8 -0
- package/dist/test_runner/config.js.map +1 -0
- package/dist/test_runner/core/analyze.js +12 -3
- package/dist/test_runner/core/analyze.js.map +1 -1
- package/dist/test_runner/core/compile.js +1 -4
- package/dist/test_runner/core/compile.js.map +1 -1
- package/dist/test_runner/core/compiler.js +2 -0
- package/dist/test_runner/core/compiler.js.map +1 -1
- package/dist/test_runner/core/execute.js +20 -3
- package/dist/test_runner/core/execute.js.map +1 -1
- package/dist/test_runner/executionResult.js +10 -8
- package/dist/test_runner/executionResult.js.map +1 -1
- package/dist/test_runner/generator/table-generator/index.js +4 -2
- package/dist/test_runner/generator/table-generator/index.js.map +1 -1
- package/dist/test_runner/parser/index.js +6 -5
- package/dist/test_runner/parser/index.js.map +1 -1
- package/dist/test_runner/parser/singleFileAnalysis.js +7 -4
- package/dist/test_runner/parser/singleFileAnalysis.js.map +1 -1
- package/dist/test_runner/parser/singleFunctionAnalysis.js +1 -1
- package/dist/test_runner/parser/singleFunctionAnalysis.js.map +1 -1
- package/dist/test_runner/utils/errorTraceHandler.js +2 -1
- package/dist/test_runner/utils/errorTraceHandler.js.map +1 -1
- package/dist/test_runner/utils/index.js +9 -2
- package/dist/test_runner/utils/index.js.map +1 -1
- package/dist/test_runner/utils/interface.js +2 -1
- package/dist/test_runner/utils/interface.js.map +1 -1
- package/dist/test_runner/utils/pathResolver.js +9 -7
- package/dist/test_runner/utils/pathResolver.js.map +1 -1
- package/dist/test_runner/utils/wasm.js +16 -8
- package/dist/test_runner/utils/wasm.js.map +1 -1
- package/dist/trace_visualizer.d.ts +2 -0
- package/dist/trace_visualizer.js +18 -0
- package/dist/trace_visualizer.js.map +1 -0
- package/dist/warpo.js +2 -1
- package/dist/warpo.js.map +1 -1
- package/package.json +9 -5
- package/dist/heap_analyzer/src/dwarfParser.js.map +0 -1
- package/dist/heap_analyzer/src/tlsf.js.map +0 -1
- package/dist/heap_analyzer/tests/tlsf.test.js.map +0 -1
- /package/dist/{heap_analyzer/tests/tlsf.test.d.ts → debug_server/tests/testModule/unittest_debug.case.d.ts} +0 -0
- /package/dist/{heap_analyzer/src → dwarf}/dwarfParser.d.ts +0 -0
- /package/dist/heap_analyzer/src/{tlsf.d.ts → tlsfBlockWalker.d.ts} +0 -0
|
@@ -9,6 +9,8 @@ import { DebuggerWasmModule } from "./debuggerWasmModule.js";
|
|
|
9
9
|
const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const DEBUG_SERVER_TRACE_ENABLED = process.env.WARPO_DEBUG_SERVER_TRACE === "1";
|
|
11
11
|
const DEBUG_SERVER_TRACE_FILE = path.join(process.cwd(), ".warpo-debug-server-trace.log");
|
|
12
|
+
const LINEAR_MEMORY_OBJECT_GROUP = "warpo-linear-memory";
|
|
13
|
+
const NODE_WAITING_FOR_DEBUGGER_DISCONNECT = "Waiting for the debugger to disconnect...";
|
|
12
14
|
if (DEBUG_SERVER_TRACE_ENABLED) {
|
|
13
15
|
try {
|
|
14
16
|
writeFileSync(DEBUG_SERVER_TRACE_FILE, "");
|
|
@@ -27,6 +29,25 @@ function trace(message) {
|
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
}
|
|
32
|
+
const READ_WASM_MEMORY_FUNCTION = function (address, byteLength) {
|
|
33
|
+
const buffer = this instanceof ArrayBuffer || (typeof SharedArrayBuffer !== "undefined" && this instanceof SharedArrayBuffer)
|
|
34
|
+
? this
|
|
35
|
+
: this.buffer;
|
|
36
|
+
if (!(buffer instanceof ArrayBuffer) &&
|
|
37
|
+
!(typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (address < 0 || byteLength < 0 || address + byteLength > buffer.byteLength) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
return Array.from(new Uint8Array(buffer, address, byteLength));
|
|
44
|
+
}.toString();
|
|
45
|
+
const GET_WASM_MEMORY_BUFFER_FUNCTION = function () {
|
|
46
|
+
if (this instanceof ArrayBuffer || (typeof SharedArrayBuffer !== "undefined" && this instanceof SharedArrayBuffer)) {
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
return this.buffer;
|
|
50
|
+
}.toString();
|
|
30
51
|
function findFreePort() {
|
|
31
52
|
return new Promise((resolve, reject) => {
|
|
32
53
|
const srv = net.createServer();
|
|
@@ -51,28 +72,99 @@ export class NodeDebugger {
|
|
|
51
72
|
name = "node";
|
|
52
73
|
onModuleLoad;
|
|
53
74
|
onPause;
|
|
75
|
+
onRuntimeError;
|
|
76
|
+
onRuntimeExit;
|
|
77
|
+
onLog;
|
|
54
78
|
child;
|
|
55
79
|
ws;
|
|
56
80
|
cdpId = 0;
|
|
57
81
|
pendingCommandCallbacks = new Map();
|
|
82
|
+
wasmBreakpointIds = new Set();
|
|
58
83
|
paused = false;
|
|
59
84
|
wasmFilePath;
|
|
85
|
+
wasmScriptId;
|
|
86
|
+
pausedCallFrames = [];
|
|
87
|
+
wasmMemoryBufferObjectId;
|
|
88
|
+
wasmGlobalsObjectId;
|
|
89
|
+
stderrOutput = "";
|
|
90
|
+
disposed = false;
|
|
91
|
+
runtimeExitPromise;
|
|
92
|
+
log(message) {
|
|
93
|
+
trace(message);
|
|
94
|
+
this.onLog?.(`[${this.name}] ${message}`);
|
|
95
|
+
}
|
|
60
96
|
async launch(config) {
|
|
97
|
+
this.disposed = false;
|
|
61
98
|
this.wasmFilePath = config.wasmFilePath;
|
|
62
99
|
const port = await findFreePort();
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
path.join(DIRNAME, "
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
100
|
+
this.log(`launch wasmFilePath=${config.wasmFilePath} inspectPort=${port}`);
|
|
101
|
+
const launchArgs = "cwd" in config
|
|
102
|
+
? [`--inspect-brk=${port}`, config.warpoPath ?? path.join(DIRNAME, "..", "warpo.js"), "test"]
|
|
103
|
+
: [
|
|
104
|
+
`--inspect-brk=${port}`,
|
|
105
|
+
path.join(DIRNAME, "wasmEntry.js"),
|
|
106
|
+
config.wasmFilePath,
|
|
107
|
+
config.entryFunctionName,
|
|
108
|
+
...config.args.map(String),
|
|
109
|
+
];
|
|
110
|
+
this.child = spawn(process.execPath, launchArgs, {
|
|
111
|
+
cwd: "cwd" in config ? config.cwd : undefined,
|
|
112
|
+
env: getRuntimeEnv(),
|
|
113
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
114
|
+
});
|
|
115
|
+
this.log(`runtime child spawned pid=${this.child.pid ?? "unknown"}`);
|
|
116
|
+
this.runtimeExitPromise = new Promise((resolve) => {
|
|
117
|
+
this.child?.once("exit", () => resolve());
|
|
118
|
+
this.child?.once("error", () => resolve());
|
|
119
|
+
});
|
|
120
|
+
this.child.stdout?.on("data", (chunk) => {
|
|
121
|
+
const text = chunk.toString().trim();
|
|
122
|
+
if (text) {
|
|
123
|
+
this.log(`runtime stdout: ${text}`);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
this.child.stderr?.on("data", (chunk) => {
|
|
127
|
+
const stderr = parseRuntimeStderr(chunk.toString());
|
|
128
|
+
this.stderrOutput += stderr.output;
|
|
129
|
+
const text = stderr.output.trim();
|
|
130
|
+
if (text) {
|
|
131
|
+
this.log(`runtime stderr: ${text}`);
|
|
132
|
+
}
|
|
133
|
+
if (stderr.waitingForDebuggerDisconnect) {
|
|
134
|
+
this.log("runtime waiting for debugger disconnect -> close websocket");
|
|
135
|
+
this.ws?.close();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
this.child.on("error", (error) => {
|
|
139
|
+
this.log(`runtime child error: ${error.message}`);
|
|
140
|
+
this.onRuntimeError?.(error.message);
|
|
141
|
+
this.onRuntimeExit?.();
|
|
142
|
+
});
|
|
143
|
+
this.child.on("exit", (code, signal) => {
|
|
144
|
+
this.log(`runtime exit code=${code ?? "null"} signal=${signal ?? "null"}`);
|
|
145
|
+
if (this.disposed) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (code && code !== 0) {
|
|
149
|
+
this.onRuntimeError?.(this.stderrOutput.trim() || `Debug runtime exited with code ${code}`);
|
|
150
|
+
}
|
|
151
|
+
this.onRuntimeExit?.();
|
|
152
|
+
});
|
|
70
153
|
const wsUrl = await this.fetchWsUrl(port);
|
|
71
|
-
|
|
154
|
+
this.log(`inspector websocket discovered: ${wsUrl}`);
|
|
155
|
+
await Promise.race([this.connectCDP(wsUrl), this.runtimeExitPromise]);
|
|
72
156
|
}
|
|
73
157
|
dispose() {
|
|
158
|
+
this.disposed = true;
|
|
74
159
|
this.paused = false;
|
|
75
160
|
this.wasmFilePath = undefined;
|
|
161
|
+
this.wasmScriptId = undefined;
|
|
162
|
+
this.pausedCallFrames = [];
|
|
163
|
+
this.wasmMemoryBufferObjectId = undefined;
|
|
164
|
+
this.wasmGlobalsObjectId = undefined;
|
|
165
|
+
this.stderrOutput = "";
|
|
166
|
+
this.runtimeExitPromise = undefined;
|
|
167
|
+
this.wasmBreakpointIds.clear();
|
|
76
168
|
this.ws?.close();
|
|
77
169
|
this.child?.kill();
|
|
78
170
|
}
|
|
@@ -85,28 +177,170 @@ export class NodeDebugger {
|
|
|
85
177
|
async resume() {
|
|
86
178
|
await this.waitForCommand("Debugger.resume");
|
|
87
179
|
this.paused = false;
|
|
180
|
+
this.pausedCallFrames = [];
|
|
181
|
+
this.wasmMemoryBufferObjectId = undefined;
|
|
182
|
+
this.wasmGlobalsObjectId = undefined;
|
|
88
183
|
}
|
|
89
184
|
finishModuleLoad() {
|
|
185
|
+
this.log("finishModuleLoad -> Runtime.runIfWaitingForDebugger");
|
|
90
186
|
this.sendCommand("Runtime.runIfWaitingForDebugger");
|
|
91
187
|
}
|
|
92
|
-
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
188
|
+
setWasmBreakpoints(module, wasmBytecodeOffsets, callbacks) {
|
|
189
|
+
this.clearWasmBreakpoints({
|
|
190
|
+
onSuccess: () => this.installWasmBreakpoints(module, wasmBytecodeOffsets, 0, callbacks),
|
|
191
|
+
onError: callbacks?.onError,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
installWasmBreakpoints(module, wasmBytecodeOffsets, offsetIndex, callbacks) {
|
|
195
|
+
if (offsetIndex >= wasmBytecodeOffsets.length) {
|
|
196
|
+
callbacks?.onSuccess?.();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const wasmBytecodeOffset = wasmBytecodeOffsets[offsetIndex];
|
|
200
|
+
this.setNodeWasmBreakpoint(module, wasmBytecodeOffset, {
|
|
201
|
+
onSuccess: () => this.installWasmBreakpoints(module, wasmBytecodeOffsets, offsetIndex + 1, callbacks),
|
|
202
|
+
onError: () => this.installWasmBreakpoints(module, wasmBytecodeOffsets, offsetIndex + 1, callbacks),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
setNodeWasmBreakpoint(module, wasmBytecodeOffset, callbacks) {
|
|
206
|
+
void (async () => {
|
|
207
|
+
try {
|
|
208
|
+
const location = this.resolveWasmBytecodeOffsetLocation(module, wasmBytecodeOffset);
|
|
209
|
+
this.log(`setWasmBreakpoint offset=${wasmBytecodeOffset} url=${module.url}`);
|
|
210
|
+
const result = await this.waitForCommandResult("Debugger.setBreakpointByUrl", {
|
|
211
|
+
lineNumber: location.lineNumber,
|
|
212
|
+
url: module.url,
|
|
213
|
+
columnNumber: location.columnNumber,
|
|
214
|
+
condition: "",
|
|
215
|
+
});
|
|
216
|
+
const breakpointId = result.breakpointId;
|
|
217
|
+
if (typeof breakpointId !== "string") {
|
|
218
|
+
throw new TypeError("CDP did not return a breakpoint ID");
|
|
219
|
+
}
|
|
220
|
+
this.wasmBreakpointIds.add(breakpointId);
|
|
221
|
+
this.log(`setWasmBreakpoint success offset=${wasmBytecodeOffset}`);
|
|
222
|
+
callbacks?.onSuccess?.();
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
const commandError = error instanceof Error ? error : new Error(String(error));
|
|
226
|
+
this.log(`setWasmBreakpoint error offset=${wasmBytecodeOffset} message=${commandError.message}`);
|
|
227
|
+
callbacks?.onError?.(commandError);
|
|
228
|
+
}
|
|
229
|
+
})();
|
|
230
|
+
}
|
|
231
|
+
clearWasmBreakpoints(callbacks) {
|
|
232
|
+
const breakpointIds = Array.from(this.wasmBreakpointIds);
|
|
233
|
+
const removeNext = (index) => {
|
|
234
|
+
if (index >= breakpointIds.length) {
|
|
235
|
+
callbacks?.onSuccess?.();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const breakpointId = breakpointIds[index];
|
|
239
|
+
this.removeNodeBreakpoint(breakpointId, {
|
|
240
|
+
onSuccess: () => {
|
|
241
|
+
this.wasmBreakpointIds.delete(breakpointId);
|
|
242
|
+
removeNext(index + 1);
|
|
243
|
+
},
|
|
244
|
+
onError: () => {
|
|
245
|
+
removeNext(index + 1);
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
removeNext(0);
|
|
250
|
+
}
|
|
251
|
+
removeNodeBreakpoint(breakpointId, callbacks) {
|
|
252
|
+
void (async () => {
|
|
253
|
+
try {
|
|
254
|
+
await this.waitForCommand("Debugger.removeBreakpoint", { breakpointId });
|
|
255
|
+
this.log(`removeWasmBreakpoint success id=${breakpointId}`);
|
|
100
256
|
callbacks?.onSuccess?.();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const commandError = error instanceof Error ? error : new Error(String(error));
|
|
260
|
+
this.log(`removeWasmBreakpoint error id=${breakpointId} message=${commandError.message}`);
|
|
261
|
+
callbacks?.onError?.(commandError);
|
|
262
|
+
}
|
|
263
|
+
})();
|
|
264
|
+
}
|
|
265
|
+
resolveWasmBytecodeOffsetLocation(module, wasmBytecodeOffset) {
|
|
266
|
+
if (!module.url) {
|
|
267
|
+
throw new Error("Wasm script URL is not available");
|
|
268
|
+
}
|
|
269
|
+
return { scriptId: module.scriptId, lineNumber: 0, columnNumber: wasmBytecodeOffset };
|
|
270
|
+
}
|
|
271
|
+
resolveWasmLocationBytecodeOffset(location) {
|
|
272
|
+
if (location.lineNumber !== 0) {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
return location.columnNumber;
|
|
276
|
+
}
|
|
277
|
+
getPausedWasmFrames() {
|
|
278
|
+
const frames = this.pausedCallFrames.map((callFrame) => this.toPausedWasmFrame(callFrame));
|
|
279
|
+
this.log(`getPausedWasmFrames count=${frames.length}`);
|
|
280
|
+
for (const [index, frame] of frames.entries()) {
|
|
281
|
+
this.log(` wasm frame ${index}: name=${frame.functionName}, offset=${frame.wasmBytecodeOffset ?? "unknown"}`);
|
|
282
|
+
}
|
|
283
|
+
return frames;
|
|
284
|
+
}
|
|
285
|
+
async getPausedWasmFrameVariables(frameIndex) {
|
|
286
|
+
const callFrame = this.pausedCallFrames[frameIndex];
|
|
287
|
+
if (!callFrame) {
|
|
288
|
+
this.log(`getPausedWasmFrameVariables frameIndex=${frameIndex} unavailable`);
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
const variables = await this.getLocalScopeVariables(callFrame);
|
|
292
|
+
this.log(`getPausedWasmFrameVariables frameIndex=${frameIndex} variables=${variables.length}`);
|
|
293
|
+
return variables;
|
|
294
|
+
}
|
|
295
|
+
async getPausedWasmGlobalVariables() {
|
|
296
|
+
const globalsObjectId = await this.getWasmGlobalsObjectId();
|
|
297
|
+
if (!globalsObjectId) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
const globalsResult = await this.waitForCommandResult("Runtime.getProperties", {
|
|
301
|
+
objectId: globalsObjectId,
|
|
302
|
+
ownProperties: true,
|
|
303
|
+
});
|
|
304
|
+
const globals = Array.isArray(globalsResult.result) ? globalsResult.result.filter(isCDPPropertyDescriptor) : [];
|
|
305
|
+
const values = [];
|
|
306
|
+
for (const [globalIndex, global] of globals.entries()) {
|
|
307
|
+
if (!global.value) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
values.push({
|
|
311
|
+
globalIndex,
|
|
312
|
+
value: await this.readRemoteObjectValue(global.value),
|
|
313
|
+
type: global.value.description ?? global.value.type,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
return values;
|
|
317
|
+
}
|
|
318
|
+
async readWasmMemory(address, byteLength) {
|
|
319
|
+
this.log(`readWasmMemory address=${address} byteLength=${byteLength}`);
|
|
320
|
+
const memoryBufferObjectId = await this.getWasmMemoryBufferObjectId();
|
|
321
|
+
if (!memoryBufferObjectId) {
|
|
322
|
+
this.log("readWasmMemory skipped: memory buffer unavailable");
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
this.log("readWasmMemory -> Runtime.callFunctionOn");
|
|
326
|
+
const result = await this.waitForCommandResult("Runtime.callFunctionOn", {
|
|
327
|
+
objectId: memoryBufferObjectId,
|
|
328
|
+
functionDeclaration: READ_WASM_MEMORY_FUNCTION,
|
|
329
|
+
arguments: [{ value: address }, { value: byteLength }],
|
|
330
|
+
silent: true,
|
|
331
|
+
returnByValue: true,
|
|
106
332
|
});
|
|
333
|
+
const remoteObject = getRuntimeEvaluateRemoteObject(result);
|
|
334
|
+
if (!Array.isArray(remoteObject?.value) || !remoteObject.value.every((value) => typeof value === "number")) {
|
|
335
|
+
this.log("readWasmMemory failed: runtime returned non-byte-array value");
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
this.log(`readWasmMemory success bytes=${remoteObject.value.length}`);
|
|
339
|
+
return Uint8Array.from(remoteObject.value);
|
|
107
340
|
}
|
|
108
341
|
async fetchWsUrl(port) {
|
|
109
342
|
const deadline = Date.now() + 5000;
|
|
343
|
+
this.log(`waiting for inspector endpoint on port ${port}`);
|
|
110
344
|
while (Date.now() < deadline) {
|
|
111
345
|
try {
|
|
112
346
|
const resp = await fetch(`http://127.0.0.1:${port}/json`);
|
|
@@ -115,10 +349,12 @@ export class NodeDebugger {
|
|
|
115
349
|
}
|
|
116
350
|
const [info] = (await resp.json());
|
|
117
351
|
if (info?.webSocketDebuggerUrl) {
|
|
352
|
+
this.log(`inspector endpoint ready on port ${port}`);
|
|
118
353
|
return info.webSocketDebuggerUrl;
|
|
119
354
|
}
|
|
120
355
|
}
|
|
121
|
-
catch {
|
|
356
|
+
catch (error) {
|
|
357
|
+
trace(`inspector endpoint not ready: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
122
358
|
// The inspector endpoint is not always ready immediately after spawn.
|
|
123
359
|
}
|
|
124
360
|
await wait(50);
|
|
@@ -133,15 +369,33 @@ export class NodeDebugger {
|
|
|
133
369
|
if (callbacks) {
|
|
134
370
|
this.pendingCommandCallbacks.set(id, callbacks);
|
|
135
371
|
}
|
|
372
|
+
this.log(`CDP -> ${method} id=${id}`);
|
|
136
373
|
this.ws.send(JSON.stringify({ id, method, params }));
|
|
374
|
+
return id;
|
|
137
375
|
}
|
|
138
376
|
waitForCommand(method, params) {
|
|
139
377
|
return new Promise((resolve, reject) => {
|
|
140
|
-
this.sendCommand(method, params, {
|
|
378
|
+
const id = this.sendCommand(method, params, {
|
|
141
379
|
onSuccess: () => {
|
|
380
|
+
this.log(`CDP command completed ${method} id=${id}`);
|
|
142
381
|
resolve();
|
|
143
382
|
},
|
|
144
383
|
onError: (error) => {
|
|
384
|
+
this.log(`CDP command failed ${method} id=${id}: ${error.message}`);
|
|
385
|
+
reject(error);
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
waitForCommandResult(method, params) {
|
|
391
|
+
return new Promise((resolve, reject) => {
|
|
392
|
+
const id = this.sendCommand(method, params, {
|
|
393
|
+
onSuccess: (result) => {
|
|
394
|
+
this.log(`CDP command completed ${method} id=${id}`);
|
|
395
|
+
resolve(result ?? {});
|
|
396
|
+
},
|
|
397
|
+
onError: (error) => {
|
|
398
|
+
this.log(`CDP command failed ${method} id=${id}: ${error.message}`);
|
|
145
399
|
reject(error);
|
|
146
400
|
},
|
|
147
401
|
});
|
|
@@ -154,60 +408,143 @@ export class NodeDebugger {
|
|
|
154
408
|
ws.addEventListener("open", () => {
|
|
155
409
|
void (async () => {
|
|
156
410
|
try {
|
|
157
|
-
|
|
411
|
+
this.log("websocket open -> Runtime.enable");
|
|
412
|
+
await this.waitForCommand("Runtime.enable");
|
|
413
|
+
this.log("Runtime.enable complete -> Debugger.enable");
|
|
158
414
|
await this.waitForCommand("Debugger.enable");
|
|
159
|
-
|
|
160
|
-
await this.waitForCommand("Debugger.setBreakpointsActive", { active: true });
|
|
161
|
-
trace("Debugger.setBreakpointsActive -> Runtime.runIfWaitingForDebugger");
|
|
415
|
+
this.log("Debugger.enable complete -> Runtime.runIfWaitingForDebugger");
|
|
162
416
|
await this.waitForCommand("Runtime.runIfWaitingForDebugger");
|
|
163
|
-
|
|
417
|
+
this.log("Runtime.runIfWaitingForDebugger complete -> Debugger.setBreakpointsActive");
|
|
418
|
+
await this.waitForCommand("Debugger.setBreakpointsActive", { active: true });
|
|
419
|
+
this.log("Debugger.setBreakpointsActive complete -> Debugger.setPauseOnExceptions");
|
|
420
|
+
await this.waitForCommand("Debugger.setPauseOnExceptions", { state: "all" });
|
|
421
|
+
this.log("Debugger.setPauseOnExceptions complete -> launch ready");
|
|
164
422
|
resolve();
|
|
165
423
|
}
|
|
166
424
|
catch (error) {
|
|
425
|
+
this.log(`CDP setup failed: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
167
426
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
168
427
|
}
|
|
169
428
|
})();
|
|
170
429
|
});
|
|
171
430
|
ws.addEventListener("error", (e) => {
|
|
172
431
|
const message = e instanceof Error ? e.message : "WebSocket connection failed";
|
|
432
|
+
this.log(`websocket error: ${message}`);
|
|
173
433
|
reject(new Error(message));
|
|
174
434
|
});
|
|
435
|
+
ws.addEventListener("close", (event) => {
|
|
436
|
+
this.log(`websocket close code=${event.code} reason=${event.reason || ""}`);
|
|
437
|
+
});
|
|
175
438
|
ws.addEventListener("message", (ev) => {
|
|
176
439
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
177
440
|
const msg = JSON.parse(String(ev.data));
|
|
178
|
-
|
|
179
|
-
this.handleCommandResponse(msg.id, msg);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (msg.method === "Debugger.paused") {
|
|
183
|
-
const reason = this.getPauseReason(msg.params);
|
|
184
|
-
this.paused = true;
|
|
185
|
-
trace(`Debugger.paused reason=${reason}`);
|
|
186
|
-
void Promise.resolve(this.onPause?.({ reason })).catch((error) => {
|
|
187
|
-
trace(`pause handler failed: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
188
|
-
});
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
if (msg.method === "Debugger.scriptParsed") {
|
|
192
|
-
const params = msg.params;
|
|
193
|
-
const lang = typeof params?.scriptLanguage === "string" ? params.scriptLanguage : "unknown";
|
|
194
|
-
const url = typeof params?.url === "string" ? params.url : "";
|
|
195
|
-
trace(`Debugger.scriptParsed language=${lang} url=${url}`);
|
|
196
|
-
if (params && lang === "WebAssembly" && typeof params.scriptId === "string") {
|
|
197
|
-
trace(`wasm module detected scriptId=${params.scriptId}`);
|
|
198
|
-
void this.handleModuleLoad({
|
|
199
|
-
scriptId: params.scriptId,
|
|
200
|
-
url,
|
|
201
|
-
}).catch((error) => {
|
|
202
|
-
const message = error instanceof Error ? error.message : "unknown error";
|
|
203
|
-
trace(`module load handler failed: ${message}`);
|
|
204
|
-
process.stderr.write(`[node-debugger] module load handler failed: ${message}\n`);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
441
|
+
this.handleCdpMessage(msg);
|
|
208
442
|
});
|
|
209
443
|
});
|
|
210
444
|
}
|
|
445
|
+
handleCdpMessage(msg) {
|
|
446
|
+
if (typeof msg.id === "number") {
|
|
447
|
+
this.handleCommandResponse(msg.id, msg);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (msg.method === "Debugger.paused") {
|
|
451
|
+
this.handleDebuggerPaused(msg.params);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (msg.method === "Debugger.scriptParsed") {
|
|
455
|
+
this.handleScriptParsed(msg.params);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
handleDebuggerPaused(params) {
|
|
459
|
+
const reason = this.getPauseReason(params);
|
|
460
|
+
this.pausedCallFrames = this.getPausedWasmCallFrames(params);
|
|
461
|
+
this.wasmMemoryBufferObjectId = undefined;
|
|
462
|
+
this.wasmGlobalsObjectId = undefined;
|
|
463
|
+
if (reason === "Break on start" && !this.wasmScriptId) {
|
|
464
|
+
this.log("Debugger.paused reason=Break on start location=inspect-brk startup");
|
|
465
|
+
void this.resumeInternalStartupPause();
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (reason === "other" && this.wasmScriptId) {
|
|
469
|
+
this.log("Debugger.paused reason=other location=wasm pre-entry gate");
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const isExceptionPause = reason === "exception" || reason === "promiseRejection";
|
|
473
|
+
// CDP can only pause on exceptions for the whole Node process, not for a specific wasm module. We need
|
|
474
|
+
// "all" because the JavaScript wrapper can catch a wasm trap, but that also pauses for unrelated JavaScript
|
|
475
|
+
// exceptions. Treat the top frame belonging to our wasm module as the filter for a debugger-visible trap.
|
|
476
|
+
if (isExceptionPause && !this.isTopFrameWasm(params)) {
|
|
477
|
+
this.log(`Debugger.paused reason=${reason} outside wasm -> resume`);
|
|
478
|
+
void this.resumeIgnoredPause();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const isWasmTrap = isExceptionPause;
|
|
482
|
+
if (isWasmTrap) {
|
|
483
|
+
this.paused = true;
|
|
484
|
+
this.log(`Debugger.paused reason=${reason} (wasm trap)`);
|
|
485
|
+
void this.notifyPause("exception");
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
this.paused = true;
|
|
489
|
+
this.log(`Debugger.paused reason=${reason}`);
|
|
490
|
+
void this.notifyPause(reason);
|
|
491
|
+
}
|
|
492
|
+
async notifyPause(reason) {
|
|
493
|
+
try {
|
|
494
|
+
const pausedCallFrame = this.pausedCallFrames[0];
|
|
495
|
+
const wasmBytecodeOffset = pausedCallFrame
|
|
496
|
+
? this.resolveWasmLocationBytecodeOffset(pausedCallFrame.location)
|
|
497
|
+
: undefined;
|
|
498
|
+
await this.onPause?.({ reason, wasmBytecodeOffset });
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
this.log(`pause handler failed: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
handleScriptParsed(params) {
|
|
505
|
+
const lang = typeof params?.scriptLanguage === "string" ? params.scriptLanguage : "unknown";
|
|
506
|
+
const url = typeof params?.url === "string" ? params.url : "";
|
|
507
|
+
this.log(`Debugger.scriptParsed language=${lang} url=${url}`);
|
|
508
|
+
if (!params || lang !== "WebAssembly" || typeof params.scriptId !== "string") {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (!this.isExpectedWasmModule(params)) {
|
|
512
|
+
this.log(`ignoring non-target wasm module scriptId=${params.scriptId} url=${url}`);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
this.wasmScriptId = params.scriptId;
|
|
516
|
+
this.log(`wasm module detected scriptId=${params.scriptId}`);
|
|
517
|
+
void this.handleModuleLoad({
|
|
518
|
+
scriptId: params.scriptId,
|
|
519
|
+
url,
|
|
520
|
+
}).catch((error) => {
|
|
521
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
522
|
+
this.log(`module load handler failed: ${message}`);
|
|
523
|
+
process.stderr.write(`Module load handler failed: ${message}\n`);
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
isExpectedWasmModule(params) {
|
|
527
|
+
if (!this.wasmFilePath) {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
return params.sourceMapURL === `${path.basename(this.wasmFilePath)}.map`;
|
|
531
|
+
}
|
|
532
|
+
async resumeInternalStartupPause() {
|
|
533
|
+
await this.resumeIgnoredPause("inspect-brk startup pause");
|
|
534
|
+
}
|
|
535
|
+
async resumeIgnoredPause(description = "non-wasm exception pause") {
|
|
536
|
+
try {
|
|
537
|
+
await this.waitForCommand("Debugger.resume");
|
|
538
|
+
this.paused = false;
|
|
539
|
+
this.pausedCallFrames = [];
|
|
540
|
+
this.wasmMemoryBufferObjectId = undefined;
|
|
541
|
+
this.wasmGlobalsObjectId = undefined;
|
|
542
|
+
this.log(`${description} resumed`);
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
this.log(`failed to resume ${description}: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
211
548
|
handleCommandResponse(id, msg) {
|
|
212
549
|
const callbacks = this.pendingCommandCallbacks.get(id);
|
|
213
550
|
if (!callbacks) {
|
|
@@ -215,10 +552,12 @@ export class NodeDebugger {
|
|
|
215
552
|
}
|
|
216
553
|
this.pendingCommandCallbacks.delete(id);
|
|
217
554
|
if (msg.error) {
|
|
555
|
+
this.log(`CDP <- error id=${id} message=${msg.error.message ?? "CDP command failed"}`);
|
|
218
556
|
callbacks.onError?.(new Error(msg.error.message ?? "CDP command failed"));
|
|
219
557
|
return;
|
|
220
558
|
}
|
|
221
|
-
|
|
559
|
+
this.log(`CDP <- success id=${id}`);
|
|
560
|
+
callbacks.onSuccess?.(msg.result);
|
|
222
561
|
}
|
|
223
562
|
getPauseReason(params) {
|
|
224
563
|
if (Array.isArray(params?.hitBreakpoints) && params.hitBreakpoints.length > 0) {
|
|
@@ -229,12 +568,262 @@ export class NodeDebugger {
|
|
|
229
568
|
}
|
|
230
569
|
return "unknown";
|
|
231
570
|
}
|
|
571
|
+
getPausedWasmCallFrames(params) {
|
|
572
|
+
if (!Array.isArray(params?.callFrames)) {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
const callFrames = params.callFrames.filter(isCDPCallFrame);
|
|
576
|
+
if (!this.wasmScriptId) {
|
|
577
|
+
this.log(`getPausedWasmCallFrames skipped ${callFrames.length} non-wasm startup frames before wasm script load`);
|
|
578
|
+
return [];
|
|
579
|
+
}
|
|
580
|
+
const wasmCallFrames = callFrames.filter((callFrame) => callFrame.location.scriptId === this.wasmScriptId);
|
|
581
|
+
this.log(`getPausedWasmCallFrames total=${callFrames.length} wasm=${wasmCallFrames.length}`);
|
|
582
|
+
return wasmCallFrames;
|
|
583
|
+
}
|
|
584
|
+
isTopFrameWasm(params) {
|
|
585
|
+
if (!Array.isArray(params?.callFrames) || !this.wasmScriptId) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
const topCallFrame = params.callFrames.find(isCDPCallFrame);
|
|
589
|
+
return topCallFrame?.location.scriptId === this.wasmScriptId;
|
|
590
|
+
}
|
|
591
|
+
toPausedWasmFrame(callFrame) {
|
|
592
|
+
return {
|
|
593
|
+
functionName: callFrame.functionName,
|
|
594
|
+
wasmBytecodeOffset: this.resolveWasmLocationBytecodeOffset(callFrame.location),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
async getLocalScopeVariables(callFrame) {
|
|
598
|
+
const localScope = callFrame.scopeChain.find((scope) => scope.type === "local") ?? callFrame.scopeChain[0];
|
|
599
|
+
const objectId = localScope?.object.objectId;
|
|
600
|
+
if (!objectId) {
|
|
601
|
+
return [];
|
|
602
|
+
}
|
|
603
|
+
const result = await this.waitForCommandResult("Runtime.getProperties", {
|
|
604
|
+
objectId,
|
|
605
|
+
ownProperties: true,
|
|
606
|
+
});
|
|
607
|
+
const properties = Array.isArray(result.result) ? result.result.filter(isCDPPropertyDescriptor) : [];
|
|
608
|
+
const variables = [];
|
|
609
|
+
for (const property of properties) {
|
|
610
|
+
if (!property.value) {
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
const localIndex = variables.length;
|
|
614
|
+
variables.push({
|
|
615
|
+
name: `local${localIndex}`,
|
|
616
|
+
value: await this.readRemoteObjectValue(property.value),
|
|
617
|
+
type: property.value.description ?? property.value.type,
|
|
618
|
+
localIndex,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
return variables;
|
|
622
|
+
}
|
|
623
|
+
async getWasmMemoryBufferObjectId() {
|
|
624
|
+
if (this.wasmMemoryBufferObjectId) {
|
|
625
|
+
return this.wasmMemoryBufferObjectId;
|
|
626
|
+
}
|
|
627
|
+
this.log("getWasmMemoryBufferObjectId -> getWasmMemoryObjectId");
|
|
628
|
+
const memoryObjectId = await this.getWasmMemoryObjectId();
|
|
629
|
+
if (!memoryObjectId) {
|
|
630
|
+
this.log("getWasmMemoryBufferObjectId failed: memory object unavailable");
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
this.log("getWasmMemoryBufferObjectId -> Runtime.callFunctionOn buffer");
|
|
634
|
+
const bufferResult = await this.waitForCommandResult("Runtime.callFunctionOn", {
|
|
635
|
+
objectId: memoryObjectId,
|
|
636
|
+
functionDeclaration: GET_WASM_MEMORY_BUFFER_FUNCTION,
|
|
637
|
+
silent: true,
|
|
638
|
+
objectGroup: LINEAR_MEMORY_OBJECT_GROUP,
|
|
639
|
+
});
|
|
640
|
+
const bufferObject = getRuntimeEvaluateRemoteObject(bufferResult);
|
|
641
|
+
this.wasmMemoryBufferObjectId = bufferObject?.objectId;
|
|
642
|
+
this.log(`getWasmMemoryBufferObjectId result=${this.wasmMemoryBufferObjectId ?? "undefined"}`);
|
|
643
|
+
return this.wasmMemoryBufferObjectId;
|
|
644
|
+
}
|
|
645
|
+
async getWasmMemoryObjectId() {
|
|
646
|
+
const moduleScope = this.pausedCallFrames[0]?.scopeChain.find((scope) => scope.type === "module");
|
|
647
|
+
const objectId = moduleScope?.object.objectId;
|
|
648
|
+
if (!objectId) {
|
|
649
|
+
return undefined;
|
|
650
|
+
}
|
|
651
|
+
this.log("getWasmMemoryObjectId -> Runtime.getProperties module scope");
|
|
652
|
+
const result = await this.waitForCommandResult("Runtime.getProperties", {
|
|
653
|
+
objectId,
|
|
654
|
+
ownProperties: false,
|
|
655
|
+
});
|
|
656
|
+
const properties = Array.isArray(result.result) ? result.result.filter(isCDPPropertyDescriptor) : [];
|
|
657
|
+
const memoriesProperty = properties.find((property) => property.name === "memories" && property.value?.objectId);
|
|
658
|
+
if (!memoriesProperty?.value?.objectId) {
|
|
659
|
+
return undefined;
|
|
660
|
+
}
|
|
661
|
+
this.log("getWasmMemoryObjectId -> Runtime.getProperties memories");
|
|
662
|
+
const memoriesResult = await this.waitForCommandResult("Runtime.getProperties", {
|
|
663
|
+
objectId: memoriesProperty.value.objectId,
|
|
664
|
+
ownProperties: false,
|
|
665
|
+
});
|
|
666
|
+
const memories = Array.isArray(memoriesResult.result) ? memoriesResult.result.filter(isCDPPropertyDescriptor) : [];
|
|
667
|
+
const nestedMemory = memories.find((property) => property.value && isWasmMemoryObject(property.value));
|
|
668
|
+
this.log(`getWasmMemoryObjectId result=${nestedMemory?.value?.objectId ?? "undefined"}`);
|
|
669
|
+
return nestedMemory?.value?.objectId;
|
|
670
|
+
}
|
|
671
|
+
async getWasmGlobalsObjectId() {
|
|
672
|
+
if (this.wasmGlobalsObjectId) {
|
|
673
|
+
return this.wasmGlobalsObjectId;
|
|
674
|
+
}
|
|
675
|
+
const moduleScope = this.pausedCallFrames[0]?.scopeChain.find((scope) => scope.type === "module");
|
|
676
|
+
const moduleObjectId = moduleScope?.object.objectId;
|
|
677
|
+
if (!moduleObjectId) {
|
|
678
|
+
return undefined;
|
|
679
|
+
}
|
|
680
|
+
const moduleResult = await this.waitForCommandResult("Runtime.getProperties", {
|
|
681
|
+
objectId: moduleObjectId,
|
|
682
|
+
ownProperties: false,
|
|
683
|
+
});
|
|
684
|
+
const moduleProperties = Array.isArray(moduleResult.result)
|
|
685
|
+
? moduleResult.result.filter(isCDPPropertyDescriptor)
|
|
686
|
+
: [];
|
|
687
|
+
this.wasmGlobalsObjectId = moduleProperties.find((property) => property.name === "globals")?.value?.objectId;
|
|
688
|
+
return this.wasmGlobalsObjectId;
|
|
689
|
+
}
|
|
690
|
+
async readRemoteObjectValue(value) {
|
|
691
|
+
if (value.subtype !== "wasmvalue" || !value.objectId) {
|
|
692
|
+
return formatRemoteObject(value);
|
|
693
|
+
}
|
|
694
|
+
const result = await this.waitForCommandResult("Runtime.getProperties", {
|
|
695
|
+
objectId: value.objectId,
|
|
696
|
+
ownProperties: true,
|
|
697
|
+
});
|
|
698
|
+
const innerProperties = Array.isArray(result.result) ? result.result.filter(isCDPPropertyDescriptor) : [];
|
|
699
|
+
const valueProperty = innerProperties.find((property) => property.name === "value" || property.name === "0");
|
|
700
|
+
return valueProperty?.value ? formatRemoteObject(valueProperty.value) : formatRemoteObject(value);
|
|
701
|
+
}
|
|
232
702
|
async handleModuleLoad(runtimeInfo) {
|
|
703
|
+
if (this.hasRuntimeExited()) {
|
|
704
|
+
this.log("skip wasm module load because runtime already exited");
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
233
707
|
if (!this.wasmFilePath) {
|
|
234
708
|
throw new Error("wasm file path is not available");
|
|
235
709
|
}
|
|
710
|
+
this.log(`loading wasm debug metadata from ${this.wasmFilePath}`);
|
|
236
711
|
const module = await DebuggerWasmModule.load(this.wasmFilePath, runtimeInfo);
|
|
712
|
+
this.log(`wasm debug metadata loaded from ${module.sourceMapFilePath}`);
|
|
237
713
|
void this.onModuleLoad?.(module);
|
|
238
714
|
}
|
|
715
|
+
hasRuntimeExited() {
|
|
716
|
+
return this.child !== undefined && (this.child.exitCode !== null || this.child.signalCode !== null);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function parseRuntimeStderr(text) {
|
|
720
|
+
let waitingForDebuggerDisconnect = false;
|
|
721
|
+
const output = text
|
|
722
|
+
.split(/(?<=\n)/)
|
|
723
|
+
.filter((line) => {
|
|
724
|
+
const trimmed = line.trim();
|
|
725
|
+
if (trimmed === NODE_WAITING_FOR_DEBUGGER_DISCONNECT) {
|
|
726
|
+
waitingForDebuggerDisconnect = true;
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
return (trimmed !== "" &&
|
|
730
|
+
!trimmed.startsWith("Debugger listening on") &&
|
|
731
|
+
!trimmed.startsWith("For help, see:") &&
|
|
732
|
+
trimmed !== "Debugger attached.");
|
|
733
|
+
})
|
|
734
|
+
.join("");
|
|
735
|
+
return { output, waitingForDebuggerDisconnect };
|
|
736
|
+
}
|
|
737
|
+
function getRuntimeEnv() {
|
|
738
|
+
const env = { ...process.env };
|
|
739
|
+
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
740
|
+
const nodeOptions = filterAutoAttachNodeOptions(env.NODE_OPTIONS);
|
|
741
|
+
if (nodeOptions) {
|
|
742
|
+
env.NODE_OPTIONS = nodeOptions;
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
delete env.NODE_OPTIONS;
|
|
746
|
+
}
|
|
747
|
+
return env;
|
|
748
|
+
}
|
|
749
|
+
function filterAutoAttachNodeOptions(nodeOptions) {
|
|
750
|
+
if (!nodeOptions) {
|
|
751
|
+
return "";
|
|
752
|
+
}
|
|
753
|
+
const parts = nodeOptions.split(/\s+/).filter(Boolean);
|
|
754
|
+
const filtered = [];
|
|
755
|
+
for (let i = 0; i < parts.length; i++) {
|
|
756
|
+
const part = parts[i];
|
|
757
|
+
const next = parts[i + 1];
|
|
758
|
+
if ((part === "--require" || part === "-r") && next && isVsCodeJsDebugBootloader(next)) {
|
|
759
|
+
i++;
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if ((part.startsWith("--require=") || part.startsWith("-r=")) && isVsCodeJsDebugBootloader(part)) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
filtered.push(part);
|
|
766
|
+
}
|
|
767
|
+
return filtered.join(" ");
|
|
768
|
+
}
|
|
769
|
+
function isVsCodeJsDebugBootloader(value) {
|
|
770
|
+
return value.includes("js-debug") || value.includes("vscode-js-debug");
|
|
771
|
+
}
|
|
772
|
+
function isCDPPropertyDescriptor(value) {
|
|
773
|
+
if (!value || typeof value !== "object") {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
return typeof value.name === "string";
|
|
777
|
+
}
|
|
778
|
+
function isCDPCallFrame(value) {
|
|
779
|
+
if (typeof value !== "object" || value === null) {
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
const callFrame = value;
|
|
783
|
+
return (typeof callFrame.callFrameId === "string" &&
|
|
784
|
+
typeof callFrame.functionName === "string" &&
|
|
785
|
+
isCDPLocation(callFrame.location) &&
|
|
786
|
+
Array.isArray(callFrame.scopeChain));
|
|
787
|
+
}
|
|
788
|
+
function isCDPLocation(value) {
|
|
789
|
+
if (typeof value !== "object" || value === null) {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
const location = value;
|
|
793
|
+
return (typeof location.scriptId === "string" &&
|
|
794
|
+
typeof location.lineNumber === "number" &&
|
|
795
|
+
typeof location.columnNumber === "number");
|
|
796
|
+
}
|
|
797
|
+
function isWasmMemoryObject(value) {
|
|
798
|
+
return value.objectId !== undefined && value.subtype === "webassemblymemory";
|
|
799
|
+
}
|
|
800
|
+
function getRuntimeEvaluateRemoteObject(result) {
|
|
801
|
+
const remoteObject = result.result;
|
|
802
|
+
if (!remoteObject || typeof remoteObject !== "object") {
|
|
803
|
+
return undefined;
|
|
804
|
+
}
|
|
805
|
+
return remoteObject;
|
|
806
|
+
}
|
|
807
|
+
function formatRemoteObject(value) {
|
|
808
|
+
if (value.unserializableValue !== undefined) {
|
|
809
|
+
return value.unserializableValue;
|
|
810
|
+
}
|
|
811
|
+
const rawValue = value.value;
|
|
812
|
+
if (rawValue !== undefined) {
|
|
813
|
+
if (rawValue === null) {
|
|
814
|
+
return "null";
|
|
815
|
+
}
|
|
816
|
+
if (typeof rawValue === "object") {
|
|
817
|
+
return value.description ?? value.type ?? "<unavailable>";
|
|
818
|
+
}
|
|
819
|
+
if (typeof rawValue === "string") {
|
|
820
|
+
return rawValue;
|
|
821
|
+
}
|
|
822
|
+
if (typeof rawValue === "number" || typeof rawValue === "boolean" || typeof rawValue === "bigint") {
|
|
823
|
+
return rawValue.toString();
|
|
824
|
+
}
|
|
825
|
+
return value.description ?? value.type ?? "<unavailable>";
|
|
826
|
+
}
|
|
827
|
+
return value.description ?? value.type ?? "<unavailable>";
|
|
239
828
|
}
|
|
240
829
|
//# sourceMappingURL=nodeDebugger.js.map
|