From a34ace5fc89710aab785fa6bfae5eddcaf86f920 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Sun, 26 Feb 2023 13:23:54 +0100 Subject: [PATCH] lib: throw C++ exceptions on exceptions from Java calls --- lib/android/src/main/cpp/fd.h | 1 - lib/android/src/main/cpp/http_client.cc | 52 ++++++++++++------------- lib/android/src/main/cpp/jvm.cc | 17 ++++++-- lib/android/src/main/cpp/jvm.h | 10 ++++- lib/android/src/main/cpp/logging.cc | 1 - lib/android/src/main/cpp/wallet.cc | 1 - 6 files changed, 47 insertions(+), 35 deletions(-) diff --git a/lib/android/src/main/cpp/fd.h b/lib/android/src/main/cpp/fd.h index cd486c3..d5326f8 100644 --- a/lib/android/src/main/cpp/fd.h +++ b/lib/android/src/main/cpp/fd.h @@ -29,7 +29,6 @@ class ScopedFd { if (!parcel_file_descriptor.is_null()) { m_fd = parcel_file_descriptor.callIntMethod(env, ParcelFileDescriptor_detachFd); - LOG_FATAL_IF(checkException(env)); } } diff --git a/lib/android/src/main/cpp/http_client.cc b/lib/android/src/main/cpp/http_client.cc index a8b192e..74923fe 100644 --- a/lib/android/src/main/cpp/http_client.cc +++ b/lib/android/src/main/cpp/http_client.cc @@ -35,11 +35,8 @@ bool RemoteNodeClient::is_connected(bool* ssl) { RemoteNodeClient::HttpResponse jvmToHttpResponse(JNIEnv* env, JvmRef& j_http_response) { jint code = j_http_response.callIntMethod(env, HttpResponse_getCode); - LOG_FATAL_IF(checkException(env)); jstring mime_type = j_http_response.callStringMethod(env, HttpResponse_getContentType); - LOG_FATAL_IF(checkException(env)); jobject body = j_http_response.callObjectMethod(env, HttpResponse_getBody); - LOG_FATAL_IF(checkException(env)); return { code, (mime_type != nullptr) ? jvmToStdString(env, mime_type) : "", @@ -58,33 +55,34 @@ bool RemoteNodeClient::invoke(const boost::string_ref uri, for (const auto& p: additional_params) { header << p.first << ": " << p.second << "\r\n"; } - ScopedJvmLocalRef j_response = { - env, m_remote_node_client.callObjectMethod( - env, IRemoteNodeClient_makeRequest, - nativeToJvmString(env, method.data()).obj(), - nativeToJvmString(env, uri.data()).obj(), - nativeToJvmString(env, header.str()).obj(), - nativeToJvmByteArray(env, body.data(), body.length()).obj() - ) - }; - if (checkException(env)) { + try { + ScopedJvmLocalRef j_response = { + env, m_remote_node_client.callObjectMethod( + env, IRemoteNodeClient_makeRequest, + nativeToJvmString(env, method.data()).obj(), + nativeToJvmString(env, uri.data()).obj(), + nativeToJvmString(env, header.str()).obj(), + nativeToJvmByteArray(env, body.data(), body.length()).obj() + ) + }; + m_response_info.clear(); + if (j_response.is_null()) { + return false; + } + HttpResponse http_response = jvmToHttpResponse(env, j_response); + if (http_response.code == 401) { + // Handle HTTP unauthorized in the same way as http_simple_client_template. + return false; + } + m_response_info.m_response_code = http_response.code; + m_response_info.m_mime_tipe = http_response.content_type; + if (http_response.body.is_valid()) { + http_response.body.read(&m_response_info.m_body); + } + } catch (std::runtime_error& e) { LOGE("Unhandled exception in RemoteNodeClient"); return false; } - m_response_info.clear(); - if (j_response.is_null()) { - return false; - } - HttpResponse http_response = jvmToHttpResponse(env, j_response); - if (http_response.code == 401) { - // Handle unauthorized in the same way as http_simple_client_template. - return false; - } - m_response_info.m_response_code = http_response.code; - m_response_info.m_mime_tipe = http_response.content_type; - if (http_response.body.is_valid()) { - http_response.body.read(&m_response_info.m_body); - } if (ppresponse_info) { *ppresponse_info = std::addressof(m_response_info); } diff --git a/lib/android/src/main/cpp/jvm.cc b/lib/android/src/main/cpp/jvm.cc index 5a3d1ab..49969b1 100644 --- a/lib/android/src/main/cpp/jvm.cc +++ b/lib/android/src/main/cpp/jvm.cc @@ -104,34 +104,43 @@ void JvmRef::callVoidMethod(JNIEnv* env, va_list args; va_start(args, method_id); env->CallVoidMethodV(obj(), method_id, args); + throwRuntimeErrorOnException(env); } jint JvmRef::callIntMethod(JNIEnv* env, jmethodID method_id, ...) const { va_list args; va_start(args, method_id); - return env->CallIntMethodV(obj(), method_id, args); + jint ret = env->CallIntMethodV(obj(), method_id, args); + throwRuntimeErrorOnException(env); + return ret; } jboolean JvmRef::callBooleanMethod(JNIEnv* env, jmethodID method_id, ...) const { va_list args; va_start(args, method_id); - return env->CallBooleanMethodV(obj(), method_id, args); + jboolean ret = env->CallBooleanMethodV(obj(), method_id, args); + throwRuntimeErrorOnException(env); + return ret; } jobject JvmRef::callObjectMethod(JNIEnv* env, jmethodID method_id, ...) const { va_list args; va_start(args, method_id); - return env->CallObjectMethodV(obj(), method_id, args); + jobject ret = env->CallObjectMethodV(obj(), method_id, args); + throwRuntimeErrorOnException(env); + return ret; } jstring JvmRef::callStringMethod(JNIEnv* env, jmethodID method_id, ...) const { va_list args; va_start(args, method_id); - return (jstring) env->CallObjectMethodV(obj(), method_id, args); + auto ret = (jstring) env->CallObjectMethodV(obj(), method_id, args); + throwRuntimeErrorOnException(env); + return ret; } jmethodID JvmRef::getMethodId(JNIEnv* env, diff --git a/lib/android/src/main/cpp/jvm.h b/lib/android/src/main/cpp/jvm.h index f8f457f..a62468c 100644 --- a/lib/android/src/main/cpp/jvm.h +++ b/lib/android/src/main/cpp/jvm.h @@ -3,6 +3,7 @@ #include +#include #include #include @@ -18,14 +19,21 @@ JNIEnv* getJniEnv(); JNIEnv* initializeJvm(JavaVM* jvm, int version); // Checks for any Java exception and clears currently thrown exception. -static bool inline checkException(JNIEnv* env) { +static bool inline checkException(JNIEnv* env, bool log_exception = true) { if (env->ExceptionCheck()) { + if (log_exception) env->ExceptionDescribe(); env->ExceptionClear(); return true; } return false; } +static void inline throwRuntimeErrorOnException(JNIEnv* env) { + if (checkException(env, false)) { + throw std::runtime_error("Uncaught exception returned from Java call"); + } +} + // -------------------------------------------------------------------------- // Originally these classes are from WebRTC and Chromium. // -------------------------------------------------------------------------- diff --git a/lib/android/src/main/cpp/logging.cc b/lib/android/src/main/cpp/logging.cc index 4018209..cbe5148 100644 --- a/lib/android/src/main/cpp/logging.cc +++ b/lib/android/src/main/cpp/logging.cc @@ -74,7 +74,6 @@ void JvmLogSink::write(const std::string& tag, ScopedJvmLocalRef j_msg = nativeToJvmString(env, msg); m_logger.callVoidMethod(env, Logger_logFromNative, pri_idx, j_tag.obj(), j_msg.obj()); - LOG_FATAL_IF(checkException(env)); } void JvmLogSink::set_logger(JNIEnv* env, const JvmRef& logger) { diff --git a/lib/android/src/main/cpp/wallet.cc b/lib/android/src/main/cpp/wallet.cc index bb1181c..453896a 100644 --- a/lib/android/src/main/cpp/wallet.cc +++ b/lib/android/src/main/cpp/wallet.cc @@ -132,7 +132,6 @@ void Wallet::handleBalanceChanged(uint64_t at_block_height) { m_blockchain_height = at_block_height; JNIEnv* env = getJniEnv(); m_callback.callVoidMethod(env, Wallet_onRefresh, at_block_height, true); - LOG_FATAL_IF(checkException(env)); } void Wallet::handleNewBlock(uint64_t height) {