1/n Add new JsErrorHandler buck target to parse JS errors in C++
Summary:
Changelog: [Internal][Added] 1/n Add new JsErrorHandler buck target to parse JS errors in C++
This JsErrorHandler.cpp class uses the same regex logic as [stack-trace-parser.js](https://github.com/errwischt/stacktrace-parser/blob/master/src/stack-trace-parser.js#L121) in order to parse JS errors into an array of stack frames.
In RN, stacktrace-parser is called from [Devtools/parseErrorStack.js](8bd3edec88/Libraries/Core/Devtools/parseErrorStack.js (L46)
).
Reviewed By: sammy-SC
Differential Revision: D40296024
fbshipit-source-id: 8e1b034e8a1ee1a8cc808bfc36c7104a8f40f9cc
This commit is contained in:
Родитель
a5754720f6
Коммит
84225573c1
|
@ -0,0 +1,31 @@
|
|||
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "CXX", "react_native_xplat_target", "rn_xplat_cxx_library")
|
||||
|
||||
# TODO: Expolre merging this module into venice so we don't to load this library seperately
|
||||
rn_xplat_cxx_library(
|
||||
name = "jserrorhandler",
|
||||
srcs = glob(["*.cpp"]),
|
||||
header_namespace = "",
|
||||
exported_headers = {"JsErrorHandler/JsErrorHandler.h": "JsErrorHandler.h"},
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
],
|
||||
labels = [
|
||||
"pfh:ReactNative_CommonInfrastructurePlaceholder",
|
||||
"supermodule:xplat/default/public.react_native.infra",
|
||||
],
|
||||
platforms = (ANDROID, APPLE, CXX),
|
||||
preprocessor_flags = [
|
||||
"-DLOG_TAG=\"ReactNative\"",
|
||||
"-DWITH_FBSYSTRACE=1",
|
||||
],
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
deps = [
|
||||
"//xplat/folly:dynamic",
|
||||
"//xplat/folly:json",
|
||||
"//xplat/jsi:jsi",
|
||||
react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"),
|
||||
],
|
||||
)
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
static MapBuffer
|
||||
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(0, std::string(searchResults[4]));
|
||||
frame.putString(1, std::string(searchResults[1]));
|
||||
frame.putInt(2, std::stoi(searchResults[5]));
|
||||
frame.putInt(3, std::stoi(searchResults[6]));
|
||||
frames.push_back(frame.build());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (std::regex_search(line, searchResults, REGEX_GECKO)) {
|
||||
frame.putString(0, std::string(searchResults[3]));
|
||||
frame.putString(1, std::string(searchResults[1]));
|
||||
frame.putInt(2, std::stoi(searchResults[4]));
|
||||
frame.putInt(3, std::stoi(searchResults[5]));
|
||||
} else if (
|
||||
std::regex_search(line, searchResults, REGEX_CHROME) ||
|
||||
std::regex_search(line, searchResults, REGEX_NODE)) {
|
||||
frame.putString(0, std::string(searchResults[2]));
|
||||
frame.putString(1, std::string(searchResults[1]));
|
||||
frame.putInt(2, std::stoi(searchResults[3]));
|
||||
frame.putInt(3, std::stoi(searchResults[4]));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
frames.push_back(frame.build());
|
||||
}
|
||||
}
|
||||
errorObj.putMapBufferList(4, std::move(frames));
|
||||
errorObj.putString(5, error.getMessage());
|
||||
// TODO: If needed, can increment exceptionId by 1 each time
|
||||
errorObj.putInt(6, 0);
|
||||
errorObj.putBool(7, 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 = parseErrorStack(error, isFatal, false);
|
||||
_jsErrorHandlingFunc(std::move(errorMap));
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 <jsi/jsi.h>
|
||||
#include <react/renderer/mapbuffer/MapBuffer.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
const int FILE_KEY_OF_JS_ERROR = 0;
|
||||
const int METHOD_NAME_KEY_OF_JS_ERROR = 1;
|
||||
const int LINE_NUMBER_KEY_OF_JS_ERROR = 2;
|
||||
const int COLUMN_KEY_OF_JS_ERROR = 3;
|
||||
const int FRAMES_KEY_OF_JS_ERROR = 4;
|
||||
const int MESSAGE_KEY_OF_JS_ERROR = 5;
|
||||
const int ID_KEY_OF_JS_ERROR = 6;
|
||||
const int IS_FATAL_KEY_OF_JS_ERROR = 7;
|
||||
|
||||
class JsErrorHandler {
|
||||
public:
|
||||
using JsErrorHandlingFunc = std::function<void(MapBuffer errorMap)>;
|
||||
|
||||
JsErrorHandler(JsErrorHandlingFunc jsErrorHandlingFunc);
|
||||
~JsErrorHandler();
|
||||
|
||||
void handleJsError(const jsi::JSError &error, bool isFatal);
|
||||
|
||||
private:
|
||||
JsErrorHandlingFunc _jsErrorHandlingFunc;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
Загрузка…
Ссылка в новой задаче