Skip to content

Commit

Permalink
Filter out Hermes internal bytecode frames from error stack traces
Browse files Browse the repository at this point in the history
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
  • Loading branch information
motiz88 authored and facebook-github-bot committed Jan 5, 2023
1 parent f723ab0 commit 4c911a2
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 7 deletions.
38 changes: 38 additions & 0 deletions Libraries/Core/Devtools/__tests__/parseHermesStack-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Core/Devtools/parseErrorStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
32 changes: 26 additions & 6 deletions Libraries/Core/Devtools/parseHermesStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,6 +38,7 @@ type HermesStackLocationBytecode = $ReadOnly<{
type HermesStackLocation =
| HermesStackLocationNative
| HermesStackLocationSource
| HermesStackLocationInternalBytecode
| HermesStackLocationBytecode;

type HermesStackEntryFrame = $ReadOnly<{
Expand Down Expand Up @@ -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) {
Expand All @@ -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],
Expand Down

0 comments on commit 4c911a2

Please sign in to comment.