metro-symbolicate
Advanced tools
Comparing version 0.53.1 to 0.54.0
{ | ||
"name": "metro-symbolicate", | ||
"version": "0.53.1", | ||
"version": "0.54.0", | ||
"description": "A tool to find the source location from JS bundles and stack traces.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -7,3 +7,3 @@ /** | ||
* | ||
* @emails oncall+react_native | ||
* @emails oncall+js_symbolication | ||
* @format | ||
@@ -71,2 +71,12 @@ */ | ||
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 () => | ||
@@ -90,2 +100,118 @@ await expect( | ||
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 () => { | ||
@@ -92,0 +218,0 @@ fs.copyFileSync( |
@@ -107,3 +107,3 @@ /** | ||
source, | ||
} /*: Position & {source: string} */) /*: ?string */ { | ||
} /*: Position & {source: ?string} */) /*: ?string */ { | ||
if (source && line != null && column != null) { | ||
@@ -198,7 +198,10 @@ const mappings = this._getFunctionMappings(source); | ||
(acc, metadata, index) => { | ||
const source = this._normalizeSource( | ||
basicMap.sources[index], | ||
basicMap, | ||
); | ||
acc[source] = metadata; | ||
let source = basicMap.sources[index]; | ||
if (source != null) { | ||
source = this._normalizeSource( | ||
source, | ||
basicMap, | ||
); | ||
acc[source] = metadata; | ||
} | ||
return acc; | ||
@@ -205,0 +208,0 @@ }, |
@@ -197,2 +197,24 @@ /** | ||
var original = getOriginalPositionFor(line, column, file, context); | ||
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 = getOriginalPositionFor(line, column, file, context); | ||
} | ||
obj.location = { | ||
@@ -199,0 +221,0 @@ file: original.source, |
@@ -104,4 +104,4 @@ /** | ||
/*: BabelSourceMap */ | ||
) /*: string */ | ||
{ | ||
) { | ||
/*: string */ | ||
const sourceRoot = map.sourceRoot; | ||
@@ -179,3 +179,3 @@ source = String(source); // Some source maps produce relative source paths like "./foo.js" instead of | ||
_ref | ||
/*: Position & {source: string} */ | ||
/*: Position & {source: ?string} */ | ||
) /*: ?string */ | ||
@@ -307,8 +307,9 @@ { | ||
(acc, metadata, index) => { | ||
const source = this._normalizeSource( | ||
basicMap.sources[index], | ||
basicMap | ||
); | ||
let source = basicMap.sources[index]; | ||
acc[source] = metadata; | ||
if (source != null) { | ||
source = this._normalizeSource(source, basicMap); | ||
acc[source] = metadata; | ||
} | ||
return acc; | ||
@@ -327,4 +328,4 @@ }, | ||
/*: ?FBSourceFunctionMap */ | ||
) /*: $ReadOnlyArray<FunctionMapping> */ | ||
{ | ||
) { | ||
/*: $ReadOnlyArray<FunctionMapping> */ | ||
if (!functionMap) { | ||
@@ -368,4 +369,4 @@ return []; | ||
/*: Position */ | ||
) /*: ?FunctionMapping */ | ||
{ | ||
) { | ||
/*: ?FunctionMapping */ | ||
let first = 0; | ||
@@ -397,4 +398,4 @@ let it = 0; | ||
/*: Position */ | ||
) /*: number */ | ||
{ | ||
) { | ||
/*: number */ | ||
if (a.line === b.line) { | ||
@@ -401,0 +402,0 @@ return a.column - b.column; |
@@ -285,2 +285,22 @@ /** | ||
var original = getOriginalPositionFor(line, column, file, context); | ||
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 = getOriginalPositionFor(line, column, file, context); | ||
} | ||
obj.location = { | ||
@@ -287,0 +307,0 @@ file: original.source, |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
128244
24
1825