Skip to content

Commit

Permalink
earlyjs: Attach js build to all errors (facebook#46868)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#46868

jsBuild prasing was done at the js level.

But, for the c++ pipeline, we have to parse the js build in native.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D63927093

fbshipit-source-id: ce7ee46714ee0e72e450003330dbca78acb264a3
  • Loading branch information
RSNara authored and facebook-github-bot committed Nov 6, 2024
1 parent 120a12f commit ef6b6f3
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ NS_ASSUME_NONNULL_BEGIN
stack:(nullable NSArray *)stack
exceptionId:(NSNumber *)exceptionId
extraDataAsJSON:(nullable NSString *)extraDataAsJSON;

@optional
- (NSDictionary<NSString *, id> *)decorateJSExceptionData:(NSDictionary<NSString *, id> *)exceptionData;
@end

@interface RCTExceptionsManager : NSObject <RCTBridgeModule>
Expand Down
44 changes: 35 additions & 9 deletions packages/react-native/React/CoreModules/RCTExceptionsManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ - (void)reportFatal:(NSString *)message
}
}

// TODO(T205456329): This method is deprecated in favour of reportException. Delete in v0.77
RCT_EXPORT_METHOD(reportSoftException
: (NSString *)message stack
: (NSArray<NSDictionary *> *)stack exceptionId
Expand All @@ -91,6 +92,7 @@ - (void)reportFatal:(NSString *)message
[self reportSoft:message stack:stack exceptionId:exceptionId extraDataAsJSON:nil];
}

// TODO(T205456329): This method is deprecated in favour of reportException. Delete in v0.77
RCT_EXPORT_METHOD(reportFatalException
: (NSString *)message stack
: (NSArray<NSDictionary *> *)stack exceptionId
Expand All @@ -103,15 +105,24 @@ - (void)reportFatal:(NSString *)message

RCT_EXPORT_METHOD(reportException : (JS::NativeExceptionsManager::ExceptionData &)data)
{
NSString *message = data.message();
double exceptionId = data.id_();
NSMutableDictionary<NSString *, id> *mutableErrorData = [NSMutableDictionary new];
mutableErrorData[@"message"] = data.message();
if (data.originalMessage()) {
mutableErrorData[@"originalMessage"] = data.originalMessage();
}
if (data.name()) {
mutableErrorData[@"name"] = data.name();
}
if (data.componentStack()) {
mutableErrorData[@"componentStack"] = data.componentStack();
}

// Reserialize data.stack() into an array of untyped dictionaries.
// TODO: (moti) T53588496 Replace `(NSArray<NSDictionary *> *)stack` in
// reportFatalException etc with a typed interface.
NSMutableArray<NSDictionary *> *stackArray = [NSMutableArray<NSDictionary *> new];
NSMutableArray<NSDictionary<NSString *, id> *> *stackArray = [NSMutableArray<NSDictionary<NSString *, id> *> new];
for (auto frame : data.stack()) {
NSMutableDictionary *frameDict = [NSMutableDictionary new];
NSMutableDictionary<NSString *, id> *frameDict = [NSMutableDictionary new];
if (frame.column().has_value()) {
frameDict[@"column"] = @(frame.column().value());
}
Expand All @@ -126,13 +137,28 @@ - (void)reportFatal:(NSString *)message
[stackArray addObject:frameDict];
}

NSDictionary *extraData = (NSDictionary *)data.extraData();
NSString *extraDataAsJSON = RCTJSONStringify(extraData, NULL);
mutableErrorData[@"stack"] = stackArray;
mutableErrorData[@"id"] = @(data.id_());
mutableErrorData[@"isFatal"] = @(data.isFatal());

if (data.extraData()) {
mutableErrorData[@"extraData"] = data.extraData();
}

NSDictionary<NSString *, id> *errorData = mutableErrorData;
if ([_delegate respondsToSelector:@selector(decorateJSExceptionData:)]) {
errorData = [_delegate decorateJSExceptionData:errorData];
}

NSString *extraDataAsJSON = RCTJSONStringify(errorData[@"extraData"], NULL);
NSString *message = errorData[@"message"];
NSArray<NSDictionary<NSString *, id> *> *stack = errorData[@"stack"];
double exceptionId = [errorData[@"id"] doubleValue];

if (data.isFatal()) {
[self reportFatal:message stack:stackArray exceptionId:exceptionId extraDataAsJSON:extraDataAsJSON];
if (errorData[@"isFatal"]) {
[self reportFatal:message stack:stack exceptionId:exceptionId extraDataAsJSON:extraDataAsJSON];
} else {
[self reportSoft:message stack:stackArray exceptionId:exceptionId extraDataAsJSON:extraDataAsJSON];
[self reportSoft:message stack:stack exceptionId:exceptionId extraDataAsJSON:extraDataAsJSON];
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ public class com/facebook/react/bridge/JavaOnlyMap : com/facebook/react/bridge/R
public fun putMap (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableMap;)V
public fun putNull (Ljava/lang/String;)V
public fun putString (Ljava/lang/String;Ljava/lang/String;)V
public fun remove (Ljava/lang/String;)V
public fun toHashMap ()Ljava/util/HashMap;
public fun toString ()Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ public String toString() {
return mBackingMap.toString();
}

public void remove(@NonNull String key) {
mBackingMap.remove(key);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,43 @@ class SetFalseOnDestruct {
}
};

void logErrorWhileReporting(
std::string message,
jsi::JSError& error,
jsi::JSError& originalError) {
LOG(ERROR) << "JsErrorHandler::" << message << std::endl
<< "Js error message: " << error.getMessage() << std::endl
<< "Original js error message: " << originalError.getMessage()
<< std::endl;
}

jsi::Value getBundleMetadata(jsi::Runtime& runtime, jsi::JSError& error) {
auto jsGetBundleMetadataValue =
runtime.global().getProperty(runtime, "__getBundleMetadata");

if (!jsGetBundleMetadataValue.isObject() ||
!jsGetBundleMetadataValue.asObject(runtime).isFunction(runtime)) {
return jsi::Value::null();
}

auto jsGetBundleMetadataValueFn =
jsGetBundleMetadataValue.asObject(runtime).asFunction(runtime);

try {
auto bundleMetadataValue = jsGetBundleMetadataValueFn.call(runtime);
if (bundleMetadataValue.isObject()) {
return bundleMetadataValue;
}
return bundleMetadataValue;
} catch (jsi::JSError& ex) {
logErrorWhileReporting(
"getBundleMetadata(): Error raised while calling __getBundleMetadata(). Returning null.",
ex,
error);
}

return jsi::Value::null();
}
} // namespace

namespace facebook::react {
Expand Down Expand Up @@ -199,12 +236,11 @@ void JsErrorHandler::handleError(
try {
handleJSError(runtime, error, isFatal);
return;
} catch (jsi::JSError& e) {
LOG(ERROR)
<< "JsErrorHandler: Failed to report js error using js pipeline. Using C++ pipeline instead."
<< std::endl
<< "Reporting failure: " << e.getMessage() << std::endl
<< "Original js error: " << error.getMessage() << std::endl;
} catch (jsi::JSError& ex) {
logErrorWhileReporting(
"handleError(): Error raised while reporting using js pipeline. Using c++ pipeline instead.",
ex,
error);
}
}

Expand Down Expand Up @@ -249,8 +285,14 @@ void JsErrorHandler::handleErrorWithCppPipeline(
objectAssign(runtime, extraData, extraDataValue.asObject(runtime));
}

auto isDEV =
isTruthy(runtime, runtime.global().getProperty(runtime, "__DEV__"));

extraData.setProperty(runtime, "jsEngine", jsEngineValue);
extraData.setProperty(runtime, "rawStack", error.getStack());
extraData.setProperty(runtime, "__DEV__", isDEV);
extraData.setProperty(
runtime, "bundleMetadata", getBundleMetadata(runtime, error));

auto cause = errorObj.getProperty(runtime, "cause");
if (cause.isObject()) {
Expand Down Expand Up @@ -324,7 +366,14 @@ void JsErrorHandler::handleErrorWithCppPipeline(
data.setProperty(runtime, "preventDefault", preventDefault);

for (auto& errorListener : _errorListeners) {
errorListener(runtime, jsi::Value(runtime, data));
try {
errorListener(runtime, jsi::Value(runtime, data));
} catch (jsi::JSError& ex) {
logErrorWhileReporting(
"handleErrorWithCppPipeline(): Error raised inside an error listener. Executing next listener.",
ex,
error);
}
}

if (*shouldPreventDefault) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@
- (void)setBundleURLProvider:(RCTHostBundleURLProvider)bundleURLProvider;
- (void)setContextContainerHandler:(id<RCTContextContainerHandling>)contextContainerHandler;

@property (nonatomic, readonly) RCTBundleManager *bundleManager;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ - (RCTSurfacePresenter *)surfacePresenter
return [_instance surfacePresenter];
}

- (RCTBundleManager *)bundleManager
{
return _bundleManager;
}

- (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args
{
[_instance callFunctionOnJSModule:moduleName method:method args:args];
Expand Down

0 comments on commit ef6b6f3

Please sign in to comment.