Filter out Hermes internal bytecode frames from error stack traces

Summary:
Changelog: [Fixed][General] Filter out Hermes internal bytecode frames (Promise implementation) from error stack traces

Currently, React Native strips native frames from call stacks reported via ExceptionsManager / LogBox. This diff does the same for *internal bytecode* frames in Hermes, which are functionally similar to native frames: they are implementation details of the engine for which source code isn't readily available.

In particular this change will avoid confusing symbolication tools that may otherwise try to treat `InternalBytecode.js` frames as belonging to the main React Native bundle (and produce garbage output).

Reviewed By: GijsWeterings

Differential Revision: D42367135

fbshipit-source-id: b7904f10c7ff6956e7b736b575dde1ce45028200
This commit is contained in:
Moti Zilberman
2023-01-05 11:05:39 -08:00
committed by Facebook GitHub Bot
parent f723ab002e
commit 4c911a2dec
3 changed files with 65 additions and 7 deletions
@@ -52,6 +52,44 @@ describe('parseHermesStack', () => {
`);
});
test('internal bytecode location', () => {
expect(
parseHermesStack(
[
'TypeError: undefined is not a function',
' at internal (address at InternalBytecode.js:1:9)',
' at notInternal (address at /js/InternalBytecode.js:10:1234)',
].join('\n'),
),
).toMatchInlineSnapshot(`
Object {
"entries": Array [
Object {
"functionName": "internal",
"location": Object {
"line1Based": 1,
"sourceUrl": "InternalBytecode.js",
"type": "INTERNAL_BYTECODE",
"virtualOffset0Based": 9,
},
"type": "FRAME",
},
Object {
"functionName": "notInternal",
"location": Object {
"line1Based": 10,
"sourceUrl": "/js/InternalBytecode.js",
"type": "BYTECODE",
"virtualOffset0Based": 1234,
},
"type": "FRAME",
},
],
"message": "TypeError: undefined is not a function",
}
`);
});
test('source location', () => {
expect(
parseHermesStack(
+1 -1
View File
@@ -22,7 +22,7 @@ function convertHermesStack(stack: HermesParsedStack): Array<StackFrame> {
continue;
}
const {location, functionName} = entry;
if (location.type === 'NATIVE') {
if (location.type === 'NATIVE' || location.type === 'INTERNAL_BYTECODE') {
continue;
}
frames.push({
+26 -6
View File
@@ -21,6 +21,13 @@ type HermesStackLocationSource = $ReadOnly<{
column1Based: number,
}>;
type HermesStackLocationInternalBytecode = $ReadOnly<{
type: 'INTERNAL_BYTECODE',
sourceUrl: string,
line1Based: number,
virtualOffset0Based: number,
}>;
type HermesStackLocationBytecode = $ReadOnly<{
type: 'BYTECODE',
sourceUrl: string,
@@ -31,6 +38,7 @@ type HermesStackLocationBytecode = $ReadOnly<{
type HermesStackLocation =
| HermesStackLocationNative
| HermesStackLocationSource
| HermesStackLocationInternalBytecode
| HermesStackLocationBytecode;
type HermesStackEntryFrame = $ReadOnly<{
@@ -65,6 +73,11 @@ const RE_FRAME =
// 1. count of skipped frames
const RE_SKIPPED = /^ {4}... skipping (\d+) frames$/;
function isInternalBytecodeSourceUrl(sourceUrl: string): boolean {
// See https://github.com/facebook/hermes/blob/3332fa020cae0bab751f648db7c94e1d687eeec7/lib/VM/Runtime.cpp#L1100
return sourceUrl === 'InternalBytecode.js';
}
function parseLine(line: string): ?HermesStackEntry {
const asFrame = line.match(RE_FRAME);
if (asFrame) {
@@ -75,12 +88,19 @@ function parseLine(line: string): ?HermesStackEntry {
asFrame[2] === 'native'
? {type: 'NATIVE'}
: asFrame[3] === 'address at '
? {
type: 'BYTECODE',
sourceUrl: asFrame[4],
line1Based: Number.parseInt(asFrame[5], 10),
virtualOffset0Based: Number.parseInt(asFrame[6], 10),
}
? isInternalBytecodeSourceUrl(asFrame[4])
? {
type: 'INTERNAL_BYTECODE',
sourceUrl: asFrame[4],
line1Based: Number.parseInt(asFrame[5], 10),
virtualOffset0Based: Number.parseInt(asFrame[6], 10),
}
: {
type: 'BYTECODE',
sourceUrl: asFrame[4],
line1Based: Number.parseInt(asFrame[5], 10),
virtualOffset0Based: Number.parseInt(asFrame[6], 10),
}
: {
type: 'SOURCE',
sourceUrl: asFrame[4],