metro-symbolicate
Advanced tools
Comparing version 0.63.0 to 0.64.0
{ | ||
"name": "metro-symbolicate", | ||
"version": "0.63.0", | ||
"version": "0.64.0", | ||
"description": "🚇 A tool to find the source location from JS bundles and stack traces.", | ||
"license": "MIT", | ||
"main": "./src/symbolicate.js", | ||
"bin": "./src/symbolicate.js", | ||
"main": "./src/index.js", | ||
"bin": "./src/index.js", | ||
"repository": { | ||
@@ -24,3 +24,4 @@ "type": "git", | ||
"invariant": "^2.2.4", | ||
"metro-source-map": "0.63.0", | ||
"metro-source-map": "0.64.0", | ||
"nullthrows": "^1.1.1", | ||
"source-map": "^0.5.6", | ||
@@ -27,0 +28,0 @@ "through2": "^2.0.1", |
@@ -7,37 +7,102 @@ /** | ||
* | ||
* @flow | ||
* | ||
* @format | ||
*/ | ||
"use strict"; | ||
'use strict'; | ||
function _slicedToArray(arr, i) { | ||
return ( | ||
_arrayWithHoles(arr) || | ||
_iterableToArrayLimit(arr, i) || | ||
_unsupportedIterableToArray(arr, i) || | ||
_nonIterableRest() | ||
); | ||
} | ||
const vlq = require('vlq'); | ||
function _nonIterableRest() { | ||
throw new TypeError( | ||
"Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | ||
); | ||
} | ||
const {normalizeSourcePath} = require('metro-source-map'); | ||
function _iterableToArrayLimit(arr, i) { | ||
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | ||
return; | ||
var _arr = []; | ||
var _n = true; | ||
var _d = false; | ||
var _e = undefined; | ||
try { | ||
for ( | ||
var _i = arr[Symbol.iterator](), _s; | ||
!(_n = (_s = _i.next()).done); | ||
_n = true | ||
) { | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
} | ||
} catch (err) { | ||
_d = true; | ||
_e = err; | ||
} finally { | ||
try { | ||
if (!_n && _i["return"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
import type { | ||
MixedSourceMap, | ||
FBSourcesArray, | ||
FBSourceFunctionMap, | ||
FBSourceMetadata, | ||
BasicSourceMap, | ||
IndexMap, | ||
} from 'metro-source-map'; | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
function _toConsumableArray(arr) { | ||
return ( | ||
_arrayWithoutHoles(arr) || | ||
_iterableToArray(arr) || | ||
_unsupportedIterableToArray(arr) || | ||
_nonIterableSpread() | ||
); | ||
} | ||
function _nonIterableSpread() { | ||
throw new TypeError( | ||
"Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | ||
); | ||
} | ||
function _unsupportedIterableToArray(o, minLen) { | ||
if (!o) return; | ||
if (typeof o === "string") return _arrayLikeToArray(o, minLen); | ||
var n = Object.prototype.toString.call(o).slice(8, -1); | ||
if (n === "Object" && o.constructor) n = o.constructor.name; | ||
if (n === "Map" || n === "Set") return Array.from(o); | ||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | ||
return _arrayLikeToArray(o, minLen); | ||
} | ||
function _iterableToArray(iter) { | ||
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) | ||
return Array.from(iter); | ||
} | ||
function _arrayWithoutHoles(arr) { | ||
if (Array.isArray(arr)) return _arrayLikeToArray(arr); | ||
} | ||
function _arrayLikeToArray(arr, len) { | ||
if (len == null || len > arr.length) len = arr.length; | ||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} | ||
const vlq = require("vlq"); | ||
const _require = require("metro-source-map"), | ||
normalizeSourcePath = _require.normalizeSourcePath; | ||
const METADATA_FIELD_FUNCTIONS = 0; | ||
type Position = { | ||
+line: number, | ||
+column: number, | ||
... | ||
}; | ||
type FunctionMapping = { | ||
+line: number, | ||
+column: number, | ||
+name: string, | ||
... | ||
}; | ||
type SourceNameNormalizer = (string, {+sourceRoot?: ?string, ...}) => string; | ||
type MetadataMap = {[source: string]: ?FBSourceMetadata, ...}; | ||
/** | ||
@@ -58,6 +123,7 @@ * Consumes the `x_facebook_sources` metadata field from a source map and | ||
class SourceMetadataMapConsumer { | ||
constructor( | ||
map: MixedSourceMap, | ||
normalizeSourceFn: SourceNameNormalizer = normalizeSourcePath, | ||
) { | ||
constructor(map) { | ||
let normalizeSourceFn = | ||
arguments.length > 1 && arguments[1] !== undefined | ||
? arguments[1] | ||
: normalizeSourcePath; | ||
this._sourceMap = map; | ||
@@ -68,7 +134,2 @@ this._decodedFunctionMapCache = new Map(); | ||
_sourceMap: MixedSourceMap; | ||
_decodedFunctionMapCache: Map<string, ?$ReadOnlyArray<FunctionMapping>>; | ||
_normalizeSource: SourceNameNormalizer; | ||
_metadataBySource: ?MetadataMap; | ||
/** | ||
@@ -82,11 +143,16 @@ * Retrieves a human-readable name for the function enclosing a particular | ||
*/ | ||
functionNameFor({ | ||
line, | ||
column, | ||
source, | ||
}: Position & {+source: ?string, ...}): ?string { | ||
functionNameFor(_ref) { | ||
let line = _ref.line, | ||
column = _ref.column, | ||
source = _ref.source; | ||
if (source && line != null && column != null) { | ||
const mappings = this._getFunctionMappings(source); | ||
if (mappings) { | ||
const mapping = findEnclosingMapping(mappings, {line, column}); | ||
const mapping = findEnclosingMapping(mappings, { | ||
line, | ||
column | ||
}); | ||
if (mapping) { | ||
@@ -97,5 +163,5 @@ return mapping.name; | ||
} | ||
return null; | ||
} | ||
/** | ||
@@ -108,18 +174,22 @@ * Returns this map's source metadata as a new array with the same order as | ||
*/ | ||
toArray(sources: $ReadOnlyArray<string>): FBSourcesArray { | ||
toArray(sources) { | ||
const metadataBySource = this._getMetadataBySource(); | ||
const encoded = []; | ||
for (const source of sources) { | ||
encoded.push(metadataBySource[source] || null); | ||
} | ||
return encoded; | ||
} | ||
/** | ||
* Prepares and caches a lookup table of metadata by source name. | ||
*/ | ||
_getMetadataBySource(): MetadataMap { | ||
_getMetadataBySource() { | ||
if (!this._metadataBySource) { | ||
this._metadataBySource = this._getMetadataObjectsBySourceNames( | ||
this._sourceMap, | ||
this._sourceMap | ||
); | ||
@@ -130,3 +200,2 @@ } | ||
} | ||
/** | ||
@@ -136,8 +205,12 @@ * Decodes the function name mappings for the given source if needed, and | ||
*/ | ||
_getFunctionMappings(source: string): ?$ReadOnlyArray<FunctionMapping> { | ||
_getFunctionMappings(source) { | ||
if (this._decodedFunctionMapCache.has(source)) { | ||
return this._decodedFunctionMapCache.get(source); | ||
} | ||
let parsedFunctionMap = null; | ||
const metadataBySource = this._getMetadataBySource(); | ||
if (Object.prototype.hasOwnProperty.call(metadataBySource, source)) { | ||
@@ -147,6 +220,7 @@ const metadata = metadataBySource[source] || []; | ||
} | ||
this._decodedFunctionMapCache.set(source, parsedFunctionMap); | ||
return parsedFunctionMap; | ||
} | ||
/** | ||
@@ -161,19 +235,25 @@ * Collects source metadata from the given map using the current source name | ||
*/ | ||
_getMetadataObjectsBySourceNames(map: MixedSourceMap): MetadataMap { | ||
_getMetadataObjectsBySourceNames(map) { | ||
// eslint-disable-next-line lint/strictly-null | ||
if (map.mappings === undefined) { | ||
const indexMap: IndexMap = map; | ||
return Object.assign( | ||
{}, | ||
...indexMap.sections.map(section => | ||
this._getMetadataObjectsBySourceNames(section.map), | ||
), | ||
const indexMap = map; | ||
return Object.assign.apply( | ||
Object, | ||
[{}].concat( | ||
_toConsumableArray( | ||
indexMap.sections.map(section => | ||
this._getMetadataObjectsBySourceNames(section.map) | ||
) | ||
) | ||
) | ||
); | ||
} | ||
if ('x_facebook_sources' in map) { | ||
const basicMap: BasicSourceMap = map; | ||
if ("x_facebook_sources" in map) { | ||
const basicMap = map; | ||
return (basicMap.x_facebook_sources || []).reduce( | ||
(acc, metadata, index) => { | ||
let source = basicMap.sources[index]; | ||
if (source != null) { | ||
@@ -183,7 +263,9 @@ source = this._normalizeSource(source, basicMap); | ||
} | ||
return acc; | ||
}, | ||
{}, | ||
{} | ||
); | ||
} | ||
return {}; | ||
@@ -193,28 +275,37 @@ } | ||
function decodeFunctionMap( | ||
functionMap: ?FBSourceFunctionMap, | ||
): $ReadOnlyArray<FunctionMapping> { | ||
function decodeFunctionMap(functionMap) { | ||
if (!functionMap) { | ||
return []; | ||
} | ||
const parsed = []; | ||
let line = 1; | ||
let nameIndex = 0; | ||
for (const lineMappings of functionMap.mappings.split(';')) { | ||
for (const lineMappings of functionMap.mappings.split(";")) { | ||
let column = 0; | ||
for (const mapping of lineMappings.split(',')) { | ||
const [columnDelta, nameDelta, lineDelta = 0] = vlq.decode(mapping); | ||
for (const mapping of lineMappings.split(",")) { | ||
const _vlq$decode = vlq.decode(mapping), | ||
_vlq$decode2 = _slicedToArray(_vlq$decode, 3), | ||
columnDelta = _vlq$decode2[0], | ||
nameDelta = _vlq$decode2[1], | ||
_vlq$decode2$ = _vlq$decode2[2], | ||
lineDelta = _vlq$decode2$ === void 0 ? 0 : _vlq$decode2$; | ||
line += lineDelta; | ||
nameIndex += nameDelta; | ||
column += columnDelta; | ||
parsed.push({line, column, name: functionMap.names[nameIndex]}); | ||
parsed.push({ | ||
line, | ||
column, | ||
name: functionMap.names[nameIndex] | ||
}); | ||
} | ||
} | ||
return parsed; | ||
} | ||
function findEnclosingMapping( | ||
mappings: $ReadOnlyArray<FunctionMapping>, | ||
target: Position, | ||
): ?FunctionMapping { | ||
function findEnclosingMapping(mappings, target) { | ||
let first = 0; | ||
@@ -224,2 +315,3 @@ let it = 0; | ||
let step; | ||
while (count > 0) { | ||
@@ -229,2 +321,3 @@ it = first; | ||
it += step; | ||
if (comparePositions(target, mappings[it]) >= 0) { | ||
@@ -237,9 +330,11 @@ first = ++it; | ||
} | ||
return first ? mappings[first - 1] : null; | ||
} | ||
function comparePositions(a: Position, b: Position): number { | ||
function comparePositions(a, b) { | ||
if (a.line === b.line) { | ||
return a.column - b.column; | ||
} | ||
return a.line - b.line; | ||
@@ -246,0 +341,0 @@ } |
@@ -1,2 +0,1 @@ | ||
#!/usr/bin/env node | ||
/** | ||
@@ -8,6 +7,5 @@ * Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* @flow strict-local | ||
* | ||
* @format | ||
*/ | ||
// Symbolicates a JavaScript stack trace using a source map. | ||
@@ -20,184 +18,248 @@ // In our first form, we read a stack trace from stdin and symbolicate it via | ||
// optionally a column. | ||
"use strict"; // flowlint-next-line untyped-import:off | ||
'use strict'; | ||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { | ||
try { | ||
var info = gen[key](arg); | ||
var value = info.value; | ||
} catch (error) { | ||
reject(error); | ||
return; | ||
} | ||
if (info.done) { | ||
resolve(value); | ||
} else { | ||
Promise.resolve(value).then(_next, _throw); | ||
} | ||
} | ||
// flowlint-next-line untyped-import:off | ||
const SourceMapConsumer = require('source-map').SourceMapConsumer; | ||
const Symbolication = require('./Symbolication.js'); | ||
function _asyncToGenerator(fn) { | ||
return function() { | ||
var self = this, | ||
args = arguments; | ||
return new Promise(function(resolve, reject) { | ||
var gen = fn.apply(self, args); | ||
function _next(value) { | ||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); | ||
} | ||
function _throw(err) { | ||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); | ||
} | ||
_next(undefined); | ||
}); | ||
}; | ||
} | ||
const fs = require('fs'); | ||
// flowlint-next-line untyped-import:off | ||
const through2 = require('through2'); | ||
const SourceMapConsumer = require("source-map").SourceMapConsumer; | ||
async function main( | ||
argvInput: Array<string> = process.argv.slice(2), | ||
{ | ||
stdin, | ||
stderr, | ||
stdout, | ||
}: { | ||
stdin: stream$Readable | tty$ReadStream, | ||
stderr: stream$Writable, | ||
stdout: stream$Writable, | ||
... | ||
} = process, | ||
): Promise<number> { | ||
const argv = argvInput.slice(); | ||
function checkAndRemoveArg(arg, valuesPerArg = 0) { | ||
let values = null; | ||
for (let idx = argv.indexOf(arg); idx !== -1; idx = argv.indexOf(arg)) { | ||
argv.splice(idx, 1); | ||
values = values || []; | ||
values.push(argv.splice(idx, valuesPerArg)); | ||
} | ||
return values; | ||
} | ||
const Symbolication = require("./Symbolication.js"); | ||
function checkAndRemoveArgWithValue(arg) { | ||
const values = checkAndRemoveArg(arg, 1); | ||
return values ? values[0][0] : null; | ||
} | ||
try { | ||
const noFunctionNames = checkAndRemoveArg('--no-function-names'); | ||
const isHermesCrash = checkAndRemoveArg('--hermes-crash'); | ||
const inputLineStart = Number.parseInt( | ||
checkAndRemoveArgWithValue('--input-line-start') || '1', | ||
10, | ||
); | ||
const inputColumnStart = Number.parseInt( | ||
checkAndRemoveArgWithValue('--input-column-start') || '0', | ||
10, | ||
); | ||
const outputLineStart = Number.parseInt( | ||
checkAndRemoveArgWithValue('--output-line-start') || '1', | ||
10, | ||
); | ||
const outputColumnStart = Number.parseInt( | ||
checkAndRemoveArgWithValue('--output-column-start') || '0', | ||
10, | ||
); | ||
const fs = require("fs"); // flowlint-next-line untyped-import:off | ||
if (argv.length < 1 || argv.length > 4) { | ||
/* eslint no-path-concat: "off" */ | ||
const through2 = require("through2"); | ||
const usages = [ | ||
'Usage: ' + __filename + ' <source-map-file>', | ||
' ' + __filename + ' <source-map-file> <line> [column]', | ||
' ' + | ||
__filename + | ||
' <source-map-file> <moduleId>.js <line> [column]', | ||
' ' + __filename + ' <source-map-file> <mapfile>.profmap', | ||
' ' + | ||
__filename + | ||
' <source-map-file> --attribution < in.jsonl > out.jsonl', | ||
' ' + __filename + ' <source-map-file> <tracefile>.cpuprofile', | ||
' Optional flags:', | ||
' --no-function-names', | ||
' --hermes-crash', | ||
' --input-line-start <line> (default: 1)', | ||
' --input-column-start <column> (default: 0)', | ||
' --output-line-start <line> (default: 1)', | ||
' --output-column-start <column> (default: 0)', | ||
]; | ||
console.error(usages.join('\n')); | ||
return 1; | ||
function main() { | ||
return _main.apply(this, arguments); | ||
} | ||
function _main() { | ||
_main = _asyncToGenerator(function*() { | ||
let argvInput = | ||
arguments.length > 0 && arguments[0] !== undefined | ||
? arguments[0] | ||
: process.argv.slice(2); | ||
let _ref = | ||
arguments.length > 1 && arguments[1] !== undefined | ||
? arguments[1] | ||
: process, | ||
stdin = _ref.stdin, | ||
stderr = _ref.stderr, | ||
stdout = _ref.stdout; | ||
const argv = argvInput.slice(); | ||
function checkAndRemoveArg(arg) { | ||
let valuesPerArg = | ||
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
let values = null; | ||
for (let idx = argv.indexOf(arg); idx !== -1; idx = argv.indexOf(arg)) { | ||
argv.splice(idx, 1); | ||
values = values || []; | ||
values.push(argv.splice(idx, valuesPerArg)); | ||
} | ||
return values; | ||
} | ||
// Read the source map. | ||
const sourceMapFileName = argv.shift(); | ||
const options = { | ||
nameSource: noFunctionNames ? 'identifier_names' : 'function_names', | ||
inputLineStart, | ||
inputColumnStart, | ||
outputLineStart, | ||
outputColumnStart, | ||
}; | ||
let context; | ||
if (fs.lstatSync(sourceMapFileName).isDirectory()) { | ||
context = Symbolication.unstable_createDirectoryContext( | ||
SourceMapConsumer, | ||
sourceMapFileName, | ||
options, | ||
function checkAndRemoveArgWithValue(arg) { | ||
const values = checkAndRemoveArg(arg, 1); | ||
return values ? values[0][0] : null; | ||
} | ||
try { | ||
const noFunctionNames = checkAndRemoveArg("--no-function-names"); | ||
const isHermesCrash = checkAndRemoveArg("--hermes-crash"); | ||
const inputLineStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--input-line-start") || "1", | ||
10 | ||
); | ||
} else { | ||
const content = fs.readFileSync(sourceMapFileName, 'utf8'); | ||
context = Symbolication.createContext( | ||
SourceMapConsumer, | ||
content, | ||
options, | ||
const inputColumnStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--input-column-start") || "0", | ||
10 | ||
); | ||
} | ||
if (argv.length === 0) { | ||
const stackTrace = await readAll(stdin); | ||
if (isHermesCrash) { | ||
const stackTraceJSON = JSON.parse(stackTrace); | ||
const symbolicatedTrace = context.symbolicateHermesMinidumpTrace( | ||
stackTraceJSON, | ||
const outputLineStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--output-line-start") || "1", | ||
10 | ||
); | ||
const outputColumnStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--output-column-start") || "0", | ||
10 | ||
); | ||
if (argv.length < 1 || argv.length > 4) { | ||
/* eslint no-path-concat: "off" */ | ||
const usages = [ | ||
"Usage: " + __filename + " <source-map-file>", | ||
" " + __filename + " <source-map-file> <line> [column]", | ||
" " + | ||
__filename + | ||
" <source-map-file> <moduleId>.js <line> [column]", | ||
" " + __filename + " <source-map-file> <mapfile>.profmap", | ||
" " + | ||
__filename + | ||
" <source-map-file> --attribution < in.jsonl > out.jsonl", | ||
" " + __filename + " <source-map-file> <tracefile>.cpuprofile", | ||
" Optional flags:", | ||
" --no-function-names", | ||
" --hermes-crash", | ||
" --input-line-start <line> (default: 1)", | ||
" --input-column-start <column> (default: 0)", | ||
" --output-line-start <line> (default: 1)", | ||
" --output-column-start <column> (default: 0)" | ||
]; | ||
console.error(usages.join("\n")); | ||
return 1; | ||
} // Read the source map. | ||
const sourceMapFileName = argv.shift(); | ||
const options = { | ||
nameSource: noFunctionNames ? "identifier_names" : "function_names", | ||
inputLineStart, | ||
inputColumnStart, | ||
outputLineStart, | ||
outputColumnStart | ||
}; | ||
let context; | ||
if (fs.lstatSync(sourceMapFileName).isDirectory()) { | ||
context = Symbolication.unstable_createDirectoryContext( | ||
SourceMapConsumer, | ||
sourceMapFileName, | ||
options | ||
); | ||
stdout.write(JSON.stringify(symbolicatedTrace)); | ||
} else { | ||
stdout.write(context.symbolicate(stackTrace)); | ||
const content = fs.readFileSync(sourceMapFileName, "utf8"); | ||
context = Symbolication.createContext( | ||
SourceMapConsumer, | ||
content, | ||
options | ||
); | ||
} | ||
} else if (argv[0].endsWith('.profmap')) { | ||
stdout.write(context.symbolicateProfilerMap(argv[0])); | ||
} else if (argv[0] === '--attribution') { | ||
let buffer = ''; | ||
await waitForStream( | ||
stdin | ||
.pipe( | ||
through2(function(data, enc, callback) { | ||
// Take arbitrary strings, output single lines | ||
buffer += data; | ||
const lines = buffer.split('\n'); | ||
for (let i = 0, e = lines.length - 1; i < e; i++) { | ||
this.push(lines[i]); | ||
} | ||
buffer = lines[lines.length - 1]; | ||
callback(); | ||
}), | ||
) | ||
.pipe( | ||
through2.obj(function(data, enc, callback) { | ||
// This is JSONL, so each line is a separate JSON object | ||
const obj = JSON.parse(data); | ||
context.symbolicateAttribution(obj); | ||
this.push(JSON.stringify(obj) + '\n'); | ||
callback(); | ||
}), | ||
) | ||
.pipe(stdout), | ||
); | ||
} else if (argv[0].endsWith('.cpuprofile')) { | ||
// NOTE: synchronous | ||
context.symbolicateChromeTrace(argv[0], {stdout, stderr}); | ||
} else { | ||
// read-from-argv form. | ||
let moduleIds; | ||
if (argv[0].endsWith('.js')) { | ||
moduleIds = context.parseFileName(argv[0]); | ||
argv.shift(); | ||
if (argv.length === 0) { | ||
const stackTrace = yield readAll(stdin); | ||
if (isHermesCrash) { | ||
const stackTraceJSON = JSON.parse(stackTrace); | ||
const symbolicatedTrace = context.symbolicateHermesMinidumpTrace( | ||
stackTraceJSON | ||
); | ||
stdout.write(JSON.stringify(symbolicatedTrace)); | ||
} else { | ||
stdout.write(context.symbolicate(stackTrace)); | ||
} | ||
} else if (argv[0].endsWith(".profmap")) { | ||
stdout.write(context.symbolicateProfilerMap(argv[0])); | ||
} else if (argv[0] === "--attribution") { | ||
let buffer = ""; | ||
yield waitForStream( | ||
stdin | ||
.pipe( | ||
through2(function(data, enc, callback) { | ||
// Take arbitrary strings, output single lines | ||
buffer += data; | ||
const lines = buffer.split("\n"); | ||
for (let i = 0, e = lines.length - 1; i < e; i++) { | ||
this.push(lines[i]); | ||
} | ||
buffer = lines[lines.length - 1]; | ||
callback(); | ||
}) | ||
) | ||
.pipe( | ||
through2.obj(function(data, enc, callback) { | ||
// This is JSONL, so each line is a separate JSON object | ||
const obj = JSON.parse(data); | ||
context.symbolicateAttribution(obj); | ||
this.push(JSON.stringify(obj) + "\n"); | ||
callback(); | ||
}) | ||
) | ||
.pipe(stdout) | ||
); | ||
} else if (argv[0].endsWith(".cpuprofile")) { | ||
// NOTE: synchronous | ||
context.symbolicateChromeTrace(argv[0], { | ||
stdout, | ||
stderr | ||
}); | ||
} else { | ||
moduleIds = null; | ||
var _original$source, _original$line, _original$name; | ||
// read-from-argv form. | ||
let moduleIds; | ||
if (argv[0].endsWith(".js")) { | ||
moduleIds = context.parseFileName(argv[0]); | ||
argv.shift(); | ||
} else { | ||
moduleIds = null; | ||
} | ||
const lineNumber = argv.shift(); | ||
const columnNumber = argv.shift() || 0; | ||
const original = context.getOriginalPositionFor( | ||
+lineNumber, | ||
+columnNumber, // $FlowFixMe context is a union here and so this parameter is a union | ||
moduleIds | ||
); | ||
stdout.write( | ||
[ | ||
(_original$source = original.source) !== null && | ||
_original$source !== void 0 | ||
? _original$source | ||
: "null", | ||
(_original$line = original.line) !== null && | ||
_original$line !== void 0 | ||
? _original$line | ||
: "null", | ||
(_original$name = original.name) !== null && | ||
_original$name !== void 0 | ||
? _original$name | ||
: "null" | ||
].join(":") + "\n" | ||
); | ||
} | ||
const lineNumber = argv.shift(); | ||
const columnNumber = argv.shift() || 0; | ||
const original = context.getOriginalPositionFor( | ||
+lineNumber, | ||
+columnNumber, | ||
// $FlowFixMe context is a union here and so this parameter is a union | ||
moduleIds, | ||
); | ||
stdout.write( | ||
[ | ||
original.source ?? 'null', | ||
original.line ?? 'null', | ||
original.name ?? 'null', | ||
].join(':') + '\n', | ||
); | ||
} catch (error) { | ||
stderr.write(error + "\n"); | ||
return 1; | ||
} | ||
} catch (error) { | ||
stderr.write(error + '\n'); | ||
return 1; | ||
} | ||
return 0; | ||
return 0; | ||
}); | ||
return _main.apply(this, arguments); | ||
} | ||
@@ -207,3 +269,4 @@ | ||
return new Promise(resolve => { | ||
let data = ''; | ||
let data = ""; | ||
if (stream.isTTY === true) { | ||
@@ -214,6 +277,6 @@ resolve(data); | ||
stream.setEncoding('utf8'); | ||
stream.on('readable', () => { | ||
let chunk; | ||
// flowlint-next-line sketchy-null-string:off | ||
stream.setEncoding("utf8"); | ||
stream.on("readable", () => { | ||
let chunk; // flowlint-next-line sketchy-null-string:off | ||
while ((chunk = stream.read())) { | ||
@@ -223,3 +286,3 @@ data += chunk.toString(); | ||
}); | ||
stream.on('end', () => { | ||
stream.on("end", () => { | ||
resolve(data); | ||
@@ -232,10 +295,6 @@ }); | ||
return new Promise(resolve => { | ||
stream.on('finish', resolve); | ||
stream.on("finish", resolve); | ||
}); | ||
} | ||
if (require.main === module) { | ||
main().then(code => process.exit(code)); | ||
} | ||
module.exports = main; |
@@ -7,106 +7,73 @@ /** | ||
* | ||
* @flow | ||
* | ||
* @format | ||
*/ | ||
"use strict"; | ||
'use strict'; | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
if (enumerableOnly) | ||
symbols = symbols.filter(function(sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
}); | ||
keys.push.apply(keys, symbols); | ||
} | ||
return keys; | ||
} | ||
const SourceMetadataMapConsumer = require('./SourceMetadataMapConsumer'); | ||
function _objectSpread(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
if (i % 2) { | ||
ownKeys(Object(source), true).forEach(function(key) { | ||
_defineProperty(target, key, source[key]); | ||
}); | ||
} else if (Object.getOwnPropertyDescriptors) { | ||
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); | ||
} else { | ||
ownKeys(Object(source)).forEach(function(key) { | ||
Object.defineProperty( | ||
target, | ||
key, | ||
Object.getOwnPropertyDescriptor(source, key) | ||
); | ||
}); | ||
} | ||
} | ||
return target; | ||
} | ||
const fs = require('fs'); | ||
const invariant = require('invariant'); | ||
const path = require('path'); | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
import type {MixedSourceMap, HermesFunctionOffsets} from 'metro-source-map'; | ||
// flowlint-next-line untyped-type-import:off | ||
import {typeof SourceMapConsumer} from 'source-map'; | ||
const SourceMetadataMapConsumer = require("./SourceMetadataMapConsumer"); | ||
type SingleMapModuleIds = { | ||
segmentId: number, | ||
localId: ?number, | ||
... | ||
}; | ||
const fs = require("fs"); | ||
type ContextOptionsInput = { | ||
+nameSource?: 'function_names' | 'identifier_names', | ||
+inputLineStart?: number, | ||
+inputColumnStart?: number, | ||
+outputLineStart?: number, | ||
+outputColumnStart?: number, | ||
... | ||
}; | ||
const invariant = require("invariant"); | ||
type SizeAttributionMap = { | ||
location: { | ||
file: ?string, | ||
filename?: string, | ||
bytecodeSize?: number, | ||
virtualOffset?: number, | ||
line: ?number, | ||
column: ?number, | ||
}, | ||
... | ||
}; | ||
const nullthrows = require("nullthrows"); | ||
type ChromeTraceEntry = { | ||
column: number, | ||
funcColumn: number, | ||
funcLine: number, | ||
funcVirtAddr: number, | ||
line: number, | ||
name: string, | ||
offset: number, | ||
}; | ||
const path = require("path"); | ||
type ChromeTrace = { | ||
stackFrames: {[string]: ChromeTraceEntry}, | ||
}; | ||
type HermesMinidumpCrashInfo = { | ||
+callstack: $ReadOnlyArray<HermesMinidumpStackFrame | NativeCodeStackFrame>, | ||
... | ||
}; | ||
type HermesMinidumpStackFrame = $ReadOnly<{| | ||
ByteCodeOffset: number, | ||
FunctionID: number, | ||
CJSModuleOffset: 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 = { | ||
const UNKNOWN_MODULE_IDS = { | ||
segmentId: 0, | ||
localId: undefined, | ||
localId: undefined | ||
}; | ||
class SymbolicationContext<ModuleIdsT> { | ||
+options: { | ||
+nameSource: 'function_names' | 'identifier_names', | ||
+inputLineStart: number, | ||
+inputColumnStart: number, | ||
+outputLineStart: number, | ||
+outputColumnStart: number, | ||
... | ||
}; | ||
constructor(options: ContextOptionsInput) { | ||
class SymbolicationContext { | ||
constructor(options) { | ||
this.options = { | ||
@@ -117,10 +84,11 @@ inputLineStart: 1, | ||
outputColumnStart: 0, | ||
nameSource: 'function_names', | ||
nameSource: "function_names" | ||
}; | ||
if (options) { | ||
for (const option of [ | ||
'inputLineStart', | ||
'inputColumnStart', | ||
'outputLineStart', | ||
'outputColumnStart', | ||
"inputLineStart", | ||
"inputColumnStart", | ||
"outputLineStart", | ||
"outputColumnStart" | ||
]) { | ||
@@ -131,2 +99,3 @@ if (options[option] != null) { | ||
} | ||
if (options.nameSource != null) { | ||
@@ -136,5 +105,3 @@ this.options.nameSource = options.nameSource; | ||
} | ||
} | ||
// parse stack trace with String.replace | ||
} // parse stack trace with String.replace | ||
// replace the matched part of stack trace to symbolicated result | ||
@@ -149,27 +116,38 @@ // sample stack trace: | ||
// IOS: foo.js:57:foo, Android: bar.js:75:bar | ||
symbolicate(stackTrace: string): string { | ||
symbolicate(stackTrace) { | ||
return stackTrace.replace( | ||
/(?:([^@: \n(]+)(@|:))?(?:(?:([^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g, | ||
(match, func, delimiter, fileName, line, column) => { | ||
if (delimiter === ':' && func && !fileName) { | ||
var _original$source, _original$line, _original$name; | ||
if (delimiter === ":" && func && !fileName) { | ||
fileName = func; | ||
func = null; | ||
} | ||
const original = this.getOriginalPositionFor( | ||
line, | ||
column, | ||
this.parseFileName(fileName || ''), | ||
this.parseFileName(fileName || "") | ||
); | ||
return ( | ||
(original.source ?? 'null') + | ||
':' + | ||
(original.line ?? 'null') + | ||
':' + | ||
(original.name ?? 'null') | ||
((_original$source = original.source) !== null && | ||
_original$source !== void 0 | ||
? _original$source | ||
: "null") + | ||
":" + | ||
((_original$line = original.line) !== null && | ||
_original$line !== void 0 | ||
? _original$line | ||
: "null") + | ||
":" + | ||
((_original$name = original.name) !== null && | ||
_original$name !== void 0 | ||
? _original$name | ||
: "null") | ||
); | ||
}, | ||
} | ||
); | ||
} | ||
// Taking in a map like | ||
} // Taking in a map like | ||
// trampoline offset (optional js function name) | ||
@@ -183,9 +161,9 @@ // JS_0158_xxxxxxxxxxxxxxxxxxxxxx fe 91081 | ||
symbolicateProfilerMap(mapFile: string): string { | ||
symbolicateProfilerMap(mapFile) { | ||
return fs | ||
.readFileSync(mapFile, 'utf8') | ||
.split('\n') | ||
.readFileSync(mapFile, "utf8") | ||
.split("\n") | ||
.slice(0, -1) | ||
.map(line => { | ||
const line_list = line.split(' '); | ||
const line_list = line.split(" "); | ||
const trampoline = line_list[0]; | ||
@@ -196,3 +174,3 @@ const js_name = line_list[1]; | ||
if (!offset) { | ||
return trampoline + ' ' + trampoline; | ||
return trampoline + " " + trampoline; | ||
} | ||
@@ -202,17 +180,16 @@ | ||
this.options.inputLineStart, | ||
offset, | ||
offset | ||
); | ||
return ( | ||
trampoline + | ||
' ' + | ||
" " + | ||
(original.name || js_name) + | ||
'::' + | ||
[original.source, original.line, original.column].join(':') | ||
"::" + | ||
[original.source, original.line, original.column].join(":") | ||
); | ||
}) | ||
.join('\n'); | ||
.join("\n"); | ||
} | ||
symbolicateAttribution(obj: SizeAttributionMap): void { | ||
symbolicateAttribution(obj) { | ||
const loc = obj.location; | ||
@@ -223,3 +200,2 @@ const line = loc.line != null ? loc.line : this.options.inputLineStart; | ||
let original = this.getOriginalPositionFor(line, column, file); | ||
const isBytecodeRange = | ||
@@ -230,5 +206,3 @@ loc.bytecodeSize != null && | ||
const virtualOffset = Number(loc.virtualOffset); | ||
const bytecodeSize = Number(loc.bytecodeSize); | ||
// Functions compiled from Metro-bundled modules will often have a little bit | ||
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. | ||
@@ -241,2 +215,3 @@ // Let's attribute them to where the inner module code originates instead. | ||
// happen for function bodies that never throw (generally very short). | ||
while ( | ||
@@ -253,34 +228,28 @@ isBytecodeRange && | ||
line: original.line, | ||
column: original.column, | ||
column: original.column | ||
}; | ||
} | ||
// Symbolicate chrome trace "stackFrames" section. | ||
} // 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')); | ||
symbolicateChromeTrace(traceFile, _ref) { | ||
let stdout = _ref.stdout, | ||
stderr = _ref.stderr; | ||
const content = JSON.parse(fs.readFileSync(traceFile, "utf8")); | ||
if (content.stackFrames == null) { | ||
throw new Error('Unable to locate `stackFrames` section in trace.'); | ||
throw new Error("Unable to locate `stackFrames` section in trace."); | ||
} | ||
const keys = Object.keys(content.stackFrames); | ||
stdout.write('Processing ' + keys.length + ' frames\n'); | ||
stdout.write("Processing " + keys.length + " frames\n"); | ||
keys.forEach(key => { | ||
var _addressOriginal$sour, _addressOriginal$line, _addressOriginal$colu; | ||
const entry = content.stackFrames[key]; | ||
let line; | ||
let column; | ||
let column; // Function entrypoint line/column; used for symbolicating function name | ||
// with legacy source maps (or when --no-function-names is set). | ||
// Function entrypoint line/column; used for symbolicating function name | ||
// with legacy source maps (or when --no-function-names is set). | ||
let funcLine; | ||
@@ -292,5 +261,5 @@ let funcColumn; | ||
const funcVirtAddr = parseInt(entry.funcVirtAddr, 10); | ||
const offsetInFunction = parseInt(entry.offset, 10); | ||
// Main bundle always use hard-coded line value 1. | ||
const offsetInFunction = parseInt(entry.offset, 10); // Main bundle always use hard-coded line value 1. | ||
// TODO: support multiple bundle/module. | ||
line = this.options.inputLineStart; | ||
@@ -307,3 +276,2 @@ column = funcVirtAddr + offsetInFunction; | ||
column = entry.column; | ||
funcLine = entry.funcLine; | ||
@@ -314,18 +282,18 @@ funcColumn = entry.funcColumn; | ||
return; | ||
} | ||
} // Symbolicate original file/line/column. | ||
// Symbolicate original file/line/column. | ||
const addressOriginal = this.getOriginalPositionDetailsFor(line, column); | ||
let frameName; | ||
let frameName; | ||
if (addressOriginal.functionName) { | ||
frameName = addressOriginal.functionName; | ||
} else { | ||
frameName = entry.name; | ||
// Symbolicate function name. | ||
frameName = entry.name; // Symbolicate function name. | ||
if (funcLine != null && funcColumn != null) { | ||
const funcOriginal = this.getOriginalPositionFor( | ||
funcLine, | ||
funcColumn, | ||
funcColumn | ||
); | ||
if (funcOriginal.name != null) { | ||
@@ -337,23 +305,30 @@ frameName = funcOriginal.name; | ||
(stderr || stdout).write( | ||
'Warning: no function prolog line/column info; name may be wrong\n', | ||
"Warning: no function prolog line/column info; name may be wrong\n" | ||
); | ||
} | ||
} | ||
} // Output format is: funcName(file:line:column) | ||
// Output format is: funcName(file:line:column) | ||
entry.name = [ | ||
frameName, | ||
'(', | ||
"(", | ||
[ | ||
addressOriginal.source ?? 'null', | ||
addressOriginal.line ?? 'null', | ||
addressOriginal.column ?? 'null', | ||
].join(':'), | ||
')', | ||
].join(''); | ||
(_addressOriginal$sour = addressOriginal.source) !== null && | ||
_addressOriginal$sour !== void 0 | ||
? _addressOriginal$sour | ||
: "null", | ||
(_addressOriginal$line = addressOriginal.line) !== null && | ||
_addressOriginal$line !== void 0 | ||
? _addressOriginal$line | ||
: "null", | ||
(_addressOriginal$colu = addressOriginal.column) !== null && | ||
_addressOriginal$colu !== void 0 | ||
? _addressOriginal$colu | ||
: "null" | ||
].join(":"), | ||
")" | ||
].join(""); | ||
}); | ||
stdout.write('Writing to ' + traceFile + '\n'); | ||
stdout.write("Writing to " + traceFile + "\n"); | ||
fs.writeFileSync(traceFile, JSON.stringify(content)); | ||
} | ||
/* | ||
@@ -363,16 +338,8 @@ * A helper function to return a mapping {line, column} object for a given input | ||
*/ | ||
getOriginalPositionFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
moduleIds: ?ModuleIdsT, | ||
): {| | ||
line: ?number, | ||
column: ?number, | ||
source: ?string, | ||
name: ?string, | ||
|} { | ||
getOriginalPositionFor(lineNumber, columnNumber, moduleIds) { | ||
const position = this.getOriginalPositionDetailsFor( | ||
lineNumber, | ||
columnNumber, | ||
moduleIds, | ||
moduleIds | ||
); | ||
@@ -383,6 +350,5 @@ return { | ||
source: position.source, | ||
name: position.functionName ? position.functionName : position.name, | ||
name: position.functionName ? position.functionName : position.name | ||
}; | ||
} | ||
/* | ||
@@ -392,8 +358,6 @@ * Symbolicates the JavaScript stack trace extracted from the minidump | ||
*/ | ||
symbolicateHermesMinidumpTrace( | ||
crashInfo: HermesMinidumpCrashInfo, | ||
): SymbolicatedStackTrace { | ||
throw new Error('Not implemented'); | ||
symbolicateHermesMinidumpTrace(crashInfo) { | ||
throw new Error("Not implemented"); | ||
} | ||
/* | ||
@@ -404,42 +368,27 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
*/ | ||
getOriginalPositionDetailsFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
moduleIds: ?ModuleIdsT, | ||
): SymbolicatedStackFrame { | ||
throw new Error('Not implemented'); | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) { | ||
throw new Error("Not implemented"); | ||
} | ||
parseFileName(str: string): ModuleIdsT { | ||
throw new Error('Not implemented'); | ||
parseFileName(str) { | ||
throw new Error("Not implemented"); | ||
} | ||
} | ||
class SingleMapSymbolicationContext extends SymbolicationContext<SingleMapModuleIds> { | ||
+_segments: { | ||
+[id: string]: {| | ||
+consumer: SourceMapConsumer, | ||
+moduleOffsets: $ReadOnlyArray<number>, | ||
+sourceFunctionsConsumer: ?SourceMetadataMapConsumer, | ||
+hermesOffsets: ?HermesFunctionOffsets, | ||
|}, | ||
..., | ||
}; | ||
+_hasLegacySegments: boolean; | ||
+_SourceMapConsumer: SourceMapConsumer; | ||
constructor( | ||
SourceMapConsumer: SourceMapConsumer, | ||
sourceMapContent: string | MixedSourceMap, | ||
options: ContextOptionsInput = {}, | ||
) { | ||
class SingleMapSymbolicationContext extends SymbolicationContext { | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, sourceMapContent) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
super(options); | ||
this._SourceMapConsumer = SourceMapConsumer; | ||
const sourceMapJson: MixedSourceMap = | ||
typeof sourceMapContent === 'string' | ||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, '')) | ||
const sourceMapJson = | ||
typeof sourceMapContent === "string" | ||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, "")) | ||
: sourceMapContent; | ||
const segments = { | ||
'0': this._initSegment(sourceMapJson), | ||
"0": this._initSegment(sourceMapJson) | ||
}; | ||
if (sourceMapJson.x_facebook_segments) { | ||
@@ -451,2 +400,3 @@ for (const key of Object.keys(sourceMapJson.x_facebook_segments)) { | ||
} | ||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null; | ||
@@ -457,27 +407,29 @@ this._segments = segments; | ||
_initSegment(map) { | ||
const useFunctionNames = this.options.nameSource === 'function_names'; | ||
const {_SourceMapConsumer: SourceMapConsumer} = this; | ||
const useFunctionNames = this.options.nameSource === "function_names"; | ||
const SourceMapConsumer = this._SourceMapConsumer; | ||
return { | ||
get consumer() { | ||
Object.defineProperty(this, 'consumer', { | ||
value: new SourceMapConsumer(map), | ||
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, | ||
Object.defineProperty(this, "sourceFunctionsConsumer", { | ||
value: useFunctionNames ? new SourceMetadataMapConsumer(map) : null | ||
}); | ||
return this.sourceFunctionsConsumer; | ||
}, | ||
hermesOffsets: map.x_hermes_function_offsets, | ||
hermesOffsets: map.x_hermes_function_offsets | ||
}; | ||
} | ||
symbolicateHermesMinidumpTrace( | ||
crashInfo: HermesMinidumpCrashInfo, | ||
): SymbolicatedStackTrace { | ||
symbolicateHermesMinidumpTrace(crashInfo) { | ||
const symbolicatedTrace = []; | ||
const {callstack} = crashInfo; | ||
const callstack = crashInfo.callstack; | ||
if (callstack != null) { | ||
@@ -488,16 +440,28 @@ for (const stackItem of callstack) { | ||
} else { | ||
const { | ||
CJSModuleOffset, | ||
SourceURL, | ||
FunctionID, | ||
ByteCodeOffset: localOffset, | ||
} = stackItem; | ||
const CJSModuleOffset = stackItem.CJSModuleOffset, | ||
SegmentID = stackItem.SegmentID, | ||
SourceURL = stackItem.SourceURL, | ||
FunctionID = stackItem.FunctionID, | ||
localOffset = stackItem.ByteCodeOffset; | ||
const cjsModuleOffsetOrSegmentID = nullthrows( | ||
CJSModuleOffset !== null && CJSModuleOffset !== void 0 | ||
? 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 = CJSModuleOffset + this.options.inputLineStart; | ||
const generatedLine = | ||
cjsModuleOffsetOrSegmentID + this.options.inputLineStart; | ||
const segment = this._segments[ | ||
moduleInformation.segmentId.toString() | ||
]; | ||
const hermesOffsets = segment?.hermesOffsets; | ||
const hermesOffsets = | ||
segment === null || segment === void 0 | ||
? void 0 | ||
: segment.hermesOffsets; | ||
if (!hermesOffsets) { | ||
@@ -509,6 +473,7 @@ symbolicatedTrace.push({ | ||
functionName: null, | ||
name: null, | ||
name: null | ||
}); | ||
} else { | ||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)]; | ||
const segmentOffsets = | ||
hermesOffsets[Number(cjsModuleOffsetOrSegmentID)]; | ||
const generatedColumn = | ||
@@ -521,3 +486,3 @@ segmentOffsets[FunctionID] + | ||
generatedColumn, | ||
moduleInformation, | ||
moduleInformation | ||
); | ||
@@ -529,5 +494,5 @@ symbolicatedTrace.push(originalPosition); | ||
} | ||
return symbolicatedTrace; | ||
} | ||
/* | ||
@@ -538,7 +503,4 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
*/ | ||
getOriginalPositionDetailsFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
moduleIds: ?SingleMapModuleIds, | ||
): SymbolicatedStackFrame { | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) { | ||
// Adjust arguments to source-map's input coordinates | ||
@@ -559,21 +521,28 @@ lineNumber = | ||
let moduleLineOffset = 0; | ||
const metadata = this._segments[moduleIds.segmentId + '']; | ||
const {localId} = moduleIds; | ||
const metadata = this._segments[moduleIds.segmentId + ""]; | ||
const _moduleIds = moduleIds, | ||
localId = _moduleIds.localId; | ||
if (localId != null) { | ||
const {moduleOffsets} = metadata; | ||
const moduleOffsets = metadata.moduleOffsets; | ||
if (!moduleOffsets) { | ||
throw new Error( | ||
'Module ID given for a source map that does not have ' + | ||
'an x_facebook_offsets field', | ||
"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); | ||
throw new Error("Unknown module ID: " + localId); | ||
} | ||
moduleLineOffset = moduleOffsets[localId]; | ||
} | ||
const original = metadata.consumer.originalPositionFor({ | ||
line: Number(lineNumber) + moduleLineOffset, | ||
column: Number(columnNumber), | ||
column: Number(columnNumber) | ||
}); | ||
if (metadata.sourceFunctionsConsumer) { | ||
@@ -585,16 +554,20 @@ original.functionName = | ||
} | ||
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, | ||
}; | ||
return _objectSpread( | ||
_objectSpread({}, 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 { | ||
parseFileName(str) { | ||
return parseSingleMapFileName(str); | ||
@@ -604,12 +577,7 @@ } | ||
class DirectorySymbolicationContext extends SymbolicationContext<string> { | ||
+_fileMaps: Map<string, SingleMapSymbolicationContext>; | ||
+_rootDir: string; | ||
+_SourceMapConsumer: SourceMapConsumer; | ||
constructor( | ||
SourceMapConsumer: SourceMapConsumer, | ||
rootDir: string, | ||
options: ContextOptionsInput = {}, | ||
) { | ||
class DirectorySymbolicationContext extends SymbolicationContext { | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, rootDir) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
super(options); | ||
@@ -621,19 +589,22 @@ this._fileMaps = new Map(); | ||
_loadMap(mapFilename: string): SingleMapSymbolicationContext { | ||
_loadMap(mapFilename) { | ||
invariant( | ||
fs.existsSync(mapFilename), | ||
`Could not read source map from '${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, | ||
fs.readFileSync(mapFilename, "utf8"), | ||
this.options | ||
); | ||
this._fileMaps.set(mapFilename, fileMap); | ||
} | ||
return fileMap; | ||
} | ||
/* | ||
@@ -644,13 +615,19 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
*/ | ||
getOriginalPositionDetailsFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
filename: ?string, | ||
): SymbolicatedStackFrame { | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, filename) { | ||
invariant( | ||
filename != null, | ||
'filename is required for DirectorySymbolicationContext', | ||
"filename is required for DirectorySymbolicationContext" | ||
); | ||
const mapFilename = path.join(this._rootDir, filename + '.map'); | ||
if (!fs.existsSync(mapFilename)) { | ||
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 | ||
@@ -669,3 +646,2 @@ lineNumber = | ||
: columnNumber; | ||
return { | ||
@@ -676,16 +652,16 @@ line: lineNumber, | ||
name: null, | ||
functionName: null, | ||
functionName: null | ||
}; | ||
} | ||
return this._loadMap(mapFilename).getOriginalPositionDetailsFor( | ||
lineNumber, | ||
columnNumber, | ||
columnNumber | ||
); | ||
} | ||
parseFileName(str: string): string { | ||
parseFileName(str) { | ||
return str; | ||
} | ||
} | ||
/* | ||
@@ -701,85 +677,64 @@ * If the file name of a stack frame is numeric (+ ".js"), we assume it's a | ||
*/ | ||
function parseSingleMapFileName(str: string): SingleMapModuleIds { | ||
function parseSingleMapFileName(str) { | ||
const modMatch = str.match(/^(\d+).js$/); | ||
if (modMatch != null) { | ||
return {segmentId: 0, localId: Number(modMatch[1])}; | ||
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, | ||
localId: segMatch[2] ? Number(segMatch[2]) : null | ||
}; | ||
} | ||
return UNKNOWN_MODULE_IDS; | ||
} | ||
function createContext( | ||
SourceMapConsumer: SourceMapConsumer, | ||
sourceMapContent: string | MixedSourceMap, | ||
options: ContextOptionsInput = {}, | ||
): SingleMapSymbolicationContext { | ||
function createContext(SourceMapConsumer, sourceMapContent) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
return new SingleMapSymbolicationContext( | ||
SourceMapConsumer, | ||
sourceMapContent, | ||
options, | ||
options | ||
); | ||
} | ||
function unstable_createDirectoryContext( | ||
SourceMapConsumer: SourceMapConsumer, | ||
rootDir: string, | ||
options: ContextOptionsInput = {}, | ||
): DirectorySymbolicationContext { | ||
function unstable_createDirectoryContext(SourceMapConsumer, rootDir) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
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, | ||
|} { | ||
function getOriginalPositionFor(lineNumber, columnNumber, moduleIds, context) { | ||
return context.getOriginalPositionFor(lineNumber, columnNumber, moduleIds); | ||
} | ||
function symbolicate<ModuleIdsT>( | ||
stackTrace: string, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): string { | ||
function symbolicate(stackTrace, context) { | ||
return context.symbolicate(stackTrace); | ||
} | ||
function symbolicateProfilerMap<ModuleIdsT>( | ||
mapFile: string, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): string { | ||
function symbolicateProfilerMap(mapFile, context) { | ||
return context.symbolicateProfilerMap(mapFile); | ||
} | ||
function symbolicateAttribution<ModuleIdsT>( | ||
obj: SizeAttributionMap, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): void { | ||
function symbolicateAttribution(obj, context) { | ||
context.symbolicateAttribution(obj); | ||
} | ||
function symbolicateChromeTrace<ModuleIdsT>( | ||
traceFile: string, | ||
{ | ||
function symbolicateChromeTrace(traceFile, _ref2, context) { | ||
let stdout = _ref2.stdout, | ||
stderr = _ref2.stderr; | ||
return context.symbolicateChromeTrace(traceFile, { | ||
stdout, | ||
stderr, | ||
}: { | ||
stdout: stream$Writable, | ||
stderr: stream$Writable, | ||
... | ||
}, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): void { | ||
return context.symbolicateChromeTrace(traceFile, {stdout, stderr}); | ||
stderr | ||
}); | ||
} | ||
@@ -796,3 +751,3 @@ | ||
symbolicateChromeTrace, | ||
SourceMetadataMapConsumer, | ||
SourceMetadataMapConsumer | ||
}; |
@@ -34,2 +34,3 @@ /** | ||
var _e = undefined; | ||
try { | ||
@@ -42,2 +43,3 @@ for ( | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
@@ -55,2 +57,3 @@ } | ||
} | ||
return _arr; | ||
@@ -99,3 +102,5 @@ } | ||
if (len == null || len > arr.length) len = arr.length; | ||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
@@ -110,3 +115,2 @@ } | ||
const METADATA_FIELD_FUNCTIONS = 0; | ||
/** | ||
@@ -126,2 +130,3 @@ * Consumes the `x_facebook_sources` metadata field from a source map and | ||
*/ | ||
class SourceMetadataMapConsumer { | ||
@@ -137,3 +142,2 @@ constructor(map) { | ||
} | ||
/** | ||
@@ -147,2 +151,3 @@ * Retrieves a human-readable name for the function enclosing a particular | ||
*/ | ||
functionNameFor(_ref) { | ||
@@ -149,0 +154,0 @@ let line = _ref.line, |
@@ -1,3 +0,1 @@ | ||
#!/usr/bin/env node | ||
/** | ||
@@ -29,2 +27,3 @@ * Copyright (c) Facebook, Inc. and its affiliates. | ||
} | ||
if (info.done) { | ||
@@ -43,8 +42,11 @@ resolve(value); | ||
var gen = fn.apply(self, args); | ||
function _next(value) { | ||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); | ||
} | ||
function _throw(err) { | ||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); | ||
} | ||
_next(undefined); | ||
@@ -223,5 +225,4 @@ }); | ||
} else { | ||
var _original$source, _original$line, _original$name; | ||
var _original$source, _original$line, _original$name; // read-from-argv form. | ||
// read-from-argv form. | ||
let moduleIds; | ||
@@ -299,6 +300,2 @@ | ||
if (require.main === module) { | ||
main().then(code => process.exit(code)); | ||
} | ||
module.exports = main; |
@@ -14,2 +14,3 @@ /** | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
@@ -23,2 +24,3 @@ var symbols = Object.getOwnPropertySymbols(object); | ||
} | ||
return keys; | ||
@@ -30,2 +32,3 @@ } | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
if (i % 2) { | ||
@@ -47,2 +50,3 @@ ownKeys(Object(source), true).forEach(function(key) { | ||
} | ||
return target; | ||
@@ -62,2 +66,3 @@ } | ||
} | ||
return obj; | ||
@@ -72,2 +77,4 @@ } | ||
const nullthrows = require("nullthrows"); | ||
const path = require("path"); | ||
@@ -364,3 +371,5 @@ | ||
class SingleMapSymbolicationContext extends SymbolicationContext { | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, sourceMapContent) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
@@ -423,9 +432,17 @@ arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
const CJSModuleOffset = stackItem.CJSModuleOffset, | ||
SegmentID = stackItem.SegmentID, | ||
SourceURL = stackItem.SourceURL, | ||
FunctionID = stackItem.FunctionID, | ||
localOffset = stackItem.ByteCodeOffset; | ||
const cjsModuleOffsetOrSegmentID = nullthrows( | ||
CJSModuleOffset !== null && CJSModuleOffset !== void 0 | ||
? 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 = CJSModuleOffset + this.options.inputLineStart; | ||
const generatedLine = | ||
cjsModuleOffsetOrSegmentID + this.options.inputLineStart; | ||
@@ -450,3 +467,4 @@ const segment = this._segments[ | ||
} else { | ||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)]; | ||
const segmentOffsets = | ||
hermesOffsets[Number(cjsModuleOffsetOrSegmentID)]; | ||
const generatedColumn = | ||
@@ -546,3 +564,5 @@ segmentOffsets[FunctionID] + | ||
class DirectorySymbolicationContext extends SymbolicationContext { | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, rootDir) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
@@ -587,5 +607,13 @@ arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
); | ||
const mapFilename = path.join(this._rootDir, filename + ".map"); | ||
let mapFilename; | ||
const relativeFilename = path.relative( | ||
this._rootDir, | ||
path.resolve(this._rootDir, filename) | ||
); // Lock down access to files outside the root dir. | ||
if (!fs.existsSync(mapFilename)) { | ||
if (!relativeFilename.startsWith("..")) { | ||
mapFilename = path.join(this._rootDir, relativeFilename + ".map"); | ||
} | ||
if (mapFilename == null || !fs.existsSync(mapFilename)) { | ||
// Adjust arguments to the output coordinates | ||
@@ -657,2 +685,3 @@ lineNumber = | ||
function createContext(SourceMapConsumer, sourceMapContent) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
@@ -668,2 +697,3 @@ arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
function unstable_createDirectoryContext(SourceMapConsumer, rootDir) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
@@ -670,0 +700,0 @@ arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
4
152798
6
17
2371
+ Addednullthrows@^1.1.1
+ Addedmetro-source-map@0.64.0(transitive)
+ Addednullthrows@1.1.1(transitive)
+ Addedob1@0.64.0(transitive)
- Removedmetro-source-map@0.63.0(transitive)
- Removedob1@0.63.0(transitive)
Updatedmetro-source-map@0.64.0