From 4c911a2deceb59fc07735205ae3a4622b4334f88 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Thu, 5 Jan 2023 11:05:39 -0800 Subject: [PATCH] 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 --- .../__tests__/parseHermesStack-test.js | 38 +++++++++++++++++++ Libraries/Core/Devtools/parseErrorStack.js | 2 +- Libraries/Core/Devtools/parseHermesStack.js | 32 +++++++++++++--- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/Libraries/Core/Devtools/__tests__/parseHermesStack-test.js b/Libraries/Core/Devtools/__tests__/parseHermesStack-test.js index dba1e4c89ab0ca..29bd88c3d7f857 100644 --- a/Libraries/Core/Devtools/__tests__/parseHermesStack-test.js +++ b/Libraries/Core/Devtools/__tests__/parseHermesStack-test.js @@ -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( diff --git a/Libraries/Core/Devtools/parseErrorStack.js b/Libraries/Core/Devtools/parseErrorStack.js index f98a95cc4bf6e7..9b84537c3d9863 100644 --- a/Libraries/Core/Devtools/parseErrorStack.js +++ b/Libraries/Core/Devtools/parseErrorStack.js @@ -22,7 +22,7 @@ function convertHermesStack(stack: HermesParsedStack): Array { continue; } const {location, functionName} = entry; - if (location.type === 'NATIVE') { + if (location.type === 'NATIVE' || location.type === 'INTERNAL_BYTECODE') { continue; } frames.push({ diff --git a/Libraries/Core/Devtools/parseHermesStack.js b/Libraries/Core/Devtools/parseHermesStack.js index ab053441ece6bf..1193afd88f9507 100644 --- a/Libraries/Core/Devtools/parseHermesStack.js +++ b/Libraries/Core/Devtools/parseHermesStack.js @@ -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],