tor-browser

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

stack.js (5394B)


      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 "use strict";
      6 
      7 /**
      8 * A helper class that stores stack frame objects.  Each frame is
      9 * assigned an index, and if a frame is added more than once, the same
     10 * index is used.  Users of the class can get an array of all frames
     11 * that have been added.
     12 */
     13 class StackFrameCache {
     14  /**
     15   * Initialize this object.
     16   */
     17  constructor() {
     18    this._framesToIndices = null;
     19    this._framesToForms = null;
     20    this._lastEventSize = 0;
     21  }
     22 
     23  /**
     24   * Prepare to accept frames.
     25   */
     26  initFrames() {
     27    if (this._framesToIndices) {
     28      // The maps are already initialized.
     29      return;
     30    }
     31 
     32    this._framesToIndices = new Map();
     33    this._framesToForms = new Map();
     34    this._lastEventSize = 0;
     35  }
     36 
     37  /**
     38   * Forget all stored frames and reset to the initialized state.
     39   */
     40  clearFrames() {
     41    this._framesToIndices.clear();
     42    this._framesToIndices = null;
     43    this._framesToForms.clear();
     44    this._framesToForms = null;
     45    this._lastEventSize = 0;
     46  }
     47 
     48  /**
     49   * Add a frame to this stack frame cache, and return the index of
     50   * the frame.
     51   */
     52  addFrame(frame) {
     53    this._assignFrameIndices(frame);
     54    this._createFrameForms(frame);
     55    return this._framesToIndices.get(frame);
     56  }
     57 
     58  /**
     59   * A helper method for the memory actor.  This populates the packet
     60   * object with "frames" property. Each of these
     61   * properties will be an array indexed by frame ID.  "frames" will
     62   * contain frame objects (see makeEvent).
     63   *
     64   * @param packet
     65   *        The packet to update.
     66   *
     67   * @returns packet
     68   */
     69  updateFramePacket(packet) {
     70    // Now that we are guaranteed to have a form for every frame, we know the
     71    // size the "frames" property's array must be. We use that information to
     72    // create dense arrays even though we populate them out of order.
     73    const size = this._framesToForms.size;
     74    packet.frames = Array(size).fill(null);
     75 
     76    // Populate the "frames" properties.
     77    for (const [stack, index] of this._framesToIndices) {
     78      packet.frames[index] = this._framesToForms.get(stack);
     79    }
     80 
     81    return packet;
     82  }
     83 
     84  /**
     85   * If any new stack frames have been added to this cache since the
     86   * last call to makeEvent (clearing the cache also resets the "last
     87   * call"), then return a new array describing the new frames.  If no
     88   * new frames are available, return null.
     89   *
     90   * The frame cache assumes that the user of the cache keeps track of
     91   * all previously-returned arrays and, in theory, concatenates them
     92   * all to form a single array holding all frames added to the cache
     93   * since the last reset.  This concatenated array can be indexed by
     94   * the frame ID.  The array returned by this function, though, is
     95   * dense and starts at 0.
     96   *
     97   * Each element in the array is an object of the form:
     98   * {
     99   *   line: <line number for this frame>,
    100   *   column: <column number for this frame>,
    101   *   source: <filename string for this frame>,
    102   *   functionDisplayName: <this frame's inferred function name function or null>,
    103   *   parent: <frame ID -- an index into the concatenated array mentioned above>
    104   *   asyncCause: the async cause, or null
    105   *   asyncParent: <frame ID -- an index into the concatenated array mentioned above>
    106   * }
    107   *
    108   * The intent of this approach is to make it simpler to efficiently
    109   * send frame information over the debugging protocol, by only
    110   * sending new frames.
    111   *
    112   * @returns array or null
    113   */
    114  makeEvent() {
    115    const size = this._framesToForms.size;
    116    if (!size || size <= this._lastEventSize) {
    117      return null;
    118    }
    119 
    120    const packet = Array(size - this._lastEventSize).fill(null);
    121    for (const [stack, index] of this._framesToIndices) {
    122      if (index >= this._lastEventSize) {
    123        packet[index - this._lastEventSize] = this._framesToForms.get(stack);
    124      }
    125    }
    126 
    127    this._lastEventSize = size;
    128 
    129    return packet;
    130  }
    131 
    132  /**
    133   * Assigns an index to the given frame and its parents, if an index is not
    134   * already assigned.
    135   *
    136   * @param SavedFrame frame
    137   *        A frame to assign an index to.
    138   */
    139  _assignFrameIndices(frame) {
    140    if (this._framesToIndices.has(frame)) {
    141      return;
    142    }
    143 
    144    if (frame) {
    145      this._assignFrameIndices(frame.parent);
    146      this._assignFrameIndices(frame.asyncParent);
    147    }
    148 
    149    const index = this._framesToIndices.size;
    150    this._framesToIndices.set(frame, index);
    151  }
    152 
    153  /**
    154   * Create the form for the given frame, if one doesn't already exist.
    155   *
    156   * @param SavedFrame frame
    157   *        A frame to create a form for.
    158   */
    159  _createFrameForms(frame) {
    160    if (this._framesToForms.has(frame)) {
    161      return;
    162    }
    163 
    164    let form = null;
    165    if (frame) {
    166      form = {
    167        line: frame.line,
    168        column: frame.column,
    169        source: frame.source,
    170        functionDisplayName: frame.functionDisplayName,
    171        parent: this._framesToIndices.get(frame.parent),
    172        asyncParent: this._framesToIndices.get(frame.asyncParent),
    173        asyncCause: frame.asyncCause,
    174      };
    175      this._createFrameForms(frame.parent);
    176      this._createFrameForms(frame.asyncParent);
    177    }
    178 
    179    this._framesToForms.set(frame, form);
    180  }
    181 }
    182 
    183 exports.StackFrameCache = StackFrameCache;