Skip to content
Closed
5 changes: 5 additions & 0 deletions packages/react-native/ReactCommon/jsc/JSCRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,11 @@ jsi::Function JSCRuntime::createFunctionFromHostFunction(
exceptionString += ex.what();
*exception = makeError(rt, exceptionString);
res = JSValueMakeUndefined(ctx);
} catch (const std::string &ex) {
std::string exceptionString("Exception in HostFunction: ");
exceptionString += ex;
*exception = makeError(rt, exceptionString);
res = JSValueMakeUndefined(ctx);
} catch (...) {
std::string exceptionString("Exception in HostFunction: <unknown>");
*exception = makeError(rt, exceptionString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,39 @@ jsi::Value JavaTurboModule::invokeJavaMethod(
} else {
TMPL::asyncMethodCallFail(moduleName, methodName);
}
throw;
auto exception = std::current_exception();
auto throwable = facebook::jni::getJavaExceptionForCppException(exception);
auto stackTrace = throwable->getStackTrace();

jsi::Array stackArray(runtime, stackTrace->size());
std::string stackTraceStr;
for (int i = 0; i < stackTrace->size(); ++i) {
auto frame = stackTrace->getElement(i);
stackTraceStr += frame->toString() + '\n';

jsi::Object frameObject(runtime);
frameObject.setProperty(runtime, "className", frame->getClassName());
frameObject.setProperty(runtime, "fileName", frame->getFileName());
frameObject.setProperty(runtime, "lineNumber", frame->getLineNumber());
frameObject.setProperty(runtime, "methodName", frame->getMethodName());
stackArray.setValueAtIndex(runtime, i, std::move(frameObject));
}

// TODO Better would be use getMessage() but its missing in fbjni interface
auto throwableString = throwable->toString();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to send this as a PR to fbjni. I'm not super comfortable with the substring assumptions done below, especially as this is already on an error path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used the dynamic calls for now, same as fbjni would do, also opened the PR there, but have some issues with testing, please see the desc in the PR.

https://github.com/facebook/react-native/pull/36925/files/847043eac5132d10f05b7e58fb3e3fbaa199bede#r1169624312

std::string name = throwableString.substr(0, throwableString.find(':'));
std::string message = throwableString.substr(throwableString.find(':') + 2, -1);
jsi::Object cause(runtime);
cause.setProperty(runtime, "name", name);
cause.setProperty(runtime, "message", message);
cause.setProperty(runtime, "stack", stackTraceStr);
cause.setProperty(runtime, "stackArray", std::move(stackArray));

jsi::Value error =
runtime.global().getPropertyAsFunction(runtime, "Error")
.call(runtime, "Exception in HostFunction: " + message);
error.asObject(runtime).setProperty(runtime, "cause", cause);
throw jsi::JSError(runtime, std::move(error));
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ static int32_t getUniqueId()
return [callback copy];
}

static NSString *convertNSExceptionStackToString(NSException *exception)
{
NSMutableString *stack = [[NSMutableString alloc] init];
for (NSString* symbol in exception.callStackSymbols) {
[stack appendFormat:@"%@\n", symbol];
}

return stack;
}

namespace facebook {
namespace react {

Expand Down Expand Up @@ -378,8 +388,23 @@ static int32_t getUniqueId()
}

// TODO(T66699874) Should we guard this with a try/catch?
[inv invokeWithTarget:strongModule];
[retainedObjectsForInvocation removeAllObjects];
@try {
[inv invokeWithTarget:strongModule];
} @catch (NSException *exception) {
std::string reason = std::string([exception.reason UTF8String]);
jsi::Object cause(runtime);
cause.setProperty(runtime, "name", std::string([exception.name UTF8String]));
cause.setProperty(runtime, "message", reason);
cause.setProperty(runtime, "stack", std::string([convertNSExceptionStackToString(exception) UTF8String]));
//TODO For release builds better would be use include callStackReturnAddresses
jsi::Value error =
runtime.global().getPropertyAsFunction(runtime, "Error")
.call(runtime, "Exception in HostFunction: " + reason);
error.asObject(runtime).setProperty(runtime, "cause", cause);
throw jsi::JSError(runtime, std::move(error));
} @finally {
[retainedObjectsForInvocation removeAllObjects];
}

if (!wasMethodSync) {
TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodNameStr.c_str(), asyncCallCounter);
Expand Down