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 @@ -401,6 +401,48 @@ jsi::Value convertFromJMapToValue(JNIEnv *env, jsi::Runtime &rt, jobject arg) {
return jsi::valueFromDynamic(rt, result->cthis()->consume());
}

jsi::Value createJSRuntimeError(jsi::Runtime &runtime, const std::string& message)
{
return runtime.global().getPropertyAsFunction(runtime, "Error").call(runtime, message);
}

/**
* Creates JSError with current JS runtime stack and Throwable stack trace.
*/
jsi::JSError convertThrowableToJSError(jsi::Runtime &runtime, facebook::jni::local_ref<facebook::jni::JThrowable> throwable)
{
auto stackTrace = throwable->getStackTrace();

jsi::Array stackElements(runtime, stackTrace->size());
for (int i = 0; i < stackTrace->size(); ++i) {
auto frame = stackTrace->getElement(i);

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());
stackElements.setValueAtIndex(runtime, i, std::move(frameObject));
}

jsi::Object cause(runtime);
auto getName = throwable->getClass()->getClass()
->getMethod<jni::local_ref<jni::JString>()>("getName");
auto getMessage = throwable->getClass()
->getMethod<jni::local_ref<jni::JString>()>("getMessage");
auto message = getMessage(throwable)->toStdString();
cause.setProperty(
runtime,
"name",
getName(throwable->getClass())->toStdString());
cause.setProperty(runtime,"message",message);
Copy link
Member

Choose a reason for hiding this comment

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

nit: formatting

cause.setProperty(runtime, "stackElements", std::move(stackElements));

jsi::Value error = createJSRuntimeError(runtime, "Exception in HostFunction: " + message);
error.asObject(runtime).setProperty(runtime, "cause", std::move(cause));
return {runtime, std::move(error)};
}

} // namespace

jsi::Value JavaTurboModule::invokeJavaMethod(
Expand Down Expand Up @@ -468,7 +510,9 @@ jsi::Value JavaTurboModule::invokeJavaMethod(
} else {
TMPL::asyncMethodCallFail(moduleName, methodName);
}
throw;
auto exception = std::current_exception();
auto throwable = jni::getJavaExceptionForCppException(exception);
throw convertThrowableToJSError(runtime, throwable);
}
};

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

static jsi::Value createJSRuntimeError(jsi::Runtime &runtime, const std::string& message)
{
return runtime.global().getPropertyAsFunction(runtime, "Error").call(runtime, message);
}

/**
* Creates JSError with current JS runtime and NSException stack trace.
*/
static jsi::JSError convertNSExceptionToJSError(jsi::Runtime &runtime, NSException *exception)
{
std::string reason = [exception.reason UTF8String];

jsi::Object cause(runtime);
cause.setProperty(runtime, "name", [exception.name UTF8String]);
cause.setProperty(runtime, "message", reason);
cause.setProperty(runtime, "stackSymbols", convertNSArrayToJSIArray(runtime, exception.callStackSymbols));
cause.setProperty(runtime, "stackReturnAddresses", convertNSArrayToJSIArray(runtime, exception.callStackReturnAddresses));

jsi::Value error = createJSRuntimeError(runtime, "Exception in HostFunction: " + reason);
error.asObject(runtime).setProperty(runtime, "cause", std::move(cause));
return {runtime, std::move(error)};
}

namespace facebook {
namespace react {

Expand Down Expand Up @@ -377,9 +400,13 @@ static int32_t getUniqueId()
TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodNameStr.c_str(), asyncCallCounter);
}

// TODO(T66699874) Should we guard this with a try/catch?
[inv invokeWithTarget:strongModule];
[retainedObjectsForInvocation removeAllObjects];
@try {
[inv invokeWithTarget:strongModule];
} @catch (NSException *exception) {
throw convertNSExceptionToJSError(runtime, exception);
} @finally {
[retainedObjectsForInvocation removeAllObjects];
}

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