metro-symbolicate
Advanced tools
Comparing version 0.80.5 to 0.80.6
{ | ||
"name": "metro-symbolicate", | ||
"version": "0.80.5", | ||
"version": "0.80.6", | ||
"description": "🚇 A tool to find the source location from JS bundles and stack traces.", | ||
@@ -21,3 +21,3 @@ "license": "MIT", | ||
"invariant": "^2.2.4", | ||
"metro-source-map": "0.80.5", | ||
"metro-source-map": "0.80.6", | ||
"nullthrows": "^1.1.1", | ||
@@ -24,0 +24,0 @@ "source-map": "^0.5.6", |
@@ -1,32 +0,6 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
"use strict"; | ||
const invariant = require("invariant"); | ||
// The snapshot metadata doesn't have a type describing the `children` field | ||
// of `trace_tree`, but modeling it as a type works really well. So we make up | ||
// our own name for it and use that internally. | ||
const CHILDREN_FIELD_TYPE = "__CHILDREN__"; | ||
// An adapter for reading and mutating a Chrome heap snapshot in-place, | ||
// including safely decoding and encoding fields that point into the global | ||
// string table and into enum types. | ||
// Care is taken to adhere to the self-describing heap snapshot schema, but | ||
// we make some additional assumptions based on what Chrome hardcodes (where | ||
// the format leaves us no other choice). | ||
class ChromeHeapSnapshotProcessor { | ||
// The raw snapshot data provided to this processor. Mutable. | ||
// An adapter for the global string table in the raw snapshot data. | ||
// This is shared across all the iterators we will create. | ||
constructor(snapshotData) { | ||
@@ -40,4 +14,2 @@ this._snapshotData = snapshotData; | ||
return new ChromeHeapSnapshotRecordIterator( | ||
// Flow is being conservative here, but we'll never change a number into RawBuffer or vice versa. | ||
// $FlowIgnore[incompatible-call] | ||
this._snapshotData.trace_function_infos, | ||
@@ -50,10 +22,7 @@ this._snapshotData.snapshot.meta.trace_function_info_fields, | ||
this._globalStringTable, | ||
undefined /* start position */ | ||
undefined | ||
); | ||
} | ||
locations() { | ||
return new ChromeHeapSnapshotRecordIterator( | ||
// Flow is being conservative here, but we'll never change a number into RawBuffer or vice versa. | ||
// $FlowIgnore[incompatible-call] | ||
this._snapshotData.locations, | ||
@@ -63,10 +32,7 @@ this._snapshotData.snapshot.meta.location_fields, | ||
this._globalStringTable, | ||
undefined /* start position */ | ||
undefined | ||
); | ||
} | ||
nodes() { | ||
return new ChromeHeapSnapshotRecordIterator( | ||
// Flow is being conservative here, but we'll never change a number into RawBuffer or vice versa. | ||
// $FlowIgnore[incompatible-call] | ||
this._snapshotData.nodes, | ||
@@ -76,10 +42,7 @@ this._snapshotData.snapshot.meta.node_fields, | ||
this._globalStringTable, | ||
undefined /* start position */ | ||
undefined | ||
); | ||
} | ||
edges() { | ||
return new ChromeHeapSnapshotRecordIterator( | ||
// Flow is being conservative here, but we'll never change a number into RawBuffer or vice versa. | ||
// $FlowIgnore[incompatible-call] | ||
this._snapshotData.edges, | ||
@@ -89,6 +52,5 @@ this._snapshotData.snapshot.meta.edge_fields, | ||
this._globalStringTable, | ||
undefined /* start position */ | ||
undefined | ||
); | ||
} | ||
traceTree() { | ||
@@ -102,15 +64,6 @@ return new ChromeHeapSnapshotRecordIterator( | ||
this._globalStringTable, | ||
undefined /* start position */ | ||
undefined | ||
); | ||
} | ||
} | ||
// An uniquing adapter for the heap snapshot's string table that allows | ||
// retrieving and adding strings. | ||
// | ||
// Assumptions: | ||
// 1. The string table is only manipulated via this class, and only via a | ||
// single instance of it. | ||
// 2. The string table array is always mutated in-place rather than being | ||
// copied / replaced with a new array in its containing object. | ||
class ChromeHeapSnapshotStringTable { | ||
@@ -120,7 +73,3 @@ constructor(strings) { | ||
this._indexCache = new Map(); | ||
// NOTE: _indexCache is lazily initialised in _syncIndexCache. | ||
} | ||
// Looks up a string in the string table, adds it if necessary, and returns | ||
// its index. | ||
add(value) { | ||
@@ -137,4 +86,2 @@ this._syncIndexCache(); | ||
} | ||
// Retrieve the string at the given index. | ||
get(index) { | ||
@@ -147,11 +94,3 @@ invariant( | ||
} | ||
// Indexes the string table for fast lookup. | ||
_syncIndexCache() { | ||
// Because we only grow the string table and we assume it's unique to begin | ||
// with, we only need to scan any strings that we may have appended since | ||
// the last time we synced the index. | ||
// NOTE: This is not even strictly necessary other than for the very first | ||
// add() call, but it might allow us to do more complicated string table | ||
// manipulation down the line. | ||
if (this._strings.length > this._indexCache.size) { | ||
@@ -164,35 +103,6 @@ for (let i = this._indexCache.size; i < this._strings.length; ++i) { | ||
} | ||
// A cursor pointing to a record-aligned position in a 1D array of N records | ||
// each with K fields in a fixed order. Supports encoding/decoding field values | ||
// in the raw array according to a schema passed to the constructor. | ||
// | ||
// Field values are stored as either numbers (representing scalars) or arrays | ||
// (representing lists of nested records). Scalar fields may represent strings | ||
// in the string table, strings in an enum, or numbers. Nested record lists are | ||
// processed according to the same schema as their parent record. | ||
// | ||
// Setters directly mutate raw data in the buffer and in the string table. | ||
class ChromeHeapSnapshotRecordAccessor { | ||
// Fast lookup tables from field names to their offsets (required) and types | ||
// (optional). These are shared with any child iterators. | ||
// The number of fields in every record (i.e. K). | ||
// The raw buffer. Mutable. | ||
// The global string table. Mutable in the ways allowed by the string table | ||
// class. | ||
// The current position in the raw buffer. | ||
constructor( | ||
buffer, | ||
recordFields, | ||
// recordTypes can be: | ||
// 1. An array: Field types as described in the snapshot itself, e.g. | ||
// node_types, edge_types. | ||
// 2. An object: Field types that are implicit (hardcoded in V8 / DevTools) | ||
// so we pass them in by field name. | ||
// 3. null: No field types are known. | ||
// Fields with unknown types are assumed to be numeric. | ||
recordTypes, | ||
@@ -210,3 +120,2 @@ globalStringTable, | ||
this._fieldToOffset = new Map( | ||
// $FlowFixMe[not-an-object] | ||
Object.entries(recordFields).map(([offsetStr, name]) => [ | ||
@@ -219,3 +128,2 @@ String(name), | ||
this._fieldToType = new Map( | ||
// $FlowFixMe[not-an-object] | ||
Object.entries(recordTypes).map(([offsetStr, type]) => [ | ||
@@ -227,3 +135,2 @@ recordFields[Number(offsetStr)], | ||
} else { | ||
// $FlowIssue[incompatible-type-arg] Object.entries is incompletely typed | ||
this._fieldToType = new Map(Object.entries(recordTypes || {})); | ||
@@ -244,9 +151,2 @@ } | ||
} | ||
/** Public API */ | ||
// Reads a scalar string or enum value from the given field. | ||
// It's an error to read a number (or other non-string) field as a string. | ||
// NOTE: The type "string_or_number" is always treated as a number and cannot | ||
// be read using this method. | ||
getString(field) { | ||
@@ -259,6 +159,2 @@ const dynamicValue = this._getScalar(field); | ||
} | ||
// Reads a scalar numeric value from the given field. | ||
// It's an error to read a string (or other non-number) field as a number. | ||
// NOTE: The type "string_or_number" is always treated as a number. | ||
getNumber(field) { | ||
@@ -271,6 +167,2 @@ const dynamicValue = this._getScalar(field); | ||
} | ||
// Returns an iterator over the children of this record that are stored in | ||
// the given field (typically 'children'). Children conform to the same | ||
// schema as the current record. | ||
getChildren(field) { | ||
@@ -289,25 +181,11 @@ const fieldType = this._fieldToType.get(field); | ||
[], | ||
// recordFields ignored when there's a parent | ||
null, | ||
// recordTypes ignored when there's a parent | ||
this._globalStringTable, | ||
-this._fieldToOffset.size /* start position */, | ||
-this._fieldToOffset.size, | ||
this | ||
); | ||
} | ||
// Writes a scalar string or enum value into the given field, updating the | ||
// global string table as needed. | ||
// It's an error to write anything other than a string into a string or enum | ||
// field. | ||
// It's an error to write an unknown enum value into an enum field. | ||
// NOTE: The type "string_or_number" is always treated as a number and cannot | ||
// be written using this method. | ||
setString(field, value) { | ||
this._setRaw(field, this._encodeString(field, value)); | ||
} | ||
// Writes a scalar numeric value into the given field. | ||
// It's an error to write anything other than a number into a numeric field. | ||
// NOTE: The type "string_or_number" is always treated as a number. | ||
setNumber(field, value) { | ||
@@ -324,15 +202,5 @@ const fieldType = this._fieldToType.get(field); | ||
} | ||
// Moves the cursor to a given index in the buffer (expressed in # of | ||
// records, NOT fields). | ||
moveToRecord(recordIndex) { | ||
this._moveToPosition(recordIndex * this._recordSize); | ||
} | ||
// Appends a new record at the end of the buffer. | ||
// | ||
// Returns the index of the appended record. All fields must be specified and | ||
// have values of the correct types. The cursor may move while writing, but | ||
// is guaranteed to return to its initial position when this function returns | ||
// (or throws). | ||
append(record) { | ||
@@ -346,20 +214,7 @@ const savedPosition = this._position; | ||
} | ||
// Moves the cursor to a given index in the buffer (expressed in # of | ||
// records, NOT fields) and inserts a record. | ||
// | ||
// Returns the index of the inserted record. All fields must be specified and | ||
// have values of the correct types. The given index may be the end of the | ||
// buffer; otherwise existing records starting at the given index will be | ||
// shifted to the right to accommodate the new record. | ||
// | ||
// NOTE: Inserting is a risky, low-level operation. Care must be taken not to | ||
// desync buffers that implicitly or explicitly depend on one another (e.g. | ||
// edge.to_node -> node position, cumulative node.edge_count -> edge indices). | ||
moveAndInsert(recordIndex, record) { | ||
this._moveToPosition(recordIndex * this._recordSize, /* allowEnd */ true); | ||
this._moveToPosition(recordIndex * this._recordSize, true); | ||
let didResizeBuffer = false; | ||
try { | ||
for (const field of this._fieldToOffset.keys()) { | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
if (!Object.prototype.hasOwnProperty.call(record, field)) { | ||
@@ -377,3 +232,2 @@ throw new Error("Missing value for field: " + field); | ||
if (didResizeBuffer) { | ||
// Roll back the write | ||
this._buffer.splice(this._position, this._recordSize); | ||
@@ -384,10 +238,4 @@ } | ||
} | ||
/** "Protected" methods (please don't use) */ | ||
// Return true if we can advance the position by one record (including from | ||
// the last record to the "end" position). | ||
protectedHasNext() { | ||
if (this._position < 0) { | ||
// We haven't started iterating yet, so this might _be_ the end position. | ||
return this._buffer.length > 0; | ||
@@ -397,16 +245,7 @@ } | ||
} | ||
// Move to the next record (or the end) if we're not already at the end. | ||
protectedTryMoveNext() { | ||
if (this.protectedHasNext()) { | ||
this._moveToPosition( | ||
this._position + this._recordSize, | ||
/* allowEnd */ true | ||
); | ||
this._moveToPosition(this._position + this._recordSize, true); | ||
} | ||
} | ||
/** Private methods */ | ||
// Reads the raw numeric value of a field. | ||
_getRaw(field) { | ||
@@ -420,4 +259,2 @@ this._validatePosition(); | ||
} | ||
// Decodes a scalar (string or number) field. | ||
_getScalar(field) { | ||
@@ -441,4 +278,2 @@ const rawValue = this._getRaw(field); | ||
} | ||
// Writes the raw value of a field. | ||
_setRaw(field, rawValue) { | ||
@@ -452,5 +287,2 @@ this._validatePosition(); | ||
} | ||
// Writes a scalar or children value to `field`, inferring the intended type | ||
// based on the runtime type of `value`. | ||
_set(field, value) { | ||
@@ -467,5 +299,2 @@ if (typeof value === "string") { | ||
} | ||
// Writes a children array to `field` by appending each element of `value` to | ||
// a new buffer using `append()`s semantics. | ||
_setChildren(field, value) { | ||
@@ -482,5 +311,2 @@ const fieldType = this._fieldToType.get(field); | ||
} | ||
// Encodes a string value according to its field schema. | ||
// The global string table may be updated as a side effect. | ||
_encodeString(field, value) { | ||
@@ -498,6 +324,2 @@ const fieldType = this._fieldToType.get(field); | ||
} | ||
// Asserts that the given position (default: the current position) is either | ||
// a valid position for reading a record, or (if allowEnd is true) the end of | ||
// the buffer. | ||
_validatePosition(allowEnd = false, position = this._position) { | ||
@@ -531,4 +353,2 @@ if (!Number.isInteger(position)) { | ||
} | ||
// Move to the given position or throw an error if it is invalid. | ||
_moveToPosition(nextPosition, allowEnd = false) { | ||
@@ -539,4 +359,2 @@ this._validatePosition(allowEnd, nextPosition); | ||
} | ||
// $FlowIssue[prop-missing] Flow doesn't see that we implement the iteration protocol | ||
class ChromeHeapSnapshotRecordIterator extends ChromeHeapSnapshotRecordAccessor { | ||
@@ -548,6 +366,2 @@ constructor( | ||
globalStringTable, | ||
// Initialise to "before the first iteration". | ||
// The Accessor constructor intentionally checks only alignment, not range, | ||
// so this works as long as we don't try to read/write (at which point | ||
// validation will kick in). | ||
position = -recordFields.length, | ||
@@ -565,4 +379,2 @@ parent | ||
} | ||
// JS Iterator protocol | ||
next() { | ||
@@ -575,5 +387,2 @@ this.protectedTryMoveNext(); | ||
} | ||
// JS Iterable protocol | ||
// $FlowIssue[unsupported-syntax] | ||
[Symbol.iterator]() { | ||
@@ -580,0 +389,0 @@ return this; |
@@ -1,29 +0,4 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
"use strict"; | ||
const { normalizeSourcePath } = require("metro-source-map"); | ||
/** | ||
* Consumes the `x_google_ignoreList` metadata field from a source map and | ||
* exposes various queries on it. | ||
* | ||
* By default, source names are normalized using the same logic that the | ||
* `source-map@0.5.6` package uses internally. This is crucial for keeping the | ||
* sources list in sync with a `SourceMapConsumer` instance. | ||
* If you're using this with a different source map reader (e.g. one that | ||
* doesn't normalize source names at all), you can switch out the normalization | ||
* function in the constructor, e.g. | ||
* | ||
* new GoogleIgnoreListConsumer(map, source => source) // Don't normalize | ||
*/ | ||
class GoogleIgnoreListConsumer { | ||
@@ -34,21 +9,5 @@ constructor(map, normalizeSourceFn = normalizeSourcePath) { | ||
} | ||
/** | ||
* Returns `true` if the given source is in this map's ignore list, `false` | ||
* otherwise. | ||
* | ||
* When used with the `source-map` package, you'll first use | ||
* `SourceMapConsumer#originalPositionFor` to retrieve a source location, | ||
* then pass that location to `isIgnored`. | ||
*/ | ||
isIgnored({ source }) { | ||
return source != null && this._getIgnoredSourceSet().has(source); | ||
} | ||
/** | ||
* Returns this map's ignore list as a new array with indices based on | ||
* `sources`. | ||
* | ||
* This array can be used as the `x_google_ignoreList` field of a map whose | ||
* `sources` field is the array that was passed into this method. | ||
*/ | ||
toArray(sources) { | ||
@@ -64,6 +23,2 @@ const ignoredSourceSet = this._getIgnoredSourceSet(); | ||
} | ||
/** | ||
* Prepares and caches a set of ignored sources for this map. | ||
*/ | ||
_getIgnoredSourceSet() { | ||
@@ -77,23 +32,3 @@ if (!this._ignoredSourceSet) { | ||
} | ||
/** | ||
* Collects ignored sources from the given map using the current source name | ||
* normalization function. Handles both index maps (with sections) and plain | ||
* maps. | ||
* | ||
* NOTE: If any sources are repeated in the map, we consider a source to be | ||
* ignored as long as a source with the same normalized name is listed in AT | ||
* LEAST one `x_google_ignoreList` array. Technically, this means we lose | ||
* the granularity afforded by index maps and by the ability to repeat source | ||
* names within a single `sources` array. | ||
* | ||
* Chrome's handling of duplicates is different: only the first occurrence of | ||
* a given source is considered when determining if a source is ignored. It's | ||
* unclear whether this is intentional. Absent a formal spec for | ||
* `x_google_ignoreList`, we will diverge from Chrome for now. | ||
* | ||
* See: https://github.com/ChromeDevTools/devtools-frontend/blob/7afc9157b8d05de06e273284119e9c55a4eadb72/front_end/core/sdk/SourceMap.ts#L425-L429 | ||
*/ | ||
_buildIgnoredSourceSet(map, ignoredSourceSet) { | ||
// eslint-disable-next-line lint/strictly-null | ||
if (map.mappings === undefined) { | ||
@@ -100,0 +35,0 @@ const indexMap = map; |
#!/usr/bin/env node | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
"use strict"; | ||
// $FlowFixMe[unused-promise] | ||
require("./symbolicate.js")().then((code) => process.exit(code)); |
@@ -1,12 +0,1 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
"use strict"; | ||
@@ -17,16 +6,2 @@ | ||
const METADATA_FIELD_FUNCTIONS = 0; | ||
/** | ||
* Consumes the `x_facebook_sources` metadata field from a source map and | ||
* exposes various queries on it. | ||
* | ||
* By default, source names are normalized using the same logic that the | ||
* `source-map@0.5.6` package uses internally. This is crucial for keeping the | ||
* sources list in sync with a `SourceMapConsumer` instance. | ||
* If you're using this with a different source map reader (e.g. one that | ||
* doesn't normalize source names at all), you can switch out the normalization | ||
* function in the constructor, e.g. | ||
* | ||
* new SourceMetadataMapConsumer(map, source => source) // Don't normalize | ||
*/ | ||
class SourceMetadataMapConsumer { | ||
@@ -38,10 +13,2 @@ constructor(map, normalizeSourceFn = normalizeSourcePath) { | ||
} | ||
/** | ||
* Retrieves a human-readable name for the function enclosing a particular | ||
* source location. | ||
* | ||
* When used with the `source-map` package, you'll first use | ||
* `SourceMapConsumer#originalPositionFor` to retrieve a source location, | ||
* then pass that location to `functionNameFor`. | ||
*/ | ||
functionNameFor({ line, column, source }) { | ||
@@ -62,10 +29,2 @@ if (source && line != null && column != null) { | ||
} | ||
/** | ||
* Returns this map's source metadata as a new array with the same order as | ||
* `sources`. | ||
* | ||
* This array can be used as the `x_facebook_sources` field of a map whose | ||
* `sources` field is the array that was passed into this method. | ||
*/ | ||
toArray(sources) { | ||
@@ -79,6 +38,2 @@ const metadataBySource = this._getMetadataBySource(); | ||
} | ||
/** | ||
* Prepares and caches a lookup table of metadata by source name. | ||
*/ | ||
_getMetadataBySource() { | ||
@@ -92,7 +47,2 @@ if (!this._metadataBySource) { | ||
} | ||
/** | ||
* Decodes the function name mappings for the given source if needed, and | ||
* retrieves a sorted, searchable array of mappings. | ||
*/ | ||
_getFunctionMappings(source) { | ||
@@ -104,3 +54,2 @@ if (this._decodedFunctionMapCache.has(source)) { | ||
const metadataBySource = this._getMetadataBySource(); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
if (Object.prototype.hasOwnProperty.call(metadataBySource, source)) { | ||
@@ -113,14 +62,3 @@ const metadata = metadataBySource[source] || []; | ||
} | ||
/** | ||
* Collects source metadata from the given map using the current source name | ||
* normalization function. Handles both index maps (with sections) and plain | ||
* maps. | ||
* | ||
* NOTE: If any sources are repeated in the map (which shouldn't happen in | ||
* Metro, but is technically possible because of index maps) we only keep the | ||
* metadata from the last occurrence of any given source. | ||
*/ | ||
_getMetadataObjectsBySourceNames(map) { | ||
// eslint-disable-next-line lint/strictly-null | ||
if (map.mappings === undefined) { | ||
@@ -127,0 +65,0 @@ const indexMap = map; |
@@ -1,20 +0,1 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
// Symbolicates a JavaScript stack trace using a source map. | ||
// In our first form, we read a stack trace from stdin and symbolicate it via | ||
// the provided source map. | ||
// In our second form, we symbolicate using an explicit line number, and | ||
// optionally a column. | ||
// In our third form, we symbolicate using a module ID, a line number, and | ||
// optionally a column. | ||
"use strict"; | ||
@@ -24,5 +5,3 @@ | ||
const fs = require("fs"); | ||
// flowlint-next-line untyped-import:off | ||
const SourceMapConsumer = require("source-map").SourceMapConsumer; | ||
// flowlint-next-line untyped-import:off | ||
const through2 = require("through2"); | ||
@@ -52,8 +31,3 @@ function printHelp() { | ||
argvInput = process.argv.slice(2), | ||
// prettier-ignore | ||
{ | ||
stdin, | ||
stderr, | ||
stdout | ||
} = process | ||
{ stdin, stderr, stdout } = process | ||
) { | ||
@@ -95,3 +69,2 @@ const argv = argvInput.slice(); | ||
if (argv.length < 1 || argv.length > 4) { | ||
/* eslint no-path-concat: "off" */ | ||
printHelp(); | ||
@@ -107,4 +80,2 @@ return 1; | ||
} | ||
// Read the source map. | ||
const sourceMapFileName = argv.shift(); | ||
@@ -164,6 +135,3 @@ const options = { | ||
.pipe( | ||
/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) | ||
* required by Flow's LTI update could not be added via codemod */ | ||
through2(function (data, enc, callback) { | ||
// Take arbitrary strings, output single lines | ||
buffer += data; | ||
@@ -179,6 +147,3 @@ const lines = buffer.split("\n"); | ||
.pipe( | ||
/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) | ||
* required by Flow's LTI update could not be added via codemod */ | ||
through2.obj(function (data, enc, callback) { | ||
// This is JSONL, so each line is a separate JSON object | ||
const obj = JSON.parse(data); | ||
@@ -193,3 +158,2 @@ context.symbolicateAttribution(obj); | ||
} else if (argv[0].endsWith(".cpuprofile")) { | ||
// NOTE: synchronous | ||
context.symbolicateChromeTrace(argv[0], { | ||
@@ -200,3 +164,2 @@ stdout, | ||
} else { | ||
// read-from-argv form. | ||
let moduleIds; | ||
@@ -214,3 +177,2 @@ if (argv[0].endsWith(".js")) { | ||
+columnNumber, | ||
// $FlowFixMe context is a union here and so this parameter is a union | ||
moduleIds | ||
@@ -242,3 +204,2 @@ ); | ||
let chunk; | ||
// flowlint-next-line sketchy-null-string:off | ||
while ((chunk = stream.read())) { | ||
@@ -245,0 +206,0 @@ data += chunk.toString(); |
"use strict"; | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
// flowlint-next-line untyped-type-import:off | ||
const { ChromeHeapSnapshotProcessor } = require("./ChromeHeapSnapshot"); | ||
@@ -48,3 +35,2 @@ const GoogleIgnoreListConsumer = require("./GoogleIgnoreListConsumer"); | ||
if (options.nameSource != null) { | ||
// $FlowFixMe[cannot-write] | ||
this.options.nameSource = options.nameSource; | ||
@@ -54,13 +40,2 @@ } | ||
} | ||
// parse stack trace with String.replace | ||
// replace the matched part of stack trace to symbolicated result | ||
// sample stack trace: | ||
// IOS: foo@4:18131, Android: bar:4:18063 | ||
// sample stack trace with module id: | ||
// IOS: foo@123.js:4:18131, Android: bar:123.js:4:18063 | ||
// sample stack trace without function name: | ||
// 123.js:4:18131 | ||
// sample result: | ||
// IOS: foo.js:57:foo, Android: bar.js:75:bar | ||
symbolicate(stackTrace) { | ||
@@ -89,12 +64,2 @@ return stackTrace.replace( | ||
} | ||
// Taking in a map like | ||
// trampoline offset (optional js function name) | ||
// JS_0158_xxxxxxxxxxxxxxxxxxxxxx fe 91081 | ||
// JS_0159_xxxxxxxxxxxxxxxxxxxxxx Ft 68651 | ||
// JS_0160_xxxxxxxxxxxxxxxxxxxxxx value 50700 | ||
// JS_0161_xxxxxxxxxxxxxxxxxxxxxx setGapAtCursor 0 | ||
// JS_0162_xxxxxxxxxxxxxxxxxxxxxx (unknown) 50818 | ||
// JS_0163_xxxxxxxxxxxxxxxxxxxxxx value 108267 | ||
symbolicateProfilerMap(mapFile) { | ||
@@ -139,11 +104,2 @@ return fs | ||
const bytecodeSize = Number(loc.bytecodeSize); | ||
// Functions compiled from Metro-bundled modules will often have a little bit | ||
// of unmapped wrapper code right at the beginning - which is where we query. | ||
// Let's attribute them to where the inner module code originates instead. | ||
// This loop is O(n*log(n)) in the size of the function, but we will generally | ||
// either: | ||
// 1. Find a non-null mapping within one or two iterations; or | ||
// 2. Reach the end of the function without encountering mappings - this might | ||
// happen for function bodies that never throw (generally very short). | ||
while ( | ||
@@ -162,7 +118,2 @@ isBytecodeRange && | ||
} | ||
// 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, { stdout, stderr }) { | ||
@@ -179,13 +130,7 @@ const content = JSON.parse(fs.readFileSync(traceFile, "utf8")); | ||
let column; | ||
// Function entrypoint line/column; used for symbolicating function name | ||
// with legacy source maps (or when --no-function-names is set). | ||
let funcLine; | ||
let funcColumn; | ||
if (entry.funcVirtAddr != null && entry.offset != null) { | ||
// Without debug information. | ||
const funcVirtAddr = parseInt(entry.funcVirtAddr, 10); | ||
const offsetInFunction = parseInt(entry.offset, 10); | ||
// Main bundle always use hard-coded line value 1. | ||
// TODO: support multiple bundle/module. | ||
line = this.options.inputLineStart; | ||
@@ -196,6 +141,2 @@ column = funcVirtAddr + offsetInFunction; | ||
} else if (entry.line != null && entry.column != null) { | ||
// For hbc bundle with debug info, name field may already have source | ||
// information for the bundle; we still can use the Metro | ||
// source map to symbolicate the bundle frame addresses further to its | ||
// original source code. | ||
line = entry.line; | ||
@@ -206,7 +147,4 @@ column = entry.column; | ||
} else { | ||
// Native frames. | ||
return; | ||
} | ||
// Symbolicate original file/line/column. | ||
const addressOriginal = this.getOriginalPositionDetailsFor(line, column); | ||
@@ -218,3 +156,2 @@ let frameName; | ||
frameName = entry.name; | ||
// Symbolicate function name. | ||
if (funcLine != null && funcColumn != null) { | ||
@@ -229,3 +166,2 @@ const funcOriginal = this.getOriginalPositionFor( | ||
} else { | ||
// No function line/column info. | ||
(stderr || stdout).write( | ||
@@ -236,4 +172,2 @@ "Warning: no function prolog line/column info; name may be wrong\n" | ||
} | ||
// Output format is: funcName(file:line:column) | ||
entry.name = [ | ||
@@ -253,7 +187,2 @@ frameName, | ||
} | ||
/* | ||
* A helper function to return a mapping {line, column} object for a given input | ||
* line and column, and optionally a module ID. | ||
*/ | ||
getOriginalPositionFor(lineNumber, columnNumber, moduleIds) { | ||
@@ -272,18 +201,5 @@ const position = this.getOriginalPositionDetailsFor( | ||
} | ||
/* | ||
* Symbolicates the JavaScript stack trace extracted from the minidump | ||
* produced by hermes | ||
*/ | ||
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) { | ||
@@ -335,7 +251,2 @@ const snapshotData = | ||
} | ||
/* | ||
* Symbolicates the JavaScript stack trace extracted from the coverage information | ||
* produced by HermesRuntime::getExecutedFunctions. | ||
*/ | ||
symbolicateHermesCoverageTrace(coverageInfo) { | ||
@@ -359,7 +270,2 @@ const symbolicatedTrace = []; | ||
} | ||
/* | ||
* An internal helper function similar to getOriginalPositionFor. This one | ||
* returns both `name` and `functionName` fields so callers can distinguish the | ||
* source of the name. | ||
*/ | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) { | ||
@@ -373,10 +279,3 @@ throw new Error("Not implemented"); | ||
class SingleMapSymbolicationContext extends SymbolicationContext { | ||
// $FlowFixMe[value-as-type] | ||
constructor( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer, | ||
sourceMapContent, | ||
options = {} | ||
) { | ||
constructor(SourceMapConsumer, sourceMapContent, options = {}) { | ||
super(options); | ||
@@ -393,5 +292,3 @@ this._SourceMapConsumer = SourceMapConsumer; | ||
for (const key of Object.keys(sourceMapJson.x_facebook_segments)) { | ||
// $FlowFixMe[incompatible-use] | ||
const map = sourceMapJson.x_facebook_segments[key]; | ||
// $FlowFixMe[prop-missing] | ||
segments[key] = this._initSegment(map); | ||
@@ -405,4 +302,2 @@ } | ||
} | ||
// $FlowFixMe[missing-local-annot] | ||
_initSegment(map) { | ||
@@ -413,7 +308,5 @@ const useFunctionNames = this.options.nameSource === "function_names"; | ||
get consumer() { | ||
// $FlowFixMe[object-this-reference] | ||
Object.defineProperty(this, "consumer", { | ||
value: new SourceMapConsumer(map), | ||
}); | ||
// $FlowFixMe[object-this-reference] | ||
return this.consumer; | ||
@@ -423,15 +316,11 @@ }, | ||
get sourceFunctionsConsumer() { | ||
// $FlowFixMe[object-this-reference] | ||
Object.defineProperty(this, "sourceFunctionsConsumer", { | ||
value: useFunctionNames ? new SourceMetadataMapConsumer(map) : null, | ||
}); | ||
// $FlowFixMe[object-this-reference] | ||
return this.sourceFunctionsConsumer; | ||
}, | ||
get googleIgnoreListConsumer() { | ||
// $FlowFixMe[object-this-reference] | ||
Object.defineProperty(this, "googleIgnoreListConsumer", { | ||
value: new GoogleIgnoreListConsumer(map), | ||
}); | ||
// $FlowFixMe[object-this-reference] | ||
return this.googleIgnoreListConsumer; | ||
@@ -513,10 +402,3 @@ }, | ||
} | ||
/* | ||
* An internal helper function similar to getOriginalPositionFor. This one | ||
* returns both `name` and `functionName` fields so callers can distinguish the | ||
* source of the name. | ||
*/ | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) { | ||
// Adjust arguments to source-map's input coordinates | ||
lineNumber = | ||
@@ -580,10 +462,3 @@ lineNumber != null | ||
class DirectorySymbolicationContext extends SymbolicationContext { | ||
// $FlowFixMe[value-as-type] | ||
constructor( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer, | ||
rootDir, | ||
options = {} | ||
) { | ||
constructor(SourceMapConsumer, rootDir, options = {}) { | ||
super(options); | ||
@@ -610,8 +485,2 @@ this._fileMaps = new Map(); | ||
} | ||
/* | ||
* An internal helper function similar to getOriginalPositionFor. This one | ||
* returns both `name` and `functionName` fields so callers can distinguish the | ||
* source of the name. | ||
*/ | ||
getOriginalPositionDetailsFor(lineNumber, columnNumber, filename) { | ||
@@ -627,3 +496,2 @@ invariant( | ||
); | ||
// Lock down access to files outside the root dir. | ||
if (!relativeFilename.startsWith("..")) { | ||
@@ -633,3 +501,2 @@ mapFilename = path.join(this._rootDir, relativeFilename + ".map"); | ||
if (mapFilename == null || !fs.existsSync(mapFilename)) { | ||
// Adjust arguments to the output coordinates | ||
lineNumber = | ||
@@ -665,13 +532,2 @@ lineNumber != null | ||
} | ||
/* | ||
* If the file name of a stack frame is numeric (+ ".js"), we assume it's a | ||
* lazily injected module coming from a "random access bundle". We are using | ||
* special source maps for these bundles, so that we can symbolicate stack | ||
* traces for multiple injected files with a single source map. | ||
* | ||
* There is also a convention for callsites that are in split segments of a | ||
* bundle, named either `seg-3.js` for segment #3 for example, or `seg-3_5.js` | ||
* for module #5 of segment #3 of a segmented RAM bundle. | ||
*/ | ||
function parseSingleMapFileName(str) { | ||
@@ -694,8 +550,3 @@ const modMatch = str.match(/^(\d+).js$/); | ||
} | ||
function createContext( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer, | ||
sourceMapContent, | ||
options = {} | ||
) { | ||
function createContext(SourceMapConsumer, sourceMapContent, options = {}) { | ||
return new SingleMapSymbolicationContext( | ||
@@ -708,3 +559,2 @@ SourceMapConsumer, | ||
function unstable_createDirectoryContext( | ||
// $FlowFixMe[value-as-type] | ||
SourceMapConsumer, | ||
@@ -711,0 +561,0 @@ rootDir, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109779
1314
+ Addedmetro-source-map@0.80.6(transitive)
+ Addedob1@0.80.6(transitive)
- Removedmetro-source-map@0.80.5(transitive)
- Removedob1@0.80.5(transitive)
Updatedmetro-source-map@0.80.6