diff options
Diffstat (limited to 'devtools/client/debugger/src/actions/pause/mapFrames.js')
-rw-r--r-- | devtools/client/debugger/src/actions/pause/mapFrames.js | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/actions/pause/mapFrames.js b/devtools/client/debugger/src/actions/pause/mapFrames.js new file mode 100644 index 0000000000..d677677505 --- /dev/null +++ b/devtools/client/debugger/src/actions/pause/mapFrames.js @@ -0,0 +1,157 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ + +import { + getFrames, + getBlackBoxRanges, + getSelectedFrame, +} from "../../selectors"; + +import { isFrameBlackBoxed } from "../../utils/source"; + +import assert from "../../utils/assert"; +import { getOriginalLocation } from "../../utils/source-maps"; +import { + debuggerToSourceMapLocation, + sourceMapToDebuggerLocation, +} from "../../utils/location"; +import { isGeneratedId } from "devtools/client/shared/source-map-loader/index"; + +function getSelectedFrameId(state, thread, frames) { + let selectedFrame = getSelectedFrame(state, thread); + const blackboxedRanges = getBlackBoxRanges(state); + + if (selectedFrame && !isFrameBlackBoxed(selectedFrame, blackboxedRanges)) { + return selectedFrame.id; + } + + selectedFrame = frames.find(frame => { + return !isFrameBlackBoxed(frame, blackboxedRanges); + }); + return selectedFrame?.id; +} + +async function updateFrameLocation(frame, thunkArgs) { + if (frame.isOriginal) { + return Promise.resolve(frame); + } + const location = await getOriginalLocation(frame.location, thunkArgs, true); + return { + ...frame, + location, + generatedLocation: frame.generatedLocation || frame.location, + }; +} + +function updateFrameLocations(frames, thunkArgs) { + if (!frames || !frames.length) { + return Promise.resolve(frames); + } + + return Promise.all( + frames.map(frame => updateFrameLocation(frame, thunkArgs)) + ); +} + +function isWasmOriginalSourceFrame(frame, getState) { + if (isGeneratedId(frame.location.sourceId)) { + return false; + } + + return Boolean(frame.generatedLocation?.source.isWasm); +} + +async function expandFrames(frames, { getState, sourceMapLoader }) { + const result = []; + for (let i = 0; i < frames.length; ++i) { + const frame = frames[i]; + if (frame.isOriginal || !isWasmOriginalSourceFrame(frame, getState)) { + result.push(frame); + continue; + } + const originalFrames = await sourceMapLoader.getOriginalStackFrames( + debuggerToSourceMapLocation(frame.generatedLocation) + ); + if (!originalFrames) { + result.push(frame); + continue; + } + + assert(!!originalFrames.length, "Expected at least one original frame"); + // First entry has not specific location -- use one from original frame. + originalFrames[0] = { + ...originalFrames[0], + location: frame.location, + }; + + originalFrames.forEach((originalFrame, j) => { + if (!originalFrame.location) { + return; + } + + // Keep outer most frame with true actor ID, and generate uniquie + // one for the nested frames. + const id = j == 0 ? frame.id : `${frame.id}-originalFrame${j}`; + result.push({ + id, + displayName: originalFrame.displayName, + location: sourceMapToDebuggerLocation( + getState(), + originalFrame.location + ), + index: frame.index, + source: null, + thread: frame.thread, + scope: frame.scope, + this: frame.this, + isOriginal: true, + // More fields that will be added by the mapDisplayNames and + // updateFrameLocation. + generatedLocation: frame.generatedLocation, + originalDisplayName: originalFrame.displayName, + originalVariables: originalFrame.variables, + asyncCause: frame.asyncCause, + state: frame.state, + }); + }); + } + return result; +} + +/** + * Map call stack frame locations and display names to originals. + * e.g. + * 1. When the debuggee pauses + * 2. When a source is pretty printed + * 3. When symbols are loaded + * @memberof actions/pause + * @static + */ +export function mapFrames(cx) { + return async function (thunkArgs) { + const { dispatch, getState } = thunkArgs; + const frames = getFrames(getState(), cx.thread); + if (!frames) { + return; + } + + let mappedFrames = await updateFrameLocations(frames, thunkArgs); + + mappedFrames = await expandFrames(mappedFrames, thunkArgs); + + const selectedFrameId = getSelectedFrameId( + getState(), + cx.thread, + mappedFrames + ); + + dispatch({ + type: "MAP_FRAMES", + cx, + thread: cx.thread, + frames: mappedFrames, + selectedFrameId, + }); + }; +} |