summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/actions/pause/mapFrames.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/actions/pause/mapFrames.js')
-rw-r--r--devtools/client/debugger/src/actions/pause/mapFrames.js162
1 files changed, 162 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..1eb2a3fd50
--- /dev/null
+++ b/devtools/client/debugger/src/actions/pause/mapFrames.js
@@ -0,0 +1,162 @@
+/* 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,
+ getLocationSource,
+ getSelectedFrame,
+} from "../../selectors";
+
+import { isFrameBlackBoxed } from "../../utils/source";
+
+import assert from "../../utils/assert";
+
+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,
+ getLocationSource(state, selectedFrame.location),
+ blackboxedRanges
+ )
+ ) {
+ return selectedFrame.id;
+ }
+
+ selectedFrame = frames.find(frame => {
+ const frameSource = getLocationSource(state, frame.location);
+ return !isFrameBlackBoxed(frame, frameSource, blackboxedRanges);
+ });
+ return selectedFrame?.id;
+}
+
+export function updateFrameLocation(frame, sourceMapLoader) {
+ if (frame.isOriginal) {
+ return Promise.resolve(frame);
+ }
+ return sourceMapLoader.getOriginalLocation(frame.location).then(loc => ({
+ ...frame,
+ location: loc,
+ generatedLocation: frame.generatedLocation || frame.location,
+ }));
+}
+
+function updateFrameLocations(frames, sourceMapLoader) {
+ if (!frames || !frames.length) {
+ return Promise.resolve(frames);
+ }
+
+ return Promise.all(
+ frames.map(frame => updateFrameLocation(frame, sourceMapLoader))
+ );
+}
+
+function isWasmOriginalSourceFrame(frame, getState) {
+ if (isGeneratedId(frame.location.sourceId)) {
+ return false;
+ }
+ const generatedSource = getLocationSource(
+ getState(),
+ frame.generatedLocation
+ );
+
+ return Boolean(generatedSource?.isWasm);
+}
+
+async function expandFrames(frames, sourceMapLoader, getState) {
+ 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(
+ 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: 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, sourceMapLoader } = thunkArgs;
+ const frames = getFrames(getState(), cx.thread);
+ if (!frames) {
+ return;
+ }
+
+ let mappedFrames = await updateFrameLocations(frames, sourceMapLoader);
+
+ mappedFrames = await expandFrames(mappedFrames, sourceMapLoader, getState);
+
+ const selectedFrameId = getSelectedFrameId(
+ getState(),
+ cx.thread,
+ mappedFrames
+ );
+
+ dispatch({
+ type: "MAP_FRAMES",
+ cx,
+ thread: cx.thread,
+ frames: mappedFrames,
+ selectedFrameId,
+ });
+ };
+}