diff --git a/libsession-util/src/main/cpp/config_base.cpp b/libsession-util/src/main/cpp/config_base.cpp index 1c90b1b81c..5af6483371 100644 --- a/libsession-util/src/main/cpp/config_base.cpp +++ b/libsession-util/src/main/cpp/config_base.cpp @@ -1,5 +1,6 @@ #include "config_base.h" #include "util.h" +#include "jni_utils.h" extern "C" { JNIEXPORT jboolean JNICALL @@ -85,29 +86,34 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_confirmPushed(JNIEnv *en JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_merge___3Lkotlin_Pair_2(JNIEnv *env, jobject thiz, jobjectArray to_merge) { - std::lock_guard lock{util::util_mutex_}; - auto conf = ptrToConfigBase(env, thiz); - size_t number = env->GetArrayLength(to_merge); - std::vector> configs = {}; - for (int i = 0; i < number; i++) { - auto jElement = (jobject) env->GetObjectArrayElement(to_merge, i); - auto pair = extractHashAndData(env, jElement); - configs.push_back(pair); - } - auto returned = conf->merge(configs); - auto string_stack = util::build_string_stack(env, returned); - return string_stack; + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto conf = ptrToConfigBase(env, thiz); + size_t number = env->GetArrayLength(to_merge); + std::vector> configs = {}; + for (int i = 0; i < number; i++) { + auto jElement = (jobject) env->GetObjectArrayElement(to_merge, i); + auto pair = extractHashAndData(env, jElement); + configs.push_back(pair); + } + auto returned = conf->merge(configs); + auto string_stack = util::build_string_stack(env, returned); + return string_stack; + }); } JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_merge__Lkotlin_Pair_2(JNIEnv *env, jobject thiz, jobject to_merge) { - std::lock_guard lock{util::util_mutex_}; - auto conf = ptrToConfigBase(env, thiz); - std::vector> configs = {extractHashAndData(env, to_merge)}; - auto returned = conf->merge(configs); - auto string_stack = util::build_string_stack(env, returned); - return string_stack; + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto conf = ptrToConfigBase(env, thiz); + std::vector> configs = { + extractHashAndData(env, to_merge)}; + auto returned = conf->merge(configs); + auto string_stack = util::build_string_stack(env, returned); + return string_stack; + }); } #pragma clang diagnostic pop diff --git a/libsession-util/src/main/cpp/contacts.cpp b/libsession-util/src/main/cpp/contacts.cpp index 7d04904802..324d0f0ea8 100644 --- a/libsession-util/src/main/cpp/contacts.cpp +++ b/libsession-util/src/main/cpp/contacts.cpp @@ -1,100 +1,121 @@ #include "contacts.h" #include "util.h" +#include "jni_utils.h" extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_Contacts_get(JNIEnv *env, jobject thiz, jstring session_id) { - std::lock_guard lock{util::util_mutex_}; - auto contacts = ptrToContacts(env, thiz); - auto session_id_chars = env->GetStringUTFChars(session_id, nullptr); - auto contact = contacts->get(session_id_chars); - env->ReleaseStringUTFChars(session_id, session_id_chars); - if (!contact) return nullptr; - jobject j_contact = serialize_contact(env, contact.value()); - return j_contact; + // If an exception is thrown, return nullptr + return jni_utils::run_catching_cxx_exception_or( + [=]() -> jobject { + std::lock_guard lock{util::util_mutex_}; + auto contacts = ptrToContacts(env, thiz); + auto session_id_chars = env->GetStringUTFChars(session_id, nullptr); + auto contact = contacts->get(session_id_chars); + env->ReleaseStringUTFChars(session_id, session_id_chars); + if (!contact) return nullptr; + jobject j_contact = serialize_contact(env, contact.value()); + return j_contact; + }, + [](const char *) -> jobject { return nullptr; } + ); } extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_Contacts_getOrConstruct(JNIEnv *env, jobject thiz, jstring session_id) { - std::lock_guard lock{util::util_mutex_}; - auto contacts = ptrToContacts(env, thiz); - auto session_id_chars = env->GetStringUTFChars(session_id, nullptr); - auto contact = contacts->get_or_construct(session_id_chars); - env->ReleaseStringUTFChars(session_id, session_id_chars); - return serialize_contact(env, contact); + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto contacts = ptrToContacts(env, thiz); + auto session_id_chars = env->GetStringUTFChars(session_id, nullptr); + auto contact = contacts->get_or_construct(session_id_chars); + env->ReleaseStringUTFChars(session_id, session_id_chars); + return serialize_contact(env, contact); + }); } extern "C" JNIEXPORT void JNICALL Java_network_loki_messenger_libsession_1util_Contacts_set(JNIEnv *env, jobject thiz, jobject contact) { - std::lock_guard lock{util::util_mutex_}; - auto contacts = ptrToContacts(env, thiz); - auto contact_info = deserialize_contact(env, contact, contacts); - contacts->set(contact_info); + jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto contacts = ptrToContacts(env, thiz); + auto contact_info = deserialize_contact(env, contact, contacts); + contacts->set(contact_info); + }); } extern "C" JNIEXPORT jboolean JNICALL Java_network_loki_messenger_libsession_1util_Contacts_erase(JNIEnv *env, jobject thiz, jstring session_id) { - std::lock_guard lock{util::util_mutex_}; - auto contacts = ptrToContacts(env, thiz); - auto session_id_chars = env->GetStringUTFChars(session_id, nullptr); + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto contacts = ptrToContacts(env, thiz); + auto session_id_chars = env->GetStringUTFChars(session_id, nullptr); - bool result = contacts->erase(session_id_chars); - env->ReleaseStringUTFChars(session_id, session_id_chars); - return result; + bool result = contacts->erase(session_id_chars); + env->ReleaseStringUTFChars(session_id, session_id_chars); + return result; + }); } extern "C" #pragma clang diagnostic push #pragma ide diagnostic ignored "bugprone-reserved-identifier" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_Contacts_00024Companion_newInstance___3B(JNIEnv *env, - jobject thiz, - jbyteArray ed25519_secret_key) { - std::lock_guard lock{util::util_mutex_}; - auto secret_key = util::ustring_from_bytes(env, ed25519_secret_key); - auto* contacts = new session::config::Contacts(secret_key, std::nullopt); + jobject thiz, + jbyteArray ed25519_secret_key) { + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto secret_key = util::ustring_from_bytes(env, ed25519_secret_key); + auto *contacts = new session::config::Contacts(secret_key, std::nullopt); - jclass contactsClass = env->FindClass("network/loki/messenger/libsession_util/Contacts"); - jmethodID constructor = env->GetMethodID(contactsClass, "", "(J)V"); - jobject newConfig = env->NewObject(contactsClass, constructor, reinterpret_cast(contacts)); + jclass contactsClass = env->FindClass("network/loki/messenger/libsession_util/Contacts"); + jmethodID constructor = env->GetMethodID(contactsClass, "", "(J)V"); + jobject newConfig = env->NewObject(contactsClass, constructor, + reinterpret_cast(contacts)); - return newConfig; + return newConfig; + }); } extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_Contacts_00024Companion_newInstance___3B_3B( JNIEnv *env, jobject thiz, jbyteArray ed25519_secret_key, jbyteArray initial_dump) { - std::lock_guard lock{util::util_mutex_}; - auto secret_key = util::ustring_from_bytes(env, ed25519_secret_key); - auto initial = util::ustring_from_bytes(env, initial_dump); + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto secret_key = util::ustring_from_bytes(env, ed25519_secret_key); + auto initial = util::ustring_from_bytes(env, initial_dump); - auto* contacts = new session::config::Contacts(secret_key, initial); + auto *contacts = new session::config::Contacts(secret_key, initial); - jclass contactsClass = env->FindClass("network/loki/messenger/libsession_util/Contacts"); - jmethodID constructor = env->GetMethodID(contactsClass, "", "(J)V"); - jobject newConfig = env->NewObject(contactsClass, constructor, reinterpret_cast(contacts)); + jclass contactsClass = env->FindClass("network/loki/messenger/libsession_util/Contacts"); + jmethodID constructor = env->GetMethodID(contactsClass, "", "(J)V"); + jobject newConfig = env->NewObject(contactsClass, constructor, + reinterpret_cast(contacts)); - return newConfig; + return newConfig; + }); } #pragma clang diagnostic pop extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_Contacts_all(JNIEnv *env, jobject thiz) { - std::lock_guard lock{util::util_mutex_}; - auto contacts = ptrToContacts(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (const auto& contact : *contacts) { - auto contact_obj = serialize_contact(env, contact); - env->CallObjectMethod(our_stack, push, contact_obj); - } - return our_stack; + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto contacts = ptrToContacts(env, thiz); + jclass stack = env->FindClass("java/util/Stack"); + jmethodID init = env->GetMethodID(stack, "", "()V"); + jobject our_stack = env->NewObject(stack, init); + jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); + for (const auto &contact: *contacts) { + auto contact_obj = serialize_contact(env, contact); + env->CallObjectMethod(our_stack, push, contact_obj); + } + return our_stack; + }); } \ No newline at end of file diff --git a/libsession-util/src/main/cpp/jni_utils.h b/libsession-util/src/main/cpp/jni_utils.h new file mode 100644 index 0000000000..c9ccd924a6 --- /dev/null +++ b/libsession-util/src/main/cpp/jni_utils.h @@ -0,0 +1,54 @@ +#ifndef SESSION_ANDROID_JNI_UTILS_H +#define SESSION_ANDROID_JNI_UTILS_H + +#include +#include + +namespace jni_utils { + /** + * Run a C++ function and catch any exceptions, throwing a Java exception if one is caught, + * and returning a default-constructed value of the specified type. + * + * @tparam RetT The return type of the function + * @tparam Func The function type + * @param f The function to run + * @param fallbackRun The function to run if an exception is caught. The optional exception message reference will be passed to this function. + * @return The return value of the function, or the return value of the fallback function if an exception was caught + */ + template + RetT run_catching_cxx_exception_or(Func f, FallbackRun fallbackRun) { + try { + return f(); + } catch (const std::exception &e) { + return fallbackRun(e.what()); + } catch (...) { + return fallbackRun(nullptr); + } + } + + /** + * Run a C++ function and catch any exceptions, throwing a Java exception if one is caught. + * + * @tparam RetT The return type of the function + * @tparam Func The function type + * @param env The JNI environment + * @param f The function to run + * @return The return value of the function, or a default-constructed value of the specified type if an exception was caught + */ + template + RetT run_catching_cxx_exception_or_throws(JNIEnv *env, Func f) { + return run_catching_cxx_exception_or(f, [env](const char *msg) { + jclass exceptionClass = env->FindClass("java/lang/RuntimeException"); + if (msg) { + auto formatted_message = std::string("libsession: C++ exception: ") + msg; + env->ThrowNew(exceptionClass, formatted_message.c_str()); + } else { + env->ThrowNew(exceptionClass, "libsession: Unknown C++ exception"); + } + + return RetT(); + }); + } +} + +#endif //SESSION_ANDROID_JNI_UTILS_H