yeet
This commit is contained in:
29
node_modules/metro-symbolicate/package.json
generated
vendored
Normal file
29
node_modules/metro-symbolicate/package.json
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "metro-symbolicate",
|
||||
"version": "0.59.0",
|
||||
"description": "A tool to find the source location from JS bundles and stack traces.",
|
||||
"license": "MIT",
|
||||
"main": "./src/symbolicate.js",
|
||||
"bin": "./src/symbolicate.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:facebook/metro.git"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare-release": "test -d build && rm -rf src.real && mv src src.real && mv build src",
|
||||
"cleanup-release": "test ! -e build && mv src build && mv src.real src"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.3"
|
||||
},
|
||||
"keywords": [
|
||||
"metro"
|
||||
],
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4",
|
||||
"metro-source-map": "0.59.0",
|
||||
"source-map": "^0.5.6",
|
||||
"through2": "^2.0.1",
|
||||
"vlq": "^1.0.0"
|
||||
}
|
||||
}
|
233
node_modules/metro-symbolicate/src.real/SourceMetadataMapConsumer.js
generated
vendored
Normal file
233
node_modules/metro-symbolicate/src.real/SourceMetadataMapConsumer.js
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const vlq = require('vlq');
|
||||
|
||||
const {normalizeSourcePath} = require('metro-source-map');
|
||||
|
||||
import type {
|
||||
MixedSourceMap,
|
||||
FBSourcesArray,
|
||||
FBSourceFunctionMap,
|
||||
FBSourceMetadata,
|
||||
BasicSourceMap,
|
||||
IndexMap,
|
||||
} from 'metro-source-map';
|
||||
|
||||
const METADATA_FIELD_FUNCTIONS = 0;
|
||||
|
||||
type Position = {
|
||||
+line: number,
|
||||
+column: number,
|
||||
...
|
||||
};
|
||||
type FunctionMapping = {
|
||||
+line: number,
|
||||
+column: number,
|
||||
+name: string,
|
||||
...
|
||||
};
|
||||
type SourceNameNormalizer = (string, {+sourceRoot?: ?string, ...}) => string;
|
||||
type MetadataMap = {[source: string]: ?FBSourceMetadata, ...};
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
constructor(
|
||||
map: MixedSourceMap,
|
||||
normalizeSourceFn: SourceNameNormalizer = normalizeSourcePath,
|
||||
) {
|
||||
this._sourceMap = map;
|
||||
this._decodedFunctionMapCache = new Map();
|
||||
this._normalizeSource = normalizeSourceFn;
|
||||
}
|
||||
|
||||
_sourceMap: MixedSourceMap;
|
||||
_decodedFunctionMapCache: Map<string, ?$ReadOnlyArray<FunctionMapping>>;
|
||||
_normalizeSource: SourceNameNormalizer;
|
||||
_metadataBySource: ?MetadataMap;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
}: Position & {+source: ?string, ...}): ?string {
|
||||
if (source && line != null && column != null) {
|
||||
const mappings = this._getFunctionMappings(source);
|
||||
if (mappings) {
|
||||
const mapping = findEnclosingMapping(mappings, {line, column});
|
||||
if (mapping) {
|
||||
return mapping.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 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: $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(): MetadataMap {
|
||||
if (!this._metadataBySource) {
|
||||
this._metadataBySource = this._getMetadataObjectsBySourceNames(
|
||||
this._sourceMap,
|
||||
);
|
||||
}
|
||||
|
||||
return this._metadataBySource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the function name mappings for the given source if needed, and
|
||||
* retrieves a sorted, searchable array of mappings.
|
||||
*/
|
||||
_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)) {
|
||||
const metadata = metadataBySource[source] || [];
|
||||
parsedFunctionMap = decodeFunctionMap(metadata[METADATA_FIELD_FUNCTIONS]);
|
||||
}
|
||||
this._decodedFunctionMapCache.set(source, parsedFunctionMap);
|
||||
return parsedFunctionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: MixedSourceMap): MetadataMap {
|
||||
// eslint-disable-next-line lint/strictly-null
|
||||
if (map.mappings === undefined) {
|
||||
const indexMap: IndexMap = map;
|
||||
return Object.assign(
|
||||
{},
|
||||
...indexMap.sections.map(section =>
|
||||
this._getMetadataObjectsBySourceNames(section.map),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
source = this._normalizeSource(source, basicMap);
|
||||
acc[source] = metadata;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function decodeFunctionMap(
|
||||
functionMap: ?FBSourceFunctionMap,
|
||||
): $ReadOnlyArray<FunctionMapping> {
|
||||
if (!functionMap) {
|
||||
return [];
|
||||
}
|
||||
const parsed = [];
|
||||
let line = 1;
|
||||
let nameIndex = 0;
|
||||
for (const lineMappings of functionMap.mappings.split(';')) {
|
||||
let column = 0;
|
||||
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]});
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function findEnclosingMapping(
|
||||
mappings: $ReadOnlyArray<FunctionMapping>,
|
||||
target: Position,
|
||||
): ?FunctionMapping {
|
||||
let first = 0;
|
||||
let it = 0;
|
||||
let count = mappings.length;
|
||||
let step;
|
||||
while (count > 0) {
|
||||
it = first;
|
||||
step = Math.floor(count / 2);
|
||||
it += step;
|
||||
if (comparePositions(target, mappings[it]) >= 0) {
|
||||
first = ++it;
|
||||
count -= step + 1;
|
||||
} else {
|
||||
count = step;
|
||||
}
|
||||
}
|
||||
return first ? mappings[first - 1] : null;
|
||||
}
|
||||
|
||||
function comparePositions(a: Position, b: Position): number {
|
||||
if (a.line === b.line) {
|
||||
return a.column - b.column;
|
||||
}
|
||||
return a.line - b.line;
|
||||
}
|
||||
|
||||
module.exports = SourceMetadataMapConsumer;
|
742
node_modules/metro-symbolicate/src.real/Symbolication.js
generated
vendored
Normal file
742
node_modules/metro-symbolicate/src.real/Symbolication.js
generated
vendored
Normal file
@ -0,0 +1,742 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const SourceMetadataMapConsumer = require('./SourceMetadataMapConsumer');
|
||||
|
||||
const fs = require('fs');
|
||||
const invariant = require('invariant');
|
||||
const path = require('path');
|
||||
|
||||
import type {MixedSourceMap, HermesFunctionOffsets} from 'metro-source-map';
|
||||
// flowlint-next-line untyped-type-import:off
|
||||
import {typeof SourceMapConsumer} from 'source-map';
|
||||
|
||||
type SingleMapModuleIds = {
|
||||
segmentId: number,
|
||||
localId: ?number,
|
||||
...
|
||||
};
|
||||
|
||||
type ContextOptionsInput = {
|
||||
+nameSource?: 'function_names' | 'identifier_names',
|
||||
+inputLineStart?: number,
|
||||
+inputColumnStart?: number,
|
||||
+outputLineStart?: number,
|
||||
+outputColumnStart?: number,
|
||||
...
|
||||
};
|
||||
|
||||
// TODO (T46584006): Write the real types for these.
|
||||
// eslint-disable-next-line lint/no-unclear-flowtypes
|
||||
type SizeAttributionMap = Object;
|
||||
// eslint-disable-next-line lint/no-unclear-flowtypes
|
||||
type ChromeTrace = Object;
|
||||
// eslint-disable-next-line lint/no-unclear-flowtypes
|
||||
type ChromeTraceEntry = Object;
|
||||
|
||||
type HermesMinidumpCrashInfo = {
|
||||
+callstack: $ReadOnlyArray<HermesMinidumpStackFrame | NativeCodeStackFrame>,
|
||||
...
|
||||
};
|
||||
|
||||
type HermesMinidumpStackFrame = $ReadOnly<{|
|
||||
ByteCodeOffset: number,
|
||||
FunctionID: number,
|
||||
CJSModuleOffset: number,
|
||||
SourceURL: string,
|
||||
StackFrameRegOffs: string,
|
||||
SourceLocation?: string,
|
||||
|}>;
|
||||
|
||||
type NativeCodeStackFrame = $ReadOnly<{|
|
||||
NativeCode: true,
|
||||
StackFrameRegOffs: string,
|
||||
|}>;
|
||||
|
||||
type SymbolicatedStackTrace = $ReadOnlyArray<
|
||||
SymbolicatedStackFrame | NativeCodeStackFrame,
|
||||
>;
|
||||
|
||||
type SymbolicatedStackFrame = $ReadOnly<{|
|
||||
line: ?number,
|
||||
column: ?number,
|
||||
source: ?string,
|
||||
functionName: ?string,
|
||||
name: ?string,
|
||||
|}>;
|
||||
|
||||
const UNKNOWN_MODULE_IDS: SingleMapModuleIds = {
|
||||
segmentId: 0,
|
||||
localId: undefined,
|
||||
};
|
||||
|
||||
class SymbolicationContext<ModuleIdsT> {
|
||||
+options: {
|
||||
+nameSource: 'function_names' | 'identifier_names',
|
||||
+inputLineStart: number,
|
||||
+inputColumnStart: number,
|
||||
+outputLineStart: number,
|
||||
+outputColumnStart: number,
|
||||
...
|
||||
};
|
||||
|
||||
constructor(options: ContextOptionsInput) {
|
||||
this.options = {
|
||||
inputLineStart: 1,
|
||||
inputColumnStart: 0,
|
||||
outputLineStart: 1,
|
||||
outputColumnStart: 0,
|
||||
nameSource: 'function_names',
|
||||
};
|
||||
if (options) {
|
||||
for (const option of [
|
||||
'inputLineStart',
|
||||
'inputColumnStart',
|
||||
'outputLineStart',
|
||||
'outputColumnStart',
|
||||
]) {
|
||||
if (options[option] != null) {
|
||||
this.options[option] = options[option];
|
||||
}
|
||||
}
|
||||
if (options.nameSource != null) {
|
||||
this.options.nameSource = options.nameSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse stack trace with String.replace
|
||||
// replace the matched part of stack trace to symbolicated result
|
||||
// sample stack trace:
|
||||
// IOS: foo@4:18131, Android: bar:4:18063
|
||||
// sample stack trace with module id:
|
||||
// IOS: foo@123.js:4:18131, Android: bar:123.js:4:18063
|
||||
// sample stack trace without function name:
|
||||
// 123.js:4:18131
|
||||
// sample result:
|
||||
// IOS: foo.js:57:foo, Android: bar.js:75:bar
|
||||
symbolicate(stackTrace: string): string {
|
||||
return stackTrace.replace(
|
||||
/(?:([^@: \n(]+)(@|:))?(?:(?:([^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g,
|
||||
(match, func, delimiter, fileName, line, column) => {
|
||||
if (delimiter === ':' && func && !fileName) {
|
||||
fileName = func;
|
||||
func = null;
|
||||
}
|
||||
const original = this.getOriginalPositionFor(
|
||||
line,
|
||||
column,
|
||||
this.parseFileName(fileName || ''),
|
||||
);
|
||||
return (
|
||||
(original.source ?? 'null') +
|
||||
':' +
|
||||
(original.line ?? 'null') +
|
||||
':' +
|
||||
(original.name ?? 'null')
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Taking in a map like
|
||||
// trampoline offset (optional js function name)
|
||||
// JS_0158_xxxxxxxxxxxxxxxxxxxxxx fe 91081
|
||||
// JS_0159_xxxxxxxxxxxxxxxxxxxxxx Ft 68651
|
||||
// JS_0160_xxxxxxxxxxxxxxxxxxxxxx value 50700
|
||||
// JS_0161_xxxxxxxxxxxxxxxxxxxxxx setGapAtCursor 0
|
||||
// JS_0162_xxxxxxxxxxxxxxxxxxxxxx (unknown) 50818
|
||||
// JS_0163_xxxxxxxxxxxxxxxxxxxxxx value 108267
|
||||
|
||||
symbolicateProfilerMap(mapFile: string): string {
|
||||
return fs
|
||||
.readFileSync(mapFile, 'utf8')
|
||||
.split('\n')
|
||||
.slice(0, -1)
|
||||
.map(line => {
|
||||
const line_list = line.split(' ');
|
||||
const trampoline = line_list[0];
|
||||
const js_name = line_list[1];
|
||||
const offset = parseInt(line_list[2], 10);
|
||||
|
||||
if (!offset) {
|
||||
return trampoline + ' ' + trampoline;
|
||||
}
|
||||
|
||||
const original = this.getOriginalPositionFor(
|
||||
this.options.inputLineStart,
|
||||
offset,
|
||||
);
|
||||
|
||||
return (
|
||||
trampoline +
|
||||
' ' +
|
||||
(original.name || js_name) +
|
||||
'::' +
|
||||
[original.source, original.line, original.column].join(':')
|
||||
);
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
symbolicateAttribution(obj: SizeAttributionMap): SizeAttributionMap {
|
||||
const loc = obj.location;
|
||||
const line = loc.line != null ? loc.line : this.options.inputLineStart;
|
||||
let column = loc.column != null ? loc.column : loc.virtualOffset;
|
||||
const file = loc.filename ? this.parseFileName(loc.filename) : null;
|
||||
let original = this.getOriginalPositionFor(line, column, file);
|
||||
|
||||
const isBytecodeRange =
|
||||
loc.bytecodeSize != null &&
|
||||
loc.virtualOffset != null &&
|
||||
!loc.column != null;
|
||||
|
||||
// Functions compiled from Metro-bundled modules will often have a little bit
|
||||
// of unmapped wrapper code right at the beginning - which is where we query.
|
||||
// Let's attribute them to where the inner module code originates instead.
|
||||
// This loop is O(n*log(n)) in the size of the function, but we will generally
|
||||
// either:
|
||||
// 1. Find a non-null mapping within one or two iterations; or
|
||||
// 2. Reach the end of the function without encountering mappings - this might
|
||||
// happen for function bodies that never throw (generally very short).
|
||||
while (
|
||||
isBytecodeRange &&
|
||||
original.source == null &&
|
||||
++column < loc.virtualOffset + loc.bytecodeSize
|
||||
) {
|
||||
original = this.getOriginalPositionFor(line, column, file);
|
||||
}
|
||||
|
||||
obj.location = {
|
||||
file: original.source,
|
||||
line: original.line,
|
||||
column: original.column,
|
||||
};
|
||||
}
|
||||
|
||||
// Symbolicate chrome trace "stackFrames" section.
|
||||
// Each frame in it has three fields: name, funcVirtAddr(optional), offset(optional).
|
||||
// funcVirtAddr and offset are only available if trace is generated from
|
||||
// hbc bundle without debug info.
|
||||
symbolicateChromeTrace(
|
||||
traceFile: string,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
}: {
|
||||
stdout: stream$Writable,
|
||||
stderr: stream$Writable,
|
||||
...
|
||||
},
|
||||
): void {
|
||||
const contentJson: ChromeTrace = JSON.parse(
|
||||
fs.readFileSync(traceFile, 'utf8'),
|
||||
);
|
||||
if (contentJson.stackFrames == null) {
|
||||
throw new Error('Unable to locate `stackFrames` section in trace.');
|
||||
}
|
||||
stdout.write(
|
||||
'Processing ' + Object.keys(contentJson.stackFrames).length + ' frames\n',
|
||||
);
|
||||
Object.values(contentJson.stackFrames).forEach(
|
||||
(entry: ChromeTraceEntry) => {
|
||||
let line;
|
||||
let column;
|
||||
|
||||
// Function entrypoint line/column; used for symbolicating function name
|
||||
// with legacy source maps (or when --no-function-names is set).
|
||||
let funcLine;
|
||||
let funcColumn;
|
||||
|
||||
if (entry.funcVirtAddr != null && entry.offset != null) {
|
||||
// Without debug information.
|
||||
const funcVirtAddr = parseInt(entry.funcVirtAddr, 10);
|
||||
const offsetInFunction = parseInt(entry.offset, 10);
|
||||
// Main bundle always use hard-coded line value 1.
|
||||
// TODO: support multiple bundle/module.
|
||||
line = this.options.inputLineStart;
|
||||
column = funcVirtAddr + offsetInFunction;
|
||||
funcLine = this.options.inputLineStart;
|
||||
funcColumn = funcVirtAddr;
|
||||
} else if (entry.line != null && entry.column != null) {
|
||||
// For hbc bundle with debug info, name field may already have source
|
||||
// information for the bundle; we still can use the Metro
|
||||
// source map to symbolicate the bundle frame addresses further to its
|
||||
// original source code.
|
||||
line = entry.line;
|
||||
column = entry.column;
|
||||
|
||||
funcLine = entry.funcLine;
|
||||
funcColumn = entry.funcColumn;
|
||||
} else {
|
||||
// Native frames.
|
||||
return;
|
||||
}
|
||||
|
||||
// Symbolicate original file/line/column.
|
||||
const addressOriginal = this.getOriginalPositionDetailsFor(
|
||||
line,
|
||||
column,
|
||||
);
|
||||
|
||||
let frameName;
|
||||
if (addressOriginal.functionName) {
|
||||
frameName = addressOriginal.functionName;
|
||||
} else {
|
||||
frameName = entry.name;
|
||||
// Symbolicate function name.
|
||||
if (funcLine != null && funcColumn != null) {
|
||||
const funcOriginal = this.getOriginalPositionFor(
|
||||
funcLine,
|
||||
funcColumn,
|
||||
);
|
||||
if (funcOriginal.name != null) {
|
||||
frameName = funcOriginal.name;
|
||||
}
|
||||
} else {
|
||||
// No function line/column info.
|
||||
(stderr || stdout).write(
|
||||
'Warning: no function prolog line/column info; name may be wrong\n',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Output format is: funcName(file:line:column)
|
||||
entry.name = [
|
||||
frameName,
|
||||
'(',
|
||||
[
|
||||
addressOriginal.source ?? 'null',
|
||||
addressOriginal.line ?? 'null',
|
||||
addressOriginal.column ?? 'null',
|
||||
].join(':'),
|
||||
')',
|
||||
].join('');
|
||||
},
|
||||
);
|
||||
stdout.write('Writing to ' + traceFile + '\n');
|
||||
fs.writeFileSync(traceFile, JSON.stringify(contentJson));
|
||||
}
|
||||
|
||||
/*
|
||||
* A helper function to return a mapping {line, column} object for a given input
|
||||
* line and column, and optionally a module ID.
|
||||
*/
|
||||
getOriginalPositionFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?ModuleIdsT,
|
||||
): {|
|
||||
line: ?number,
|
||||
column: ?number,
|
||||
source: ?string,
|
||||
name: ?string,
|
||||
|} {
|
||||
const position = this.getOriginalPositionDetailsFor(
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
moduleIds,
|
||||
);
|
||||
return {
|
||||
line: position.line,
|
||||
column: position.column,
|
||||
source: position.source,
|
||||
name: position.functionName ? position.functionName : position.name,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Symbolicates the JavaScript stack trace extracted from the minidump
|
||||
* produced by hermes
|
||||
*/
|
||||
symbolicateHermesMinidumpTrace(
|
||||
crashInfo: HermesMinidumpCrashInfo,
|
||||
): SymbolicatedStackTrace {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
getOriginalPositionDetailsFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?ModuleIdsT,
|
||||
): SymbolicatedStackFrame {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
parseFileName(str: string): ModuleIdsT {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
class SingleMapSymbolicationContext extends SymbolicationContext<SingleMapModuleIds> {
|
||||
+_segments: {
|
||||
+[id: string]: {|
|
||||
+consumer: SourceMapConsumer,
|
||||
+moduleOffsets: $ReadOnlyArray<number>,
|
||||
+sourceFunctionsConsumer: ?SourceMetadataMapConsumer,
|
||||
+hermesOffsets: ?HermesFunctionOffsets,
|
||||
|},
|
||||
...,
|
||||
};
|
||||
+_hasLegacySegments: boolean;
|
||||
|
||||
constructor(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
sourceMapContent: string | MixedSourceMap,
|
||||
options: ContextOptionsInput = {},
|
||||
) {
|
||||
super(options);
|
||||
const useFunctionNames = this.options.nameSource === 'function_names';
|
||||
const sourceMapJson: MixedSourceMap =
|
||||
typeof sourceMapContent === 'string'
|
||||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, ''))
|
||||
: sourceMapContent;
|
||||
const {x_hermes_function_offsets} = sourceMapJson;
|
||||
const segments = {
|
||||
'0': {
|
||||
consumer: new SourceMapConsumer(sourceMapJson),
|
||||
moduleOffsets: sourceMapJson.x_facebook_offsets || [],
|
||||
sourceFunctionsConsumer: useFunctionNames
|
||||
? new SourceMetadataMapConsumer(sourceMapJson)
|
||||
: null,
|
||||
hermesOffsets: x_hermes_function_offsets,
|
||||
},
|
||||
};
|
||||
if (sourceMapJson.x_facebook_segments) {
|
||||
for (const key of Object.keys(sourceMapJson.x_facebook_segments)) {
|
||||
const map = sourceMapJson.x_facebook_segments[key];
|
||||
segments[key] = {
|
||||
consumer: new SourceMapConsumer(map),
|
||||
moduleOffsets: map.x_facebook_offsets || [],
|
||||
sourceFunctionsConsumer: useFunctionNames
|
||||
? new SourceMetadataMapConsumer(map)
|
||||
: null,
|
||||
hermesOffsets: map.x_hermes_function_offsets,
|
||||
};
|
||||
}
|
||||
}
|
||||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null;
|
||||
this._segments = segments;
|
||||
}
|
||||
|
||||
symbolicateHermesMinidumpTrace(
|
||||
crashInfo: HermesMinidumpCrashInfo,
|
||||
): SymbolicatedStackTrace {
|
||||
const symbolicatedTrace = [];
|
||||
const {callstack} = crashInfo;
|
||||
if (callstack != null) {
|
||||
for (const stackItem of callstack) {
|
||||
if (stackItem.NativeCode) {
|
||||
symbolicatedTrace.push(stackItem);
|
||||
} else {
|
||||
const {
|
||||
CJSModuleOffset,
|
||||
SourceURL,
|
||||
FunctionID,
|
||||
ByteCodeOffset: localOffset,
|
||||
} = stackItem;
|
||||
const moduleInformation = this._hasLegacySegments
|
||||
? this.parseFileName(SourceURL)
|
||||
: UNKNOWN_MODULE_IDS;
|
||||
const generatedLine = CJSModuleOffset + this.options.inputLineStart;
|
||||
const segment = this._segments[
|
||||
moduleInformation.segmentId.toString()
|
||||
];
|
||||
const hermesOffsets = segment?.hermesOffsets;
|
||||
if (!hermesOffsets) {
|
||||
symbolicatedTrace.push({
|
||||
line: null,
|
||||
column: null,
|
||||
source: null,
|
||||
functionName: null,
|
||||
name: null,
|
||||
});
|
||||
} else {
|
||||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)];
|
||||
const generatedColumn =
|
||||
segmentOffsets[FunctionID] +
|
||||
localOffset +
|
||||
this.options.inputColumnStart;
|
||||
const originalPosition = this.getOriginalPositionDetailsFor(
|
||||
generatedLine,
|
||||
generatedColumn,
|
||||
moduleInformation,
|
||||
);
|
||||
symbolicatedTrace.push(originalPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return symbolicatedTrace;
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
getOriginalPositionDetailsFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?SingleMapModuleIds,
|
||||
): SymbolicatedStackFrame {
|
||||
// Adjust arguments to source-map's input coordinates
|
||||
lineNumber =
|
||||
lineNumber != null
|
||||
? lineNumber - this.options.inputLineStart + 1
|
||||
: lineNumber;
|
||||
columnNumber =
|
||||
columnNumber != null
|
||||
? columnNumber - this.options.inputColumnStart + 0
|
||||
: columnNumber;
|
||||
|
||||
if (!moduleIds) {
|
||||
moduleIds = UNKNOWN_MODULE_IDS;
|
||||
}
|
||||
|
||||
let moduleLineOffset = 0;
|
||||
const metadata = this._segments[moduleIds.segmentId + ''];
|
||||
const {localId} = moduleIds;
|
||||
if (localId != null) {
|
||||
const {moduleOffsets} = metadata;
|
||||
if (!moduleOffsets) {
|
||||
throw new Error(
|
||||
'Module ID given for a source map that does not have ' +
|
||||
'an x_facebook_offsets field',
|
||||
);
|
||||
}
|
||||
if (moduleOffsets[localId] == null) {
|
||||
throw new Error('Unknown module ID: ' + localId);
|
||||
}
|
||||
moduleLineOffset = moduleOffsets[localId];
|
||||
}
|
||||
const original = metadata.consumer.originalPositionFor({
|
||||
line: Number(lineNumber) + moduleLineOffset,
|
||||
column: Number(columnNumber),
|
||||
});
|
||||
if (metadata.sourceFunctionsConsumer) {
|
||||
original.functionName =
|
||||
metadata.sourceFunctionsConsumer.functionNameFor(original) || null;
|
||||
} else {
|
||||
original.functionName = null;
|
||||
}
|
||||
return {
|
||||
...original,
|
||||
line:
|
||||
original.line != null
|
||||
? original.line - 1 + this.options.outputLineStart
|
||||
: original.line,
|
||||
column:
|
||||
original.column != null
|
||||
? original.column - 0 + this.options.outputColumnStart
|
||||
: original.column,
|
||||
};
|
||||
}
|
||||
|
||||
parseFileName(str: string): SingleMapModuleIds {
|
||||
return parseSingleMapFileName(str);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectorySymbolicationContext extends SymbolicationContext<string> {
|
||||
+_fileMaps: Map<string, SingleMapSymbolicationContext>;
|
||||
+_rootDir: string;
|
||||
+_SourceMapConsumer: SourceMapConsumer;
|
||||
|
||||
constructor(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
rootDir: string,
|
||||
options: ContextOptionsInput = {},
|
||||
) {
|
||||
super(options);
|
||||
this._fileMaps = new Map();
|
||||
this._rootDir = rootDir;
|
||||
this._SourceMapConsumer = SourceMapConsumer;
|
||||
}
|
||||
|
||||
_loadMap(mapFilename: string): SingleMapSymbolicationContext {
|
||||
invariant(
|
||||
fs.existsSync(mapFilename),
|
||||
`Could not read source map from '${mapFilename}'`,
|
||||
);
|
||||
let fileMap = this._fileMaps.get(mapFilename);
|
||||
if (fileMap == null) {
|
||||
fileMap = new SingleMapSymbolicationContext(
|
||||
this._SourceMapConsumer,
|
||||
fs.readFileSync(mapFilename, 'utf8'),
|
||||
this.options,
|
||||
);
|
||||
this._fileMaps.set(mapFilename, fileMap);
|
||||
}
|
||||
return fileMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
getOriginalPositionDetailsFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
filename: ?string,
|
||||
): SymbolicatedStackFrame {
|
||||
invariant(
|
||||
filename != null,
|
||||
'filename is required for DirectorySymbolicationContext',
|
||||
);
|
||||
const mapFilename = path.join(this._rootDir, filename + '.map');
|
||||
if (!fs.existsSync(mapFilename)) {
|
||||
// Adjust arguments to the output coordinates
|
||||
lineNumber =
|
||||
lineNumber != null
|
||||
? lineNumber -
|
||||
this.options.inputLineStart +
|
||||
this.options.outputLineStart
|
||||
: lineNumber;
|
||||
columnNumber =
|
||||
columnNumber != null
|
||||
? columnNumber -
|
||||
this.options.inputColumnStart +
|
||||
this.options.outputColumnStart
|
||||
: columnNumber;
|
||||
|
||||
return {
|
||||
line: lineNumber,
|
||||
column: columnNumber,
|
||||
source: filename,
|
||||
name: null,
|
||||
functionName: null,
|
||||
};
|
||||
}
|
||||
return this._loadMap(mapFilename).getOriginalPositionDetailsFor(
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
);
|
||||
}
|
||||
|
||||
parseFileName(str: string): string {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the file name of a stack frame is numeric (+ ".js"), we assume it's a
|
||||
* lazily injected module coming from a "random access bundle". We are using
|
||||
* special source maps for these bundles, so that we can symbolicate stack
|
||||
* traces for multiple injected files with a single source map.
|
||||
*
|
||||
* There is also a convention for callsites that are in split segments of a
|
||||
* bundle, named either `seg-3.js` for segment #3 for example, or `seg-3_5.js`
|
||||
* for module #5 of segment #3 of a segmented RAM bundle.
|
||||
*/
|
||||
function parseSingleMapFileName(str: string): SingleMapModuleIds {
|
||||
const modMatch = str.match(/^(\d+).js$/);
|
||||
if (modMatch != null) {
|
||||
return {segmentId: 0, localId: Number(modMatch[1])};
|
||||
}
|
||||
const segMatch = str.match(/^seg-(\d+)(?:_(\d+))?.js$/);
|
||||
if (segMatch != null) {
|
||||
return {
|
||||
segmentId: Number(segMatch[1]),
|
||||
localId: segMatch[2] ? Number(segMatch[2]) : null,
|
||||
};
|
||||
}
|
||||
return UNKNOWN_MODULE_IDS;
|
||||
}
|
||||
|
||||
function createContext(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
sourceMapContent: string | MixedSourceMap,
|
||||
options: ContextOptionsInput = {},
|
||||
): SingleMapSymbolicationContext {
|
||||
return new SingleMapSymbolicationContext(
|
||||
SourceMapConsumer,
|
||||
sourceMapContent,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
function unstable_createDirectoryContext(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
rootDir: string,
|
||||
options: ContextOptionsInput = {},
|
||||
): DirectorySymbolicationContext {
|
||||
return new DirectorySymbolicationContext(SourceMapConsumer, rootDir, options);
|
||||
}
|
||||
|
||||
function getOriginalPositionFor<ModuleIdsT>(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?ModuleIdsT,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): {|
|
||||
line: ?number,
|
||||
column: ?number,
|
||||
source: ?string,
|
||||
name: ?string,
|
||||
|} {
|
||||
return context.getOriginalPositionFor(lineNumber, columnNumber, moduleIds);
|
||||
}
|
||||
|
||||
function symbolicate<ModuleIdsT>(
|
||||
stackTrace: string,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): string {
|
||||
return context.symbolicate(stackTrace);
|
||||
}
|
||||
|
||||
function symbolicateProfilerMap<ModuleIdsT>(
|
||||
mapFile: string,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): string {
|
||||
return context.symbolicateProfilerMap(mapFile);
|
||||
}
|
||||
|
||||
function symbolicateAttribution<ModuleIdsT>(
|
||||
obj: SizeAttributionMap,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): SizeAttributionMap {
|
||||
return context.symbolicateAttribution(obj);
|
||||
}
|
||||
|
||||
function symbolicateChromeTrace<ModuleIdsT>(
|
||||
traceFile: string,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
}: {
|
||||
stdout: stream$Writable,
|
||||
stderr: stream$Writable,
|
||||
...
|
||||
},
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): void {
|
||||
return context.symbolicateChromeTrace(traceFile, {stdout, stderr});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createContext,
|
||||
unstable_createDirectoryContext,
|
||||
getOriginalPositionFor,
|
||||
parseFileName: parseSingleMapFileName,
|
||||
symbolicate,
|
||||
symbolicateProfilerMap,
|
||||
symbolicateAttribution,
|
||||
symbolicateChromeTrace,
|
||||
SourceMetadataMapConsumer,
|
||||
};
|
86
node_modules/metro-symbolicate/src.real/__tests__/SourceMetadataMapConsumer-test.js
generated
vendored
Normal file
86
node_modules/metro-symbolicate/src.real/__tests__/SourceMetadataMapConsumer-test.js
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+js_symbolication
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const SourceMetadataMapConsumer = require('../SourceMetadataMapConsumer.js');
|
||||
|
||||
describe('SourceMetadataMapConsumer', () => {
|
||||
it('ignores metadata beyond the range of the sources array', () => {
|
||||
const consumer = new SourceMetadataMapConsumer({
|
||||
version: 3,
|
||||
mappings: '',
|
||||
sources: ['foo'],
|
||||
names: [],
|
||||
x_facebook_sources: [
|
||||
null,
|
||||
[
|
||||
{
|
||||
mappings: '',
|
||||
names: [],
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
expect(consumer.toArray(['foo'])).toEqual([null]);
|
||||
});
|
||||
|
||||
it('ignores metadata for a null source', () => {
|
||||
const consumer = new SourceMetadataMapConsumer({
|
||||
version: 3,
|
||||
mappings: '',
|
||||
sources: ['foo', null],
|
||||
names: [],
|
||||
x_facebook_sources: [
|
||||
[
|
||||
{
|
||||
mappings: '',
|
||||
names: [],
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
expect(consumer.toArray(['foo', null])).toEqual([
|
||||
[
|
||||
{
|
||||
mappings: '',
|
||||
names: [],
|
||||
},
|
||||
],
|
||||
null,
|
||||
]);
|
||||
});
|
||||
|
||||
it('accepts metadata blob with null function map', () => {
|
||||
const consumer = new SourceMetadataMapConsumer({
|
||||
version: 3,
|
||||
mappings: 'AAAA',
|
||||
sources: ['foo'],
|
||||
names: [],
|
||||
x_facebook_sources: [[null]],
|
||||
});
|
||||
expect(consumer.functionNameFor({line: 1, column: 0, source: 'foo'})).toBe(
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts null metadata blob', () => {
|
||||
const consumer = new SourceMetadataMapConsumer({
|
||||
version: 3,
|
||||
mappings: 'AAAA',
|
||||
sources: ['foo'],
|
||||
names: [],
|
||||
x_facebook_sources: [null],
|
||||
});
|
||||
expect(consumer.functionNameFor({line: 1, column: 0, source: 'foo'})).toBe(
|
||||
null,
|
||||
);
|
||||
});
|
||||
});
|
72
node_modules/metro-symbolicate/src.real/__tests__/Symbolication-test.js
generated
vendored
Normal file
72
node_modules/metro-symbolicate/src.real/__tests__/Symbolication-test.js
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+js_symbolication
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Symbolication = require('../Symbolication.js');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const {SourceMapConsumer} = require('source-map');
|
||||
|
||||
const resolve = fileName => path.resolve(__dirname, '__fixtures__', fileName);
|
||||
const read = fileName => fs.readFileSync(resolve(fileName), 'utf8');
|
||||
|
||||
const TESTFILE_MAP = resolve('testfile.js.map');
|
||||
|
||||
const UNKNOWN_MODULE_IDS = {
|
||||
segmentId: 0,
|
||||
localId: undefined,
|
||||
};
|
||||
|
||||
test('symbolicating with context created from source map object', async () => {
|
||||
const map = JSON.parse(read(TESTFILE_MAP));
|
||||
const context = Symbolication.createContext(SourceMapConsumer, map);
|
||||
expect(
|
||||
Symbolication.getOriginalPositionFor(1, 161, UNKNOWN_MODULE_IDS, context),
|
||||
).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"column": 20,
|
||||
"line": 18,
|
||||
"name": null,
|
||||
"source": "thrower.js",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('symbolicating with context created from source map string', async () => {
|
||||
const map = read(TESTFILE_MAP);
|
||||
const context = Symbolication.createContext(SourceMapConsumer, map);
|
||||
expect(
|
||||
Symbolication.getOriginalPositionFor(1, 161, UNKNOWN_MODULE_IDS, context),
|
||||
).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"column": 20,
|
||||
"line": 18,
|
||||
"name": null,
|
||||
"source": "thrower.js",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('symbolicating without specifying module IDs', async () => {
|
||||
const map = read(TESTFILE_MAP);
|
||||
const context = Symbolication.createContext(SourceMapConsumer, map);
|
||||
expect(Symbolication.getOriginalPositionFor(1, 161, null, context))
|
||||
.toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"column": 20,
|
||||
"line": 18,
|
||||
"name": null,
|
||||
"source": "thrower.js",
|
||||
}
|
||||
`);
|
||||
});
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/directory/foo.js.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/directory/foo.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["/js/react-native-github/Libraries/BatchedBridge/BatchedBridge.js"],"names":["BatchedBridge","require","Object","defineProperty","global","configurable","value","module","exports"],"mappings":"AAUA;;AAIA,IAAMA,aAAa,GAAG,KAAAC,OAAA,mEAAA,GAAtB;AAQAC,MAAM,CAACC,cAAPD,CAAsBE,MAAtBF,EAA8B,mBAA9BA,EAAmD;AACjDG,EAAAA,YAAY,EAAE,IADmC;AAEjDC,EAAAA,KAAK,EAAEN;AAF0C,CAAnDE;AAKAK,MAAM,CAACC,OAAPD,GAAiBP,aAAjBO"}
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/directory/subdir1/bar.js.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/directory/subdir1/bar.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
3
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/directory/test.stack
generated
vendored
Normal file
3
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/directory/test.stack
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
someFunc@foo.js:4:0
|
||||
someOtherFunc@subdir1/bar.js:255:6
|
||||
fn@fileThatDoesntExist.js:10:20
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermes.js.hbc.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermes.js.hbc.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
39
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermesStackTrace.json
generated
vendored
Normal file
39
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermesStackTrace.json
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"callstack": [
|
||||
{
|
||||
"CJSModuleOffset": 0,
|
||||
"FunctionID": 50,
|
||||
"ByteCodeOffset": 58,
|
||||
"SourceURL": "/a/b/c/main.jsbundle"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 0,
|
||||
"FunctionID": 290,
|
||||
"ByteCodeOffset": 3,
|
||||
"SourceURL": "/a/b/c/main.jsbundle"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 0,
|
||||
"FunctionID": 3,
|
||||
"ByteCodeOffset": 4,
|
||||
"SourceURL": "seg-1.js"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 0,
|
||||
"FunctionID": 3,
|
||||
"ByteCodeOffset": 4,
|
||||
"SourceURL": "seg-16.js"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 0,
|
||||
"FunctionID": 3,
|
||||
"ByteCodeOffset": 4,
|
||||
"SourceURL": "seg-1.js",
|
||||
"StackFrameRegOffs": 1912,
|
||||
"SourceLocation": "file.js:23:14"
|
||||
},
|
||||
{
|
||||
"NativeCode": true
|
||||
}
|
||||
]
|
||||
}
|
33
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermesStackTraceCJS.json
generated
vendored
Normal file
33
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermesStackTraceCJS.json
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"callstack": [
|
||||
{
|
||||
"CJSModuleOffset": 0,
|
||||
"FunctionID": 270,
|
||||
"ByteCodeOffset": 12,
|
||||
"SourceURL": "Fb4aBundle.js"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 51,
|
||||
"FunctionID": 3,
|
||||
"ByteCodeOffset": 4,
|
||||
"SourceURL": "Fb4aBundle.js"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 51,
|
||||
"FunctionID": 3,
|
||||
"ByteCodeOffset": 4,
|
||||
"SourceURL": "seg-5.js"
|
||||
},
|
||||
{
|
||||
"CJSModuleOffset": 51,
|
||||
"FunctionID": 3,
|
||||
"ByteCodeOffset": 4,
|
||||
"SourceURL": "Fb4aBundle.js",
|
||||
"StackFrameRegOffs": 1219,
|
||||
"SourceLocation": "file.js:12:11"
|
||||
},
|
||||
{
|
||||
"NativeCode": true
|
||||
}
|
||||
]
|
||||
}
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermescjs.js.hbc.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/hermescjs.js.hbc.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.attribution.input
generated
vendored
Normal file
2
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.attribution.input
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
{"functionId":1,"location":{"virtualOffset":22},"usage":[]}
|
||||
{"functionId":2,"location":{"virtualOffset":99},"usage":[]}
|
42
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.cpuprofile
generated
vendored
Normal file
42
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.cpuprofile
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"stackFrames": {
|
||||
"1": {
|
||||
"funcVirtAddr": "0",
|
||||
"offset": "0",
|
||||
"name": "global+0",
|
||||
"category": "JavaScript"
|
||||
},
|
||||
"2": {
|
||||
"funcVirtAddr": "0",
|
||||
"offset": "55",
|
||||
"name": "global+55",
|
||||
"category": "JavaScript"
|
||||
},
|
||||
"3": {
|
||||
"funcVirtAddr": "67",
|
||||
"offset": "16",
|
||||
"name": "entryPoint+16",
|
||||
"category": "JavaScript",
|
||||
"parent": 2
|
||||
},
|
||||
"4": {
|
||||
"funcVirtAddr": "89",
|
||||
"offset": "0",
|
||||
"name": "helper+0",
|
||||
"category": "JavaScript",
|
||||
"parent": 3
|
||||
},
|
||||
"5": {
|
||||
"funcVirtAddr": "89",
|
||||
"offset": "146",
|
||||
"name": "helper+146",
|
||||
"category": "JavaScript",
|
||||
"parent": 3
|
||||
},
|
||||
"6": {
|
||||
"name": "[Native]4367295792",
|
||||
"category": "Native",
|
||||
"parent": 5
|
||||
}
|
||||
}
|
||||
}
|
15
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.cpuprofile.map
generated
vendored
Normal file
15
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.cpuprofile.map
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [
|
||||
"temp/bench.js"
|
||||
],
|
||||
"x_facebook_sources": [
|
||||
[
|
||||
{
|
||||
"names": ["<global>","entryPoint","helper"],
|
||||
"mappings": "AAA,uBCC,JCI,RFe"
|
||||
}
|
||||
]
|
||||
],
|
||||
"mappings": "AACC,uDAmBU,YAnBY,gBACd,MAGU,wFAKA,WACT,iBACC,IAAD,YACU,cAAA,YAHY,eAAb;"
|
||||
}
|
2
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.js.map
generated
vendored
Normal file
2
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.js.map
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
{"version":3,"sources":["thrower.js"],"names":["notCalled","alsoNotCalled","throws6a","Error","throws6","throws6b","throws4","obj","throws5","apply","this","throws2","throws3","throws1","throws0","t","eval","o","forEach","call","arguments","arg","err","print","stack"],"mappings":"CAAA,WAGE,SAASA,YACP,SAASC,IACPA,IAEFA,IACAD,YAIF,SAASE,WACP,MAAM,IAAIC,MAAM,wBAGlB,SAASC,UACP,MAAM,IAAID,MAAM,wBAGlB,SAASE,WACP,MAAM,IAAIF,MAAM,wBAYlB,SAASG,UACPC,IAAIC,EAAQC,MAAMC,MAAON,QAASA,QAASA,UAG7C,SAASO,UACPJ,IAAIK,IAGN,SAASC,UAELF,UAIJ,SAASG,UACPD,UAxBF,IAAIN,KACFQ,EAAS,WACPC,KAAK,eAEPC,EAAS,cACJC,QAAQC,KAAKC,UAAW,SAASC,GAAOA,QAsB/C,IACEP,UACA,MAAOQ,GACPC,MAAMD,EAAIE,QAtDd"}
|
||||
|
13
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.node.stack
generated
vendored
Normal file
13
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.node.stack
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
TypeError: undefined is not a function
|
||||
at throws6 (/js/thrower.min.js:1:161)
|
||||
at /js/thrower.min.js:1:488
|
||||
at forEach ([native code])
|
||||
at o (/js/thrower.min.js:1:464)
|
||||
at throws4 (/js/thrower.min.js:1:276)
|
||||
at eval (eval at /js/thrower.min.js:1:451)
|
||||
at t (/js/thrower.min.js:1:420)
|
||||
at throws2 (/js/thrower.min.js:1:333)
|
||||
at throws1 (/js/thrower.min.js:1:362)
|
||||
at throws0 (/js/thrower.min.js:1:391)
|
||||
at /js/thrower.min.js:1:506
|
||||
at global code (/js/thrower.min.js:1:534)
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.partial.js.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.partial.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["foo.js"],"names":[],"mappings":"K,CACC,K,CACC,K"}
|
4
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.profmap
generated
vendored
Normal file
4
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.profmap
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
JS_0000_xxxxxxxxxxxxxxxxxxxxxx (unknown) 373
|
||||
JS_0001_xxxxxxxxxxxxxxxxxxxxxx garbage 296
|
||||
JS_0002_xxxxxxxxxxxxxxxxxxxxxx name 1234
|
||||
JS_0003_xxxxxxxxxxxxxxxxxxxxxx (unknown) 5678
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.ram.js.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.ram.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.ram.stack
generated
vendored
Normal file
5
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.ram.stack
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
Object.throwSmthInner@seg-1_2.js:6:13
|
||||
Object.throwSmth@seg-1_1.js:8:18
|
||||
makeItThrowInner@1.js:15:9
|
||||
makeItThrow@1.js:11:5
|
||||
1.js:6:7
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.sectioned.js.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.sectioned.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
13
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.stack
generated
vendored
Normal file
13
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/testfile.stack
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
throws6@thrower.min.js:1:161
|
||||
thrower.min.js:1:488
|
||||
forEach@[native code]
|
||||
o@thrower.min.js:1:464
|
||||
throws4@thrower.min.js:1:276
|
||||
eval code
|
||||
eval@[native code]
|
||||
t@thrower.min.js:1:420
|
||||
throws2@thrower.min.js:1:333
|
||||
throws1@thrower.min.js:1:362
|
||||
throws0@thrower.min.js:1:391
|
||||
thrower.min.js:1:506
|
||||
global code@thrower.min.js:1:534
|
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/with-function-map.js.map
generated
vendored
Normal file
1
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/with-function-map.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/with-function-map.stack
generated
vendored
Normal file
5
node_modules/metro-symbolicate/src.real/__tests__/__fixtures__/with-function-map.stack
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
Object.throwSmthInner@bundle.js:427:174
|
||||
Object.throwSmth@bundle.js:426:200
|
||||
makeItThrowInner@bundle.js:425:311
|
||||
makeItThrow@bundle.js:425:253
|
||||
unknown@bundle.js:425:206
|
257
node_modules/metro-symbolicate/src.real/__tests__/__snapshots__/symbolicate-test.js.snap
generated
vendored
Normal file
257
node_modules/metro-symbolicate/src.real/__tests__/__snapshots__/symbolicate-test.js.snap
generated
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`directory context symbolicating a stack trace 1`] = `
|
||||
"/js/react-native-github/Libraries/BatchedBridge/BatchedBridge.js:23:Object
|
||||
/js/react-native-github/Libraries/BatchedBridge/MessageQueue.js:89:null
|
||||
fileThatDoesntExist.js:10:null
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating a hermes stack trace 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"column": 21,
|
||||
"functionName": "columns.forEach$argument_0",
|
||||
"line": 480,
|
||||
"name": "rows",
|
||||
"source": "/js/node_modules/react-native/Libraries/polyfills/console.js",
|
||||
},
|
||||
Object {
|
||||
"column": 8,
|
||||
"functionName": "setEnabled",
|
||||
"line": 101,
|
||||
"name": "_enabled",
|
||||
"source": "/js/react-native-github/Libraries/Performance/Systrace.js",
|
||||
},
|
||||
Object {
|
||||
"column": 2,
|
||||
"functionName": "getValue",
|
||||
"line": 13,
|
||||
"name": null,
|
||||
"source": "/js/RKJSModules/Apps/HermesModulesTest/HMTLazyFetched.js",
|
||||
},
|
||||
Object {
|
||||
"column": null,
|
||||
"functionName": null,
|
||||
"line": null,
|
||||
"name": null,
|
||||
"source": null,
|
||||
},
|
||||
Object {
|
||||
"column": 2,
|
||||
"functionName": "getValue",
|
||||
"line": 13,
|
||||
"name": null,
|
||||
"source": "/js/RKJSModules/Apps/HermesModulesTest/HMTLazyFetched.js",
|
||||
},
|
||||
Object {
|
||||
"NativeCode": true,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`symbolicating a hermes stack trace CJS 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"column": 36,
|
||||
"functionName": "result.clearExceptTimespans",
|
||||
"line": 160,
|
||||
"name": null,
|
||||
"source": "/js/react-native-github/Libraries/Utilities/createPerformanceLogger.js",
|
||||
},
|
||||
Object {
|
||||
"column": 2,
|
||||
"functionName": "getValue",
|
||||
"line": 13,
|
||||
"name": null,
|
||||
"source": "/js/RKJSModules/Apps/HermesModulesTest/HMTLazyFetched.js",
|
||||
},
|
||||
Object {
|
||||
"column": 2,
|
||||
"functionName": "getValue",
|
||||
"line": 13,
|
||||
"name": null,
|
||||
"source": "/js/RKJSModules/Apps/HermesModulesTest/HMTLazyFetched.js",
|
||||
},
|
||||
Object {
|
||||
"column": 2,
|
||||
"functionName": "getValue",
|
||||
"line": 13,
|
||||
"name": null,
|
||||
"source": "/js/RKJSModules/Apps/HermesModulesTest/HMTLazyFetched.js",
|
||||
},
|
||||
Object {
|
||||
"NativeCode": true,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`symbolicating a profiler map 1`] = `
|
||||
"JS_0000_xxxxxxxxxxxxxxxxxxxxxx throws0::thrower.js:48:11
|
||||
JS_0001_xxxxxxxxxxxxxxxxxxxxxx throws6::thrower.js:35:38
|
||||
JS_0002_xxxxxxxxxxxxxxxxxxxxxx name::thrower.js:1:0
|
||||
JS_0003_xxxxxxxxxxxxxxxxxxxxxx (unknown)::thrower.js:1:0"
|
||||
`;
|
||||
|
||||
exports[`symbolicating a stack trace 1`] = `
|
||||
"thrower.js:18:null
|
||||
thrower.js:30:arg
|
||||
null:null:null
|
||||
thrower.js:30:arguments
|
||||
thrower.js:35:this
|
||||
eval code
|
||||
null:null:null
|
||||
thrower.js:27:null
|
||||
thrower.js:39:throws3
|
||||
thrower.js:44:throws2
|
||||
thrower.js:49:throws1
|
||||
thrower.js:53:throws0
|
||||
global thrower.js:1:null
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating a stack trace ignoring a function map 1`] = `
|
||||
"/js/RKJSModules/segmented/biz.js:5:null
|
||||
/js/RKJSModules/segmented/glo.js:7:throwSmthInner
|
||||
/js/RKJSModules/bar.js:14:throwSmth
|
||||
/js/RKJSModules/bar.js:10:makeItThrowInner
|
||||
/js/RKJSModules/bar.js:5:makeItThrow
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating a stack trace in Node format 1`] = `
|
||||
"TypeError: undefined is not a function
|
||||
at throws6 (thrower.js:18:null)
|
||||
at thrower.js:30:arg
|
||||
at forEach (null:null:null)
|
||||
at o (thrower.js:30:arguments)
|
||||
at throws4 (thrower.js:35:this)
|
||||
at eval (eval at thrower.js:30:forEach)
|
||||
at t (thrower.js:27:null)
|
||||
at throws2 (thrower.js:39:throws3)
|
||||
at throws1 (thrower.js:44:throws2)
|
||||
at throws0 (thrower.js:49:throws1)
|
||||
at thrower.js:53:throws0
|
||||
at global code (thrower.js:1:null)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating a stack trace with a function map 1`] = `
|
||||
"/js/RKJSModules/segmented/biz.js:5:module.exports.throwSmthInner
|
||||
/js/RKJSModules/segmented/glo.js:7:module.exports.throwSmth
|
||||
/js/RKJSModules/bar.js:14:makeItThrowInner
|
||||
/js/RKJSModules/bar.js:10:makeItThrow
|
||||
/js/RKJSModules/bar.js:5:import.then$argument_0
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating a stack trace with a segmented RAM bundle map 1`] = `
|
||||
"/js/RKJSModules/segmented/biz.js:5:null
|
||||
/js/RKJSModules/segmented/glo.js:7:throwSmthInner
|
||||
/js/RKJSModules/bar.js:14:throwSmth
|
||||
/js/RKJSModules/bar.js:10:makeItThrowInner
|
||||
/js/RKJSModules/bar.js:5:makeItThrow
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating an attribution file 1`] = `
|
||||
"{\\"functionId\\":1,\\"location\\":{\\"file\\":\\"thrower.js\\",\\"line\\":4,\\"column\\":11},\\"usage\\":[]}
|
||||
{\\"functionId\\":2,\\"location\\":{\\"file\\":\\"thrower.js\\",\\"line\\":14,\\"column\\":14},\\"usage\\":[]}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating an attribution file with 1-based column output 1`] = `
|
||||
"{\\"functionId\\":1,\\"location\\":{\\"file\\":\\"thrower.js\\",\\"line\\":4,\\"column\\":12},\\"usage\\":[]}
|
||||
{\\"functionId\\":2,\\"location\\":{\\"file\\":\\"thrower.js\\",\\"line\\":14,\\"column\\":15},\\"usage\\":[]}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`symbolicating with a cpuprofile 1`] = `
|
||||
Object {
|
||||
"stackFrames": Object {
|
||||
"1": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "0",
|
||||
"name": "<global>(temp/bench.js:2:1)",
|
||||
"offset": "0",
|
||||
},
|
||||
"2": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "0",
|
||||
"name": "<global>(temp/bench.js:21:11)",
|
||||
"offset": "55",
|
||||
},
|
||||
"3": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "67",
|
||||
"name": "entryPoint(temp/bench.js:3:9)",
|
||||
"offset": "16",
|
||||
"parent": 2,
|
||||
},
|
||||
"4": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "89",
|
||||
"name": "helper(temp/bench.js:6:19)",
|
||||
"offset": "0",
|
||||
"parent": 3,
|
||||
},
|
||||
"5": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "89",
|
||||
"name": "helper(temp/bench.js:14:20)",
|
||||
"offset": "146",
|
||||
"parent": 3,
|
||||
},
|
||||
"6": Object {
|
||||
"category": "Native",
|
||||
"name": "[Native]4367295792",
|
||||
"parent": 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`symbolicating with a cpuprofile ignoring a function map 1`] = `
|
||||
Object {
|
||||
"stackFrames": Object {
|
||||
"1": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "0",
|
||||
"name": "global+0(temp/bench.js:2:1)",
|
||||
"offset": "0",
|
||||
},
|
||||
"2": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "0",
|
||||
"name": "global+55(temp/bench.js:21:11)",
|
||||
"offset": "55",
|
||||
},
|
||||
"3": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "67",
|
||||
"name": "entryPoint+16(temp/bench.js:3:9)",
|
||||
"offset": "16",
|
||||
"parent": 2,
|
||||
},
|
||||
"4": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "89",
|
||||
"name": "helper+0(temp/bench.js:6:19)",
|
||||
"offset": "0",
|
||||
"parent": 3,
|
||||
},
|
||||
"5": Object {
|
||||
"category": "JavaScript",
|
||||
"funcVirtAddr": "89",
|
||||
"name": "helper+146(temp/bench.js:14:20)",
|
||||
"offset": "146",
|
||||
"parent": 3,
|
||||
},
|
||||
"6": Object {
|
||||
"category": "Native",
|
||||
"name": "[Native]4367295792",
|
||||
"parent": 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
352
node_modules/metro-symbolicate/src.real/__tests__/symbolicate-test.js
generated
vendored
Normal file
352
node_modules/metro-symbolicate/src.real/__tests__/symbolicate-test.js
generated
vendored
Normal file
@ -0,0 +1,352 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+js_symbolication
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const symbolicate = require('../symbolicate');
|
||||
|
||||
const {PassThrough} = require('stream');
|
||||
const resolve = fileName => path.resolve(__dirname, '__fixtures__', fileName);
|
||||
const read = fileName => fs.readFileSync(resolve(fileName), 'utf8');
|
||||
|
||||
const execute = async (args: Array<string>, stdin: string): Promise<string> => {
|
||||
const streams = {
|
||||
stdin: new PassThrough(),
|
||||
stdout: new PassThrough(),
|
||||
stderr: new PassThrough(),
|
||||
};
|
||||
const stdout = [];
|
||||
const output = ['Process failed with the following output:\n======\n'];
|
||||
streams.stdout.on('data', data => {
|
||||
output.push(data);
|
||||
stdout.push(data);
|
||||
});
|
||||
streams.stderr.on('data', data => {
|
||||
output.push(data);
|
||||
});
|
||||
if (stdin) {
|
||||
streams.stdin.write(stdin);
|
||||
streams.stdin.end();
|
||||
}
|
||||
const code = await symbolicate(args, streams);
|
||||
|
||||
if (code !== 0) {
|
||||
output.push('======\n');
|
||||
throw new Error(output.join(''));
|
||||
}
|
||||
return stdout.join('');
|
||||
};
|
||||
|
||||
afterAll(() => {
|
||||
try {
|
||||
fs.unlinkSync(resolve('testfile.temp.cpuprofile'));
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
const TESTFILE_MAP = resolve('testfile.js.map');
|
||||
const WITH_FUNCTION_MAP_MAP = resolve('with-function-map.js.map');
|
||||
const TESTFILE_RAM_MAP = resolve('testfile.ram.js.map');
|
||||
const HERMES_MAP = resolve('hermes.js.hbc.map');
|
||||
const HERMES_MAP_CJS = resolve('hermescjs.js.hbc.map');
|
||||
|
||||
test('symbolicating a hermes stack trace', async () => {
|
||||
const output = JSON.parse(
|
||||
await execute(
|
||||
[HERMES_MAP, '--hermes-crash'],
|
||||
read('hermesStackTrace.json'),
|
||||
),
|
||||
);
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('symbolicating a hermes stack trace with input-line-start and input-column-start', async () => {
|
||||
const output0Based = JSON.parse(
|
||||
await execute(
|
||||
[
|
||||
HERMES_MAP,
|
||||
'--hermes-crash',
|
||||
'--input-line-start',
|
||||
'0',
|
||||
'--input-column-start',
|
||||
'0',
|
||||
],
|
||||
read('hermesStackTrace.json'),
|
||||
),
|
||||
);
|
||||
const output1Based = JSON.parse(
|
||||
await execute(
|
||||
[
|
||||
HERMES_MAP,
|
||||
'--hermes-crash',
|
||||
'--input-line-start',
|
||||
'1',
|
||||
'--input-column-start',
|
||||
'1',
|
||||
],
|
||||
read('hermesStackTrace.json'),
|
||||
),
|
||||
);
|
||||
const outputNoFlag = JSON.parse(
|
||||
await execute(
|
||||
[HERMES_MAP, '--hermes-crash'],
|
||||
read('hermesStackTrace.json'),
|
||||
),
|
||||
);
|
||||
expect(outputNoFlag).toMatchObject(output0Based);
|
||||
expect(outputNoFlag).toMatchObject(output1Based);
|
||||
});
|
||||
|
||||
test('symbolicating a hermes stack trace CJS', async () => {
|
||||
const output = JSON.parse(
|
||||
await execute(
|
||||
[HERMES_MAP_CJS, '--hermes-crash'],
|
||||
read('hermesStackTraceCJS.json'),
|
||||
),
|
||||
);
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('symbolicating a stack trace', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_MAP], read('testfile.stack')),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
test('symbolicating a stack trace in Node format', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_MAP], read('testfile.node.stack')),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
test('symbolicating a single entry', async () =>
|
||||
await expect(execute([TESTFILE_MAP, '1', '161'])).resolves.toEqual(
|
||||
'thrower.js:18:null\n',
|
||||
));
|
||||
|
||||
test('symbolicating a single entry with 0-based line output', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_MAP, '1', '161', '--output-line-start', '0']),
|
||||
).resolves.toEqual('thrower.js:17:null\n'));
|
||||
|
||||
test('symbolicating a single entry with 1-based column input', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_MAP, '1', '161', '--input-column-start', '1']),
|
||||
).resolves.toEqual('thrower.js:18:Error\n'));
|
||||
|
||||
test('symbolicating a single entry with 0-based line input', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_MAP, '0', '161', '--input-line-start', '0']),
|
||||
).resolves.toEqual('thrower.js:18:null\n'));
|
||||
|
||||
test('symbolicating a single entry with an out of range column', async () =>
|
||||
await expect(execute([TESTFILE_MAP, '1', '1000000'])).resolves.toEqual(
|
||||
'thrower.js:1:null\n',
|
||||
));
|
||||
|
||||
test('symbolicating a single entry with out of range line', async () =>
|
||||
await expect(execute([TESTFILE_MAP, '1000000', '161'])).resolves.toEqual(
|
||||
'null:null:null\n',
|
||||
));
|
||||
|
||||
test('symbolicating a sectioned source map', async () =>
|
||||
await expect(
|
||||
execute([resolve('testfile.sectioned.js.map'), '353.js', '1', '72']),
|
||||
).resolves.toEqual('nested-thrower.js:6:start\n'));
|
||||
|
||||
test('symbolicating a profiler map', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_MAP, resolve('testfile.profmap')]),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
test('symbolicating an attribution file', async () =>
|
||||
await expect(
|
||||
execute(
|
||||
[TESTFILE_MAP, '--attribution'],
|
||||
read('testfile.attribution.input'),
|
||||
),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
test('symbolicating an attribution file with 1-based column output', async () =>
|
||||
await expect(
|
||||
execute(
|
||||
[TESTFILE_MAP, '--attribution', '--output-column-start', '1'],
|
||||
read('testfile.attribution.input'),
|
||||
),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
describe('symbolicating an attribution file specifying unmapped offsets', () => {
|
||||
const attribute = async obj =>
|
||||
(await execute(
|
||||
[resolve('testfile.partial.js.map'), '--attribution'],
|
||||
JSON.stringify(obj) + '\n',
|
||||
))
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
test('Lookup falls before all mappings with no non-null mapping in range', async () =>
|
||||
await expect(
|
||||
attribute({
|
||||
functionId: 0,
|
||||
location: {virtualOffset: 0, bytecodeSize: 5},
|
||||
usage: [],
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"functionId": 0,
|
||||
"location": Object {
|
||||
"column": null,
|
||||
"file": null,
|
||||
"line": null,
|
||||
},
|
||||
"usage": Array [],
|
||||
},
|
||||
]
|
||||
`));
|
||||
|
||||
test('Lookup finds a null mapping and falls back to a non-null mapping in range', async () =>
|
||||
await expect(
|
||||
attribute({
|
||||
functionId: 1,
|
||||
location: {virtualOffset: 5, bytecodeSize: 2},
|
||||
usage: [],
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"functionId": 1,
|
||||
"location": Object {
|
||||
"column": 1,
|
||||
"file": "foo.js",
|
||||
"line": 2,
|
||||
},
|
||||
"usage": Array [],
|
||||
},
|
||||
]
|
||||
`));
|
||||
|
||||
test('Lookup finds a null mapping with no bytecodeSize specified', async () =>
|
||||
await expect(
|
||||
attribute({
|
||||
functionId: 1,
|
||||
location: {virtualOffset: 5},
|
||||
usage: [],
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"functionId": 1,
|
||||
"location": Object {
|
||||
"column": null,
|
||||
"file": null,
|
||||
"line": null,
|
||||
},
|
||||
"usage": Array [],
|
||||
},
|
||||
]
|
||||
`));
|
||||
|
||||
test('Lookup finds a null mapping with no non-null mapping in range', async () =>
|
||||
await expect(
|
||||
attribute({
|
||||
functionId: 2,
|
||||
location: {virtualOffset: 11, bytecodeSize: 1},
|
||||
usage: [],
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"functionId": 2,
|
||||
"location": Object {
|
||||
"column": null,
|
||||
"file": null,
|
||||
"line": null,
|
||||
},
|
||||
"usage": Array [],
|
||||
},
|
||||
]
|
||||
`));
|
||||
|
||||
test('Lookup finds the last mapping and it is null', async () =>
|
||||
await expect(
|
||||
attribute({
|
||||
functionId: 3,
|
||||
location: {virtualOffset: 17, bytecodeSize: 1},
|
||||
usage: [],
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"functionId": 3,
|
||||
"location": Object {
|
||||
"column": null,
|
||||
"file": null,
|
||||
"line": null,
|
||||
},
|
||||
"usage": Array [],
|
||||
},
|
||||
]
|
||||
`));
|
||||
});
|
||||
|
||||
test('symbolicating with a cpuprofile', async () => {
|
||||
fs.copyFileSync(
|
||||
resolve('testfile.cpuprofile'),
|
||||
resolve('testfile.temp.cpuprofile'),
|
||||
);
|
||||
|
||||
await execute([
|
||||
resolve('testfile.cpuprofile.map'),
|
||||
resolve('testfile.temp.cpuprofile'),
|
||||
]);
|
||||
|
||||
expect(JSON.parse(read('testfile.temp.cpuprofile'))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('symbolicating with a cpuprofile ignoring a function map', async () => {
|
||||
fs.copyFileSync(
|
||||
resolve('testfile.cpuprofile'),
|
||||
resolve('testfile.temp.cpuprofile'),
|
||||
);
|
||||
|
||||
await execute([
|
||||
'--no-function-names',
|
||||
resolve('testfile.cpuprofile.map'),
|
||||
resolve('testfile.temp.cpuprofile'),
|
||||
]);
|
||||
|
||||
expect(JSON.parse(read('testfile.temp.cpuprofile'))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('symbolicating a stack trace with a function map', async () =>
|
||||
await expect(
|
||||
execute([WITH_FUNCTION_MAP_MAP], read('with-function-map.stack')),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
test('symbolicating a stack trace with a segmented RAM bundle map', async () =>
|
||||
await expect(
|
||||
execute([TESTFILE_RAM_MAP], read('testfile.ram.stack')),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
test('symbolicating a stack trace ignoring a function map', async () =>
|
||||
await expect(
|
||||
execute(
|
||||
['--no-function-names', WITH_FUNCTION_MAP_MAP],
|
||||
read('with-function-map.stack'),
|
||||
),
|
||||
).resolves.toMatchSnapshot());
|
||||
|
||||
describe('directory context', () => {
|
||||
test('symbolicating a stack trace', async () =>
|
||||
await expect(
|
||||
execute([resolve('directory')], read('directory/test.stack')),
|
||||
).resolves.toMatchSnapshot());
|
||||
});
|
235
node_modules/metro-symbolicate/src.real/symbolicate.js
generated
vendored
Executable file
235
node_modules/metro-symbolicate/src.real/symbolicate.js
generated
vendored
Executable file
@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
// 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';
|
||||
|
||||
// flowlint-next-line untyped-import:off
|
||||
const SourceMapConsumer = require('source-map').SourceMapConsumer;
|
||||
const Symbolication = require('./Symbolication.js');
|
||||
|
||||
const fs = require('fs');
|
||||
// flowlint-next-line untyped-import:off
|
||||
const through2 = require('through2');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
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,
|
||||
);
|
||||
} else {
|
||||
const content = fs.readFileSync(sourceMapFileName, 'utf8');
|
||||
context = Symbolication.createContext(
|
||||
SourceMapConsumer,
|
||||
content,
|
||||
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 {
|
||||
stdout.write(context.symbolicate(stackTrace));
|
||||
}
|
||||
} else if (argv[0].endsWith('.profmap')) {
|
||||
stdout.write(context.symbolicateProfilerMap(argv[0]));
|
||||
} else if (argv[0] === '--attribution') {
|
||||
let buffer = '';
|
||||
await waitForStream(
|
||||
stdin
|
||||
.pipe(
|
||||
through2(function(data, enc, callback) {
|
||||
// Take arbitrary strings, output single lines
|
||||
buffer += data;
|
||||
const lines = buffer.split('\n');
|
||||
for (let i = 0, e = lines.length - 1; i < e; i++) {
|
||||
this.push(lines[i]);
|
||||
}
|
||||
buffer = lines[lines.length - 1];
|
||||
callback();
|
||||
}),
|
||||
)
|
||||
.pipe(
|
||||
through2.obj(function(data, enc, callback) {
|
||||
// This is JSONL, so each line is a separate JSON object
|
||||
const obj = JSON.parse(data);
|
||||
context.symbolicateAttribution(obj);
|
||||
this.push(JSON.stringify(obj) + '\n');
|
||||
callback();
|
||||
}),
|
||||
)
|
||||
.pipe(stdout),
|
||||
);
|
||||
} else if (argv[0].endsWith('.cpuprofile')) {
|
||||
// NOTE: synchronous
|
||||
context.symbolicateChromeTrace(argv[0], {stdout, stderr});
|
||||
} else {
|
||||
// read-from-argv form.
|
||||
let moduleIds;
|
||||
if (argv[0].endsWith('.js')) {
|
||||
moduleIds = context.parseFileName(argv[0]);
|
||||
argv.shift();
|
||||
} 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 ?? 'null',
|
||||
original.line ?? 'null',
|
||||
original.name ?? 'null',
|
||||
].join(':') + '\n',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
stderr.write(error + '\n');
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function readAll(stream) {
|
||||
return new Promise(resolve => {
|
||||
let data = '';
|
||||
if (stream.isTTY === true) {
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.setEncoding('utf8');
|
||||
stream.on('readable', () => {
|
||||
let chunk;
|
||||
// flowlint-next-line sketchy-null-string:off
|
||||
while ((chunk = stream.read())) {
|
||||
data += chunk.toString();
|
||||
}
|
||||
});
|
||||
stream.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForStream(stream) {
|
||||
return new Promise(resolve => {
|
||||
stream.on('finish', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().then(code => process.exit(code));
|
||||
}
|
||||
|
||||
module.exports = main;
|
307
node_modules/metro-symbolicate/src/SourceMetadataMapConsumer.js
generated
vendored
Normal file
307
node_modules/metro-symbolicate/src/SourceMetadataMapConsumer.js
generated
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
function _slicedToArray(arr, i) {
|
||||
return (
|
||||
_arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest()
|
||||
);
|
||||
}
|
||||
|
||||
function _nonIterableRest() {
|
||||
throw new TypeError("Invalid attempt to destructure non-iterable instance");
|
||||
}
|
||||
|
||||
function _iterableToArrayLimit(arr, i) {
|
||||
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) || _nonIterableSpread()
|
||||
);
|
||||
}
|
||||
|
||||
function _nonIterableSpread() {
|
||||
throw new TypeError("Invalid attempt to spread non-iterable instance");
|
||||
}
|
||||
|
||||
function _iterableToArray(iter) {
|
||||
if (
|
||||
Symbol.iterator in Object(iter) ||
|
||||
Object.prototype.toString.call(iter) === "[object Arguments]"
|
||||
)
|
||||
return Array.from(iter);
|
||||
}
|
||||
|
||||
function _arrayWithoutHoles(arr) {
|
||||
if (Array.isArray(arr)) {
|
||||
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; 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;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
constructor(map) {
|
||||
let normalizeSourceFn =
|
||||
arguments.length > 1 && arguments[1] !== undefined
|
||||
? arguments[1]
|
||||
: normalizeSourcePath;
|
||||
this._sourceMap = map;
|
||||
this._decodedFunctionMapCache = new Map();
|
||||
this._normalizeSource = normalizeSourceFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(_ref) {
|
||||
let line = _ref.line,
|
||||
column = _ref.column,
|
||||
source = _ref.source;
|
||||
|
||||
if (source && line != null && column != null) {
|
||||
const mappings = this._getFunctionMappings(source);
|
||||
|
||||
if (mappings) {
|
||||
const mapping = findEnclosingMapping(mappings, {
|
||||
line,
|
||||
column
|
||||
});
|
||||
|
||||
if (mapping) {
|
||||
return mapping.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 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) {
|
||||
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() {
|
||||
if (!this._metadataBySource) {
|
||||
this._metadataBySource = this._getMetadataObjectsBySourceNames(
|
||||
this._sourceMap
|
||||
);
|
||||
}
|
||||
|
||||
return this._metadataBySource;
|
||||
}
|
||||
/**
|
||||
* Decodes the function name mappings for the given source if needed, and
|
||||
* retrieves a sorted, searchable array of mappings.
|
||||
*/
|
||||
|
||||
_getFunctionMappings(source) {
|
||||
if (this._decodedFunctionMapCache.has(source)) {
|
||||
return this._decodedFunctionMapCache.get(source);
|
||||
}
|
||||
|
||||
let parsedFunctionMap = null;
|
||||
|
||||
const metadataBySource = this._getMetadataBySource();
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(metadataBySource, source)) {
|
||||
const metadata = metadataBySource[source] || [];
|
||||
parsedFunctionMap = decodeFunctionMap(metadata[METADATA_FIELD_FUNCTIONS]);
|
||||
}
|
||||
|
||||
this._decodedFunctionMapCache.set(source, parsedFunctionMap);
|
||||
|
||||
return parsedFunctionMap;
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
const indexMap = map;
|
||||
return Object.assign.apply(
|
||||
Object,
|
||||
[{}].concat(
|
||||
_toConsumableArray(
|
||||
indexMap.sections.map(section =>
|
||||
this._getMetadataObjectsBySourceNames(section.map)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ("x_facebook_sources" in map) {
|
||||
const basicMap = map;
|
||||
return (basicMap.x_facebook_sources || []).reduce(
|
||||
(acc, metadata, index) => {
|
||||
let source = basicMap.sources[index];
|
||||
|
||||
if (source != null) {
|
||||
source = this._normalizeSource(source, basicMap);
|
||||
acc[source] = metadata;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function decodeFunctionMap(functionMap) {
|
||||
if (!functionMap) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parsed = [];
|
||||
let line = 1;
|
||||
let nameIndex = 0;
|
||||
|
||||
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$;
|
||||
|
||||
line += lineDelta;
|
||||
nameIndex += nameDelta;
|
||||
column += columnDelta;
|
||||
parsed.push({
|
||||
line,
|
||||
column,
|
||||
name: functionMap.names[nameIndex]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function findEnclosingMapping(mappings, target) {
|
||||
let first = 0;
|
||||
let it = 0;
|
||||
let count = mappings.length;
|
||||
let step;
|
||||
|
||||
while (count > 0) {
|
||||
it = first;
|
||||
step = Math.floor(count / 2);
|
||||
it += step;
|
||||
|
||||
if (comparePositions(target, mappings[it]) >= 0) {
|
||||
first = ++it;
|
||||
count -= step + 1;
|
||||
} else {
|
||||
count = step;
|
||||
}
|
||||
}
|
||||
|
||||
return first ? mappings[first - 1] : null;
|
||||
}
|
||||
|
||||
function comparePositions(a, b) {
|
||||
if (a.line === b.line) {
|
||||
return a.column - b.column;
|
||||
}
|
||||
|
||||
return a.line - b.line;
|
||||
}
|
||||
|
||||
module.exports = SourceMetadataMapConsumer;
|
233
node_modules/metro-symbolicate/src/SourceMetadataMapConsumer.js.flow
generated
vendored
Normal file
233
node_modules/metro-symbolicate/src/SourceMetadataMapConsumer.js.flow
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const vlq = require('vlq');
|
||||
|
||||
const {normalizeSourcePath} = require('metro-source-map');
|
||||
|
||||
import type {
|
||||
MixedSourceMap,
|
||||
FBSourcesArray,
|
||||
FBSourceFunctionMap,
|
||||
FBSourceMetadata,
|
||||
BasicSourceMap,
|
||||
IndexMap,
|
||||
} from 'metro-source-map';
|
||||
|
||||
const METADATA_FIELD_FUNCTIONS = 0;
|
||||
|
||||
type Position = {
|
||||
+line: number,
|
||||
+column: number,
|
||||
...
|
||||
};
|
||||
type FunctionMapping = {
|
||||
+line: number,
|
||||
+column: number,
|
||||
+name: string,
|
||||
...
|
||||
};
|
||||
type SourceNameNormalizer = (string, {+sourceRoot?: ?string, ...}) => string;
|
||||
type MetadataMap = {[source: string]: ?FBSourceMetadata, ...};
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
constructor(
|
||||
map: MixedSourceMap,
|
||||
normalizeSourceFn: SourceNameNormalizer = normalizeSourcePath,
|
||||
) {
|
||||
this._sourceMap = map;
|
||||
this._decodedFunctionMapCache = new Map();
|
||||
this._normalizeSource = normalizeSourceFn;
|
||||
}
|
||||
|
||||
_sourceMap: MixedSourceMap;
|
||||
_decodedFunctionMapCache: Map<string, ?$ReadOnlyArray<FunctionMapping>>;
|
||||
_normalizeSource: SourceNameNormalizer;
|
||||
_metadataBySource: ?MetadataMap;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
}: Position & {+source: ?string, ...}): ?string {
|
||||
if (source && line != null && column != null) {
|
||||
const mappings = this._getFunctionMappings(source);
|
||||
if (mappings) {
|
||||
const mapping = findEnclosingMapping(mappings, {line, column});
|
||||
if (mapping) {
|
||||
return mapping.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 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: $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(): MetadataMap {
|
||||
if (!this._metadataBySource) {
|
||||
this._metadataBySource = this._getMetadataObjectsBySourceNames(
|
||||
this._sourceMap,
|
||||
);
|
||||
}
|
||||
|
||||
return this._metadataBySource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the function name mappings for the given source if needed, and
|
||||
* retrieves a sorted, searchable array of mappings.
|
||||
*/
|
||||
_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)) {
|
||||
const metadata = metadataBySource[source] || [];
|
||||
parsedFunctionMap = decodeFunctionMap(metadata[METADATA_FIELD_FUNCTIONS]);
|
||||
}
|
||||
this._decodedFunctionMapCache.set(source, parsedFunctionMap);
|
||||
return parsedFunctionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: MixedSourceMap): MetadataMap {
|
||||
// eslint-disable-next-line lint/strictly-null
|
||||
if (map.mappings === undefined) {
|
||||
const indexMap: IndexMap = map;
|
||||
return Object.assign(
|
||||
{},
|
||||
...indexMap.sections.map(section =>
|
||||
this._getMetadataObjectsBySourceNames(section.map),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
source = this._normalizeSource(source, basicMap);
|
||||
acc[source] = metadata;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function decodeFunctionMap(
|
||||
functionMap: ?FBSourceFunctionMap,
|
||||
): $ReadOnlyArray<FunctionMapping> {
|
||||
if (!functionMap) {
|
||||
return [];
|
||||
}
|
||||
const parsed = [];
|
||||
let line = 1;
|
||||
let nameIndex = 0;
|
||||
for (const lineMappings of functionMap.mappings.split(';')) {
|
||||
let column = 0;
|
||||
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]});
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function findEnclosingMapping(
|
||||
mappings: $ReadOnlyArray<FunctionMapping>,
|
||||
target: Position,
|
||||
): ?FunctionMapping {
|
||||
let first = 0;
|
||||
let it = 0;
|
||||
let count = mappings.length;
|
||||
let step;
|
||||
while (count > 0) {
|
||||
it = first;
|
||||
step = Math.floor(count / 2);
|
||||
it += step;
|
||||
if (comparePositions(target, mappings[it]) >= 0) {
|
||||
first = ++it;
|
||||
count -= step + 1;
|
||||
} else {
|
||||
count = step;
|
||||
}
|
||||
}
|
||||
return first ? mappings[first - 1] : null;
|
||||
}
|
||||
|
||||
function comparePositions(a: Position, b: Position): number {
|
||||
if (a.line === b.line) {
|
||||
return a.column - b.column;
|
||||
}
|
||||
return a.line - b.line;
|
||||
}
|
||||
|
||||
module.exports = SourceMetadataMapConsumer;
|
664
node_modules/metro-symbolicate/src/Symbolication.js
generated
vendored
Normal file
664
node_modules/metro-symbolicate/src/Symbolication.js
generated
vendored
Normal file
@ -0,0 +1,664 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
function _objectSpread(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i] != null ? arguments[i] : {};
|
||||
var ownKeys = Object.keys(source);
|
||||
if (typeof Object.getOwnPropertySymbols === "function") {
|
||||
ownKeys = ownKeys.concat(
|
||||
Object.getOwnPropertySymbols(source).filter(function(sym) {
|
||||
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
||||
})
|
||||
);
|
||||
}
|
||||
ownKeys.forEach(function(key) {
|
||||
_defineProperty(target, key, 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");
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const invariant = require("invariant");
|
||||
|
||||
const path = require("path");
|
||||
|
||||
const UNKNOWN_MODULE_IDS = {
|
||||
segmentId: 0,
|
||||
localId: undefined
|
||||
};
|
||||
|
||||
class SymbolicationContext {
|
||||
constructor(options) {
|
||||
this.options = {
|
||||
inputLineStart: 1,
|
||||
inputColumnStart: 0,
|
||||
outputLineStart: 1,
|
||||
outputColumnStart: 0,
|
||||
nameSource: "function_names"
|
||||
};
|
||||
|
||||
if (options) {
|
||||
for (const option of [
|
||||
"inputLineStart",
|
||||
"inputColumnStart",
|
||||
"outputLineStart",
|
||||
"outputColumnStart"
|
||||
]) {
|
||||
if (options[option] != null) {
|
||||
this.options[option] = options[option];
|
||||
}
|
||||
}
|
||||
|
||||
if (options.nameSource != null) {
|
||||
this.options.nameSource = options.nameSource;
|
||||
}
|
||||
}
|
||||
} // parse stack trace with String.replace
|
||||
// replace the matched part of stack trace to symbolicated result
|
||||
// sample stack trace:
|
||||
// IOS: foo@4:18131, Android: bar:4:18063
|
||||
// sample stack trace with module id:
|
||||
// IOS: foo@123.js:4:18131, Android: bar:123.js:4:18063
|
||||
// sample stack trace without function name:
|
||||
// 123.js:4:18131
|
||||
// sample result:
|
||||
// IOS: foo.js:57:foo, Android: bar.js:75:bar
|
||||
|
||||
symbolicate(stackTrace) {
|
||||
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) {
|
||||
fileName = func;
|
||||
func = null;
|
||||
}
|
||||
|
||||
const original = this.getOriginalPositionFor(
|
||||
line,
|
||||
column,
|
||||
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")
|
||||
);
|
||||
}
|
||||
);
|
||||
} // 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) {
|
||||
return fs
|
||||
.readFileSync(mapFile, "utf8")
|
||||
.split("\n")
|
||||
.slice(0, -1)
|
||||
.map(line => {
|
||||
const line_list = line.split(" ");
|
||||
const trampoline = line_list[0];
|
||||
const js_name = line_list[1];
|
||||
const offset = parseInt(line_list[2], 10);
|
||||
|
||||
if (!offset) {
|
||||
return trampoline + " " + trampoline;
|
||||
}
|
||||
|
||||
const original = this.getOriginalPositionFor(
|
||||
this.options.inputLineStart,
|
||||
offset
|
||||
);
|
||||
return (
|
||||
trampoline +
|
||||
" " +
|
||||
(original.name || js_name) +
|
||||
"::" +
|
||||
[original.source, original.line, original.column].join(":")
|
||||
);
|
||||
})
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
symbolicateAttribution(obj) {
|
||||
const loc = obj.location;
|
||||
const line = loc.line != null ? loc.line : this.options.inputLineStart;
|
||||
let column = loc.column != null ? loc.column : loc.virtualOffset;
|
||||
const file = loc.filename ? this.parseFileName(loc.filename) : null;
|
||||
let original = this.getOriginalPositionFor(line, column, file);
|
||||
const isBytecodeRange =
|
||||
loc.bytecodeSize != null &&
|
||||
loc.virtualOffset != null &&
|
||||
!loc.column != null; // Functions compiled from Metro-bundled modules will often have a little bit
|
||||
// of unmapped wrapper code right at the beginning - which is where we query.
|
||||
// Let's attribute them to where the inner module code originates instead.
|
||||
// This loop is O(n*log(n)) in the size of the function, but we will generally
|
||||
// either:
|
||||
// 1. Find a non-null mapping within one or two iterations; or
|
||||
// 2. Reach the end of the function without encountering mappings - this might
|
||||
// happen for function bodies that never throw (generally very short).
|
||||
|
||||
while (
|
||||
isBytecodeRange &&
|
||||
original.source == null &&
|
||||
++column < loc.virtualOffset + loc.bytecodeSize
|
||||
) {
|
||||
original = this.getOriginalPositionFor(line, column, file);
|
||||
}
|
||||
|
||||
obj.location = {
|
||||
file: original.source,
|
||||
line: original.line,
|
||||
column: original.column
|
||||
};
|
||||
} // Symbolicate chrome trace "stackFrames" section.
|
||||
// Each frame in it has three fields: name, funcVirtAddr(optional), offset(optional).
|
||||
// funcVirtAddr and offset are only available if trace is generated from
|
||||
// hbc bundle without debug info.
|
||||
|
||||
symbolicateChromeTrace(traceFile, _ref) {
|
||||
let stdout = _ref.stdout,
|
||||
stderr = _ref.stderr;
|
||||
const contentJson = JSON.parse(fs.readFileSync(traceFile, "utf8"));
|
||||
|
||||
if (contentJson.stackFrames == null) {
|
||||
throw new Error("Unable to locate `stackFrames` section in trace.");
|
||||
}
|
||||
|
||||
stdout.write(
|
||||
"Processing " + Object.keys(contentJson.stackFrames).length + " frames\n"
|
||||
);
|
||||
Object.values(contentJson.stackFrames).forEach(entry => {
|
||||
var _addressOriginal$sour, _addressOriginal$line, _addressOriginal$colu;
|
||||
|
||||
let line;
|
||||
let column; // Function entrypoint line/column; used for symbolicating function name
|
||||
// with legacy source maps (or when --no-function-names is set).
|
||||
|
||||
let funcLine;
|
||||
let funcColumn;
|
||||
|
||||
if (entry.funcVirtAddr != null && entry.offset != null) {
|
||||
// Without debug information.
|
||||
const funcVirtAddr = parseInt(entry.funcVirtAddr, 10);
|
||||
const offsetInFunction = parseInt(entry.offset, 10); // Main bundle always use hard-coded line value 1.
|
||||
// TODO: support multiple bundle/module.
|
||||
|
||||
line = this.options.inputLineStart;
|
||||
column = funcVirtAddr + offsetInFunction;
|
||||
funcLine = this.options.inputLineStart;
|
||||
funcColumn = funcVirtAddr;
|
||||
} else if (entry.line != null && entry.column != null) {
|
||||
// For hbc bundle with debug info, name field may already have source
|
||||
// information for the bundle; we still can use the Metro
|
||||
// source map to symbolicate the bundle frame addresses further to its
|
||||
// original source code.
|
||||
line = entry.line;
|
||||
column = entry.column;
|
||||
funcLine = entry.funcLine;
|
||||
funcColumn = entry.funcColumn;
|
||||
} else {
|
||||
// Native frames.
|
||||
return;
|
||||
} // Symbolicate original file/line/column.
|
||||
|
||||
const addressOriginal = this.getOriginalPositionDetailsFor(line, column);
|
||||
let frameName;
|
||||
|
||||
if (addressOriginal.functionName) {
|
||||
frameName = addressOriginal.functionName;
|
||||
} else {
|
||||
frameName = entry.name; // Symbolicate function name.
|
||||
|
||||
if (funcLine != null && funcColumn != null) {
|
||||
const funcOriginal = this.getOriginalPositionFor(
|
||||
funcLine,
|
||||
funcColumn
|
||||
);
|
||||
|
||||
if (funcOriginal.name != null) {
|
||||
frameName = funcOriginal.name;
|
||||
}
|
||||
} else {
|
||||
// No function line/column info.
|
||||
(stderr || stdout).write(
|
||||
"Warning: no function prolog line/column info; name may be wrong\n"
|
||||
);
|
||||
}
|
||||
} // Output format is: funcName(file:line:column)
|
||||
|
||||
entry.name = [
|
||||
frameName,
|
||||
"(",
|
||||
[
|
||||
(_addressOriginal$sour = addressOriginal.source) !== null &&
|
||||
_addressOriginal$sour !== void 0
|
||||
? _addressOriginal$sour
|
||||
: "null",
|
||||
(_addressOriginal$line = addressOriginal.line) !== null &&
|
||||
_addressOriginal$line !== void 0
|
||||
? _addressOriginal$line
|
||||
: "null",
|
||||
(_addressOriginal$colu = addressOriginal.column) !== null &&
|
||||
_addressOriginal$colu !== void 0
|
||||
? _addressOriginal$colu
|
||||
: "null"
|
||||
].join(":"),
|
||||
")"
|
||||
].join("");
|
||||
});
|
||||
stdout.write("Writing to " + traceFile + "\n");
|
||||
fs.writeFileSync(traceFile, JSON.stringify(contentJson));
|
||||
}
|
||||
/*
|
||||
* 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) {
|
||||
const position = this.getOriginalPositionDetailsFor(
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
moduleIds
|
||||
);
|
||||
return {
|
||||
line: position.line,
|
||||
column: position.column,
|
||||
source: position.source,
|
||||
name: position.functionName ? position.functionName : position.name
|
||||
};
|
||||
}
|
||||
/*
|
||||
* Symbolicates the JavaScript stack trace extracted from the minidump
|
||||
* produced by hermes
|
||||
*/
|
||||
|
||||
symbolicateHermesMinidumpTrace(crashInfo) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
/*
|
||||
* 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) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
parseFileName(str) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
class SingleMapSymbolicationContext extends SymbolicationContext {
|
||||
constructor(SourceMapConsumer, sourceMapContent) {
|
||||
let options =
|
||||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
super(options);
|
||||
const useFunctionNames = this.options.nameSource === "function_names";
|
||||
const sourceMapJson =
|
||||
typeof sourceMapContent === "string"
|
||||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, ""))
|
||||
: sourceMapContent;
|
||||
const x_hermes_function_offsets = sourceMapJson.x_hermes_function_offsets;
|
||||
const segments = {
|
||||
"0": {
|
||||
consumer: new SourceMapConsumer(sourceMapJson),
|
||||
moduleOffsets: sourceMapJson.x_facebook_offsets || [],
|
||||
sourceFunctionsConsumer: useFunctionNames
|
||||
? new SourceMetadataMapConsumer(sourceMapJson)
|
||||
: null,
|
||||
hermesOffsets: x_hermes_function_offsets
|
||||
}
|
||||
};
|
||||
|
||||
if (sourceMapJson.x_facebook_segments) {
|
||||
for (const key of Object.keys(sourceMapJson.x_facebook_segments)) {
|
||||
const map = sourceMapJson.x_facebook_segments[key];
|
||||
segments[key] = {
|
||||
consumer: new SourceMapConsumer(map),
|
||||
moduleOffsets: map.x_facebook_offsets || [],
|
||||
sourceFunctionsConsumer: useFunctionNames
|
||||
? new SourceMetadataMapConsumer(map)
|
||||
: null,
|
||||
hermesOffsets: map.x_hermes_function_offsets
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null;
|
||||
this._segments = segments;
|
||||
}
|
||||
|
||||
symbolicateHermesMinidumpTrace(crashInfo) {
|
||||
const symbolicatedTrace = [];
|
||||
const callstack = crashInfo.callstack;
|
||||
|
||||
if (callstack != null) {
|
||||
for (const stackItem of callstack) {
|
||||
if (stackItem.NativeCode) {
|
||||
symbolicatedTrace.push(stackItem);
|
||||
} else {
|
||||
const CJSModuleOffset = stackItem.CJSModuleOffset,
|
||||
SourceURL = stackItem.SourceURL,
|
||||
FunctionID = stackItem.FunctionID,
|
||||
localOffset = stackItem.ByteCodeOffset;
|
||||
const moduleInformation = this._hasLegacySegments
|
||||
? this.parseFileName(SourceURL)
|
||||
: UNKNOWN_MODULE_IDS;
|
||||
const generatedLine = CJSModuleOffset + this.options.inputLineStart;
|
||||
|
||||
const segment = this._segments[
|
||||
moduleInformation.segmentId.toString()
|
||||
];
|
||||
|
||||
const hermesOffsets =
|
||||
segment === null || segment === void 0
|
||||
? void 0
|
||||
: segment.hermesOffsets;
|
||||
|
||||
if (!hermesOffsets) {
|
||||
symbolicatedTrace.push({
|
||||
line: null,
|
||||
column: null,
|
||||
source: null,
|
||||
functionName: null,
|
||||
name: null
|
||||
});
|
||||
} else {
|
||||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)];
|
||||
const generatedColumn =
|
||||
segmentOffsets[FunctionID] +
|
||||
localOffset +
|
||||
this.options.inputColumnStart;
|
||||
const originalPosition = this.getOriginalPositionDetailsFor(
|
||||
generatedLine,
|
||||
generatedColumn,
|
||||
moduleInformation
|
||||
);
|
||||
symbolicatedTrace.push(originalPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return symbolicatedTrace;
|
||||
}
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
|
||||
getOriginalPositionDetailsFor(lineNumber, columnNumber, moduleIds) {
|
||||
// Adjust arguments to source-map's input coordinates
|
||||
lineNumber =
|
||||
lineNumber != null
|
||||
? lineNumber - this.options.inputLineStart + 1
|
||||
: lineNumber;
|
||||
columnNumber =
|
||||
columnNumber != null
|
||||
? columnNumber - this.options.inputColumnStart + 0
|
||||
: columnNumber;
|
||||
|
||||
if (!moduleIds) {
|
||||
moduleIds = UNKNOWN_MODULE_IDS;
|
||||
}
|
||||
|
||||
let moduleLineOffset = 0;
|
||||
const metadata = this._segments[moduleIds.segmentId + ""];
|
||||
const _moduleIds = moduleIds,
|
||||
localId = _moduleIds.localId;
|
||||
|
||||
if (localId != null) {
|
||||
const moduleOffsets = metadata.moduleOffsets;
|
||||
|
||||
if (!moduleOffsets) {
|
||||
throw new Error(
|
||||
"Module ID given for a source map that does not have " +
|
||||
"an x_facebook_offsets field"
|
||||
);
|
||||
}
|
||||
|
||||
if (moduleOffsets[localId] == null) {
|
||||
throw new Error("Unknown module ID: " + localId);
|
||||
}
|
||||
|
||||
moduleLineOffset = moduleOffsets[localId];
|
||||
}
|
||||
|
||||
const original = metadata.consumer.originalPositionFor({
|
||||
line: Number(lineNumber) + moduleLineOffset,
|
||||
column: Number(columnNumber)
|
||||
});
|
||||
|
||||
if (metadata.sourceFunctionsConsumer) {
|
||||
original.functionName =
|
||||
metadata.sourceFunctionsConsumer.functionNameFor(original) || null;
|
||||
} else {
|
||||
original.functionName = null;
|
||||
}
|
||||
|
||||
return _objectSpread({}, original, {
|
||||
line:
|
||||
original.line != null
|
||||
? original.line - 1 + this.options.outputLineStart
|
||||
: original.line,
|
||||
column:
|
||||
original.column != null
|
||||
? original.column - 0 + this.options.outputColumnStart
|
||||
: original.column
|
||||
});
|
||||
}
|
||||
|
||||
parseFileName(str) {
|
||||
return parseSingleMapFileName(str);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectorySymbolicationContext extends SymbolicationContext {
|
||||
constructor(SourceMapConsumer, rootDir) {
|
||||
let options =
|
||||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
super(options);
|
||||
this._fileMaps = new Map();
|
||||
this._rootDir = rootDir;
|
||||
this._SourceMapConsumer = SourceMapConsumer;
|
||||
}
|
||||
|
||||
_loadMap(mapFilename) {
|
||||
invariant(
|
||||
fs.existsSync(mapFilename),
|
||||
`Could not read source map from '${mapFilename}'`
|
||||
);
|
||||
|
||||
let fileMap = this._fileMaps.get(mapFilename);
|
||||
|
||||
if (fileMap == null) {
|
||||
fileMap = new SingleMapSymbolicationContext(
|
||||
this._SourceMapConsumer,
|
||||
fs.readFileSync(mapFilename, "utf8"),
|
||||
this.options
|
||||
);
|
||||
|
||||
this._fileMaps.set(mapFilename, fileMap);
|
||||
}
|
||||
|
||||
return fileMap;
|
||||
}
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
|
||||
getOriginalPositionDetailsFor(lineNumber, columnNumber, filename) {
|
||||
invariant(
|
||||
filename != null,
|
||||
"filename is required for DirectorySymbolicationContext"
|
||||
);
|
||||
const mapFilename = path.join(this._rootDir, filename + ".map");
|
||||
|
||||
if (!fs.existsSync(mapFilename)) {
|
||||
// Adjust arguments to the output coordinates
|
||||
lineNumber =
|
||||
lineNumber != null
|
||||
? lineNumber -
|
||||
this.options.inputLineStart +
|
||||
this.options.outputLineStart
|
||||
: lineNumber;
|
||||
columnNumber =
|
||||
columnNumber != null
|
||||
? columnNumber -
|
||||
this.options.inputColumnStart +
|
||||
this.options.outputColumnStart
|
||||
: columnNumber;
|
||||
return {
|
||||
line: lineNumber,
|
||||
column: columnNumber,
|
||||
source: filename,
|
||||
name: null,
|
||||
functionName: null
|
||||
};
|
||||
}
|
||||
|
||||
return this._loadMap(mapFilename).getOriginalPositionDetailsFor(
|
||||
lineNumber,
|
||||
columnNumber
|
||||
);
|
||||
}
|
||||
|
||||
parseFileName(str) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the file name of a stack frame is numeric (+ ".js"), we assume it's a
|
||||
* lazily injected module coming from a "random access bundle". We are using
|
||||
* special source maps for these bundles, so that we can symbolicate stack
|
||||
* traces for multiple injected files with a single source map.
|
||||
*
|
||||
* There is also a convention for callsites that are in split segments of a
|
||||
* bundle, named either `seg-3.js` for segment #3 for example, or `seg-3_5.js`
|
||||
* for module #5 of segment #3 of a segmented RAM bundle.
|
||||
*/
|
||||
|
||||
function parseSingleMapFileName(str) {
|
||||
const modMatch = str.match(/^(\d+).js$/);
|
||||
|
||||
if (modMatch != null) {
|
||||
return {
|
||||
segmentId: 0,
|
||||
localId: Number(modMatch[1])
|
||||
};
|
||||
}
|
||||
|
||||
const segMatch = str.match(/^seg-(\d+)(?:_(\d+))?.js$/);
|
||||
|
||||
if (segMatch != null) {
|
||||
return {
|
||||
segmentId: Number(segMatch[1]),
|
||||
localId: segMatch[2] ? Number(segMatch[2]) : null
|
||||
};
|
||||
}
|
||||
|
||||
return UNKNOWN_MODULE_IDS;
|
||||
}
|
||||
|
||||
function createContext(SourceMapConsumer, sourceMapContent) {
|
||||
let options =
|
||||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
return new SingleMapSymbolicationContext(
|
||||
SourceMapConsumer,
|
||||
sourceMapContent,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
function unstable_createDirectoryContext(SourceMapConsumer, rootDir) {
|
||||
let options =
|
||||
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
return new DirectorySymbolicationContext(SourceMapConsumer, rootDir, options);
|
||||
}
|
||||
|
||||
function getOriginalPositionFor(lineNumber, columnNumber, moduleIds, context) {
|
||||
return context.getOriginalPositionFor(lineNumber, columnNumber, moduleIds);
|
||||
}
|
||||
|
||||
function symbolicate(stackTrace, context) {
|
||||
return context.symbolicate(stackTrace);
|
||||
}
|
||||
|
||||
function symbolicateProfilerMap(mapFile, context) {
|
||||
return context.symbolicateProfilerMap(mapFile);
|
||||
}
|
||||
|
||||
function symbolicateAttribution(obj, context) {
|
||||
return context.symbolicateAttribution(obj);
|
||||
}
|
||||
|
||||
function symbolicateChromeTrace(traceFile, _ref2, context) {
|
||||
let stdout = _ref2.stdout,
|
||||
stderr = _ref2.stderr;
|
||||
return context.symbolicateChromeTrace(traceFile, {
|
||||
stdout,
|
||||
stderr
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createContext,
|
||||
unstable_createDirectoryContext,
|
||||
getOriginalPositionFor,
|
||||
parseFileName: parseSingleMapFileName,
|
||||
symbolicate,
|
||||
symbolicateProfilerMap,
|
||||
symbolicateAttribution,
|
||||
symbolicateChromeTrace,
|
||||
SourceMetadataMapConsumer
|
||||
};
|
742
node_modules/metro-symbolicate/src/Symbolication.js.flow
generated
vendored
Normal file
742
node_modules/metro-symbolicate/src/Symbolication.js.flow
generated
vendored
Normal file
@ -0,0 +1,742 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const SourceMetadataMapConsumer = require('./SourceMetadataMapConsumer');
|
||||
|
||||
const fs = require('fs');
|
||||
const invariant = require('invariant');
|
||||
const path = require('path');
|
||||
|
||||
import type {MixedSourceMap, HermesFunctionOffsets} from 'metro-source-map';
|
||||
// flowlint-next-line untyped-type-import:off
|
||||
import {typeof SourceMapConsumer} from 'source-map';
|
||||
|
||||
type SingleMapModuleIds = {
|
||||
segmentId: number,
|
||||
localId: ?number,
|
||||
...
|
||||
};
|
||||
|
||||
type ContextOptionsInput = {
|
||||
+nameSource?: 'function_names' | 'identifier_names',
|
||||
+inputLineStart?: number,
|
||||
+inputColumnStart?: number,
|
||||
+outputLineStart?: number,
|
||||
+outputColumnStart?: number,
|
||||
...
|
||||
};
|
||||
|
||||
// TODO (T46584006): Write the real types for these.
|
||||
// eslint-disable-next-line lint/no-unclear-flowtypes
|
||||
type SizeAttributionMap = Object;
|
||||
// eslint-disable-next-line lint/no-unclear-flowtypes
|
||||
type ChromeTrace = Object;
|
||||
// eslint-disable-next-line lint/no-unclear-flowtypes
|
||||
type ChromeTraceEntry = Object;
|
||||
|
||||
type HermesMinidumpCrashInfo = {
|
||||
+callstack: $ReadOnlyArray<HermesMinidumpStackFrame | NativeCodeStackFrame>,
|
||||
...
|
||||
};
|
||||
|
||||
type HermesMinidumpStackFrame = $ReadOnly<{|
|
||||
ByteCodeOffset: number,
|
||||
FunctionID: number,
|
||||
CJSModuleOffset: number,
|
||||
SourceURL: string,
|
||||
StackFrameRegOffs: string,
|
||||
SourceLocation?: string,
|
||||
|}>;
|
||||
|
||||
type NativeCodeStackFrame = $ReadOnly<{|
|
||||
NativeCode: true,
|
||||
StackFrameRegOffs: string,
|
||||
|}>;
|
||||
|
||||
type SymbolicatedStackTrace = $ReadOnlyArray<
|
||||
SymbolicatedStackFrame | NativeCodeStackFrame,
|
||||
>;
|
||||
|
||||
type SymbolicatedStackFrame = $ReadOnly<{|
|
||||
line: ?number,
|
||||
column: ?number,
|
||||
source: ?string,
|
||||
functionName: ?string,
|
||||
name: ?string,
|
||||
|}>;
|
||||
|
||||
const UNKNOWN_MODULE_IDS: SingleMapModuleIds = {
|
||||
segmentId: 0,
|
||||
localId: undefined,
|
||||
};
|
||||
|
||||
class SymbolicationContext<ModuleIdsT> {
|
||||
+options: {
|
||||
+nameSource: 'function_names' | 'identifier_names',
|
||||
+inputLineStart: number,
|
||||
+inputColumnStart: number,
|
||||
+outputLineStart: number,
|
||||
+outputColumnStart: number,
|
||||
...
|
||||
};
|
||||
|
||||
constructor(options: ContextOptionsInput) {
|
||||
this.options = {
|
||||
inputLineStart: 1,
|
||||
inputColumnStart: 0,
|
||||
outputLineStart: 1,
|
||||
outputColumnStart: 0,
|
||||
nameSource: 'function_names',
|
||||
};
|
||||
if (options) {
|
||||
for (const option of [
|
||||
'inputLineStart',
|
||||
'inputColumnStart',
|
||||
'outputLineStart',
|
||||
'outputColumnStart',
|
||||
]) {
|
||||
if (options[option] != null) {
|
||||
this.options[option] = options[option];
|
||||
}
|
||||
}
|
||||
if (options.nameSource != null) {
|
||||
this.options.nameSource = options.nameSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse stack trace with String.replace
|
||||
// replace the matched part of stack trace to symbolicated result
|
||||
// sample stack trace:
|
||||
// IOS: foo@4:18131, Android: bar:4:18063
|
||||
// sample stack trace with module id:
|
||||
// IOS: foo@123.js:4:18131, Android: bar:123.js:4:18063
|
||||
// sample stack trace without function name:
|
||||
// 123.js:4:18131
|
||||
// sample result:
|
||||
// IOS: foo.js:57:foo, Android: bar.js:75:bar
|
||||
symbolicate(stackTrace: string): string {
|
||||
return stackTrace.replace(
|
||||
/(?:([^@: \n(]+)(@|:))?(?:(?:([^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g,
|
||||
(match, func, delimiter, fileName, line, column) => {
|
||||
if (delimiter === ':' && func && !fileName) {
|
||||
fileName = func;
|
||||
func = null;
|
||||
}
|
||||
const original = this.getOriginalPositionFor(
|
||||
line,
|
||||
column,
|
||||
this.parseFileName(fileName || ''),
|
||||
);
|
||||
return (
|
||||
(original.source ?? 'null') +
|
||||
':' +
|
||||
(original.line ?? 'null') +
|
||||
':' +
|
||||
(original.name ?? 'null')
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Taking in a map like
|
||||
// trampoline offset (optional js function name)
|
||||
// JS_0158_xxxxxxxxxxxxxxxxxxxxxx fe 91081
|
||||
// JS_0159_xxxxxxxxxxxxxxxxxxxxxx Ft 68651
|
||||
// JS_0160_xxxxxxxxxxxxxxxxxxxxxx value 50700
|
||||
// JS_0161_xxxxxxxxxxxxxxxxxxxxxx setGapAtCursor 0
|
||||
// JS_0162_xxxxxxxxxxxxxxxxxxxxxx (unknown) 50818
|
||||
// JS_0163_xxxxxxxxxxxxxxxxxxxxxx value 108267
|
||||
|
||||
symbolicateProfilerMap(mapFile: string): string {
|
||||
return fs
|
||||
.readFileSync(mapFile, 'utf8')
|
||||
.split('\n')
|
||||
.slice(0, -1)
|
||||
.map(line => {
|
||||
const line_list = line.split(' ');
|
||||
const trampoline = line_list[0];
|
||||
const js_name = line_list[1];
|
||||
const offset = parseInt(line_list[2], 10);
|
||||
|
||||
if (!offset) {
|
||||
return trampoline + ' ' + trampoline;
|
||||
}
|
||||
|
||||
const original = this.getOriginalPositionFor(
|
||||
this.options.inputLineStart,
|
||||
offset,
|
||||
);
|
||||
|
||||
return (
|
||||
trampoline +
|
||||
' ' +
|
||||
(original.name || js_name) +
|
||||
'::' +
|
||||
[original.source, original.line, original.column].join(':')
|
||||
);
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
symbolicateAttribution(obj: SizeAttributionMap): SizeAttributionMap {
|
||||
const loc = obj.location;
|
||||
const line = loc.line != null ? loc.line : this.options.inputLineStart;
|
||||
let column = loc.column != null ? loc.column : loc.virtualOffset;
|
||||
const file = loc.filename ? this.parseFileName(loc.filename) : null;
|
||||
let original = this.getOriginalPositionFor(line, column, file);
|
||||
|
||||
const isBytecodeRange =
|
||||
loc.bytecodeSize != null &&
|
||||
loc.virtualOffset != null &&
|
||||
!loc.column != null;
|
||||
|
||||
// Functions compiled from Metro-bundled modules will often have a little bit
|
||||
// of unmapped wrapper code right at the beginning - which is where we query.
|
||||
// Let's attribute them to where the inner module code originates instead.
|
||||
// This loop is O(n*log(n)) in the size of the function, but we will generally
|
||||
// either:
|
||||
// 1. Find a non-null mapping within one or two iterations; or
|
||||
// 2. Reach the end of the function without encountering mappings - this might
|
||||
// happen for function bodies that never throw (generally very short).
|
||||
while (
|
||||
isBytecodeRange &&
|
||||
original.source == null &&
|
||||
++column < loc.virtualOffset + loc.bytecodeSize
|
||||
) {
|
||||
original = this.getOriginalPositionFor(line, column, file);
|
||||
}
|
||||
|
||||
obj.location = {
|
||||
file: original.source,
|
||||
line: original.line,
|
||||
column: original.column,
|
||||
};
|
||||
}
|
||||
|
||||
// Symbolicate chrome trace "stackFrames" section.
|
||||
// Each frame in it has three fields: name, funcVirtAddr(optional), offset(optional).
|
||||
// funcVirtAddr and offset are only available if trace is generated from
|
||||
// hbc bundle without debug info.
|
||||
symbolicateChromeTrace(
|
||||
traceFile: string,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
}: {
|
||||
stdout: stream$Writable,
|
||||
stderr: stream$Writable,
|
||||
...
|
||||
},
|
||||
): void {
|
||||
const contentJson: ChromeTrace = JSON.parse(
|
||||
fs.readFileSync(traceFile, 'utf8'),
|
||||
);
|
||||
if (contentJson.stackFrames == null) {
|
||||
throw new Error('Unable to locate `stackFrames` section in trace.');
|
||||
}
|
||||
stdout.write(
|
||||
'Processing ' + Object.keys(contentJson.stackFrames).length + ' frames\n',
|
||||
);
|
||||
Object.values(contentJson.stackFrames).forEach(
|
||||
(entry: ChromeTraceEntry) => {
|
||||
let line;
|
||||
let column;
|
||||
|
||||
// Function entrypoint line/column; used for symbolicating function name
|
||||
// with legacy source maps (or when --no-function-names is set).
|
||||
let funcLine;
|
||||
let funcColumn;
|
||||
|
||||
if (entry.funcVirtAddr != null && entry.offset != null) {
|
||||
// Without debug information.
|
||||
const funcVirtAddr = parseInt(entry.funcVirtAddr, 10);
|
||||
const offsetInFunction = parseInt(entry.offset, 10);
|
||||
// Main bundle always use hard-coded line value 1.
|
||||
// TODO: support multiple bundle/module.
|
||||
line = this.options.inputLineStart;
|
||||
column = funcVirtAddr + offsetInFunction;
|
||||
funcLine = this.options.inputLineStart;
|
||||
funcColumn = funcVirtAddr;
|
||||
} else if (entry.line != null && entry.column != null) {
|
||||
// For hbc bundle with debug info, name field may already have source
|
||||
// information for the bundle; we still can use the Metro
|
||||
// source map to symbolicate the bundle frame addresses further to its
|
||||
// original source code.
|
||||
line = entry.line;
|
||||
column = entry.column;
|
||||
|
||||
funcLine = entry.funcLine;
|
||||
funcColumn = entry.funcColumn;
|
||||
} else {
|
||||
// Native frames.
|
||||
return;
|
||||
}
|
||||
|
||||
// Symbolicate original file/line/column.
|
||||
const addressOriginal = this.getOriginalPositionDetailsFor(
|
||||
line,
|
||||
column,
|
||||
);
|
||||
|
||||
let frameName;
|
||||
if (addressOriginal.functionName) {
|
||||
frameName = addressOriginal.functionName;
|
||||
} else {
|
||||
frameName = entry.name;
|
||||
// Symbolicate function name.
|
||||
if (funcLine != null && funcColumn != null) {
|
||||
const funcOriginal = this.getOriginalPositionFor(
|
||||
funcLine,
|
||||
funcColumn,
|
||||
);
|
||||
if (funcOriginal.name != null) {
|
||||
frameName = funcOriginal.name;
|
||||
}
|
||||
} else {
|
||||
// No function line/column info.
|
||||
(stderr || stdout).write(
|
||||
'Warning: no function prolog line/column info; name may be wrong\n',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Output format is: funcName(file:line:column)
|
||||
entry.name = [
|
||||
frameName,
|
||||
'(',
|
||||
[
|
||||
addressOriginal.source ?? 'null',
|
||||
addressOriginal.line ?? 'null',
|
||||
addressOriginal.column ?? 'null',
|
||||
].join(':'),
|
||||
')',
|
||||
].join('');
|
||||
},
|
||||
);
|
||||
stdout.write('Writing to ' + traceFile + '\n');
|
||||
fs.writeFileSync(traceFile, JSON.stringify(contentJson));
|
||||
}
|
||||
|
||||
/*
|
||||
* A helper function to return a mapping {line, column} object for a given input
|
||||
* line and column, and optionally a module ID.
|
||||
*/
|
||||
getOriginalPositionFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?ModuleIdsT,
|
||||
): {|
|
||||
line: ?number,
|
||||
column: ?number,
|
||||
source: ?string,
|
||||
name: ?string,
|
||||
|} {
|
||||
const position = this.getOriginalPositionDetailsFor(
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
moduleIds,
|
||||
);
|
||||
return {
|
||||
line: position.line,
|
||||
column: position.column,
|
||||
source: position.source,
|
||||
name: position.functionName ? position.functionName : position.name,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Symbolicates the JavaScript stack trace extracted from the minidump
|
||||
* produced by hermes
|
||||
*/
|
||||
symbolicateHermesMinidumpTrace(
|
||||
crashInfo: HermesMinidumpCrashInfo,
|
||||
): SymbolicatedStackTrace {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
getOriginalPositionDetailsFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?ModuleIdsT,
|
||||
): SymbolicatedStackFrame {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
parseFileName(str: string): ModuleIdsT {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
class SingleMapSymbolicationContext extends SymbolicationContext<SingleMapModuleIds> {
|
||||
+_segments: {
|
||||
+[id: string]: {|
|
||||
+consumer: SourceMapConsumer,
|
||||
+moduleOffsets: $ReadOnlyArray<number>,
|
||||
+sourceFunctionsConsumer: ?SourceMetadataMapConsumer,
|
||||
+hermesOffsets: ?HermesFunctionOffsets,
|
||||
|},
|
||||
...,
|
||||
};
|
||||
+_hasLegacySegments: boolean;
|
||||
|
||||
constructor(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
sourceMapContent: string | MixedSourceMap,
|
||||
options: ContextOptionsInput = {},
|
||||
) {
|
||||
super(options);
|
||||
const useFunctionNames = this.options.nameSource === 'function_names';
|
||||
const sourceMapJson: MixedSourceMap =
|
||||
typeof sourceMapContent === 'string'
|
||||
? JSON.parse(sourceMapContent.replace(/^\)\]\}'/, ''))
|
||||
: sourceMapContent;
|
||||
const {x_hermes_function_offsets} = sourceMapJson;
|
||||
const segments = {
|
||||
'0': {
|
||||
consumer: new SourceMapConsumer(sourceMapJson),
|
||||
moduleOffsets: sourceMapJson.x_facebook_offsets || [],
|
||||
sourceFunctionsConsumer: useFunctionNames
|
||||
? new SourceMetadataMapConsumer(sourceMapJson)
|
||||
: null,
|
||||
hermesOffsets: x_hermes_function_offsets,
|
||||
},
|
||||
};
|
||||
if (sourceMapJson.x_facebook_segments) {
|
||||
for (const key of Object.keys(sourceMapJson.x_facebook_segments)) {
|
||||
const map = sourceMapJson.x_facebook_segments[key];
|
||||
segments[key] = {
|
||||
consumer: new SourceMapConsumer(map),
|
||||
moduleOffsets: map.x_facebook_offsets || [],
|
||||
sourceFunctionsConsumer: useFunctionNames
|
||||
? new SourceMetadataMapConsumer(map)
|
||||
: null,
|
||||
hermesOffsets: map.x_hermes_function_offsets,
|
||||
};
|
||||
}
|
||||
}
|
||||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null;
|
||||
this._segments = segments;
|
||||
}
|
||||
|
||||
symbolicateHermesMinidumpTrace(
|
||||
crashInfo: HermesMinidumpCrashInfo,
|
||||
): SymbolicatedStackTrace {
|
||||
const symbolicatedTrace = [];
|
||||
const {callstack} = crashInfo;
|
||||
if (callstack != null) {
|
||||
for (const stackItem of callstack) {
|
||||
if (stackItem.NativeCode) {
|
||||
symbolicatedTrace.push(stackItem);
|
||||
} else {
|
||||
const {
|
||||
CJSModuleOffset,
|
||||
SourceURL,
|
||||
FunctionID,
|
||||
ByteCodeOffset: localOffset,
|
||||
} = stackItem;
|
||||
const moduleInformation = this._hasLegacySegments
|
||||
? this.parseFileName(SourceURL)
|
||||
: UNKNOWN_MODULE_IDS;
|
||||
const generatedLine = CJSModuleOffset + this.options.inputLineStart;
|
||||
const segment = this._segments[
|
||||
moduleInformation.segmentId.toString()
|
||||
];
|
||||
const hermesOffsets = segment?.hermesOffsets;
|
||||
if (!hermesOffsets) {
|
||||
symbolicatedTrace.push({
|
||||
line: null,
|
||||
column: null,
|
||||
source: null,
|
||||
functionName: null,
|
||||
name: null,
|
||||
});
|
||||
} else {
|
||||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)];
|
||||
const generatedColumn =
|
||||
segmentOffsets[FunctionID] +
|
||||
localOffset +
|
||||
this.options.inputColumnStart;
|
||||
const originalPosition = this.getOriginalPositionDetailsFor(
|
||||
generatedLine,
|
||||
generatedColumn,
|
||||
moduleInformation,
|
||||
);
|
||||
symbolicatedTrace.push(originalPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return symbolicatedTrace;
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
getOriginalPositionDetailsFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?SingleMapModuleIds,
|
||||
): SymbolicatedStackFrame {
|
||||
// Adjust arguments to source-map's input coordinates
|
||||
lineNumber =
|
||||
lineNumber != null
|
||||
? lineNumber - this.options.inputLineStart + 1
|
||||
: lineNumber;
|
||||
columnNumber =
|
||||
columnNumber != null
|
||||
? columnNumber - this.options.inputColumnStart + 0
|
||||
: columnNumber;
|
||||
|
||||
if (!moduleIds) {
|
||||
moduleIds = UNKNOWN_MODULE_IDS;
|
||||
}
|
||||
|
||||
let moduleLineOffset = 0;
|
||||
const metadata = this._segments[moduleIds.segmentId + ''];
|
||||
const {localId} = moduleIds;
|
||||
if (localId != null) {
|
||||
const {moduleOffsets} = metadata;
|
||||
if (!moduleOffsets) {
|
||||
throw new Error(
|
||||
'Module ID given for a source map that does not have ' +
|
||||
'an x_facebook_offsets field',
|
||||
);
|
||||
}
|
||||
if (moduleOffsets[localId] == null) {
|
||||
throw new Error('Unknown module ID: ' + localId);
|
||||
}
|
||||
moduleLineOffset = moduleOffsets[localId];
|
||||
}
|
||||
const original = metadata.consumer.originalPositionFor({
|
||||
line: Number(lineNumber) + moduleLineOffset,
|
||||
column: Number(columnNumber),
|
||||
});
|
||||
if (metadata.sourceFunctionsConsumer) {
|
||||
original.functionName =
|
||||
metadata.sourceFunctionsConsumer.functionNameFor(original) || null;
|
||||
} else {
|
||||
original.functionName = null;
|
||||
}
|
||||
return {
|
||||
...original,
|
||||
line:
|
||||
original.line != null
|
||||
? original.line - 1 + this.options.outputLineStart
|
||||
: original.line,
|
||||
column:
|
||||
original.column != null
|
||||
? original.column - 0 + this.options.outputColumnStart
|
||||
: original.column,
|
||||
};
|
||||
}
|
||||
|
||||
parseFileName(str: string): SingleMapModuleIds {
|
||||
return parseSingleMapFileName(str);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectorySymbolicationContext extends SymbolicationContext<string> {
|
||||
+_fileMaps: Map<string, SingleMapSymbolicationContext>;
|
||||
+_rootDir: string;
|
||||
+_SourceMapConsumer: SourceMapConsumer;
|
||||
|
||||
constructor(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
rootDir: string,
|
||||
options: ContextOptionsInput = {},
|
||||
) {
|
||||
super(options);
|
||||
this._fileMaps = new Map();
|
||||
this._rootDir = rootDir;
|
||||
this._SourceMapConsumer = SourceMapConsumer;
|
||||
}
|
||||
|
||||
_loadMap(mapFilename: string): SingleMapSymbolicationContext {
|
||||
invariant(
|
||||
fs.existsSync(mapFilename),
|
||||
`Could not read source map from '${mapFilename}'`,
|
||||
);
|
||||
let fileMap = this._fileMaps.get(mapFilename);
|
||||
if (fileMap == null) {
|
||||
fileMap = new SingleMapSymbolicationContext(
|
||||
this._SourceMapConsumer,
|
||||
fs.readFileSync(mapFilename, 'utf8'),
|
||||
this.options,
|
||||
);
|
||||
this._fileMaps.set(mapFilename, fileMap);
|
||||
}
|
||||
return fileMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal helper function similar to getOriginalPositionFor. This one
|
||||
* returns both `name` and `functionName` fields so callers can distinguish the
|
||||
* source of the name.
|
||||
*/
|
||||
getOriginalPositionDetailsFor(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
filename: ?string,
|
||||
): SymbolicatedStackFrame {
|
||||
invariant(
|
||||
filename != null,
|
||||
'filename is required for DirectorySymbolicationContext',
|
||||
);
|
||||
const mapFilename = path.join(this._rootDir, filename + '.map');
|
||||
if (!fs.existsSync(mapFilename)) {
|
||||
// Adjust arguments to the output coordinates
|
||||
lineNumber =
|
||||
lineNumber != null
|
||||
? lineNumber -
|
||||
this.options.inputLineStart +
|
||||
this.options.outputLineStart
|
||||
: lineNumber;
|
||||
columnNumber =
|
||||
columnNumber != null
|
||||
? columnNumber -
|
||||
this.options.inputColumnStart +
|
||||
this.options.outputColumnStart
|
||||
: columnNumber;
|
||||
|
||||
return {
|
||||
line: lineNumber,
|
||||
column: columnNumber,
|
||||
source: filename,
|
||||
name: null,
|
||||
functionName: null,
|
||||
};
|
||||
}
|
||||
return this._loadMap(mapFilename).getOriginalPositionDetailsFor(
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
);
|
||||
}
|
||||
|
||||
parseFileName(str: string): string {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the file name of a stack frame is numeric (+ ".js"), we assume it's a
|
||||
* lazily injected module coming from a "random access bundle". We are using
|
||||
* special source maps for these bundles, so that we can symbolicate stack
|
||||
* traces for multiple injected files with a single source map.
|
||||
*
|
||||
* There is also a convention for callsites that are in split segments of a
|
||||
* bundle, named either `seg-3.js` for segment #3 for example, or `seg-3_5.js`
|
||||
* for module #5 of segment #3 of a segmented RAM bundle.
|
||||
*/
|
||||
function parseSingleMapFileName(str: string): SingleMapModuleIds {
|
||||
const modMatch = str.match(/^(\d+).js$/);
|
||||
if (modMatch != null) {
|
||||
return {segmentId: 0, localId: Number(modMatch[1])};
|
||||
}
|
||||
const segMatch = str.match(/^seg-(\d+)(?:_(\d+))?.js$/);
|
||||
if (segMatch != null) {
|
||||
return {
|
||||
segmentId: Number(segMatch[1]),
|
||||
localId: segMatch[2] ? Number(segMatch[2]) : null,
|
||||
};
|
||||
}
|
||||
return UNKNOWN_MODULE_IDS;
|
||||
}
|
||||
|
||||
function createContext(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
sourceMapContent: string | MixedSourceMap,
|
||||
options: ContextOptionsInput = {},
|
||||
): SingleMapSymbolicationContext {
|
||||
return new SingleMapSymbolicationContext(
|
||||
SourceMapConsumer,
|
||||
sourceMapContent,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
function unstable_createDirectoryContext(
|
||||
SourceMapConsumer: SourceMapConsumer,
|
||||
rootDir: string,
|
||||
options: ContextOptionsInput = {},
|
||||
): DirectorySymbolicationContext {
|
||||
return new DirectorySymbolicationContext(SourceMapConsumer, rootDir, options);
|
||||
}
|
||||
|
||||
function getOriginalPositionFor<ModuleIdsT>(
|
||||
lineNumber: ?number,
|
||||
columnNumber: ?number,
|
||||
moduleIds: ?ModuleIdsT,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): {|
|
||||
line: ?number,
|
||||
column: ?number,
|
||||
source: ?string,
|
||||
name: ?string,
|
||||
|} {
|
||||
return context.getOriginalPositionFor(lineNumber, columnNumber, moduleIds);
|
||||
}
|
||||
|
||||
function symbolicate<ModuleIdsT>(
|
||||
stackTrace: string,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): string {
|
||||
return context.symbolicate(stackTrace);
|
||||
}
|
||||
|
||||
function symbolicateProfilerMap<ModuleIdsT>(
|
||||
mapFile: string,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): string {
|
||||
return context.symbolicateProfilerMap(mapFile);
|
||||
}
|
||||
|
||||
function symbolicateAttribution<ModuleIdsT>(
|
||||
obj: SizeAttributionMap,
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): SizeAttributionMap {
|
||||
return context.symbolicateAttribution(obj);
|
||||
}
|
||||
|
||||
function symbolicateChromeTrace<ModuleIdsT>(
|
||||
traceFile: string,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
}: {
|
||||
stdout: stream$Writable,
|
||||
stderr: stream$Writable,
|
||||
...
|
||||
},
|
||||
context: SymbolicationContext<ModuleIdsT>,
|
||||
): void {
|
||||
return context.symbolicateChromeTrace(traceFile, {stdout, stderr});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createContext,
|
||||
unstable_createDirectoryContext,
|
||||
getOriginalPositionFor,
|
||||
parseFileName: parseSingleMapFileName,
|
||||
symbolicate,
|
||||
symbolicateProfilerMap,
|
||||
symbolicateAttribution,
|
||||
symbolicateChromeTrace,
|
||||
SourceMetadataMapConsumer,
|
||||
};
|
300
node_modules/metro-symbolicate/src/symbolicate.js
generated
vendored
Executable file
300
node_modules/metro-symbolicate/src/symbolicate.js
generated
vendored
Executable file
@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict-local
|
||||
* @format
|
||||
*/
|
||||
// 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"; // 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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const Symbolication = require("./Symbolication.js");
|
||||
|
||||
const fs = require("fs"); // flowlint-next-line untyped-import:off
|
||||
|
||||
const through2 = require("through2");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
} else {
|
||||
const content = fs.readFileSync(sourceMapFileName, "utf8");
|
||||
context = Symbolication.createContext(
|
||||
SourceMapConsumer,
|
||||
content,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
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"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
stderr.write(error + "\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
return _main.apply(this, arguments);
|
||||
}
|
||||
|
||||
function readAll(stream) {
|
||||
return new Promise(resolve => {
|
||||
let data = "";
|
||||
|
||||
if (stream.isTTY === true) {
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.setEncoding("utf8");
|
||||
stream.on("readable", () => {
|
||||
let chunk; // flowlint-next-line sketchy-null-string:off
|
||||
|
||||
while ((chunk = stream.read())) {
|
||||
data += chunk.toString();
|
||||
}
|
||||
});
|
||||
stream.on("end", () => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForStream(stream) {
|
||||
return new Promise(resolve => {
|
||||
stream.on("finish", resolve);
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().then(code => process.exit(code));
|
||||
}
|
||||
|
||||
module.exports = main;
|
235
node_modules/metro-symbolicate/src/symbolicate.js.flow
generated
vendored
Normal file
235
node_modules/metro-symbolicate/src/symbolicate.js.flow
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
// 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';
|
||||
|
||||
// flowlint-next-line untyped-import:off
|
||||
const SourceMapConsumer = require('source-map').SourceMapConsumer;
|
||||
const Symbolication = require('./Symbolication.js');
|
||||
|
||||
const fs = require('fs');
|
||||
// flowlint-next-line untyped-import:off
|
||||
const through2 = require('through2');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
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,
|
||||
);
|
||||
} else {
|
||||
const content = fs.readFileSync(sourceMapFileName, 'utf8');
|
||||
context = Symbolication.createContext(
|
||||
SourceMapConsumer,
|
||||
content,
|
||||
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 {
|
||||
stdout.write(context.symbolicate(stackTrace));
|
||||
}
|
||||
} else if (argv[0].endsWith('.profmap')) {
|
||||
stdout.write(context.symbolicateProfilerMap(argv[0]));
|
||||
} else if (argv[0] === '--attribution') {
|
||||
let buffer = '';
|
||||
await waitForStream(
|
||||
stdin
|
||||
.pipe(
|
||||
through2(function(data, enc, callback) {
|
||||
// Take arbitrary strings, output single lines
|
||||
buffer += data;
|
||||
const lines = buffer.split('\n');
|
||||
for (let i = 0, e = lines.length - 1; i < e; i++) {
|
||||
this.push(lines[i]);
|
||||
}
|
||||
buffer = lines[lines.length - 1];
|
||||
callback();
|
||||
}),
|
||||
)
|
||||
.pipe(
|
||||
through2.obj(function(data, enc, callback) {
|
||||
// This is JSONL, so each line is a separate JSON object
|
||||
const obj = JSON.parse(data);
|
||||
context.symbolicateAttribution(obj);
|
||||
this.push(JSON.stringify(obj) + '\n');
|
||||
callback();
|
||||
}),
|
||||
)
|
||||
.pipe(stdout),
|
||||
);
|
||||
} else if (argv[0].endsWith('.cpuprofile')) {
|
||||
// NOTE: synchronous
|
||||
context.symbolicateChromeTrace(argv[0], {stdout, stderr});
|
||||
} else {
|
||||
// read-from-argv form.
|
||||
let moduleIds;
|
||||
if (argv[0].endsWith('.js')) {
|
||||
moduleIds = context.parseFileName(argv[0]);
|
||||
argv.shift();
|
||||
} 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 ?? 'null',
|
||||
original.line ?? 'null',
|
||||
original.name ?? 'null',
|
||||
].join(':') + '\n',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
stderr.write(error + '\n');
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function readAll(stream) {
|
||||
return new Promise(resolve => {
|
||||
let data = '';
|
||||
if (stream.isTTY === true) {
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.setEncoding('utf8');
|
||||
stream.on('readable', () => {
|
||||
let chunk;
|
||||
// flowlint-next-line sketchy-null-string:off
|
||||
while ((chunk = stream.read())) {
|
||||
data += chunk.toString();
|
||||
}
|
||||
});
|
||||
stream.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForStream(stream) {
|
||||
return new Promise(resolve => {
|
||||
stream.on('finish', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().then(code => process.exit(code));
|
||||
}
|
||||
|
||||
module.exports = main;
|
Reference in New Issue
Block a user