text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow
   * @format
   */
  
  'use strict';
  
  const SourceMetadataMapConsumer = require('./SourceMetadataMapConsumer');
  
  const fs = require('fs');
  const invariant = require('invariant');
  const nullthrows = require('nullthrows');
  const path = require('path');
  
  const {ChromeHeapSnapshotProcessor} = require('./ChromeHeapSnapshot');
  
  import type {ChromeHeapSnapshot} from './ChromeHeapSnapshot';
  import type {MixedSourceMap, HermesFunctionOffsets} from 'metro-source-map';
  // flowlint-next-line untyped-type-import:off
  import {typeof SourceMapConsumer} from 'source-map';
  
  type SingleMapModuleIds = {
    segmentId: number,
    localId: ?number,
    ...
  };
  
  type ContextOptionsInput = {
    +nameSource?: 'function_names' | 'identifier_names',
    +inputLineStart?: number,
    +inputColumnStart?: number,
    +outputLineStart?: number,
    +outputColumnStart?: number,
    ...
  };
  
  type SizeAttributionMap = {
    location: {
      file: ?string,
      filename?: string,
      bytecodeSize?: number,
      virtualOffset?: number,
      line: ?number,
      column: ?number,
    },
    ...
  };
  
  type ChromeTraceEntry = {
    column: number,
    funcColumn: number,
    funcLine: number,
    funcVirtAddr: number,
    line: number,
    name: string,
    offset: number,
  };
  
  type ChromeTrace = {
    stackFrames: {[string]: ChromeTraceEntry},
  };
  
  type HermesMinidumpCrashInfo = {
    +callstack: $ReadOnlyArray<HermesMinidumpStackFrame | NativeCodeStackFrame>,
    ...
  };
  
  type HermesMinidumpStackFrame = $ReadOnly<{|
    ByteCodeOffset: number,
    FunctionID: number,
    // NOTE: CJSModuleOffset has been renamed to SegmentID. Support both formats for now.
    CJSModuleOffset?: number,
    SegmentID?: number,
    SourceURL: string,
    StackFrameRegOffs: string,
    SourceLocation?: string,
  |}>;
  
  type NativeCodeStackFrame = $ReadOnly<{|
    NativeCode: true,
    StackFrameRegOffs: string,
  |}>;
  
  type SymbolicatedStackTrace = $ReadOnlyArray<
    SymbolicatedStackFrame | NativeCodeStackFrame,
  >;
  
  type SymbolicatedStackFrame = $ReadOnly<{|
    line: ?number,
    column: ?number,
    source: ?string,
    functionName: ?string,
    name: ?string,
  |}>;
  
  const UNKNOWN_MODULE_IDS: SingleMapModuleIds = {
    segmentId: 0,
    localId: undefined,
  };
  
  class SymbolicationContext<ModuleIdsT> {
    +options: {
      +nameSource: 'function_names' | 'identifier_names',
      +inputLineStart: number,
      +inputColumnStart: number,
      +outputLineStart: number,
      +outputColumnStart: number,
      ...
    };
  
    constructor(options: ContextOptionsInput) {
      this.options = {
        inputLineStart: 1,
        inputColumnStart: 0,
        outputLineStart: 1,
        outputColumnStart: 0,
        nameSource: 'function_names',
      };
      if (options) {
        for (const option of [
          'inputLineStart',
          'inputColumnStart',
          'outputLineStart',
          'outputColumnStart',
        ]) {
          if (options[option] != null) {
            this.options[option] = options[option];
          }
        }
        if (options.nameSource != null) {
          this.options.nameSource = options.nameSource;
        }
      }
    }
  
    // parse stack trace with String.replace
    // replace the matched part of stack trace to symbolicated result
    // sample stack trace:
    //  IOS: foo@4:18131, Android: bar:4:18063
    // sample stack trace with module id:
    //  IOS: foo@123.js:4:18131, Android: bar:123.js:4:18063
    // sample stack trace without function name:
    //  123.js:4:18131
    // sample result:
    //  IOS: foo.js:57:foo, Android: bar.js:75:bar
    symbolicate(stackTrace: string): string {
      return stackTrace.replace(
        /(?:([^@: \n(]+)(@|:))?(?:(?:([^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g,
        (match, func, delimiter, fileName, line, column) => {
          if (delimiter === ':' && func && !fileName) {
            fileName = func;
            func = null;
          }
          const original = this.getOriginalPositionFor(
            line,
            column,
            this.parseFileName(fileName || ''),
          );
          return (
            (original.source ?? 'null') +
            ':' +
            (original.line ?? 'null') +
            ':' +
            (original.name ?? 'null')
          );
        },
      );
    }
  
    // Taking in a map like
    // trampoline offset (optional js function name)
    // JS_0158_xxxxxxxxxxxxxxxxxxxxxx fe 91081
    // JS_0159_xxxxxxxxxxxxxxxxxxxxxx Ft 68651
    // JS_0160_xxxxxxxxxxxxxxxxxxxxxx value 50700
    // JS_0161_xxxxxxxxxxxxxxxxxxxxxx setGapAtCursor 0
    // JS_0162_xxxxxxxxxxxxxxxxxxxxxx (unknown) 50818
    // JS_0163_xxxxxxxxxxxxxxxxxxxxxx value 108267
  
    symbolicateProfilerMap(mapFile: string): string {
      return fs
        .readFileSync(mapFile, 'utf8')
        .split('\n')
        .slice(0, -1)
        .map(line => {
          const line_list = line.split(' ');
          const trampoline = line_list[0];
          const js_name = line_list[1];
          const offset = parseInt(line_list[2], 10);
  
          if (!offset) {
            return trampoline + ' ' + trampoline;
          }
  
          const original = this.getOriginalPositionFor(
            this.options.inputLineStart,
            offset,
          );
  
          return (
            trampoline +
            ' ' +
            (original.name || js_name) +
            '::' +
            [original.source, original.line, original.column].join(':')
          );
        })
        .join('\n');
    }
  
    symbolicateAttribution(obj: SizeAttributionMap): void {
      const loc = obj.location;
      const line = loc.line != null ? loc.line : this.options.inputLineStart;
      let column = Number(loc.column != null ? loc.column : loc.virtualOffset);
      const file = loc.filename ? this.parseFileName(loc.filename) : null;
      let original = this.getOriginalPositionFor(line, column, file);
  
      const isBytecodeRange =
        loc.bytecodeSize != null &&
        loc.virtualOffset != null &&
        loc.column == null;
      const virtualOffset = Number(loc.virtualOffset);
      const bytecodeSize = Number(loc.bytecodeSize);
  
      // Functions compiled from Metro-bundled modules will often have a little bit
      // of unmapped wrapper code right at the beginning - which is where we query.
      // Let's attribute them to where the inner module code originates instead.
      // This loop is O(n*log(n)) in the size of the function, but we will generally
      // either:
      // 1. Find a non-null mapping within one or two iterations; or
      // 2. Reach the end of the function without encountering mappings - this might
      //    happen for function bodies that never throw (generally very short).
      while (
        isBytecodeRange &&
        original.source == null &&
        ++column < virtualOffset + bytecodeSize
      ) {
        original = this.getOriginalPositionFor(line, column, file);
      }
  
      obj.location = {
        file: original.source,
        line: original.line,
        column: original.column,
      };
    }
  
    // Symbolicate chrome trace "stackFrames" section.
    // Each frame in it has three fields: name, funcVirtAddr(optional), offset(optional).
    // funcVirtAddr and offset are only available if trace is generated from
    // hbc bundle without debug info.
    symbolicateChromeTrace(
      traceFile: string,
      {
        stdout,
        stderr,
      }: {
        stdout: stream$Writable,
        stderr: stream$Writable,
        ...
      },
    ): void {
      const content: ChromeTrace = JSON.parse(fs.readFileSync(traceFile, 'utf8'));
      if (content.stackFrames == null) {
        throw new Error('Unable to locate `stackFrames` section in trace.');
      }
      const keys = Object.keys(content.stackFrames);
      stdout.write('Processing ' + keys.length + ' frames\n');
      keys.forEach(key => {
        const entry = content.stackFrames[key];
        let line;
        let column;
  
        // Function entrypoint line/column; used for symbolicating function name
        // with legacy source maps (or when --no-function-names is set).
        let funcLine;
        let funcColumn;
  
        if (entry.funcVirtAddr != null && entry.offset != null) {
          // Without debug information.
          const funcVirtAddr = parseInt(entry.funcVirtAddr, 10);
          const offsetInFunction = parseInt(entry.offset, 10);
          // Main bundle always use hard-coded line value 1.
          // TODO: support multiple bundle/module.
          line = this.options.inputLineStart;
          column = funcVirtAddr + offsetInFunction;
          funcLine = this.options.inputLineStart;
          funcColumn = funcVirtAddr;
        } else if (entry.line != null && entry.column != null) {
          // For hbc bundle with debug info, name field may already have source
          // information for the bundle; we still can use the Metro
          // source map to symbolicate the bundle frame addresses further to its
          // original source code.
          line = entry.line;
          column = entry.column;
  
          funcLine = entry.funcLine;
          funcColumn = entry.funcColumn;
        } else {
          // Native frames.
          return;
        }
  
        // Symbolicate original file/line/column.
        const addressOriginal = this.getOriginalPositionDetailsFor(line, column);
  
        let frameName;
        if (addressOriginal.functionName) {
          frameName = addressOriginal.functionName;
        } else {
          frameName = entry.name;
          // Symbolicate function name.
          if (funcLine != null && funcColumn != null) {
            const funcOriginal = this.getOriginalPositionFor(
              funcLine,
              funcColumn,
            );
            if (funcOriginal.name != null) {
              frameName = funcOriginal.name;
            }
          } else {
            // No function line/column info.
            (stderr || stdout).write(
              'Warning: no function prolog line/column info; name may be wrong\n',
            );
          }
        }
  
        // Output format is: funcName(file:line:column)
        entry.name = [
          frameName,
          '(',
          [
            addressOriginal.source ?? 'null',
            addressOriginal.line ?? 'null',
            addressOriginal.column ?? 'null',
          ].join(':'),
          ')',
        ].join('');
      });
      stdout.write('Writing to ' + traceFile + '\n');
      fs.writeFileSync(traceFile, JSON.stringify(content));
    }
  
    /*
     * A helper function to return a mapping {line, column} object for a given input
     * line and column, and optionally a module ID.
     */
    getOriginalPositionFor(
      lineNumber: ?number,
      columnNumber: ?number,
      moduleIds: ?ModuleIdsT,
    ): {|
      line: ?number,
      column: ?number,
      source: ?string,
      name: ?string,
    |} {
      const position = this.getOriginalPositionDetailsFor(
        lineNumber,
        columnNumber,
        moduleIds,
      );
      return {
        line: position.line,
        column: position.column,
        source: position.source,
        name: position.functionName ? position.functionName : position.name,
      };
    }
  
    /*
     * Symbolicates the JavaScript stack trace extracted from the minidump
     * produced by hermes
     */
    symbolicateHermesMinidumpTrace(
      crashInfo: HermesMinidumpCrashInfo,
    ): SymbolicatedStackTrace {
      throw new Error('Not implemented');
    }
  
    /**
     * Symbolicates heap alloction stacks in a Chrome-formatted heap
     * snapshot/timeline.
     * Line and column offsets in options (both input and output) are _ignored_,
     * because this format has a well-defined convention (1-based lines and
     * columns).
     */
    symbolicateHeapSnapshot(
      snapshotContents: string | ChromeHeapSnapshot,
    ): ChromeHeapSnapshot {
      const snapshotData: ChromeHeapSnapshot =
        typeof snapshotContents === 'string'
          ? JSON.parse(snapshotContents)
          : snapshotContents;
      const processor = new ChromeHeapSnapshotProcessor(snapshotData);
      for (const frame of processor.traceFunctionInfos()) {
        const moduleIds = this.parseFileName(frame.getString('script_name'));
        const generatedLine = frame.getNumber('line');
        const generatedColumn = frame.getNumber('column');
        if (generatedLine === 0 && generatedColumn === 0) {
          continue;
        }
        const {
          line: originalLine,
          column: originalColumn,
          source: originalSource,
          functionName: originalFunctionName,
        } = this.getOriginalPositionDetailsFor(
          frame.getNumber('line') - 1 + this.options.inputLineStart,
          frame.getNumber('column') - 1 + this.options.inputColumnStart,
          moduleIds,
        );
        if (originalSource != null) {
          frame.setString('script_name', originalSource);
          if (originalLine != null) {
            frame.setNumber(
              'line',
              originalLine - this.options.outputLineStart + 1,
            );
          } else {
            frame.setNumber('line', 0);
          }
          if (originalColumn != null) {
            frame.setNumber(
              'column',
              originalColumn - this.options.outputColumnStart + 1,
            );
          } else {
            frame.setNumber('column', 0);
          }
        }
        frame.setString('name', originalFunctionName ?? frame.getString('name'));
      }
      return snapshotData;
    }
  
    /*
     * An internal helper function similar to getOriginalPositionFor. This one
     * returns both `name` and `functionName` fields so callers can distinguish the
     * source of the name.
     */
    getOriginalPositionDetailsFor(
      lineNumber: ?number,
      columnNumber: ?number,
      moduleIds: ?ModuleIdsT,
    ): SymbolicatedStackFrame {
      throw new Error('Not implemented');
    }
  
    parseFileName(str: string): ModuleIdsT {
      throw new Error('Not implemented');
    }
  }
  
  class SingleMapSymbolicationContext extends SymbolicationContext<SingleMapModuleIds> {
    +_segments: {
      +[id: string]: {|
        // $FlowFixMe[value-as-type]
        +consumer: SourceMapConsumer,
        +moduleOffsets: $ReadOnlyArray<number>,
        +sourceFunctionsConsumer: ?SourceMetadataMapConsumer,
        +hermesOffsets: ?HermesFunctionOffsets,
      |},
      ...,
    };
    +_hasLegacySegments: boolean;
    // $FlowFixMe[value-as-type]
    +_SourceMapConsumer: SourceMapConsumer;
  
    constructor(
      // $FlowFixMe[value-as-type]
      SourceMapConsumer: SourceMapConsumer,
      sourceMapContent: string | MixedSourceMap,
      options: ContextOptionsInput = {},
    ) {
      super(options);
      this._SourceMapConsumer = SourceMapConsumer;
      const sourceMapJson: MixedSourceMap =
        typeof sourceMapContent === 'string'
          ? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, ''))
          : sourceMapContent;
      const segments = {
        '0': this._initSegment(sourceMapJson),
      };
      if (sourceMapJson.x_facebook_segments) {
        for (const key of Object.keys(sourceMapJson.x_facebook_segments)) {
          const map = sourceMapJson.x_facebook_segments[key];
          segments[key] = this._initSegment(map);
        }
      }
      this._hasLegacySegments = sourceMapJson.x_facebook_segments != null;
      this._segments = segments;
    }
  
    _initSegment(map) {
      const useFunctionNames = this.options.nameSource === 'function_names';
      const {_SourceMapConsumer: SourceMapConsumer} = this;
      return {
        get consumer() {
          Object.defineProperty(this, 'consumer', {
            value: new SourceMapConsumer(map),
          });
          return this.consumer;
        },
        moduleOffsets: map.x_facebook_offsets || [],
        get sourceFunctionsConsumer() {
          Object.defineProperty(this, 'sourceFunctionsConsumer', {
            value: useFunctionNames ? new SourceMetadataMapConsumer(map) : null,
          });
          return this.sourceFunctionsConsumer;
        },
        hermesOffsets: map.x_hermes_function_offsets,
      };
    }
  
    symbolicateHermesMinidumpTrace(
      crashInfo: HermesMinidumpCrashInfo,
    ): SymbolicatedStackTrace {
      const symbolicatedTrace = [];
      const {callstack} = crashInfo;
      if (callstack != null) {
        for (const stackItem of callstack) {
          if (stackItem.NativeCode) {
            symbolicatedTrace.push(stackItem);
          } else {
            const {
              CJSModuleOffset,
              SegmentID,
              SourceURL,
              FunctionID,
              ByteCodeOffset: localOffset,
            } = stackItem;
            const cjsModuleOffsetOrSegmentID = nullthrows(
              CJSModuleOffset ?? SegmentID,
              'Either CJSModuleOffset or SegmentID must be specified in the Hermes stack frame',
            );
            const moduleInformation = this._hasLegacySegments
              ? this.parseFileName(SourceURL)
              : UNKNOWN_MODULE_IDS;
            const generatedLine =
              cjsModuleOffsetOrSegmentID + this.options.inputLineStart;
            const segment = this._segments[
              moduleInformation.segmentId.toString()
            ];
            const hermesOffsets = segment?.hermesOffsets;
            if (!hermesOffsets) {
              symbolicatedTrace.push({
                line: null,
                column: null,
                source: null,
                functionName: null,
                name: null,
              });
            } else {
              const segmentOffsets =
                hermesOffsets[Number(cjsModuleOffsetOrSegmentID)];
              const generatedColumn =
                segmentOffsets[FunctionID] +
                localOffset +
                this.options.inputColumnStart;
              const originalPosition = this.getOriginalPositionDetailsFor(
                generatedLine,
                generatedColumn,
                moduleInformation,
              );
              symbolicatedTrace.push(originalPosition);
            }
          }
        }
      }
      return symbolicatedTrace;
    }
  
    /*
     * An internal helper function similar to getOriginalPositionFor. This one
     * returns both `name` and `functionName` fields so callers can distinguish the
     * source of the name.
     */
    getOriginalPositionDetailsFor(
      lineNumber: ?number,
      columnNumber: ?number,
      moduleIds: ?SingleMapModuleIds,
    ): SymbolicatedStackFrame {
      // Adjust arguments to source-map's input coordinates
      lineNumber =
        lineNumber != null
          ? lineNumber - this.options.inputLineStart + 1
          : lineNumber;
      columnNumber =
        columnNumber != null
          ? columnNumber - this.options.inputColumnStart + 0
          : columnNumber;
  
      if (!moduleIds) {
        moduleIds = UNKNOWN_MODULE_IDS;
      }
  
      let moduleLineOffset = 0;
      const metadata = this._segments[moduleIds.segmentId + ''];
      const {localId} = moduleIds;
      if (localId != null) {
        const {moduleOffsets} = metadata;
        if (!moduleOffsets) {
          throw new Error(
            'Module ID given for a source map that does not have ' +
              'an x_facebook_offsets field',
          );
        }
        if (moduleOffsets[localId] == null) {
          throw new Error('Unknown module ID: ' + localId);
        }
        moduleLineOffset = moduleOffsets[localId];
      }
      const original = metadata.consumer.originalPositionFor({
        line: Number(lineNumber) + moduleLineOffset,
        column: Number(columnNumber),
      });
      if (metadata.sourceFunctionsConsumer) {
        original.functionName =
          metadata.sourceFunctionsConsumer.functionNameFor(original) || null;
      } else {
        original.functionName = null;
      }
      return {
        ...original,
        line:
          original.line != null
            ? original.line - 1 + this.options.outputLineStart
            : original.line,
        column:
          original.column != null
            ? original.column - 0 + this.options.outputColumnStart
            : original.column,
      };
    }
  
    parseFileName(str: string): SingleMapModuleIds {
      return parseSingleMapFileName(str);
    }
  }
  
  class DirectorySymbolicationContext extends SymbolicationContext<string> {
    +_fileMaps: Map<string, SingleMapSymbolicationContext>;
    +_rootDir: string;
    // $FlowFixMe[value-as-type]
    +_SourceMapConsumer: SourceMapConsumer;
  
    constructor(
      // $FlowFixMe[value-as-type]
      SourceMapConsumer: SourceMapConsumer,
      rootDir: string,
      options: ContextOptionsInput = {},
    ) {
      super(options);
      this._fileMaps = new Map();
      this._rootDir = rootDir;
      this._SourceMapConsumer = SourceMapConsumer;
    }
  
    _loadMap(mapFilename: string): SingleMapSymbolicationContext {
      invariant(
        fs.existsSync(mapFilename),
        `Could not read source map from '${mapFilename}'`,
      );
      let fileMap = this._fileMaps.get(mapFilename);
      if (fileMap == null) {
        fileMap = new SingleMapSymbolicationContext(
          this._SourceMapConsumer,
          fs.readFileSync(mapFilename, 'utf8'),
          this.options,
        );
        this._fileMaps.set(mapFilename, fileMap);
      }
      return fileMap;
    }
  
    /*
     * An internal helper function similar to getOriginalPositionFor. This one
     * returns both `name` and `functionName` fields so callers can distinguish the
     * source of the name.
     */
    getOriginalPositionDetailsFor(
      lineNumber: ?number,
      columnNumber: ?number,
      filename: ?string,
    ): SymbolicatedStackFrame {
      invariant(
        filename != null,
        'filename is required for DirectorySymbolicationContext',
      );
      let mapFilename;
      const relativeFilename = path.relative(
        this._rootDir,
        path.resolve(this._rootDir, filename),
      );
      // Lock down access to files outside the root dir.
      if (!relativeFilename.startsWith('..')) {
        mapFilename = path.join(this._rootDir, relativeFilename + '.map');
      }
      if (mapFilename == null || !fs.existsSync(mapFilename)) {
        // Adjust arguments to the output coordinates
        lineNumber =
          lineNumber != null
            ? lineNumber -
              this.options.inputLineStart +
              this.options.outputLineStart
            : lineNumber;
        columnNumber =
          columnNumber != null
            ? columnNumber -
              this.options.inputColumnStart +
              this.options.outputColumnStart
            : columnNumber;
  
        return {
          line: lineNumber,
          column: columnNumber,
          source: filename,
          name: null,
          functionName: null,
        };
      }
      return this._loadMap(mapFilename).getOriginalPositionDetailsFor(
        lineNumber,
        columnNumber,
      );
    }
  
    parseFileName(str: string): string {
      return str;
    }
  }
  
  /*
   * If the file name of a stack frame is numeric (+ ".js"), we assume it's a
   * lazily injected module coming from a "random access bundle". We are using
   * special source maps for these bundles, so that we can symbolicate stack
   * traces for multiple injected files with a single source map.
   *
   * There is also a convention for callsites that are in split segments of a
   * bundle, named either `seg-3.js` for segment #3 for example, or `seg-3_5.js`
   * for module #5 of segment #3 of a segmented RAM bundle.
   */
  function parseSingleMapFileName(str: string): SingleMapModuleIds {
    const modMatch = str.match(/^(\d+).js$/);
    if (modMatch != null) {
      return {segmentId: 0, localId: Number(modMatch[1])};
    }
    const segMatch = str.match(/^seg-(\d+)(?:_(\d+))?.js$/);
    if (segMatch != null) {
      return {
        segmentId: Number(segMatch[1]),
        localId: segMatch[2] ? Number(segMatch[2]) : null,
      };
    }
    return UNKNOWN_MODULE_IDS;
  }
  
  function createContext(
    // $FlowFixMe[value-as-type]
    SourceMapConsumer: SourceMapConsumer,
    sourceMapContent: string | MixedSourceMap,
    options: ContextOptionsInput = {},
  ): SingleMapSymbolicationContext {
    return new SingleMapSymbolicationContext(
      SourceMapConsumer,
      sourceMapContent,
      options,
    );
  }
  
  function unstable_createDirectoryContext(
    // $FlowFixMe[value-as-type]
    SourceMapConsumer: SourceMapConsumer,
    rootDir: string,
    options: ContextOptionsInput = {},
  ): DirectorySymbolicationContext {
    return new DirectorySymbolicationContext(SourceMapConsumer, rootDir, options);
  }
  
  function getOriginalPositionFor<ModuleIdsT>(
    lineNumber: ?number,
    columnNumber: ?number,
    moduleIds: ?ModuleIdsT,
    context: SymbolicationContext<ModuleIdsT>,
  ): {|
    line: ?number,
    column: ?number,
    source: ?string,
    name: ?string,
  |} {
    return context.getOriginalPositionFor(lineNumber, columnNumber, moduleIds);
  }
  
  function symbolicate<ModuleIdsT>(
    stackTrace: string,
    context: SymbolicationContext<ModuleIdsT>,
  ): string {
    return context.symbolicate(stackTrace);
  }
  
  function symbolicateProfilerMap<ModuleIdsT>(
    mapFile: string,
    context: SymbolicationContext<ModuleIdsT>,
  ): string {
    return context.symbolicateProfilerMap(mapFile);
  }
  
  function symbolicateAttribution<ModuleIdsT>(
    obj: SizeAttributionMap,
    context: SymbolicationContext<ModuleIdsT>,
  ): void {
    context.symbolicateAttribution(obj);
  }
  
  function symbolicateChromeTrace<ModuleIdsT>(
    traceFile: string,
    {
      stdout,
      stderr,
    }: {
      stdout: stream$Writable,
      stderr: stream$Writable,
      ...
    },
    context: SymbolicationContext<ModuleIdsT>,
  ): void {
    return context.symbolicateChromeTrace(traceFile, {stdout, stderr});
  }
  
  module.exports = {
    createContext,
    unstable_createDirectoryContext,
    getOriginalPositionFor,
    parseFileName: parseSingleMapFileName,
    symbolicate,
    symbolicateProfilerMap,
    symbolicateAttribution,
    symbolicateChromeTrace,
    SourceMetadataMapConsumer,
  };
  