mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
earlyjs: Integrate c++ pipeline with console.error
Summary: ## Changes If the c++ pipeline is active: If someone calls console.error: - The c++ pipeline will report it as a soft error If someone reports an error: - The c++ pipeline will log it via console.error Changelog: [Internal] Reviewed By: javache Differential Revision: D64506069 fbshipit-source-id: 293cd1774f19ee03d7eb61572b7b1f03a47a4756
This commit is contained in:
committed by
Facebook GitHub Bot
parent
5b53c07329
commit
cc6f75bb75
+25
-11
@@ -142,7 +142,11 @@ let inExceptionHandler = false;
|
||||
*/
|
||||
function handleException(e: mixed, isFatal: boolean) {
|
||||
// TODO(T196834299): We should really use a c++ turbomodule for this
|
||||
if (!global.RN$handleException || !global.RN$handleException(e, isFatal)) {
|
||||
const reportToConsole = true;
|
||||
if (
|
||||
!global.RN$handleException ||
|
||||
!global.RN$handleException(e, isFatal, reportToConsole)
|
||||
) {
|
||||
let error: Error;
|
||||
if (e instanceof Error) {
|
||||
error = e;
|
||||
@@ -158,7 +162,7 @@ function handleException(e: mixed, isFatal: boolean) {
|
||||
/* $FlowFixMe[class-object-subtyping] added when improving typing for this
|
||||
* parameters */
|
||||
// $FlowFixMe[incompatible-call]
|
||||
reportException(error, isFatal, /*reportToConsole*/ true);
|
||||
reportException(error, isFatal, reportToConsole);
|
||||
} finally {
|
||||
inExceptionHandler = false;
|
||||
}
|
||||
@@ -173,7 +177,10 @@ function reactConsoleErrorHandler(...args) {
|
||||
if (!console.reportErrorsAsExceptions) {
|
||||
return;
|
||||
}
|
||||
if (inExceptionHandler) {
|
||||
if (
|
||||
inExceptionHandler ||
|
||||
(global.RN$inExceptionHandler && global.RN$inExceptionHandler())
|
||||
) {
|
||||
// The fundamental trick here is that are multiple entry point to logging errors:
|
||||
// (see D19743075 for more background)
|
||||
//
|
||||
@@ -227,14 +234,21 @@ function reactConsoleErrorHandler(...args) {
|
||||
error.name = 'console.error';
|
||||
}
|
||||
|
||||
reportException(
|
||||
/* $FlowFixMe[class-object-subtyping] added when improving typing for this
|
||||
* parameters */
|
||||
// $FlowFixMe[incompatible-call]
|
||||
error,
|
||||
false, // isFatal
|
||||
false, // reportToConsole
|
||||
);
|
||||
const isFatal = false;
|
||||
const reportToConsole = false;
|
||||
if (
|
||||
!global.RN$handleException ||
|
||||
!global.RN$handleException(error, isFatal, reportToConsole)
|
||||
) {
|
||||
reportException(
|
||||
/* $FlowFixMe[class-object-subtyping] added when improving typing for this
|
||||
* parameters */
|
||||
// $FlowFixMe[incompatible-call]
|
||||
error,
|
||||
isFatal,
|
||||
reportToConsole,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,6 +62,22 @@ jsi::Object wrapInErrorIfNecessary(
|
||||
: Error.callAsConstructor(runtime, value).getObject(runtime);
|
||||
return error;
|
||||
}
|
||||
|
||||
class SetFalseOnDestruct {
|
||||
std::shared_ptr<bool> _value;
|
||||
|
||||
public:
|
||||
SetFalseOnDestruct(const SetFalseOnDestruct&) = delete;
|
||||
SetFalseOnDestruct& operator=(const SetFalseOnDestruct&) = delete;
|
||||
SetFalseOnDestruct(SetFalseOnDestruct&&) = delete;
|
||||
SetFalseOnDestruct& operator=(SetFalseOnDestruct&&) = delete;
|
||||
explicit SetFalseOnDestruct(std::shared_ptr<bool> value)
|
||||
: _value(std::move(value)) {}
|
||||
~SetFalseOnDestruct() {
|
||||
*_value = false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace facebook::react {
|
||||
@@ -162,7 +178,7 @@ std::ostream& operator<<(
|
||||
|
||||
JsErrorHandler::JsErrorHandler(JsErrorHandler::OnJsError onJsError)
|
||||
: _onJsError(std::move(onJsError)),
|
||||
_hasHandledFatalError(false){
|
||||
_inErrorHandler(std::make_shared<bool>(false)){
|
||||
|
||||
};
|
||||
|
||||
@@ -171,7 +187,8 @@ JsErrorHandler::~JsErrorHandler() {}
|
||||
void JsErrorHandler::handleError(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal) {
|
||||
bool isFatal,
|
||||
bool logToConsole) {
|
||||
// TODO: Current error parsing works and is stable. Can investigate using
|
||||
// REGEX_HERMES to get additional Hermes data, though it requires JS setup
|
||||
if (_isRuntimeReady) {
|
||||
@@ -191,13 +208,17 @@ void JsErrorHandler::handleError(
|
||||
}
|
||||
}
|
||||
|
||||
emitError(runtime, error, isFatal);
|
||||
handleErrorWithCppPipeline(runtime, error, isFatal, logToConsole);
|
||||
}
|
||||
|
||||
void JsErrorHandler::emitError(
|
||||
void JsErrorHandler::handleErrorWithCppPipeline(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal) {
|
||||
bool isFatal,
|
||||
bool logToConsole) {
|
||||
*_inErrorHandler = true;
|
||||
SetFalseOnDestruct temp{_inErrorHandler};
|
||||
|
||||
auto message = error.getMessage();
|
||||
auto errorObj = wrapInErrorIfNecessary(runtime, error.value());
|
||||
auto componentStackValue = errorObj.getProperty(runtime, "componentStack");
|
||||
@@ -278,6 +299,14 @@ void JsErrorHandler::emitError(
|
||||
isTruthy(runtime, errorObj.getProperty(runtime, "isComponentError"));
|
||||
data.setProperty(runtime, "isComponentError", isComponentError);
|
||||
|
||||
if (logToConsole) {
|
||||
auto console = runtime.global().getPropertyAsObject(runtime, "console");
|
||||
auto errorFn = console.getPropertyAsFunction(runtime, "error");
|
||||
auto finalMessage =
|
||||
jsi::String::createFromUtf8(runtime, parsedError.message);
|
||||
errorFn.callWithThis(runtime, console, finalMessage);
|
||||
}
|
||||
|
||||
std::shared_ptr<bool> shouldPreventDefault = std::make_shared<bool>(false);
|
||||
auto preventDefault = jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
@@ -330,4 +359,8 @@ void JsErrorHandler::notifyOfFatalError() {
|
||||
_hasHandledFatalError = true;
|
||||
}
|
||||
|
||||
bool JsErrorHandler::inErrorHandler() {
|
||||
return *_inErrorHandler;
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -43,13 +43,18 @@ class JsErrorHandler {
|
||||
explicit JsErrorHandler(OnJsError onJsError);
|
||||
~JsErrorHandler();
|
||||
|
||||
void handleError(jsi::Runtime& runtime, jsi::JSError& error, bool isFatal);
|
||||
void handleError(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal,
|
||||
bool logToConsole = true);
|
||||
bool hasHandledFatalError();
|
||||
void registerErrorListener(
|
||||
const std::function<void(jsi::Runtime&, jsi::Value)>& listener);
|
||||
void setRuntimeReady();
|
||||
bool isRuntimeReady();
|
||||
void notifyOfFatalError();
|
||||
bool inErrorHandler();
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -60,11 +65,16 @@ class JsErrorHandler {
|
||||
* teardown get reported properly.
|
||||
**/
|
||||
OnJsError _onJsError;
|
||||
bool _hasHandledFatalError;
|
||||
bool _hasHandledFatalError{};
|
||||
bool _isRuntimeReady{};
|
||||
std::shared_ptr<bool> _inErrorHandler;
|
||||
std::vector<std::function<void(jsi::Runtime&, jsi::Value)>> _errorListeners;
|
||||
|
||||
void emitError(jsi::Runtime& runtime, jsi::JSError& error, bool isFatal);
|
||||
void handleErrorWithCppPipeline(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal,
|
||||
bool logToConsole);
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -409,6 +409,21 @@ void ReactInstance::initializeRuntime(
|
||||
|
||||
defineReactInstanceFlags(runtime, options);
|
||||
|
||||
defineReadOnlyGlobal(
|
||||
runtime,
|
||||
"RN$inExceptionHandler",
|
||||
jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
jsi::PropNameID::forAscii(runtime, "inExceptionHandler"),
|
||||
0,
|
||||
[jsErrorHandler = jsErrorHandler_](
|
||||
jsi::Runtime& /*runtime*/,
|
||||
const jsi::Value& /*unused*/,
|
||||
const jsi::Value* /*args*/,
|
||||
size_t /*count*/) {
|
||||
return jsErrorHandler->inErrorHandler();
|
||||
}));
|
||||
|
||||
// TODO(T196834299): We should really use a C++ turbomodule for this
|
||||
defineReadOnlyGlobal(
|
||||
runtime,
|
||||
@@ -416,7 +431,7 @@ void ReactInstance::initializeRuntime(
|
||||
jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
jsi::PropNameID::forAscii(runtime, "handleException"),
|
||||
2,
|
||||
3,
|
||||
[jsErrorHandler = jsErrorHandler_](
|
||||
jsi::Runtime& runtime,
|
||||
const jsi::Value& /*unused*/,
|
||||
@@ -425,7 +440,7 @@ void ReactInstance::initializeRuntime(
|
||||
if (count < 2) {
|
||||
throw jsi::JSError(
|
||||
runtime,
|
||||
"handleException requires 2 arguments: error, isFatal");
|
||||
"handleException requires 3 arguments: error, isFatal, logToConsole (optional)");
|
||||
}
|
||||
|
||||
auto isFatal = isTruthy(runtime, args[1]);
|
||||
@@ -439,7 +454,14 @@ void ReactInstance::initializeRuntime(
|
||||
|
||||
auto jsError =
|
||||
jsi::JSError(runtime, jsi::Value(runtime, args[0]));
|
||||
jsErrorHandler->handleError(runtime, jsError, isFatal);
|
||||
|
||||
if (count == 2) {
|
||||
jsErrorHandler->handleError(runtime, jsError, isFatal);
|
||||
} else {
|
||||
auto logToConsole = isTruthy(runtime, args[2]);
|
||||
jsErrorHandler->handleError(
|
||||
runtime, jsError, isFatal, logToConsole);
|
||||
}
|
||||
|
||||
return jsi::Value(true);
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user