metro-symbolicate
Advanced tools
Comparing version
{ | ||
"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
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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
705519
361.73%46
170.59%4626
95.11%8
100%1
Infinity%+ Added
+ Added
- Removed
- Removed
Updated