servo/resources/debugger.js
shuppy 4ba34b038c
devtools: Fix available breakpoint positions with nested scripts (#38826)
in the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), there is a
separate
[Debugger.Script](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html)
object for each function in a script, forming a tree of Script objects.
since we were only issuing
[getPossibleBreakpoints()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getpossiblebreakpoints-query)
queries to the [root Script
object](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewscript-script-global)
for each script, we were failing to report locations inside functions as
available for setting breakpoints.

this patch fixes that by recursively issuing the
getPossibleBreakpoints() requests over the
[getChildScripts()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getchildscripts)
tree.

Testing: this patch adds a new devtools test

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-21 11:00:32 +00:00

52 lines
1.9 KiB
JavaScript

if ("dbg" in this) {
throw new Error("Debugger script must not run more than once!");
}
const dbg = new Debugger;
const debuggeesToPipelineIds = new Map;
const debuggeesToWorkerIds = new Map;
const sourceIdsToScripts = new Map;
dbg.uncaughtExceptionHook = function(error) {
console.error(`[debugger] Uncaught exception at ${error.fileName}:${error.lineNumber}:${error.columnNumber}: ${error.name}: ${error.message}`);
};
dbg.onNewScript = function(script, /* undefined; seems to be `script.global` now */ global) {
// TODO: handle wasm (`script.source.introductionType == wasm`)
sourceIdsToScripts.set(script.source.id, script);
notifyNewSource({
pipelineId: debuggeesToPipelineIds.get(script.global),
workerId: debuggeesToWorkerIds.get(script.global),
spidermonkeyId: script.source.id,
url: script.source.url,
urlOverride: script.source.displayURL,
text: script.source.text,
introductionType: script.source.introductionType ?? null,
});
};
addEventListener("addDebuggee", event => {
const {global, pipelineId: {namespaceId, index}, workerId} = event;
dbg.addDebuggee(global);
const debuggerObject = dbg.addDebuggee(global);
debuggeesToPipelineIds.set(debuggerObject, {
namespaceId,
index,
});
debuggeesToWorkerIds.set(debuggerObject, workerId);
});
addEventListener("getPossibleBreakpoints", event => {
const {spidermonkeyId} = event;
const script = sourceIdsToScripts.get(spidermonkeyId);
function getPossibleBreakpointsRecursive(script) {
const result = script.getPossibleBreakpoints(/* TODO: `query` */);
for (const child of script.getChildScripts()) {
for (const location of getPossibleBreakpointsRecursive(child)) {
result.push(location);
}
}
return result;
}
getPossibleBreakpointsResult(event, getPossibleBreakpointsRecursive(script));
});