lib: throw C++ exceptions on exceptions from Java calls

This commit is contained in:
Oscar Mira 2023-02-26 13:23:54 +01:00
parent 56ecb91657
commit a34ace5fc8
6 changed files with 47 additions and 35 deletions

View File

@ -29,7 +29,6 @@ class ScopedFd {
if (!parcel_file_descriptor.is_null()) { if (!parcel_file_descriptor.is_null()) {
m_fd = parcel_file_descriptor.callIntMethod(env, m_fd = parcel_file_descriptor.callIntMethod(env,
ParcelFileDescriptor_detachFd); ParcelFileDescriptor_detachFd);
LOG_FATAL_IF(checkException(env));
} }
} }

View File

@ -35,11 +35,8 @@ bool RemoteNodeClient::is_connected(bool* ssl) {
RemoteNodeClient::HttpResponse jvmToHttpResponse(JNIEnv* env, JvmRef<jobject>& j_http_response) { RemoteNodeClient::HttpResponse jvmToHttpResponse(JNIEnv* env, JvmRef<jobject>& j_http_response) {
jint code = j_http_response.callIntMethod(env, HttpResponse_getCode); jint code = j_http_response.callIntMethod(env, HttpResponse_getCode);
LOG_FATAL_IF(checkException(env));
jstring mime_type = j_http_response.callStringMethod(env, HttpResponse_getContentType); jstring mime_type = j_http_response.callStringMethod(env, HttpResponse_getContentType);
LOG_FATAL_IF(checkException(env));
jobject body = j_http_response.callObjectMethod(env, HttpResponse_getBody); jobject body = j_http_response.callObjectMethod(env, HttpResponse_getBody);
LOG_FATAL_IF(checkException(env));
return { return {
code, code,
(mime_type != nullptr) ? jvmToStdString(env, mime_type) : "", (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) { for (const auto& p: additional_params) {
header << p.first << ": " << p.second << "\r\n"; header << p.first << ": " << p.second << "\r\n";
} }
ScopedJvmLocalRef<jobject> j_response = { try {
env, m_remote_node_client.callObjectMethod( ScopedJvmLocalRef<jobject> j_response = {
env, IRemoteNodeClient_makeRequest, env, m_remote_node_client.callObjectMethod(
nativeToJvmString(env, method.data()).obj(), env, IRemoteNodeClient_makeRequest,
nativeToJvmString(env, uri.data()).obj(), nativeToJvmString(env, method.data()).obj(),
nativeToJvmString(env, header.str()).obj(), nativeToJvmString(env, uri.data()).obj(),
nativeToJvmByteArray(env, body.data(), body.length()).obj() nativeToJvmString(env, header.str()).obj(),
) nativeToJvmByteArray(env, body.data(), body.length()).obj()
}; )
if (checkException(env)) { };
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"); LOGE("Unhandled exception in RemoteNodeClient");
return false; 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) { if (ppresponse_info) {
*ppresponse_info = std::addressof(m_response_info); *ppresponse_info = std::addressof(m_response_info);
} }

View File

@ -104,34 +104,43 @@ void JvmRef<jobject>::callVoidMethod(JNIEnv* env,
va_list args; va_list args;
va_start(args, method_id); va_start(args, method_id);
env->CallVoidMethodV(obj(), method_id, args); env->CallVoidMethodV(obj(), method_id, args);
throwRuntimeErrorOnException(env);
} }
jint JvmRef<jobject>::callIntMethod(JNIEnv* env, jint JvmRef<jobject>::callIntMethod(JNIEnv* env,
jmethodID method_id, ...) const { jmethodID method_id, ...) const {
va_list args; va_list args;
va_start(args, method_id); 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<jobject>::callBooleanMethod(JNIEnv* env, jboolean JvmRef<jobject>::callBooleanMethod(JNIEnv* env,
jmethodID method_id, ...) const { jmethodID method_id, ...) const {
va_list args; va_list args;
va_start(args, method_id); 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<jobject>::callObjectMethod(JNIEnv* env, jobject JvmRef<jobject>::callObjectMethod(JNIEnv* env,
jmethodID method_id, ...) const { jmethodID method_id, ...) const {
va_list args; va_list args;
va_start(args, method_id); 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<jobject>::callStringMethod(JNIEnv* env, jstring JvmRef<jobject>::callStringMethod(JNIEnv* env,
jmethodID method_id, ...) const { jmethodID method_id, ...) const {
va_list args; va_list args;
va_start(args, method_id); 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<jobject>::getMethodId(JNIEnv* env, jmethodID JvmRef<jobject>::getMethodId(JNIEnv* env,

View File

@ -3,6 +3,7 @@
#include <jni.h> #include <jni.h>
#include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
@ -18,14 +19,21 @@ JNIEnv* getJniEnv();
JNIEnv* initializeJvm(JavaVM* jvm, int version); JNIEnv* initializeJvm(JavaVM* jvm, int version);
// Checks for any Java exception and clears currently thrown exception. // 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 (env->ExceptionCheck()) {
if (log_exception) env->ExceptionDescribe();
env->ExceptionClear(); env->ExceptionClear();
return true; return true;
} }
return false; 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. // Originally these classes are from WebRTC and Chromium.
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------

View File

@ -74,7 +74,6 @@ void JvmLogSink::write(const std::string& tag,
ScopedJvmLocalRef<jstring> j_msg = nativeToJvmString(env, msg); ScopedJvmLocalRef<jstring> j_msg = nativeToJvmString(env, msg);
m_logger.callVoidMethod(env, Logger_logFromNative, m_logger.callVoidMethod(env, Logger_logFromNative,
pri_idx, j_tag.obj(), j_msg.obj()); pri_idx, j_tag.obj(), j_msg.obj());
LOG_FATAL_IF(checkException(env));
} }
void JvmLogSink::set_logger(JNIEnv* env, const JvmRef<jobject>& logger) { void JvmLogSink::set_logger(JNIEnv* env, const JvmRef<jobject>& logger) {

View File

@ -132,7 +132,6 @@ void Wallet::handleBalanceChanged(uint64_t at_block_height) {
m_blockchain_height = at_block_height; m_blockchain_height = at_block_height;
JNIEnv* env = getJniEnv(); JNIEnv* env = getJniEnv();
m_callback.callVoidMethod(env, Wallet_onRefresh, at_block_height, true); m_callback.callVoidMethod(env, Wallet_onRefresh, at_block_height, true);
LOG_FATAL_IF(checkException(env));
} }
void Wallet::handleNewBlock(uint64_t height) { void Wallet::handleNewBlock(uint64_t height) {