tor-browser

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

wasmXScopes.js (5635B)


      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 /* eslint camelcase: 0*/
      6 
      7 "use strict";
      8 
      9 const {
     10  decodeExpr,
     11 } = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions");
     12 
     13 const xScopes = new Map();
     14 
     15 function indexLinkingNames(items) {
     16  const result = new Map();
     17  let queue = [...items];
     18  while (queue.length) {
     19    const item = queue.shift();
     20    if ("uid" in item) {
     21      result.set(item.uid, item);
     22    } else if ("linkage_name" in item) {
     23      // TODO the linkage_name string value is used for compatibility
     24      // with old format. Remove in favour of the uid referencing.
     25      result.set(item.linkage_name, item);
     26    }
     27    if ("children" in item) {
     28      queue = [...queue, ...item.children];
     29    }
     30  }
     31  return result;
     32 }
     33 
     34 function getIndexedItem(index, key) {
     35  if (typeof key === "object" && key != null) {
     36    return index.get(key.uid);
     37  }
     38  if (typeof key === "string") {
     39    return index.get(key);
     40  }
     41  return null;
     42 }
     43 
     44 async function getXScopes(sourceId, getSourceMap) {
     45  if (xScopes.has(sourceId)) {
     46    return xScopes.get(sourceId);
     47  }
     48  const map = await getSourceMap(sourceId);
     49  if (!map || !map.xScopes) {
     50    xScopes.set(sourceId, null);
     51    return null;
     52  }
     53  const { code_section_offset, debug_info } = map.xScopes;
     54  const xScope = {
     55    code_section_offset,
     56    debug_info,
     57    idIndex: indexLinkingNames(debug_info),
     58    sources: map.sources,
     59  };
     60  xScopes.set(sourceId, xScope);
     61  return xScope;
     62 }
     63 
     64 function isInRange(item, pc) {
     65  if ("ranges" in item) {
     66    return item.ranges.some(r => r[0] <= pc && pc < r[1]);
     67  }
     68  if ("high_pc" in item) {
     69    return item.low_pc <= pc && pc < item.high_pc;
     70  }
     71  return false;
     72 }
     73 
     74 function decodeExprAt(expr, pc) {
     75  if (typeof expr === "string") {
     76    return decodeExpr(expr);
     77  }
     78  const foundAt = expr.find(i => i.range[0] <= pc && pc < i.range[1]);
     79  return foundAt ? decodeExpr(foundAt.expr) : null;
     80 }
     81 
     82 function getVariables(scope, pc) {
     83  const vars = scope.children
     84    ? scope.children.reduce((result, item) => {
     85        switch (item.tag) {
     86          case "variable":
     87          case "formal_parameter":
     88            result.push({
     89              name: item.name || "",
     90              expr: item.location ? decodeExprAt(item.location, pc) : null,
     91            });
     92            break;
     93          case "lexical_block": {
     94            // FIXME build scope blocks (instead of combining)
     95            const tmp = getVariables(item, pc);
     96            result = [...tmp.vars, ...result];
     97            break;
     98          }
     99        }
    100        return result;
    101      }, [])
    102    : [];
    103  const frameBase = scope.frame_base ? decodeExpr(scope.frame_base) : null;
    104  return {
    105    vars,
    106    frameBase,
    107  };
    108 }
    109 
    110 function filterScopes(items, pc, lastItem, index) {
    111  if (!items) {
    112    return [];
    113  }
    114  return items.reduce((result, item) => {
    115    switch (item.tag) {
    116      case "compile_unit":
    117        if (isInRange(item, pc)) {
    118          result = [
    119            ...result,
    120            ...filterScopes(item.children, pc, lastItem, index),
    121          ];
    122        }
    123        break;
    124      case "namespace":
    125      case "structure_type":
    126      case "union_type":
    127        result = [
    128          ...result,
    129          ...filterScopes(item.children, pc, lastItem, index),
    130        ];
    131        break;
    132      case "subprogram":
    133        if (isInRange(item, pc)) {
    134          const s = {
    135            id: item.linkage_name,
    136            name: item.name,
    137            variables: getVariables(item, pc),
    138          };
    139          result = [...result, s, ...filterScopes(item.children, pc, s, index)];
    140        }
    141        break;
    142      case "inlined_subroutine":
    143        if (isInRange(item, pc)) {
    144          const linkedItem = getIndexedItem(index, item.abstract_origin);
    145          const s = {
    146            id: item.abstract_origin,
    147            name: linkedItem ? linkedItem.name : void 0,
    148            variables: getVariables(item, pc),
    149          };
    150          if (lastItem) {
    151            lastItem.file = item.call_file;
    152            lastItem.line = item.call_line;
    153          }
    154          result = [...result, s, ...filterScopes(item.children, pc, s, index)];
    155        }
    156        break;
    157    }
    158    return result;
    159  }, []);
    160 }
    161 
    162 class XScope {
    163  xScope;
    164  sourceMapContext;
    165 
    166  constructor(xScopeData, sourceMapContext) {
    167    this.xScope = xScopeData;
    168    this.sourceMapContext = sourceMapContext;
    169  }
    170 
    171  search(generatedLocation) {
    172    const { code_section_offset, debug_info, sources, idIndex } = this.xScope;
    173    const pc = generatedLocation.line - (code_section_offset || 0);
    174    const scopes = filterScopes(debug_info, pc, null, idIndex);
    175    scopes.reverse();
    176 
    177    return scopes.map(i => {
    178      if (!("file" in i)) {
    179        return {
    180          displayName: i.name || "",
    181          variables: i.variables,
    182        };
    183      }
    184      const sourceId = this.sourceMapContext.generatedToOriginalId(
    185        generatedLocation.sourceId,
    186        sources[i.file || 0]
    187      );
    188      return {
    189        displayName: i.name || "",
    190        variables: i.variables,
    191        location: {
    192          line: i.line || 0,
    193          sourceId,
    194        },
    195      };
    196    });
    197  }
    198 }
    199 
    200 async function getWasmXScopes(sourceId, sourceMapContext) {
    201  const { getSourceMap } = sourceMapContext;
    202  const xScopeData = await getXScopes(sourceId, getSourceMap);
    203  if (!xScopeData) {
    204    return null;
    205  }
    206  return new XScope(xScopeData, sourceMapContext);
    207 }
    208 
    209 function clearWasmXScopes() {
    210  xScopes.clear();
    211 }
    212 
    213 module.exports = {
    214  getWasmXScopes,
    215  clearWasmXScopes,
    216 };