metro-symbolicate
Advanced tools
Comparing version 0.64.0 to 0.65.0
{ | ||
"name": "metro-symbolicate", | ||
"version": "0.64.0", | ||
"version": "0.65.0", | ||
"description": "🚇 A tool to find the source location from JS bundles and stack traces.", | ||
@@ -24,3 +24,3 @@ "license": "MIT", | ||
"invariant": "^2.2.4", | ||
"metro-source-map": "0.64.0", | ||
"metro-source-map": "0.65.0", | ||
"nullthrows": "^1.1.1", | ||
@@ -27,0 +27,0 @@ "source-map": "^0.5.6", |
#!/usr/bin/env node | ||
/** | ||
@@ -9,7 +8,8 @@ * Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* | ||
* @flow strict-local | ||
* @format | ||
*/ | ||
"use strict"; | ||
require("./symbolicate.js")().then(code => process.exit(code)); | ||
'use strict'; | ||
require('./symbolicate.js')().then(code => process.exit(code)); |
@@ -7,102 +7,37 @@ /** | ||
* | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
"use strict"; | ||
function _slicedToArray(arr, i) { | ||
return ( | ||
_arrayWithHoles(arr) || | ||
_iterableToArrayLimit(arr, i) || | ||
_unsupportedIterableToArray(arr, i) || | ||
_nonIterableRest() | ||
); | ||
} | ||
'use strict'; | ||
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 vlq = require('vlq'); | ||
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; | ||
} | ||
const {normalizeSourcePath} = require('metro-source-map'); | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
import type { | ||
MixedSourceMap, | ||
FBSourcesArray, | ||
FBSourceFunctionMap, | ||
FBSourceMetadata, | ||
BasicSourceMap, | ||
IndexMap, | ||
} from 'metro-source-map'; | ||
function _toConsumableArray(arr) { | ||
return ( | ||
_arrayWithoutHoles(arr) || | ||
_iterableToArray(arr) || | ||
_unsupportedIterableToArray(arr) || | ||
_nonIterableSpread() | ||
); | ||
} | ||
const METADATA_FIELD_FUNCTIONS = 0; | ||
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." | ||
); | ||
} | ||
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, ...}; | ||
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; | ||
/** | ||
@@ -123,7 +58,6 @@ * Consumes the `x_facebook_sources` metadata field from a source map and | ||
class SourceMetadataMapConsumer { | ||
constructor(map) { | ||
let normalizeSourceFn = | ||
arguments.length > 1 && arguments[1] !== undefined | ||
? arguments[1] | ||
: normalizeSourcePath; | ||
constructor( | ||
map: MixedSourceMap, | ||
normalizeSourceFn: SourceNameNormalizer = normalizeSourcePath, | ||
) { | ||
this._sourceMap = map; | ||
@@ -134,2 +68,7 @@ this._decodedFunctionMapCache = new Map(); | ||
_sourceMap: MixedSourceMap; | ||
_decodedFunctionMapCache: Map<string, ?$ReadOnlyArray<FunctionMapping>>; | ||
_normalizeSource: SourceNameNormalizer; | ||
_metadataBySource: ?MetadataMap; | ||
/** | ||
@@ -143,16 +82,11 @@ * Retrieves a human-readable name for the function enclosing a particular | ||
*/ | ||
functionNameFor(_ref) { | ||
let line = _ref.line, | ||
column = _ref.column, | ||
source = _ref.source; | ||
functionNameFor({ | ||
line, | ||
column, | ||
source, | ||
}: Position & {+source: ?string, ...}): ?string { | ||
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) { | ||
@@ -163,5 +97,5 @@ return mapping.name; | ||
} | ||
return null; | ||
} | ||
/** | ||
@@ -174,22 +108,18 @@ * Returns this map's source metadata as a new array with the same order as | ||
*/ | ||
toArray(sources) { | ||
toArray(sources: $ReadOnlyArray<string>): FBSourcesArray { | ||
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() { | ||
_getMetadataBySource(): MetadataMap { | ||
if (!this._metadataBySource) { | ||
this._metadataBySource = this._getMetadataObjectsBySourceNames( | ||
this._sourceMap | ||
this._sourceMap, | ||
); | ||
@@ -200,2 +130,3 @@ } | ||
} | ||
/** | ||
@@ -205,12 +136,8 @@ * Decodes the function name mappings for the given source if needed, and | ||
*/ | ||
_getFunctionMappings(source) { | ||
_getFunctionMappings(source: string): ?$ReadOnlyArray<FunctionMapping> { | ||
if (this._decodedFunctionMapCache.has(source)) { | ||
return this._decodedFunctionMapCache.get(source); | ||
} | ||
let parsedFunctionMap = null; | ||
const metadataBySource = this._getMetadataBySource(); | ||
if (Object.prototype.hasOwnProperty.call(metadataBySource, source)) { | ||
@@ -220,7 +147,6 @@ const metadata = metadataBySource[source] || []; | ||
} | ||
this._decodedFunctionMapCache.set(source, parsedFunctionMap); | ||
return parsedFunctionMap; | ||
} | ||
/** | ||
@@ -235,25 +161,19 @@ * Collects source metadata from the given map using the current source name | ||
*/ | ||
_getMetadataObjectsBySourceNames(map) { | ||
_getMetadataObjectsBySourceNames(map: MixedSourceMap): MetadataMap { | ||
// eslint-disable-next-line lint/strictly-null | ||
if (map.mappings === undefined) { | ||
const indexMap = map; | ||
return Object.assign.apply( | ||
Object, | ||
[{}].concat( | ||
_toConsumableArray( | ||
indexMap.sections.map(section => | ||
this._getMetadataObjectsBySourceNames(section.map) | ||
) | ||
) | ||
) | ||
const indexMap: IndexMap = map; | ||
return Object.assign( | ||
{}, | ||
...indexMap.sections.map(section => | ||
this._getMetadataObjectsBySourceNames(section.map), | ||
), | ||
); | ||
} | ||
if ("x_facebook_sources" in map) { | ||
const basicMap = map; | ||
if ('x_facebook_sources' in map) { | ||
const basicMap: BasicSourceMap = map; | ||
return (basicMap.x_facebook_sources || []).reduce( | ||
(acc, metadata, index) => { | ||
let source = basicMap.sources[index]; | ||
if (source != null) { | ||
@@ -263,9 +183,7 @@ source = this._normalizeSource(source, basicMap); | ||
} | ||
return acc; | ||
}, | ||
{} | ||
{}, | ||
); | ||
} | ||
return {}; | ||
@@ -275,37 +193,28 @@ } | ||
function decodeFunctionMap(functionMap) { | ||
function decodeFunctionMap( | ||
functionMap: ?FBSourceFunctionMap, | ||
): $ReadOnlyArray<FunctionMapping> { | ||
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 _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$; | ||
for (const mapping of lineMappings.split(',')) { | ||
const [columnDelta, nameDelta, lineDelta = 0] = vlq.decode(mapping); | ||
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, target) { | ||
function findEnclosingMapping( | ||
mappings: $ReadOnlyArray<FunctionMapping>, | ||
target: Position, | ||
): ?FunctionMapping { | ||
let first = 0; | ||
@@ -315,3 +224,2 @@ let it = 0; | ||
let step; | ||
while (count > 0) { | ||
@@ -321,3 +229,2 @@ it = first; | ||
it += step; | ||
if (comparePositions(target, mappings[it]) >= 0) { | ||
@@ -330,11 +237,9 @@ first = ++it; | ||
} | ||
return first ? mappings[first - 1] : null; | ||
} | ||
function comparePositions(a, b) { | ||
function comparePositions(a: Position, b: Position): number { | ||
if (a.line === b.line) { | ||
return a.column - b.column; | ||
} | ||
return a.line - b.line; | ||
@@ -341,0 +246,0 @@ } |
@@ -7,5 +7,6 @@ /** | ||
* | ||
* | ||
* @flow strict-local | ||
* @format | ||
*/ | ||
// Symbolicates a JavaScript stack trace using a source map. | ||
@@ -18,248 +19,193 @@ // 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 | ||
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); | ||
} | ||
} | ||
'use strict'; | ||
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); | ||
}); | ||
}; | ||
} | ||
// flowlint-next-line untyped-import:off | ||
const SourceMapConsumer = require('source-map').SourceMapConsumer; | ||
const Symbolication = require('./Symbolication.js'); | ||
const SourceMapConsumer = require("source-map").SourceMapConsumer; | ||
const fs = require('fs'); | ||
// flowlint-next-line untyped-import:off | ||
const through2 = require('through2'); | ||
const Symbolication = require("./Symbolication.js"); | ||
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 fs = require("fs"); // flowlint-next-line untyped-import:off | ||
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 through2 = require("through2"); | ||
if (argv.length < 1 || argv.length > 4) { | ||
/* eslint no-path-concat: "off" */ | ||
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; | ||
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 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 | ||
// 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, | ||
); | ||
const inputColumnStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--input-column-start") || "0", | ||
10 | ||
} else { | ||
const content = fs.readFileSync(sourceMapFileName, 'utf8'); | ||
context = Symbolication.createContext( | ||
SourceMapConsumer, | ||
content, | ||
options, | ||
); | ||
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 | ||
} | ||
if (argv.length === 0) { | ||
const stackTrace = await readAll(stdin); | ||
if (isHermesCrash) { | ||
const stackTraceJSON = JSON.parse(stackTrace); | ||
const symbolicatedTrace = context.symbolicateHermesMinidumpTrace( | ||
stackTraceJSON, | ||
); | ||
stdout.write(JSON.stringify(symbolicatedTrace)); | ||
} else { | ||
const content = fs.readFileSync(sourceMapFileName, "utf8"); | ||
context = Symbolication.createContext( | ||
SourceMapConsumer, | ||
content, | ||
options | ||
); | ||
stdout.write(context.symbolicate(stackTrace)); | ||
} | ||
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 if (argv[0].endsWith('.profmap')) { | ||
stdout.write(context.symbolicateProfilerMap(argv[0])); | ||
} else if ( | ||
argv[0].endsWith('.heapsnapshot') || | ||
argv[0].endsWith('.heaptimeline') | ||
) { | ||
stdout.write( | ||
JSON.stringify( | ||
context.symbolicateHeapSnapshot(fs.readFileSync(argv[0], 'utf8')), | ||
), | ||
); | ||
} 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(); | ||
} else { | ||
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" | ||
); | ||
moduleIds = null; | ||
} | ||
} catch (error) { | ||
stderr.write(error + "\n"); | ||
return 1; | ||
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', | ||
); | ||
} | ||
return 0; | ||
}); | ||
return _main.apply(this, arguments); | ||
} catch (error) { | ||
stderr.write(error + '\n'); | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
@@ -269,4 +215,3 @@ | ||
return new Promise(resolve => { | ||
let data = ""; | ||
let data = ''; | ||
if (stream.isTTY === true) { | ||
@@ -277,6 +222,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())) { | ||
@@ -286,3 +231,3 @@ data += chunk.toString(); | ||
}); | ||
stream.on("end", () => { | ||
stream.on('end', () => { | ||
resolve(data); | ||
@@ -295,3 +240,3 @@ }); | ||
return new Promise(resolve => { | ||
stream.on("finish", resolve); | ||
stream.on('finish', resolve); | ||
}); | ||
@@ -298,0 +243,0 @@ } |
@@ -7,73 +7,112 @@ /** | ||
* | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
"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; | ||
} | ||
'use strict'; | ||
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 SourceMetadataMapConsumer = require('./SourceMetadataMapConsumer'); | ||
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; | ||
} | ||
const fs = require('fs'); | ||
const invariant = require('invariant'); | ||
const nullthrows = require('nullthrows'); | ||
const path = require('path'); | ||
const SourceMetadataMapConsumer = require("./SourceMetadataMapConsumer"); | ||
const {ChromeHeapSnapshotProcessor} = require('./ChromeHeapSnapshot'); | ||
const fs = require("fs"); | ||
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'; | ||
const invariant = require("invariant"); | ||
type SingleMapModuleIds = { | ||
segmentId: number, | ||
localId: ?number, | ||
... | ||
}; | ||
const nullthrows = require("nullthrows"); | ||
type ContextOptionsInput = { | ||
+nameSource?: 'function_names' | 'identifier_names', | ||
+inputLineStart?: number, | ||
+inputColumnStart?: number, | ||
+outputLineStart?: number, | ||
+outputColumnStart?: number, | ||
... | ||
}; | ||
const path = require("path"); | ||
type SizeAttributionMap = { | ||
location: { | ||
file: ?string, | ||
filename?: string, | ||
bytecodeSize?: number, | ||
virtualOffset?: number, | ||
line: ?number, | ||
column: ?number, | ||
}, | ||
... | ||
}; | ||
const UNKNOWN_MODULE_IDS = { | ||
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 | ||
localId: undefined, | ||
}; | ||
class SymbolicationContext { | ||
constructor(options) { | ||
class SymbolicationContext<ModuleIdsT> { | ||
+options: { | ||
+nameSource: 'function_names' | 'identifier_names', | ||
+inputLineStart: number, | ||
+inputColumnStart: number, | ||
+outputLineStart: number, | ||
+outputColumnStart: number, | ||
... | ||
}; | ||
constructor(options: ContextOptionsInput) { | ||
this.options = { | ||
@@ -84,11 +123,10 @@ inputLineStart: 1, | ||
outputColumnStart: 0, | ||
nameSource: "function_names" | ||
nameSource: 'function_names', | ||
}; | ||
if (options) { | ||
for (const option of [ | ||
"inputLineStart", | ||
"inputColumnStart", | ||
"outputLineStart", | ||
"outputColumnStart" | ||
'inputLineStart', | ||
'inputColumnStart', | ||
'outputLineStart', | ||
'outputColumnStart', | ||
]) { | ||
@@ -99,3 +137,2 @@ if (options[option] != null) { | ||
} | ||
if (options.nameSource != null) { | ||
@@ -105,3 +142,5 @@ 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 | ||
@@ -116,38 +155,27 @@ // sample stack trace: | ||
// IOS: foo.js:57:foo, Android: bar.js:75:bar | ||
symbolicate(stackTrace) { | ||
symbolicate(stackTrace: string): string { | ||
return stackTrace.replace( | ||
/(?:([^@: \n(]+)(@|:))?(?:(?:([^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g, | ||
(match, func, delimiter, fileName, line, column) => { | ||
var _original$source, _original$line, _original$name; | ||
if (delimiter === ":" && func && !fileName) { | ||
if (delimiter === ':' && func && !fileName) { | ||
fileName = func; | ||
func = null; | ||
} | ||
const original = this.getOriginalPositionFor( | ||
line, | ||
column, | ||
this.parseFileName(fileName || "") | ||
this.parseFileName(fileName || ''), | ||
); | ||
return ( | ||
((_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") | ||
(original.source ?? 'null') + | ||
':' + | ||
(original.line ?? 'null') + | ||
':' + | ||
(original.name ?? 'null') | ||
); | ||
} | ||
}, | ||
); | ||
} // Taking in a map like | ||
} | ||
// Taking in a map like | ||
// trampoline offset (optional js function name) | ||
@@ -161,9 +189,9 @@ // JS_0158_xxxxxxxxxxxxxxxxxxxxxx fe 91081 | ||
symbolicateProfilerMap(mapFile) { | ||
symbolicateProfilerMap(mapFile: string): string { | ||
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]; | ||
@@ -174,3 +202,3 @@ const js_name = line_list[1]; | ||
if (!offset) { | ||
return trampoline + " " + trampoline; | ||
return trampoline + ' ' + trampoline; | ||
} | ||
@@ -180,16 +208,17 @@ | ||
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) { | ||
symbolicateAttribution(obj: SizeAttributionMap): void { | ||
const loc = obj.location; | ||
@@ -200,2 +229,3 @@ const line = loc.line != null ? loc.line : this.options.inputLineStart; | ||
let original = this.getOriginalPositionFor(line, column, file); | ||
const isBytecodeRange = | ||
@@ -206,3 +236,5 @@ 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. | ||
@@ -215,3 +247,2 @@ // Let's attribute them to where the inner module code originates instead. | ||
// happen for function bodies that never throw (generally very short). | ||
while ( | ||
@@ -228,28 +259,34 @@ 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, _ref) { | ||
let stdout = _ref.stdout, | ||
stderr = _ref.stderr; | ||
const content = JSON.parse(fs.readFileSync(traceFile, "utf8")); | ||
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."); | ||
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; // Function entrypoint line/column; used for symbolicating function name | ||
let column; | ||
// Function entrypoint line/column; used for symbolicating function name | ||
// with legacy source maps (or when --no-function-names is set). | ||
let funcLine; | ||
@@ -261,5 +298,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; | ||
@@ -276,2 +313,3 @@ column = funcVirtAddr + offsetInFunction; | ||
column = entry.column; | ||
funcLine = entry.funcLine; | ||
@@ -282,18 +320,18 @@ funcColumn = entry.funcColumn; | ||
return; | ||
} // Symbolicate original file/line/column. | ||
} | ||
// 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. | ||
frameName = entry.name; | ||
// Symbolicate function name. | ||
if (funcLine != null && funcColumn != null) { | ||
const funcOriginal = this.getOriginalPositionFor( | ||
funcLine, | ||
funcColumn | ||
funcColumn, | ||
); | ||
if (funcOriginal.name != null) { | ||
@@ -305,30 +343,23 @@ 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$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(""); | ||
addressOriginal.source ?? 'null', | ||
addressOriginal.line ?? 'null', | ||
addressOriginal.column ?? 'null', | ||
].join(':'), | ||
')', | ||
].join(''); | ||
}); | ||
stdout.write("Writing to " + traceFile + "\n"); | ||
stdout.write('Writing to ' + traceFile + '\n'); | ||
fs.writeFileSync(traceFile, JSON.stringify(content)); | ||
} | ||
/* | ||
@@ -338,8 +369,16 @@ * A helper function to return a mapping {line, column} object for a given input | ||
*/ | ||
getOriginalPositionFor(lineNumber, columnNumber, moduleIds) { | ||
getOriginalPositionFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
moduleIds: ?ModuleIdsT, | ||
): {| | ||
line: ?number, | ||
column: ?number, | ||
source: ?string, | ||
name: ?string, | ||
|} { | ||
const position = this.getOriginalPositionDetailsFor( | ||
lineNumber, | ||
columnNumber, | ||
moduleIds | ||
moduleIds, | ||
); | ||
@@ -350,5 +389,6 @@ return { | ||
source: position.source, | ||
name: position.functionName ? position.functionName : position.name | ||
name: position.functionName ? position.functionName : position.name, | ||
}; | ||
} | ||
/* | ||
@@ -358,6 +398,64 @@ * 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"); | ||
/** | ||
* 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; | ||
} | ||
/* | ||
@@ -368,27 +466,45 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
*/ | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) { | ||
throw new Error("Not implemented"); | ||
getOriginalPositionDetailsFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
moduleIds: ?ModuleIdsT, | ||
): SymbolicatedStackFrame { | ||
throw new Error('Not implemented'); | ||
} | ||
parseFileName(str) { | ||
throw new Error("Not implemented"); | ||
parseFileName(str: string): ModuleIdsT { | ||
throw new Error('Not implemented'); | ||
} | ||
} | ||
class SingleMapSymbolicationContext extends SymbolicationContext { | ||
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] | ||
constructor(SourceMapConsumer, sourceMapContent) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
+_SourceMapConsumer: SourceMapConsumer; | ||
constructor( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer: SourceMapConsumer, | ||
sourceMapContent: string | MixedSourceMap, | ||
options: ContextOptionsInput = {}, | ||
) { | ||
super(options); | ||
this._SourceMapConsumer = SourceMapConsumer; | ||
const sourceMapJson = | ||
typeof sourceMapContent === "string" | ||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, "")) | ||
const sourceMapJson: MixedSourceMap = | ||
typeof sourceMapContent === 'string' | ||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, '')) | ||
: sourceMapContent; | ||
const segments = { | ||
"0": this._initSegment(sourceMapJson) | ||
'0': this._initSegment(sourceMapJson), | ||
}; | ||
if (sourceMapJson.x_facebook_segments) { | ||
@@ -400,3 +516,2 @@ for (const key of Object.keys(sourceMapJson.x_facebook_segments)) { | ||
} | ||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null; | ||
@@ -407,29 +522,27 @@ this._segments = segments; | ||
_initSegment(map) { | ||
const useFunctionNames = this.options.nameSource === "function_names"; | ||
const SourceMapConsumer = this._SourceMapConsumer; | ||
const useFunctionNames = this.options.nameSource === 'function_names'; | ||
const {_SourceMapConsumer: SourceMapConsumer} = this; | ||
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) { | ||
symbolicateHermesMinidumpTrace( | ||
crashInfo: HermesMinidumpCrashInfo, | ||
): SymbolicatedStackTrace { | ||
const symbolicatedTrace = []; | ||
const callstack = crashInfo.callstack; | ||
const {callstack} = crashInfo; | ||
if (callstack != null) { | ||
@@ -440,12 +553,12 @@ for (const stackItem of callstack) { | ||
} else { | ||
const CJSModuleOffset = stackItem.CJSModuleOffset, | ||
SegmentID = stackItem.SegmentID, | ||
SourceURL = stackItem.SourceURL, | ||
FunctionID = stackItem.FunctionID, | ||
localOffset = stackItem.ByteCodeOffset; | ||
const { | ||
CJSModuleOffset, | ||
SegmentID, | ||
SourceURL, | ||
FunctionID, | ||
ByteCodeOffset: localOffset, | ||
} = stackItem; | ||
const cjsModuleOffsetOrSegmentID = nullthrows( | ||
CJSModuleOffset !== null && CJSModuleOffset !== void 0 | ||
? CJSModuleOffset | ||
: SegmentID, | ||
"Either CJSModuleOffset or SegmentID must be specified in the Hermes stack frame" | ||
CJSModuleOffset ?? SegmentID, | ||
'Either CJSModuleOffset or SegmentID must be specified in the Hermes stack frame', | ||
); | ||
@@ -457,12 +570,6 @@ const moduleInformation = this._hasLegacySegments | ||
cjsModuleOffsetOrSegmentID + this.options.inputLineStart; | ||
const segment = this._segments[ | ||
moduleInformation.segmentId.toString() | ||
]; | ||
const hermesOffsets = | ||
segment === null || segment === void 0 | ||
? void 0 | ||
: segment.hermesOffsets; | ||
const hermesOffsets = segment?.hermesOffsets; | ||
if (!hermesOffsets) { | ||
@@ -474,3 +581,3 @@ symbolicatedTrace.push({ | ||
functionName: null, | ||
name: null | ||
name: null, | ||
}); | ||
@@ -487,3 +594,3 @@ } else { | ||
generatedColumn, | ||
moduleInformation | ||
moduleInformation, | ||
); | ||
@@ -495,5 +602,5 @@ symbolicatedTrace.push(originalPosition); | ||
} | ||
return symbolicatedTrace; | ||
} | ||
/* | ||
@@ -504,4 +611,7 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
*/ | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) { | ||
getOriginalPositionDetailsFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
moduleIds: ?SingleMapModuleIds, | ||
): SymbolicatedStackFrame { | ||
// Adjust arguments to source-map's input coordinates | ||
@@ -522,28 +632,21 @@ lineNumber = | ||
let moduleLineOffset = 0; | ||
const metadata = this._segments[moduleIds.segmentId + ""]; | ||
const _moduleIds = moduleIds, | ||
localId = _moduleIds.localId; | ||
const metadata = this._segments[moduleIds.segmentId + '']; | ||
const {localId} = moduleIds; | ||
if (localId != null) { | ||
const moduleOffsets = metadata.moduleOffsets; | ||
const {moduleOffsets} = metadata; | ||
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) { | ||
@@ -555,20 +658,16 @@ original.functionName = | ||
} | ||
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 | ||
} | ||
); | ||
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) { | ||
parseFileName(str: string): SingleMapModuleIds { | ||
return parseSingleMapFileName(str); | ||
@@ -578,7 +677,14 @@ } | ||
class DirectorySymbolicationContext extends SymbolicationContext { | ||
class DirectorySymbolicationContext extends SymbolicationContext<string> { | ||
+_fileMaps: Map<string, SingleMapSymbolicationContext>; | ||
+_rootDir: string; | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, rootDir) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
+_SourceMapConsumer: SourceMapConsumer; | ||
constructor( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer: SourceMapConsumer, | ||
rootDir: string, | ||
options: ContextOptionsInput = {}, | ||
) { | ||
super(options); | ||
@@ -590,22 +696,19 @@ this._fileMaps = new Map(); | ||
_loadMap(mapFilename) { | ||
_loadMap(mapFilename: string): SingleMapSymbolicationContext { | ||
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; | ||
} | ||
/* | ||
@@ -616,7 +719,10 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
*/ | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, filename) { | ||
getOriginalPositionDetailsFor( | ||
lineNumber: ?number, | ||
columnNumber: ?number, | ||
filename: ?string, | ||
): SymbolicatedStackFrame { | ||
invariant( | ||
filename != null, | ||
"filename is required for DirectorySymbolicationContext" | ||
'filename is required for DirectorySymbolicationContext', | ||
); | ||
@@ -626,9 +732,8 @@ let mapFilename; | ||
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"); | ||
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)) { | ||
@@ -648,2 +753,3 @@ // Adjust arguments to the output coordinates | ||
: columnNumber; | ||
return { | ||
@@ -654,16 +760,16 @@ line: lineNumber, | ||
name: null, | ||
functionName: null | ||
functionName: null, | ||
}; | ||
} | ||
return this._loadMap(mapFilename).getOriginalPositionDetailsFor( | ||
lineNumber, | ||
columnNumber | ||
columnNumber, | ||
); | ||
} | ||
parseFileName(str) { | ||
parseFileName(str: string): string { | ||
return str; | ||
} | ||
} | ||
/* | ||
@@ -679,64 +785,87 @@ * If the file name of a stack frame is numeric (+ ".js"), we assume it's a | ||
*/ | ||
function parseSingleMapFileName(str) { | ||
function parseSingleMapFileName(str: string): SingleMapModuleIds { | ||
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, sourceMapContent) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
function createContext( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer: SourceMapConsumer, | ||
sourceMapContent: string | MixedSourceMap, | ||
options: ContextOptionsInput = {}, | ||
): SingleMapSymbolicationContext { | ||
return new SingleMapSymbolicationContext( | ||
SourceMapConsumer, | ||
sourceMapContent, | ||
options | ||
options, | ||
); | ||
} | ||
function unstable_createDirectoryContext(SourceMapConsumer, rootDir) { // $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
function unstable_createDirectoryContext( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer: SourceMapConsumer, | ||
rootDir: string, | ||
options: ContextOptionsInput = {}, | ||
): DirectorySymbolicationContext { | ||
return new DirectorySymbolicationContext(SourceMapConsumer, rootDir, options); | ||
} | ||
function getOriginalPositionFor(lineNumber, columnNumber, moduleIds, context) { | ||
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(stackTrace, context) { | ||
function symbolicate<ModuleIdsT>( | ||
stackTrace: string, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): string { | ||
return context.symbolicate(stackTrace); | ||
} | ||
function symbolicateProfilerMap(mapFile, context) { | ||
function symbolicateProfilerMap<ModuleIdsT>( | ||
mapFile: string, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): string { | ||
return context.symbolicateProfilerMap(mapFile); | ||
} | ||
function symbolicateAttribution(obj, context) { | ||
function symbolicateAttribution<ModuleIdsT>( | ||
obj: SizeAttributionMap, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): void { | ||
context.symbolicateAttribution(obj); | ||
} | ||
function symbolicateChromeTrace(traceFile, _ref2, context) { | ||
let stdout = _ref2.stdout, | ||
stderr = _ref2.stderr; | ||
return context.symbolicateChromeTrace(traceFile, { | ||
function symbolicateChromeTrace<ModuleIdsT>( | ||
traceFile: string, | ||
{ | ||
stdout, | ||
stderr | ||
}); | ||
stderr, | ||
}: { | ||
stdout: stream$Writable, | ||
stderr: stream$Writable, | ||
... | ||
}, | ||
context: SymbolicationContext<ModuleIdsT>, | ||
): void { | ||
return context.symbolicateChromeTrace(traceFile, {stdout, stderr}); | ||
} | ||
@@ -753,3 +882,3 @@ | ||
symbolicateChromeTrace, | ||
SourceMetadataMapConsumer | ||
SourceMetadataMapConsumer, | ||
}; |
@@ -12,101 +12,8 @@ /** | ||
function _slicedToArray(arr, i) { | ||
return ( | ||
_arrayWithHoles(arr) || | ||
_iterableToArrayLimit(arr, i) || | ||
_unsupportedIterableToArray(arr, i) || | ||
_nonIterableRest() | ||
); | ||
} | ||
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." | ||
); | ||
} | ||
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; | ||
} | ||
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 { normalizeSourcePath } = require("metro-source-map"); | ||
const METADATA_FIELD_FUNCTIONS = 0; | ||
/** | ||
@@ -126,9 +33,4 @@ * Consumes the `x_facebook_sources` metadata field from a source map and | ||
*/ | ||
class SourceMetadataMapConsumer { | ||
constructor(map) { | ||
let normalizeSourceFn = | ||
arguments.length > 1 && arguments[1] !== undefined | ||
? arguments[1] | ||
: normalizeSourcePath; | ||
constructor(map, normalizeSourceFn = normalizeSourcePath) { | ||
this._sourceMap = map; | ||
@@ -138,2 +40,3 @@ this._decodedFunctionMapCache = new Map(); | ||
} | ||
/** | ||
@@ -147,8 +50,3 @@ * Retrieves a human-readable name for the function enclosing a particular | ||
*/ | ||
functionNameFor(_ref) { | ||
let line = _ref.line, | ||
column = _ref.column, | ||
source = _ref.source; | ||
functionNameFor({ line, column, source }) { | ||
if (source && line != null && column != null) { | ||
@@ -240,10 +138,6 @@ const mappings = this._getFunctionMappings(source); | ||
const indexMap = map; | ||
return Object.assign.apply( | ||
Object, | ||
[{}].concat( | ||
_toConsumableArray( | ||
indexMap.sections.map(section => | ||
this._getMetadataObjectsBySourceNames(section.map) | ||
) | ||
) | ||
return Object.assign( | ||
{}, | ||
...indexMap.sections.map(section => | ||
this._getMetadataObjectsBySourceNames(section.map) | ||
) | ||
@@ -287,9 +181,3 @@ ); | ||
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$; | ||
const [columnDelta, nameDelta, lineDelta = 0] = vlq.decode(mapping); | ||
line += lineDelta; | ||
@@ -296,0 +184,0 @@ nameIndex += nameDelta; |
@@ -19,38 +19,2 @@ /** | ||
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); | ||
} | ||
} | ||
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 SourceMapConsumer = require("source-map").SourceMapConsumer; | ||
@@ -64,205 +28,194 @@ | ||
function main() { | ||
return _main.apply(this, arguments); | ||
} | ||
async function main( | ||
argvInput = process.argv.slice(2), | ||
{ stdin, stderr, stdout } = process | ||
) { | ||
const argv = argvInput.slice(); | ||
function _main() { | ||
_main = _asyncToGenerator(function*() { | ||
let argvInput = | ||
arguments.length > 0 && arguments[0] !== undefined | ||
? arguments[0] | ||
: process.argv.slice(2); | ||
function checkAndRemoveArg(arg, valuesPerArg = 0) { | ||
let values = null; | ||
let _ref = | ||
arguments.length > 1 && arguments[1] !== undefined | ||
? arguments[1] | ||
: process, | ||
stdin = _ref.stdin, | ||
stderr = _ref.stderr, | ||
stdout = _ref.stdout; | ||
for (let idx = argv.indexOf(arg); idx !== -1; idx = argv.indexOf(arg)) { | ||
argv.splice(idx, 1); | ||
values = values || []; | ||
values.push(argv.splice(idx, valuesPerArg)); | ||
} | ||
const argv = argvInput.slice(); | ||
return values; | ||
} | ||
function checkAndRemoveArg(arg) { | ||
let valuesPerArg = | ||
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
let values = null; | ||
function checkAndRemoveArgWithValue(arg) { | ||
const values = checkAndRemoveArg(arg, 1); | ||
return values ? values[0][0] : 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)); | ||
} | ||
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 | ||
); | ||
return values; | ||
} | ||
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. | ||
function checkAndRemoveArgWithValue(arg) { | ||
const values = checkAndRemoveArg(arg, 1); | ||
return values ? values[0][0] : null; | ||
} | ||
const sourceMapFileName = argv.shift(); | ||
const options = { | ||
nameSource: noFunctionNames ? "identifier_names" : "function_names", | ||
inputLineStart, | ||
inputColumnStart, | ||
outputLineStart, | ||
outputColumnStart | ||
}; | ||
let context; | ||
try { | ||
const noFunctionNames = checkAndRemoveArg("--no-function-names"); | ||
const isHermesCrash = checkAndRemoveArg("--hermes-crash"); | ||
const inputLineStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--input-line-start") || "1", | ||
10 | ||
if (fs.lstatSync(sourceMapFileName).isDirectory()) { | ||
context = Symbolication.unstable_createDirectoryContext( | ||
SourceMapConsumer, | ||
sourceMapFileName, | ||
options | ||
); | ||
const inputColumnStart = Number.parseInt( | ||
checkAndRemoveArgWithValue("--input-column-start") || "0", | ||
10 | ||
} else { | ||
const content = fs.readFileSync(sourceMapFileName, "utf8"); | ||
context = Symbolication.createContext( | ||
SourceMapConsumer, | ||
content, | ||
options | ||
); | ||
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. | ||
if (argv.length === 0) { | ||
const stackTrace = await readAll(stdin); | ||
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 | ||
if (isHermesCrash) { | ||
const stackTraceJSON = JSON.parse(stackTrace); | ||
const symbolicatedTrace = context.symbolicateHermesMinidumpTrace( | ||
stackTraceJSON | ||
); | ||
stdout.write(JSON.stringify(symbolicatedTrace)); | ||
} else { | ||
const content = fs.readFileSync(sourceMapFileName, "utf8"); | ||
context = Symbolication.createContext( | ||
SourceMapConsumer, | ||
content, | ||
options | ||
); | ||
stdout.write(context.symbolicate(stackTrace)); | ||
} | ||
} else if (argv[0].endsWith(".profmap")) { | ||
stdout.write(context.symbolicateProfilerMap(argv[0])); | ||
} else if ( | ||
argv[0].endsWith(".heapsnapshot") || | ||
argv[0].endsWith(".heaptimeline") | ||
) { | ||
stdout.write( | ||
JSON.stringify( | ||
context.symbolicateHeapSnapshot(fs.readFileSync(argv[0], "utf8")) | ||
) | ||
); | ||
} 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"); | ||
if (argv.length === 0) { | ||
const stackTrace = yield readAll(stdin); | ||
for (let i = 0, e = lines.length - 1; i < e; i++) { | ||
this.push(lines[i]); | ||
} | ||
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"); | ||
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 { | ||
var _original$source, _original$line, _original$name; | ||
for (let i = 0, e = lines.length - 1; i < e; i++) { | ||
this.push(lines[i]); | ||
} | ||
// read-from-argv form. | ||
let moduleIds; | ||
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 | ||
}); | ||
if (argv[0].endsWith(".js")) { | ||
moduleIds = context.parseFileName(argv[0]); | ||
argv.shift(); | ||
} else { | ||
var _original$source, _original$line, _original$name; // read-from-argv form. | ||
moduleIds = null; | ||
} | ||
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" | ||
); | ||
} | ||
} catch (error) { | ||
stderr.write(error + "\n"); | ||
return 1; | ||
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" | ||
); | ||
} | ||
} catch (error) { | ||
stderr.write(error + "\n"); | ||
return 1; | ||
} | ||
return 0; | ||
}); | ||
return _main.apply(this, arguments); | ||
return 0; | ||
} | ||
@@ -269,0 +222,0 @@ |
@@ -12,56 +12,2 @@ /** | ||
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; | ||
} | ||
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; | ||
} | ||
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; | ||
} | ||
const SourceMetadataMapConsumer = require("./SourceMetadataMapConsumer"); | ||
@@ -77,2 +23,4 @@ | ||
const { ChromeHeapSnapshotProcessor } = require("./ChromeHeapSnapshot"); | ||
const UNKNOWN_MODULE_IDS = { | ||
@@ -231,5 +179,3 @@ segmentId: 0, | ||
symbolicateChromeTrace(traceFile, _ref) { | ||
let stdout = _ref.stdout, | ||
stderr = _ref.stderr; | ||
symbolicateChromeTrace(traceFile, { stdout, stderr }) { | ||
const content = JSON.parse(fs.readFileSync(traceFile, "utf8")); | ||
@@ -352,2 +298,69 @@ | ||
} | ||
/** | ||
* 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) { | ||
const snapshotData = | ||
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 !== null && originalFunctionName !== void 0 | ||
? originalFunctionName | ||
: frame.getString("name") | ||
); | ||
} | ||
return snapshotData; | ||
} | ||
/* | ||
@@ -370,6 +383,3 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, sourceMapContent) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
constructor(SourceMapConsumer, sourceMapContent, options = {}) { // $FlowFixMe[value-as-type] | ||
super(options); | ||
@@ -398,3 +408,3 @@ this._SourceMapConsumer = SourceMapConsumer; | ||
const useFunctionNames = this.options.nameSource === "function_names"; | ||
const SourceMapConsumer = this._SourceMapConsumer; | ||
const { _SourceMapConsumer: SourceMapConsumer } = this; | ||
return { | ||
@@ -423,3 +433,3 @@ get consumer() { | ||
const symbolicatedTrace = []; | ||
const callstack = crashInfo.callstack; | ||
const { callstack } = crashInfo; | ||
@@ -431,7 +441,9 @@ if (callstack != null) { | ||
} else { | ||
const CJSModuleOffset = stackItem.CJSModuleOffset, | ||
SegmentID = stackItem.SegmentID, | ||
SourceURL = stackItem.SourceURL, | ||
FunctionID = stackItem.FunctionID, | ||
localOffset = stackItem.ByteCodeOffset; | ||
const { | ||
CJSModuleOffset, | ||
SegmentID, | ||
SourceURL, | ||
FunctionID, | ||
ByteCodeOffset: localOffset | ||
} = stackItem; | ||
const cjsModuleOffsetOrSegmentID = nullthrows( | ||
@@ -509,7 +521,6 @@ CJSModuleOffset !== null && CJSModuleOffset !== void 0 | ||
const metadata = this._segments[moduleIds.segmentId + ""]; | ||
const _moduleIds = moduleIds, | ||
localId = _moduleIds.localId; | ||
const { localId } = moduleIds; | ||
if (localId != null) { | ||
const moduleOffsets = metadata.moduleOffsets; | ||
const { moduleOffsets } = metadata; | ||
@@ -542,16 +553,13 @@ if (!moduleOffsets) { | ||
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 | ||
} | ||
); | ||
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 | ||
}; | ||
} | ||
@@ -566,6 +574,3 @@ | ||
// $FlowFixMe[value-as-type] | ||
constructor(SourceMapConsumer, rootDir) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
constructor(SourceMapConsumer, rootDir, options = {}) { // $FlowFixMe[value-as-type] | ||
super(options); | ||
@@ -684,6 +689,3 @@ this._fileMaps = new Map(); | ||
function createContext(SourceMapConsumer, sourceMapContent) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
function createContext(SourceMapConsumer, sourceMapContent, options = {}) { // $FlowFixMe[value-as-type] | ||
return new SingleMapSymbolicationContext( | ||
@@ -696,6 +698,7 @@ SourceMapConsumer, | ||
function unstable_createDirectoryContext(SourceMapConsumer, rootDir) { | ||
// $FlowFixMe[value-as-type] | ||
let options = | ||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
function unstable_createDirectoryContext( // $FlowFixMe[value-as-type] | ||
SourceMapConsumer, | ||
rootDir, | ||
options = {} | ||
) { | ||
return new DirectorySymbolicationContext(SourceMapConsumer, rootDir, options); | ||
@@ -720,5 +723,3 @@ } | ||
function symbolicateChromeTrace(traceFile, _ref2, context) { | ||
let stdout = _ref2.stdout, | ||
stderr = _ref2.stderr; | ||
function symbolicateChromeTrace(traceFile, { stdout, stderr }, context) { | ||
return context.symbolicateChromeTrace(traceFile, { | ||
@@ -725,0 +726,0 @@ stdout, |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
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
705519
46
4626
7
1
+ Addedmetro-source-map@0.65.0(transitive)
+ Addedob1@0.65.0(transitive)
- Removedmetro-source-map@0.64.0(transitive)
- Removedob1@0.64.0(transitive)
Updatedmetro-source-map@0.65.0