tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mapFrames.js (5938B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 import {
      6  getFrames,
      7  getBlackBoxRanges,
      8  getSelectedFrame,
      9 } from "../../selectors/index";
     10 
     11 import { isFrameBlackBoxed } from "../../utils/source";
     12 
     13 import assert from "../../utils/assert";
     14 import { getOriginalLocation } from "../../utils/source-maps";
     15 import {
     16  debuggerToSourceMapLocation,
     17  sourceMapToDebuggerLocation,
     18 } from "../../utils/location";
     19 import { annotateFramesWithLibrary } from "../../utils/pause/frames/annotateFrames";
     20 import { createWasmOriginalFrame } from "../../client/firefox/create";
     21 
     22 import { getOriginalFunctionDisplayName } from "../sources/index";
     23 
     24 function getSelectedFrameId(state, thread, frames) {
     25  let selectedFrame = getSelectedFrame(state, thread);
     26  const blackboxedRanges = getBlackBoxRanges(state);
     27 
     28  if (selectedFrame && !isFrameBlackBoxed(selectedFrame, blackboxedRanges)) {
     29    return selectedFrame.id;
     30  }
     31 
     32  selectedFrame = frames.find(frame => {
     33    return !isFrameBlackBoxed(frame, blackboxedRanges);
     34  });
     35  return selectedFrame?.id;
     36 }
     37 
     38 async function updateFrameLocation(frame, thunkArgs) {
     39  // Ignore WASM original sources
     40  if (isWasmOriginalSourceFrame(frame)) {
     41    return frame;
     42  }
     43 
     44  const location = await getOriginalLocation(
     45    frame.generatedLocation || frame.location,
     46    thunkArgs,
     47    {
     48      waitForSource: true,
     49    }
     50  );
     51  // Avoid instantiating new frame objects if the frame location isn't mapped
     52  if (location == frame.location) {
     53    return frame;
     54  }
     55 
     56  // As we modify frame object, fork it to force causing re-renders
     57  return {
     58    ...frame,
     59    location,
     60    generatedLocation: frame.generatedLocation || frame.location,
     61  };
     62 }
     63 
     64 function isWasmOriginalSourceFrame(frame) {
     65  if (!frame.location.source.isOriginal) {
     66    return false;
     67  }
     68 
     69  return Boolean(frame.generatedLocation?.source.isWasm);
     70 }
     71 
     72 /**
     73 * Wasm Source Maps can come with an non-standard "xScopes" attribute
     74 * which allows mapping the scope of a given location.
     75 */
     76 async function expandWasmFrames(frames, { getState, sourceMapLoader }) {
     77  const result = [];
     78  for (let i = 0; i < frames.length; ++i) {
     79    const frame = frames[i];
     80    if (frame.isOriginal || !isWasmOriginalSourceFrame(frame)) {
     81      result.push(frame);
     82      continue;
     83    }
     84    const originalFrames = await sourceMapLoader.getOriginalStackFrames(
     85      debuggerToSourceMapLocation(frame.generatedLocation)
     86    );
     87    if (!originalFrames) {
     88      result.push(frame);
     89      continue;
     90    }
     91 
     92    assert(!!originalFrames.length, "Expected at least one original frame");
     93    // First entry has no specific location -- use one from the generated frame.
     94    originalFrames[0].location = frame.location;
     95 
     96    originalFrames.forEach((originalFrame, j) => {
     97      if (!originalFrame.location) {
     98        return;
     99      }
    100 
    101      // Keep outer most frame with true actor ID, and generate unique
    102      // one for the nested frames.
    103      const id = j == 0 ? frame.id : `${frame.id}-originalFrame${j}`;
    104      const originalFrameLocation = sourceMapToDebuggerLocation(
    105        getState(),
    106        originalFrame.location
    107      );
    108      result.push(
    109        createWasmOriginalFrame(frame, id, originalFrame, originalFrameLocation)
    110      );
    111    });
    112  }
    113  return result;
    114 }
    115 
    116 async function updateFrameDisplayName(frame, thunkArgs) {
    117  const location = frame.location;
    118  // Ignore WASM original, generated and pretty printed sources
    119  if (
    120    location.source.isWasm ||
    121    !location.source.isOriginal ||
    122    location.source.isPrettyPrinted
    123  ) {
    124    return frame;
    125  }
    126 
    127  // As we now know that this frame relates to an original source...
    128  // Compute the frame's originalDisplayName.
    129  const originalDisplayName = location.source.isPrettyPrinted
    130    ? frame.displayName
    131    : await thunkArgs.dispatch(getOriginalFunctionDisplayName(location));
    132 
    133  // As we modify frame object, fork it to force causing re-renders
    134  return {
    135    ...frame,
    136    originalDisplayName,
    137  };
    138 }
    139 
    140 /**
    141 * Update the display names of the mapped original frames
    142 *
    143 * @param {object} thread
    144 * @returns
    145 */
    146 export function updateAllFrameDisplayNames(thread) {
    147  return async function (thunkArgs) {
    148    const { dispatch, getState } = thunkArgs;
    149    const frames = getFrames(getState(), thread);
    150    if (!frames || !frames.length) {
    151      return;
    152    }
    153 
    154    // Update frame's originalDisplayNames in case it relates to an original source
    155    const updatedFrames = await Promise.all(
    156      frames.map(frame => updateFrameDisplayName(frame, thunkArgs))
    157    );
    158 
    159    dispatch({
    160      type: "UPDATE_FRAMES",
    161      frames: updatedFrames,
    162      thread,
    163    });
    164  };
    165 }
    166 
    167 /**
    168 * Map call stack frame locations and display names to originals.
    169 * e.g.
    170 * 1. When the debuggee pauses
    171 * 2. When a source is pretty printed
    172 *
    173 * @memberof actions/pause
    174 * @static
    175 */
    176 export function mapFrames(thread) {
    177  return async function (thunkArgs) {
    178    const { dispatch, getState } = thunkArgs;
    179    const frames = getFrames(getState(), thread);
    180    if (!frames || !frames.length) {
    181      return;
    182    }
    183 
    184    // Update frame's location/generatedLocation in case it relates to an original source
    185    let mappedFrames = await Promise.all(
    186      frames.map(frame => updateFrameLocation(frame, thunkArgs))
    187    );
    188 
    189    mappedFrames = await expandWasmFrames(mappedFrames, thunkArgs);
    190 
    191    // Add the "library" attribute on all frame objects (if relevant)
    192    annotateFramesWithLibrary(mappedFrames);
    193 
    194    // After having mapped the frames, we should update the selected frame
    195    // just in case the selected frame is now set on a blackboxed original source
    196    const selectedFrameId = getSelectedFrameId(
    197      getState(),
    198      thread,
    199      mappedFrames
    200    );
    201 
    202    dispatch({
    203      type: "MAP_FRAMES",
    204      thread,
    205      frames: mappedFrames,
    206      selectedFrameId,
    207    });
    208  };
    209 }