Skip to content

Commit 63172e6

Browse files
committed
fix: fix exception handling in C++ native code
1 parent cfe8795 commit 63172e6

File tree

6 files changed

+63
-36
lines changed

6 files changed

+63
-36
lines changed

android/src/main/cpp/ETInstallerModule.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ void ETInstallerModule::injectJSIBindings() {
5353
jbyteArray byteData =
5454
(jbyteArray)env->CallStaticObjectMethod(cls, method, jUrl);
5555

56+
if (env->IsSameObject(byteData, NULL)) {
57+
throw std::runtime_error("Error fetching data from a url");
58+
}
59+
5660
int size = env->GetArrayLength(byteData);
5761
jbyte *bytes = env->GetByteArrayElements(byteData, JNI_FALSE);
5862
std::byte *dataBytePtr = reinterpret_cast<std::byte *>(bytes);

android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt

+12-8
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,20 @@ class ETInstaller(
2121
@JvmStatic
2222
@DoNotStrip
2323
@Throws(Exception::class)
24-
fun fetchByteDataFromUrl(source: String): ByteArray {
25-
val url = URL(source)
26-
val connection = url.openConnection()
27-
connection.connect()
24+
fun fetchByteDataFromUrl(source: String): ByteArray? {
25+
try {
26+
val url = URL(source)
27+
val connection = url.openConnection()
28+
connection.connect()
2829

29-
val inputStream: InputStream = connection.getInputStream()
30-
val data = inputStream.readBytes()
31-
inputStream.close()
30+
val inputStream: InputStream = connection.getInputStream()
31+
val data = inputStream.readBytes()
32+
inputStream.close()
3233

33-
return data
34+
return data
35+
} catch (exception: Throwable) {
36+
return null
37+
}
3438
}
3539
}
3640

common/rnexecutorch/data_processing/ImageProcessing.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,14 @@ cv::Mat readImage(const std::string &imageURI) {
104104
// local file
105105
auto url = ada::parse(imageURI);
106106
image = cv::imread(std::string{url->get_pathname()}, cv::IMREAD_COLOR);
107-
} else {
107+
} else if (imageURI.starts_with("http")) {
108108
// remote file
109109
std::vector<std::byte> imageData = fetchUrlFunc(imageURI);
110110
image = cv::imdecode(
111111
cv::Mat(1, imageData.size(), CV_8UC1, (void *)imageData.data()),
112112
cv::IMREAD_COLOR);
113+
} else {
114+
throw std::runtime_error("Read image error: unknown protocol");
113115
}
114116

115117
if (image.empty()) {

common/rnexecutorch/host_objects/ModelHostObject.h

+27-16
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,43 @@ template <typename Model> class ModelHostObject : public JsiHostObject {
1919
}
2020

2121
JSI_HOST_FUNCTION(forward) {
22-
2322
auto promise = promiseVendor.createPromise(
2423
[this, count, args, &runtime](std::shared_ptr<Promise> promise) {
25-
std::thread([this, promise = std::move(promise), count, args,
26-
&runtime]() {
27-
constexpr std::size_t forwardArgCount =
28-
jsiconversion::getArgumentCount(&Model::forward);
29-
if (forwardArgCount != count) {
30-
promise->reject("Argument count mismatch");
31-
return;
32-
}
24+
// Check if the function call matches the expected number of arguments
25+
constexpr std::size_t forwardArgCount =
26+
jsiconversion::getArgumentCount(&Model::forward);
27+
if (forwardArgCount != count) {
28+
promise->reject("Argument count mismatch");
29+
return;
30+
}
3331

32+
// Do the asynchronous work
33+
std::thread([this, promise = std::move(promise), args, &runtime]() {
3434
try {
3535
auto argsConverted = jsiconversion::createArgsTupleFromJsi(
3636
&Model::forward, args, runtime);
37-
promise->resolve([this, argsConverted = std::move(argsConverted)](
38-
jsi::Runtime &runtime) {
39-
auto result = std::apply(
40-
std::bind_front(&Model::forward, model), argsConverted);
41-
auto resultValue =
42-
jsiconversion::getJsiValue(std::move(result), runtime);
43-
return resultValue;
37+
auto result = std::apply(std::bind_front(&Model::forward, model),
38+
argsConverted);
39+
40+
promise->resolve([result =
41+
std::move(result)](jsi::Runtime &runtime) {
42+
return jsiconversion::getJsiValue(std::move(result), runtime);
4443
});
44+
} catch (const std::runtime_error &e) {
45+
// This catch should be merged with the next one
46+
// (std::runtime_error inherits from std::exception) HOWEVER react
47+
// native has broken RTTI which breaks proper exception type
48+
// checking. Remove when the following change is present in our
49+
// version:
50+
// https://github.com/facebook/react-native/commit/3132cc88dd46f95898a756456bebeeb6c248f20e
51+
promise->reject(e.what());
52+
return;
4553
} catch (const std::exception &e) {
4654
promise->reject(e.what());
4755
return;
56+
} catch (...) {
57+
promise->reject("Unknown error");
58+
return;
4859
}
4960
}).detach();
5061
});

common/rnexecutorch/jsi/JsiPromise.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ jsi::Value PromiseVendor::createPromise(
4040

4141
auto rejectWrapper = [reject, &runtime, callInvoker](
4242
const std::string &errorMessage) -> void {
43-
auto error = jsi::JSError(runtime, errorMessage);
44-
auto errorShared = std::make_shared<jsi::JSError>(error);
45-
callInvoker->invokeAsync([reject, &runtime, errorShared]() -> void {
43+
callInvoker->invokeAsync([reject, &runtime, errorMessage]() -> void {
44+
auto error = jsi::JSError(runtime, errorMessage);
45+
auto errorShared = std::make_shared<jsi::JSError>(error);
4646
reject->call(runtime, errorShared->value());
4747
});
4848
};

ios/RnExecutorch/ETInstaller.mm

+14-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#import <React/RCTCallInvoker.h>
66
#import <ReactCommon/RCTTurboModule.h>
77
#include <rnexecutorch/RnExecutorchInstaller.h>
8+
#include <stdexcept>
89

910
using namespace facebook::react;
1011

@@ -26,14 +27,19 @@ @implementation ETInstaller
2627
assert(jsiRuntime != nullptr);
2728

2829
auto fetchUrl = [](std::string url) {
29-
NSString *nsUrlStr =
30-
[NSString stringWithCString:url.c_str()
31-
encoding:[NSString defaultCStringEncoding]];
32-
NSURL *nsUrl = [NSURL URLWithString:nsUrlStr];
33-
NSData *data = [NSData dataWithContentsOfURL:nsUrl];
34-
const std::byte *bytePtr = reinterpret_cast<const std::byte *>(data.bytes);
35-
int bufferLength = [data length];
36-
return std::vector<std::byte>(bytePtr, bytePtr + bufferLength);
30+
@try {
31+
NSString *nsUrlStr =
32+
[NSString stringWithCString:url.c_str()
33+
encoding:[NSString defaultCStringEncoding]];
34+
NSURL *nsUrl = [NSURL URLWithString:nsUrlStr];
35+
NSData *data = [NSData dataWithContentsOfURL:nsUrl];
36+
const std::byte *bytePtr =
37+
reinterpret_cast<const std::byte *>(data.bytes);
38+
int bufferLength = [data length];
39+
return std::vector<std::byte>(bytePtr, bytePtr + bufferLength);
40+
} @catch (NSException *exception) {
41+
throw std::runtime_error("Error fetching data from a url");
42+
}
3743
};
3844
rnexecutorch::RnExecutorchInstaller::injectJSIBindings(
3945
jsiRuntime, jsCallInvoker, fetchUrl);

0 commit comments

Comments
 (0)