From 54377f595c818ee3e0e44ee29c8c3dc13bc2b877 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Wed, 9 Oct 2024 14:27:10 -0700 Subject: [PATCH] earlyjs: Implement decoratedExtraDataKey (#46932) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46932 In the old error handling pipeline, when the app [assigns an object to error[ExceptionsManager.decoratedExtraDataKey]](https://fburl.com/code/9t9u8rgv) ``` const error = new Error('Some error happened'); // Annotates the error with some custom extra data. error[ExceptionsManager.decoratedExtraDataKey] = {foo: 'bar'}; ExceptionsManager.handleException(error, true); ``` That object [gets forwarded as extraData](https://fburl.com/code/gy7v173u) to ExceptionsManager. This diff implements that functionality within the c++ earlyjs pipeline. Changelog: [Internal] Reviewed By: javache Differential Revision: D63927091 --- .../Libraries/Core/ExceptionsManager.js | 5 +++-- .../__snapshots__/public-api-test.js.snap | 2 +- .../react-native/ReactCommon/jsc/JSCRuntime.cpp | 6 +++--- .../jserrorhandler/JsErrorHandler.cpp | 17 ++++++++++++++++- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/react-native/Libraries/Core/ExceptionsManager.js b/packages/react-native/Libraries/Core/ExceptionsManager.js index eac2be19b981f1..e64a3a0fd6e54f 100644 --- a/packages/react-native/Libraries/Core/ExceptionsManager.js +++ b/packages/react-native/Libraries/Core/ExceptionsManager.js @@ -22,10 +22,11 @@ type ExceptionDecorator = ExceptionData => ExceptionData; let userExceptionDecorator: ?ExceptionDecorator; let inUserExceptionDecorator = false; -// This Symbol is used to decorate an ExtendedError with extra data in select usecases. +// This string is used to decorate an ExtendedError with extra data in select usecases. // Note that data passed using this method should be strictly contained, // as data that's not serializable/too large may cause issues with passing the error to the native code. -const decoratedExtraDataKey: symbol = Symbol('decoratedExtraDataKey'); +// TODO(T204185517): We should use a Symbol for this, but jsi through jsc doesn't support it yet. +const decoratedExtraDataKey = 'RN$ErrorExtraDataKey'; /** * Allows the app to add information to the exception report before it is sent diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 8c0753fc380ee7..8ecccf15b20482 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -4337,7 +4337,7 @@ exports[`public API should not change unintentionally Libraries/Core/ExceptionsM name: string; } type ExceptionDecorator = (ExceptionData) => ExceptionData; -declare const decoratedExtraDataKey: symbol; +declare const decoratedExtraDataKey: "RN$ErrorExtraDataKey"; declare function unstable_setExceptionDecorator( exceptionDecorator: ?ExceptionDecorator ): void; diff --git a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp index e63260acb21b18..2216126af41028 100644 --- a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp +++ b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp @@ -668,9 +668,9 @@ jsi::PropNameID JSCRuntime::createPropNameIDFromString(const jsi::String& str) { } jsi::PropNameID JSCRuntime::createPropNameIDFromSymbol(const jsi::Symbol& sym) { - // TODO: Support for symbols through the native API in JSC is very limited. - // While we could construct a PropNameID here, we would not be able to get a - // symbol property through the C++ API. + // TODO(T204185517): Support for symbols through the native API in JSC is very + // limited. While we could construct a PropNameID here, we would not be able + // to get a symbol property through the C++ API. throw std::logic_error("Not implemented"); } diff --git a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp index f8d80127fa98e7..0f4aba9929b9a8 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp +++ b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp @@ -40,6 +40,15 @@ bool isTruthy(jsi::Runtime& runtime, const jsi::Value& value) { auto Boolean = runtime.global().getPropertyAsFunction(runtime, "Boolean"); return Boolean.call(runtime, value).getBool(); } + +void objectAssign( + jsi::Runtime& runtime, + jsi::Object& target, + const jsi::Object& value) { + auto Object = runtime.global().getPropertyAsObject(runtime, "Object"); + auto assign = Object.getPropertyAsFunction(runtime, "assign"); + assign.callWithThis(runtime, Object, target, jsi::Value(runtime, value)); +} } // namespace namespace facebook::react { @@ -198,8 +207,14 @@ void JsErrorHandler::emitError( message += ", js engine: " + stringifyToCpp(runtime, jsEngineValue); } - // TODO: What about spreading in decoratedExtraDataKey? + auto extraDataKey = jsi::PropNameID::forUtf8(runtime, "RN$ErrorExtraDataKey"); + auto extraDataValue = errorObj.getProperty(runtime, extraDataKey); + auto extraData = jsi::Object(runtime); + if (extraDataValue.isObject()) { + objectAssign(runtime, extraData, extraDataValue.asObject(runtime)); + } + extraData.setProperty(runtime, "jsEngine", jsEngineValue); extraData.setProperty(runtime, "rawStack", error.getStack());