mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
ff398e4e26
Summary: Changelog: [iOS][Changed] Display a RedBox with the JS stack (instead of native stack) when an unhandled JS exceptions occurs ----- # A0) Bridge mode, Unhandled Exception, FBiOS Unhandled exception goes to FBReactModule, but the JS Stack is not parsed correctly. https://www.internalfb.com/code/fbsource/[312d5cbdd7278247a84619786b12a44c4400fcc0]/fbobjc/Apps/Wilde/FBReactModule2/FBReactModuleAPI/FBReactModuleAPI/Exported/FBReactModule.mm?lines=1488%2C1493 See `****** FBReactModule handleFatalError` in the logs P539306390, and compare with correct behavior in (A1) in the Test Plan. https://pxl.cl/2h6h3 {F782257996} ----- # A) Before diff, rn-tester Open to rn-tester -> APIs -> Crash Examples -> JS Crash. Set `RCTParseUnhandledJSErrorStackNatively` to YES. https://www.internalfb.com/code/fbsource/[98880e52ee78be3614e5d9a2ce3292f6a7b5e413]/xplat/js/react-native-github/React/Base/RCTConstants.m?lines=73 {F783395297} Reviewed By: sammy-SC Differential Revision: D40387938 fbshipit-source-id: 2abea657476d3bf61ad5b1c643f129e44c6f3f35
110 lines
4.1 KiB
C++
110 lines
4.1 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "JsErrorHandler.h"
|
|
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
using facebook::react::JSErrorHandlerKey;
|
|
|
|
MapBuffer JsErrorHandler::parseErrorStack(
|
|
const jsi::JSError &error,
|
|
bool isFatal,
|
|
bool isHermes) {
|
|
/**
|
|
* This parses the different stack traces and puts them into one format
|
|
* This borrows heavily from TraceKit (https://github.com/occ/TraceKit)
|
|
* This is the same regex from stacktrace-parser.js.
|
|
*/
|
|
const std::regex REGEX_CHROME(
|
|
R"(^\s*at (?:(?:(?:Anonymous function)?|((?:\[object object\])?\S+(?: \[as \S+\])?)) )?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$)");
|
|
const std::regex REGEX_GECKO(
|
|
R"(^(?:\s*([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$)");
|
|
const std::regex REGEX_NODE(
|
|
R"(^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$)");
|
|
|
|
// Capture groups for Hermes (from parseHermesStack.js):
|
|
// 1. function name
|
|
// 2. is this a native stack frame?
|
|
// 3. is this a bytecode address or a source location?
|
|
// 4. source URL (filename)
|
|
// 5. line number (1 based)
|
|
// 6. column number (1 based) or virtual offset (0 based)
|
|
const std::regex REGEX_HERMES(
|
|
R"(^ {4}at (.+?)(?: \((native)\)?| \((address at )?(.*?):(\d+):(\d+)\))$)");
|
|
|
|
std::string line;
|
|
std::stringstream strStream(error.getStack());
|
|
|
|
auto errorObj = MapBufferBuilder();
|
|
std::vector<MapBuffer> frames;
|
|
|
|
while (std::getline(strStream, line, '\n')) {
|
|
auto frame = MapBufferBuilder();
|
|
auto searchResults = std::smatch{};
|
|
|
|
if (isHermes) {
|
|
if (std::regex_search(line, searchResults, REGEX_HERMES)) {
|
|
std::string str2 = std::string(searchResults[2]);
|
|
if (str2.compare("native")) {
|
|
frame.putString(kFrameFileName, std::string(searchResults[4]));
|
|
frame.putString(kFrameMethodName, std::string(searchResults[1]));
|
|
frame.putInt(kFrameLineNumber, std::stoi(searchResults[5]));
|
|
frame.putInt(kFrameColumnNumber, std::stoi(searchResults[6]));
|
|
frames.push_back(frame.build());
|
|
}
|
|
}
|
|
} else {
|
|
if (std::regex_search(line, searchResults, REGEX_GECKO)) {
|
|
frame.putString(kFrameFileName, std::string(searchResults[3]));
|
|
frame.putString(kFrameMethodName, std::string(searchResults[1]));
|
|
frame.putInt(kFrameLineNumber, std::stoi(searchResults[4]));
|
|
frame.putInt(kFrameColumnNumber, std::stoi(searchResults[5]));
|
|
} else if (
|
|
std::regex_search(line, searchResults, REGEX_CHROME) ||
|
|
std::regex_search(line, searchResults, REGEX_NODE)) {
|
|
frame.putString(kFrameFileName, std::string(searchResults[2]));
|
|
frame.putString(kFrameMethodName, std::string(searchResults[1]));
|
|
frame.putInt(kFrameLineNumber, std::stoi(searchResults[3]));
|
|
frame.putInt(kFrameColumnNumber, std::stoi(searchResults[4]));
|
|
} else {
|
|
continue;
|
|
}
|
|
frames.push_back(frame.build());
|
|
}
|
|
}
|
|
errorObj.putMapBufferList(kAllStackFrames, std::move(frames));
|
|
errorObj.putString(kErrorMessage, "EarlyJsError: " + error.getMessage());
|
|
// TODO: If needed, can increment exceptionId by 1 each time
|
|
errorObj.putInt(kExceptionId, 0);
|
|
errorObj.putBool(kIsFatal, isFatal);
|
|
return errorObj.build();
|
|
}
|
|
|
|
JsErrorHandler::JsErrorHandler(
|
|
JsErrorHandler::JsErrorHandlingFunc jsErrorHandlingFunc) {
|
|
this->_jsErrorHandlingFunc = jsErrorHandlingFunc;
|
|
};
|
|
|
|
JsErrorHandler::~JsErrorHandler() {}
|
|
|
|
void JsErrorHandler::handleJsError(const jsi::JSError &error, bool isFatal) {
|
|
// TODO: Current error parsing works and is stable. Can investigate using
|
|
// REGEX_HERMES to get additional Hermes data, though it requires JS setup.
|
|
MapBuffer errorMap = JsErrorHandler::parseErrorStack(error, isFatal, false);
|
|
_jsErrorHandlingFunc(std::move(errorMap));
|
|
}
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|