tor-browser

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

mapScopes.js (5630B)


      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  getSettledSourceTextContent,
      7  isMapScopesEnabled,
      8  getSelectedFrame,
      9  getGeneratedFrameScope,
     10  getOriginalFrameScope,
     11  getFirstSourceActorForGeneratedSource,
     12 } from "../../selectors/index";
     13 import {
     14  loadOriginalSourceText,
     15  loadGeneratedSourceText,
     16 } from "../sources/loadSourceText";
     17 import { validateSelectedFrame } from "../../utils/context";
     18 const {
     19  PROMISE,
     20 } = require("resource://devtools/client/shared/redux/middleware/promise.js");
     21 
     22 import { log } from "../../utils/log";
     23 
     24 import { buildMappedScopes } from "../../utils/pause/mapScopes/index";
     25 import { isFulfilled } from "../../utils/async-value";
     26 
     27 import { getMappedLocation } from "../../utils/source-maps";
     28 
     29 const expressionRegex = /\bfp\(\)/g;
     30 
     31 export async function buildOriginalScopes(
     32  selectedFrame,
     33  client,
     34  generatedScopes
     35 ) {
     36  if (!selectedFrame.originalVariables) {
     37    throw new TypeError("(frame.originalVariables: XScopeVariables)");
     38  }
     39  const originalVariables = selectedFrame.originalVariables;
     40  const frameBase = originalVariables.frameBase || "";
     41 
     42  const inputs = [];
     43  for (let i = 0; i < originalVariables.vars.length; i++) {
     44    const { expr } = originalVariables.vars[i];
     45    const expression = expr
     46      ? expr.replace(expressionRegex, frameBase)
     47      : "void 0";
     48 
     49    inputs[i] = expression;
     50  }
     51 
     52  const results = await client.evaluateExpressions(inputs, {
     53    frameId: selectedFrame.id,
     54  });
     55 
     56  const variables = {};
     57  for (let i = 0; i < originalVariables.vars.length; i++) {
     58    const { name } = originalVariables.vars[i];
     59    variables[name] = { value: results[i].result };
     60  }
     61 
     62  const bindings = {
     63    arguments: [],
     64    variables,
     65  };
     66 
     67  const { actor } = await generatedScopes;
     68  const scope = {
     69    type: "function",
     70    scopeKind: "",
     71    actor,
     72    bindings,
     73    parent: null,
     74    function: null,
     75    block: null,
     76  };
     77  return {
     78    mappings: {},
     79    scope,
     80  };
     81 }
     82 
     83 export function toggleMapScopes() {
     84  return async function ({ dispatch, getState }) {
     85    if (isMapScopesEnabled(getState())) {
     86      dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: false });
     87      return;
     88    }
     89 
     90    dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: true });
     91 
     92    // Ignore the call if there is no selected frame (we are not paused?)
     93    const state = getState();
     94    const selectedFrame = getSelectedFrame(state);
     95    if (!selectedFrame) {
     96      return;
     97    }
     98 
     99    if (getOriginalFrameScope(getState(), selectedFrame)) {
    100      return;
    101    }
    102 
    103    // Also ignore the call if we didn't fetch the scopes for the selected frame
    104    const scopes = getGeneratedFrameScope(getState(), selectedFrame);
    105    if (!scopes) {
    106      return;
    107    }
    108 
    109    dispatch(mapScopes(selectedFrame, Promise.resolve(scopes.scope)));
    110  };
    111 }
    112 
    113 export function mapScopes(selectedFrame, scopes) {
    114  return async function (thunkArgs) {
    115    const { getState, dispatch, client } = thunkArgs;
    116 
    117    await dispatch({
    118      type: "MAP_SCOPES",
    119      selectedFrame,
    120      [PROMISE]: (async function () {
    121        if (selectedFrame.isOriginal && selectedFrame.originalVariables) {
    122          return buildOriginalScopes(selectedFrame, client, scopes);
    123        }
    124 
    125        // getMappedScopes is only specific to the sources where we map the variables
    126        // in scope and so only need a thread context. Assert that we are on the same thread
    127        // before retrieving a thread context.
    128        validateSelectedFrame(getState(), selectedFrame);
    129 
    130        return dispatch(getMappedScopes(scopes, selectedFrame));
    131      })(),
    132    });
    133  };
    134 }
    135 
    136 /**
    137 * Get scopes mapped for a precise location.
    138 *
    139 * @param {Promise} scopes
    140 *        Can be null. Result of Commands.js's client.getFrameScopes
    141 * @param {Objects locations
    142 *        Frame object, or custom object with 'location' and 'generatedLocation' attributes.
    143 */
    144 export function getMappedScopes(scopes, locations) {
    145  return async function (thunkArgs) {
    146    const { getState, dispatch } = thunkArgs;
    147    const generatedSource = locations.generatedLocation.source;
    148    const source = locations.location.source;
    149 
    150    if (
    151      !isMapScopesEnabled(getState()) ||
    152      !source ||
    153      !generatedSource ||
    154      generatedSource.isWasm ||
    155      source.isPrettyPrinted ||
    156      !source.isOriginal
    157    ) {
    158      return null;
    159    }
    160 
    161    // Load source text for the original source
    162    await dispatch(loadOriginalSourceText(source));
    163 
    164    const generatedSourceActor = getFirstSourceActorForGeneratedSource(
    165      getState(),
    166      generatedSource.id
    167    );
    168 
    169    // Also load source text for its corresponding generated source
    170    await dispatch(loadGeneratedSourceText(generatedSourceActor));
    171 
    172    try {
    173      const content =
    174        // load original source text content
    175        getSettledSourceTextContent(getState(), locations.location);
    176 
    177      return await buildMappedScopes(
    178        source,
    179        content && isFulfilled(content)
    180          ? content.value
    181          : { type: "text", value: "", contentType: undefined },
    182        locations,
    183        await scopes,
    184        thunkArgs
    185      );
    186    } catch (e) {
    187      log(e);
    188      return null;
    189    }
    190  };
    191 }
    192 
    193 /**
    194 * Used to map variables used within conditional and log breakpoints.
    195 */
    196 export function getMappedScopesForLocation(location) {
    197  return async function (thunkArgs) {
    198    const { dispatch } = thunkArgs;
    199    const mappedLocation = await getMappedLocation(location, thunkArgs);
    200    return dispatch(getMappedScopes(null, mappedLocation));
    201  };
    202 }