From 3531dcf9a2e63db68d5109884a4adea75de09346 Mon Sep 17 00:00:00 2001 From: clementetb Date: Wed, 29 Nov 2023 10:26:26 +0100 Subject: [PATCH] Delete local references (#1560) --- CHANGELOG.md | 1 + .../src/main/jni/realm_api_helpers.cpp | 297 +++++++++++------- 2 files changed, 191 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99e9ed341f..fe688af6aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixed * Fix craches caused by posting to a released scheduler. (Issue [#1543](https://github.com/realm/realm-kotlin/issues/1543)) * Fix NPE when applying query aggregators on classes annotated with `@PersistedName`. (Issue [1569](https://github.com/realm/realm-kotlin/pull/1569)) +* [Sync] Fix crash when syncing data if the log level was set to `LogLevel.TRACE` or `LogLevel.ALL`. (Issue [#1560](https://github.com/realm/realm-kotlin/pull/1560)) ### Compatibility * File format: Generates Realms with file format v23. diff --git a/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp b/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp index 03783aaf80..c982551024 100644 --- a/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp +++ b/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp @@ -34,6 +34,32 @@ jobject wrap_pointer(JNIEnv* jenv, jlong pointer, jboolean managed = false) { managed); } +inline jboolean jni_check_exception(JNIEnv *jenv = get_env(), bool registrable_callback_error = false) { + // FIXME https://github.com/realm/realm-kotlin/issues/665 This function is catching and swallowing + // the exception. This behavior could leave the SDK in an illegal state. + if (jenv->ExceptionCheck()) { + if (registrable_callback_error) { + // setting the user code error is only propagated on certain callbacks. + jthrowable exception = jenv->ExceptionOccurred(); + jenv->ExceptionClear(); + realm_register_user_code_callback_error(jenv->NewGlobalRef(exception)); + } else { + // Print the exception stacktrace in stderr. + jenv->ExceptionDescribe(); + jenv->ExceptionClear(); + } + return false; + } + return true; +} + +inline void push_local_frame(JNIEnv *jenv, jint frame_size) { + if (jenv->PushLocalFrame(frame_size) != 0) { + jni_check_exception(jenv); + throw std::runtime_error("Failed pushing a local frame with size " + std::to_string(frame_size)); + } +} + inline jobject create_java_exception(JNIEnv *jenv, realm_error_t error) { // Invoke CoreErrorConverter.asThrowable() to retrieve an exception instance that // maps to the core error. @@ -42,6 +68,8 @@ inline jobject create_java_exception(JNIEnv *jenv, realm_error_t error) { error_type_class, "asThrowable", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)Ljava/lang/Throwable;", true); + + push_local_frame(jenv, 3); jstring error_message = (jenv)->NewStringUTF(error.message); jstring error_path = (jenv)->NewStringUTF(error.path); jobject exception = (jenv)->CallStaticObjectMethod( @@ -53,8 +81,11 @@ inline jobject create_java_exception(JNIEnv *jenv, realm_error_t error) { error_path, static_cast(error.usercode_error) ); - (jenv)->DeleteGlobalRef(static_cast(error.usercode_error)); - return exception; + if (error.usercode_error) { + (jenv)->DeleteGlobalRef(static_cast(error.usercode_error)); + } + jni_check_exception(jenv); + return jenv->PopLocalFrame(exception); } bool throw_last_error_as_java_exception(JNIEnv *jenv) { @@ -69,16 +100,6 @@ bool throw_last_error_as_java_exception(JNIEnv *jenv) { } } -inline jboolean jni_check_exception(JNIEnv *jenv = get_env()) { - if (jenv->ExceptionCheck()) { - jthrowable exception = jenv->ExceptionOccurred(); - jenv->ExceptionClear(); - realm_register_user_code_callback_error(jenv->NewGlobalRef(exception)); - return false; - } - return true; -} - inline std::string get_exception_message(JNIEnv *env) { jthrowable e = env->ExceptionOccurred(); env->ExceptionClear(); @@ -105,7 +126,7 @@ realm_changed_callback(void* userdata) { static JavaClass java_callback_class(env, "kotlin/jvm/functions/Function0"); static JavaMethod java_callback_method(env, java_callback_class, "invoke", "()Ljava/lang/Object;"); - // TODOO Align exceptions handling https://github.com/realm/realm-kotlin/issues/665 + // TODO Align exceptions handling https://github.com/realm/realm-kotlin/issues/665 jni_check_exception(env); env->CallObjectMethod(static_cast(userdata), java_callback_method); jni_check_exception(env); @@ -117,11 +138,13 @@ schema_changed_callback(void* userdata, const realm_schema_t* new_schema) { static JavaClass java_callback_class(env, "kotlin/jvm/functions/Function1"); static JavaMethod java_callback_method(env, java_callback_class, "invoke", "(Ljava/lang/Object;)Ljava/lang/Object;"); + env->PushLocalFrame(1); jobject schema_pointer_wrapper = wrap_pointer(env,reinterpret_cast(new_schema)); - // TODOO Align exceptions handling https://github.com/realm/realm-kotlin/issues/665 + // TODO Align exceptions handling https://github.com/realm/realm-kotlin/issues/665 jni_check_exception(env); env->CallObjectMethod(static_cast(userdata), java_callback_method, schema_pointer_wrapper); jni_check_exception(env); + env->PopLocalFrame(NULL); } bool migration_callback(void *userdata, realm_t *old_realm, realm_t *new_realm, @@ -130,14 +153,17 @@ bool migration_callback(void *userdata, realm_t *old_realm, realm_t *new_realm, static JavaClass java_callback_class(env, "io/realm/kotlin/internal/interop/MigrationCallback"); static JavaMethod java_callback_method(env, java_callback_class, "migrate", "(Lio/realm/kotlin/internal/interop/NativePointer;Lio/realm/kotlin/internal/interop/NativePointer;Lio/realm/kotlin/internal/interop/NativePointer;)V"); - // These realm/schema pointers are only valid for the duraction of the + // These realm/schema pointers are only valid for the duration of the // migration so don't let ownership follow the NativePointer-objects + env->PushLocalFrame(3); env->CallVoidMethod(static_cast(userdata), java_callback_method, - wrap_pointer(env, reinterpret_cast(old_realm), false), - wrap_pointer(env, reinterpret_cast(new_realm), false), - wrap_pointer(env, reinterpret_cast(schema)) + wrap_pointer(env, reinterpret_cast(old_realm), false), + wrap_pointer(env, reinterpret_cast(new_realm), false), + wrap_pointer(env, reinterpret_cast(schema)) ); - return jni_check_exception(env); + bool failed = jni_check_exception(env, true); + env->PopLocalFrame(NULL); + return failed; } // TODO OPTIMIZE Abstract pattern for all notification registrations for collections that receives @@ -166,6 +192,7 @@ register_results_notification_cb(realm_results_t *results, jobject callback) { jenv->CallVoidMethod(static_cast(userdata), on_change_method, reinterpret_cast(changes)); + jni_check_exception(jenv); } ); } @@ -183,6 +210,7 @@ realm_on_object_change_func_t get_on_object_change() { jenv->CallVoidMethod(static_cast(userdata), on_change_method, reinterpret_cast(changes)); + jni_check_exception(jenv); }; } @@ -199,6 +227,7 @@ realm_on_collection_change_func_t get_on_collection_change() { jenv->CallVoidMethod(static_cast(userdata), on_change_method, reinterpret_cast(changes)); + jni_check_exception(jenv); }; } @@ -215,6 +244,7 @@ realm_on_dictionary_change_func_t get_on_dictionary_change() { jenv->CallVoidMethod(static_cast(userdata), on_change_method, reinterpret_cast(changes)); + jni_check_exception(jenv); }; } @@ -280,6 +310,7 @@ class CustomJVMScheduler { jni_check_exception(jenv); jenv->CallVoidMethod(m_jvm_dispatch_scheduler, m_notify_method, reinterpret_cast(work_queue)); + jni_check_exception(jenv); } bool is_on_thread() const noexcept { @@ -343,20 +374,22 @@ jobject convert_to_jvm_app_error(JNIEnv* env, const realm_app_error_t* error) { "newInstance", "(IIILjava/lang/String;Ljava/lang/String;)Lio/realm/kotlin/internal/interop/sync/AppError;", true); - + env->PushLocalFrame(3); jint category = static_cast(error->categories); jint code = static_cast(error->error); jint httpCode = static_cast(error->http_status_code); jstring message = to_jstring(env, error->message); jstring serverLogs = to_jstring(env, error->link_to_server_logs); - return env->CallStaticObjectMethod(JavaClassGlobalDef::app_error(), + auto result = env->CallStaticObjectMethod(JavaClassGlobalDef::app_error(), app_error_constructor, category, code, httpCode, message, serverLogs); + jni_check_exception(env); + return env->PopLocalFrame(result); } @@ -369,8 +402,10 @@ void app_complete_void_callback(void *userdata, const realm_app_error_t *error) static JavaClass unit_class(env, "kotlin/Unit"); static JavaMethod unit_constructor(env, unit_class, "", "()V"); + env->PushLocalFrame(1); if (env->ExceptionCheck()) { env->ExceptionDescribe(); + env->PopLocalFrame(NULL); throw std::runtime_error("An unexpected Error was thrown from Java. See LogCat"); } else if (error) { jobject app_error = convert_to_jvm_app_error(env, error); @@ -379,6 +414,8 @@ void app_complete_void_callback(void *userdata, const realm_app_error_t *error) jobject unit = env->NewObject(unit_class, unit_constructor); env->CallVoidMethod(static_cast(userdata), java_notify_onsuccess, unit); } + jni_check_exception(env); + env->PopLocalFrame(NULL); } void app_complete_result_callback(void* userdata, void* result, const realm_app_error_t* error) { @@ -391,8 +428,10 @@ void app_complete_result_callback(void* userdata, void* result, const realm_app_ static JavaClass native_pointer_class(env, "io/realm/kotlin/internal/interop/LongPointerWrapper"); static JavaMethod native_pointer_constructor(env, native_pointer_class, "", "(JZ)V"); + env->PushLocalFrame(1); if (env->ExceptionCheck()) { env->ExceptionDescribe(); + env->PopLocalFrame(NULL); throw std::runtime_error("An unexpected Error was thrown from Java. See LogCat"); } else if (error) { jobject app_exception = convert_to_jvm_app_error(env, error); @@ -404,43 +443,45 @@ void app_complete_result_callback(void* userdata, void* result, const realm_app_ reinterpret_cast(cloned_result), false); env->CallVoidMethod(static_cast(userdata), java_notify_onsuccess, pointer); } + jni_check_exception(env); + env->PopLocalFrame(NULL); } jobject create_api_key_wrapper(JNIEnv* env, const realm_app_user_apikey_t* key_data) { - static JavaClass api_key_wrapper_class(env, "io/realm/kotlin/internal/interop/sync/ApiKeyWrapper"); - static JavaMethod api_key_wrapper_constructor(env, api_key_wrapper_class, "", "([BLjava/lang/String;Ljava/lang/String;Z)V"); - auto id_size = sizeof(key_data->id.bytes); - jbyteArray id = env->NewByteArray(id_size); - env->SetByteArrayRegion(id, 0, id_size, reinterpret_cast(key_data->id.bytes)); - jstring key = to_jstring(env, key_data->key); - jstring name = to_jstring(env, key_data->name); - jboolean disabled = key_data->disabled; - return env->NewObject(api_key_wrapper_class, - api_key_wrapper_constructor, - id, - key, - name, - disabled, - false); + static JavaClass api_key_wrapper_class(env, "io/realm/kotlin/internal/interop/sync/ApiKeyWrapper"); + static JavaMethod api_key_wrapper_constructor(env, api_key_wrapper_class, "", "([BLjava/lang/String;Ljava/lang/String;Z)V"); + auto id_size = sizeof(key_data->id.bytes); + jbyteArray id = env->NewByteArray(id_size); + env->SetByteArrayRegion(id, 0, id_size, reinterpret_cast(key_data->id.bytes)); + jstring key = to_jstring(env, key_data->key); + jstring name = to_jstring(env, key_data->name); + jboolean disabled = key_data->disabled; + auto result = env->NewObject(api_key_wrapper_class, + api_key_wrapper_constructor, + id, + key, + name, + disabled, + false); + return result; } - - void app_apikey_callback(realm_userdata_t userdata, realm_app_user_apikey_t* apikey, const realm_app_error_t* error) { auto env = get_env(true); static JavaMethod java_notify_onerror(env, JavaClassGlobalDef::app_callback(), "onError", "(Lio/realm/kotlin/internal/interop/sync/AppError;)V"); static JavaMethod java_notify_onsuccess(env, JavaClassGlobalDef::app_callback(), "onSuccess", "(Ljava/lang/Object;)V"); + env->PushLocalFrame(1); if (error) { jobject app_exception = convert_to_jvm_app_error(env, error); env->CallVoidMethod(static_cast(userdata), java_notify_onerror, app_exception); - jni_check_exception(env); } else { jobject api_key_wrapper_obj = create_api_key_wrapper(env, apikey); env->CallVoidMethod(static_cast(userdata), java_notify_onsuccess, api_key_wrapper_obj); - jni_check_exception(env); } + jni_check_exception(env); + env->PopLocalFrame(NULL); } void app_string_callback(realm_userdata_t userdata, const char *serialized_ejson_response, @@ -459,15 +500,17 @@ void app_string_callback(realm_userdata_t userdata, const char *serialized_ejson "onSuccess", "(Ljava/lang/Object;)V" ); + + env->PushLocalFrame(1); if (error) { jobject app_exception = convert_to_jvm_app_error(env, error); env->CallVoidMethod(static_cast(userdata), java_notify_onerror, app_exception); - jni_check_exception(env); } else { jstring jserialized_ejson_response = to_jstring(env, serialized_ejson_response); env->CallVoidMethod(static_cast(userdata), java_notify_onsuccess, jserialized_ejson_response); - jni_check_exception(env); } + jni_check_exception(env); + env->PopLocalFrame(NULL); } void app_apikey_list_callback(realm_userdata_t userdata, realm_app_user_apikey_t* keys, size_t count, realm_app_error_t* error) { @@ -478,10 +521,11 @@ void app_apikey_list_callback(realm_userdata_t userdata, realm_app_user_apikey_t "(Lio/realm/kotlin/internal/interop/sync/AppError;)V"); static JavaMethod java_notify_onsuccess(env, JavaClassGlobalDef::app_callback(), "onSuccess", "(Ljava/lang/Object;)V"); + + env->PushLocalFrame(1); if (error) { jobject app_exception = convert_to_jvm_app_error(env, error); env->CallVoidMethod(static_cast(userdata), java_notify_onerror, app_exception); - jni_check_exception(env); } else { // Create Object[] array jobjectArray key_array = env->NewObjectArray(count, api_key_wrapper_class, nullptr); @@ -491,12 +535,14 @@ void app_apikey_list_callback(realm_userdata_t userdata, realm_app_user_apikey_t realm_app_user_apikey_t api_key = keys[i]; jobject api_key_wrapper_obj = create_api_key_wrapper(env, &api_key); env->SetObjectArrayElement(key_array, i, api_key_wrapper_obj); + env->DeleteLocalRef(api_key_wrapper_obj); } // Return Object[] to Kotlin env->CallVoidMethod(static_cast(userdata), java_notify_onsuccess, key_array); - jni_check_exception(env); } + jni_check_exception(env); + env->PopLocalFrame(NULL); } bool realm_should_compact_callback(void* userdata, uint64_t total_bytes, uint64_t used_bytes) { @@ -506,18 +552,17 @@ bool realm_should_compact_callback(void* userdata, uint64_t total_bytes, uint64_ jobject callback = static_cast(userdata); jboolean result = env->CallBooleanMethod(callback, java_should_compact_method, jlong(total_bytes), jlong(used_bytes)); - return jni_check_exception(env) && result; + return jni_check_exception(env, true) && result; } -bool realm_data_initialization_callback(void* userdata, realm_t* realm) { +bool realm_data_initialization_callback(void* userdata, realm_t*) { auto env = get_env(true); static JavaClass java_data_init_class(env, "io/realm/kotlin/internal/interop/DataInitializationCallback"); static JavaMethod java_data_init_method(env, java_data_init_class, "invoke", "()V"); - (void)realm; // Ignore Realm as we don't expose the Realm in this callback right now. jobject callback = static_cast(userdata); env->CallVoidMethod(callback, java_data_init_method); - return jni_check_exception(env); + return jni_check_exception(env, true); } static void send_request_via_jvm_transport(JNIEnv *jenv, jobject network_transport, const realm_http_request_t request, jobject j_response_callback) { @@ -557,15 +602,19 @@ static void send_request_via_jvm_transport(JNIEnv *jenv, jobject network_transpo "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); size_t map_size = request.num_headers; + push_local_frame(jenv, 1); jobject request_headers = jenv->NewObject(JavaClassGlobalDef::java_util_hashmap(), init, (jsize) map_size); for (int i = 0; i < map_size; i++) { + push_local_frame(jenv, 2); + realm_http_header_t header_pair = request.headers[i]; jstring key = to_jstring(jenv, header_pair.name); jstring value = to_jstring(jenv, header_pair.value); + jenv->CallObjectMethod(request_headers, put_method, key, value); - jenv->DeleteLocalRef(key); - jenv->DeleteLocalRef(value); + jni_check_exception(jenv); + jenv->PopLocalFrame(NULL); } // Send request @@ -577,6 +626,8 @@ static void send_request_via_jvm_transport(JNIEnv *jenv, jobject network_transpo to_jstring(jenv, request.body), j_response_callback ); + jni_check_exception(jenv); + jenv->PopLocalFrame(NULL); } void complete_http_request(void* request_context, jobject j_response) { @@ -654,12 +705,14 @@ static void network_request_lambda_function(void* userdata, static jmethodID response_callback_constructor = jenv->GetMethodID(response_callback_class, "", "(Lio/realm/kotlin/internal/interop/sync/NetworkTransport;J)V"); + push_local_frame(jenv, 1); jobject response_callback = jenv->NewObject(response_callback_class, response_callback_constructor, reinterpret_cast(userdata), reinterpret_cast(request_context)); send_request_via_jvm_transport(jenv, network_transport, request, response_callback); + jenv->PopLocalFrame(NULL); } catch (std::runtime_error &e) { // Runtime exception while processing the request/response realm_http_response_t response_error; @@ -683,32 +736,37 @@ realm_http_transport_t* realm_network_transport_new(jobject network_transport) { } void set_log_callback(jint j_log_level, jobject log_callback) { - auto jenv = get_env(false); - auto log_level = static_cast(j_log_level); - realm_set_log_callback([](void* userdata, realm_log_level_e level, const char* message) { - auto log_callback = static_cast(userdata); - auto jenv = get_env(true); - auto java_level = static_cast(level); - static JavaMethod log_method(jenv, - JavaClassGlobalDef::log_callback(), - "log", - "(SLjava/lang/String;)V"); - jenv->CallVoidMethod(log_callback, log_method, java_level, to_jstring(jenv, message)); - jni_check_exception(jenv); - }, - log_level, - jenv->NewGlobalRef(log_callback), // userdata is the log callback - [](void* userdata) { - // The log callback is a static global method that is intended to - // live for the lifetime of the application. On JVM it looks like - // is being destroyed after the JNIEnv has been destroyed, which - // will e.g. crash the Gradle test setup. So instead, we just do a - // best effort of cleaning up the registered callback. - JNIEnv *env = get_env_or_null(); - if (env) { - env->DeleteGlobalRef(static_cast(userdata)); - } - }); +auto jenv = get_env(false); +auto log_level = static_cast(j_log_level); +realm_set_log_callback([](void *userdata, realm_log_level_e level, const char *message) { + auto log_callback = static_cast(userdata); + auto jenv = get_env(true); + + + auto java_level = static_cast(level); + static JavaMethod log_method(jenv, + JavaClassGlobalDef::log_callback(), + "log", + "(SLjava/lang/String;)V"); + + push_local_frame(jenv, 1); + jenv->CallVoidMethod(log_callback, log_method, java_level, to_jstring(jenv, message)); + jni_check_exception(jenv); + jenv->PopLocalFrame(NULL); + }, + log_level, + jenv->NewGlobalRef(log_callback), // userdata is the log callback + [](void* userdata) { + // The log callback is a static global method that is intended to + // live for the lifetime of the application. On JVM it looks like + // is being destroyed after the JNIEnv has been destroyed, which + // will e.g. crash the Gradle test setup. So instead, we just do a + // best effort of cleaning up the registered callback. + JNIEnv *env = get_env_or_null(); + if (env) { + env->DeleteGlobalRef(static_cast(userdata)); + } + }); } jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error) { @@ -740,6 +798,7 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error) "(Ljava/lang/String;Ljava/lang/String;J)V" ); + push_local_frame(jenv, 3); auto j_compensating_write_info_array = jenv->NewObjectArray( error.compensating_writes_length, JavaClassGlobalDef::core_compensating_write_info(), @@ -749,6 +808,8 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error) for (int index = 0; index < error.compensating_writes_length; index++) { realm_sync_error_compensating_write_info_t& compensating_write_info = error.compensating_writes[index]; + push_local_frame(jenv, 3); + auto reason = to_jstring(jenv, compensating_write_info.reason); auto object_name = to_jstring(jenv, compensating_write_info.object_name); @@ -765,6 +826,8 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error) index, j_compensating_write_info ); + + jenv->PopLocalFrame(NULL); } // We can't only rely on 'error.is_client_reset_requested' (even though we should) to extract @@ -788,7 +851,7 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error) } } - return jenv->NewObject( + jobject result = jenv->NewObject( JavaClassGlobalDef::sync_error(), sync_error_constructor, category, @@ -801,6 +864,9 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error) is_client_reset_requested, j_compensating_write_info_array ); + + jni_check_exception(jenv); + return jenv->PopLocalFrame(result); } void sync_set_error_handler(realm_sync_config_t* sync_config, jobject error_handler) { @@ -808,18 +874,22 @@ void sync_set_error_handler(realm_sync_config_t* sync_config, jobject error_hand [](void* userdata, realm_sync_session_t* session, const realm_sync_error_t error) { auto jenv = get_env(true); auto sync_error_callback = static_cast(userdata); - - jobject session_pointer_wrapper = wrap_pointer(jenv,reinterpret_cast(session)); - jobject sync_error = convert_to_jvm_sync_error(jenv, error); - static JavaMethod sync_error_method(jenv, JavaClassGlobalDef::sync_error_callback(), "onSyncError", "(Lio/realm/kotlin/internal/interop/NativePointer;Lio/realm/kotlin/internal/interop/sync/SyncError;)V"); + + push_local_frame(jenv, 2); + + jobject session_pointer_wrapper = wrap_pointer(jenv,reinterpret_cast(session)); + jobject sync_error = convert_to_jvm_sync_error(jenv, error); + jenv->CallVoidMethod(sync_error_callback, sync_error_method, session_pointer_wrapper, sync_error); + jni_check_exception(jenv); + jenv->PopLocalFrame(NULL); }, static_cast(get_env()->NewGlobalRef(error_handler)), [](void *userdata) { @@ -840,8 +910,10 @@ void transfer_completion_callback(void* userdata, realm_error_t* error) { if (error) { jint category = static_cast(error->categories); jint value = error->error; - jstring msg = to_jstring(env, error->message); - env->CallVoidMethod(static_cast(userdata), java_error_callback_method, category, value, msg); + env->PushLocalFrame(1); + env->CallVoidMethod(static_cast(userdata), java_error_callback_method, category, value, to_jstring(env, error->message)); + jni_check_exception(env); + env->PopLocalFrame(NULL); } else { env->CallVoidMethod(static_cast(userdata), java_success_callback_method); } @@ -850,6 +922,7 @@ void transfer_completion_callback(void* userdata, realm_error_t* error) { void realm_subscriptionset_changed_callback(void* userdata, realm_flx_sync_subscription_set_state_e state) { auto env = get_env(true); + env->PushLocalFrame(1); jobject state_value = JavaClassGlobalDef::new_int(env, static_cast(state)); env->CallObjectMethod( static_cast(userdata), @@ -857,6 +930,7 @@ void realm_subscriptionset_changed_callback(void* userdata, realm_flx_sync_subsc state_value ); jni_check_exception(env); + env->PopLocalFrame(NULL); } void realm_async_open_task_callback(void* userdata, realm_thread_safe_reference_t* realm, const realm_async_error_t* error) { @@ -865,6 +939,9 @@ void realm_async_open_task_callback(void* userdata, realm_thread_safe_reference_ JavaClassGlobalDef::async_open_callback(), "invoke", "(Ljava/lang/Throwable;)V"); + jobject callback = static_cast(userdata); + + env->PushLocalFrame(1); jobject exception = nullptr; if (error) { realm_error_t err; @@ -873,9 +950,9 @@ void realm_async_open_task_callback(void* userdata, realm_thread_safe_reference_ } else { realm_release(realm); } - jobject callback = static_cast(userdata); env->CallVoidMethod(callback, java_invoke_method, exception); jni_check_exception(env); + env->PopLocalFrame(NULL); } bool @@ -885,16 +962,19 @@ before_client_reset(void* userdata, realm_t* before_realm) { JavaClassGlobalDef::sync_before_client_reset(), "onBeforeReset", "(Lio/realm/kotlin/internal/interop/NativePointer;)V"); - auto before_pointer = wrap_pointer(env, reinterpret_cast(before_realm), false); + env->PushLocalFrame(1); + jobject before_pointer = wrap_pointer(env, reinterpret_cast(before_realm), false); env->CallVoidMethod(static_cast(userdata), java_before_callback_function, before_pointer); + + bool result = true; if (env->ExceptionCheck()) { std::string exception_message = get_exception_message(env); std::string message_template = "An error has occurred in the 'onBefore' callback: "; system_out_println(env, message_template.append(exception_message)); - return false; + result = false; } - - return true; + env->PopLocalFrame(NULL); + return result; } bool @@ -905,22 +985,25 @@ after_client_reset(void* userdata, realm_t* before_realm, JavaClassGlobalDef::sync_after_client_reset(), "onAfterReset", "(Lio/realm/kotlin/internal/interop/NativePointer;Lio/realm/kotlin/internal/interop/NativePointer;Z)V"); - auto before_pointer = wrap_pointer(env, reinterpret_cast(before_realm), false); + env->PushLocalFrame(2); + jobject before_pointer = wrap_pointer(env, reinterpret_cast(before_realm), false); // Reuse the scheduler from the beforeRealm, otherwise Core will attempt to recreate a new one, // which will fail on platforms that hasn't defined a default scheduler factory. realm_scheduler_t scheduler = realm_scheduler(before_realm->get()->scheduler()); realm_t* after_realm_ptr = realm_from_thread_safe_reference(after_realm, &scheduler); - auto after_pointer = wrap_pointer(env, reinterpret_cast(after_realm_ptr), false); + + jobject after_pointer = wrap_pointer(env, reinterpret_cast(after_realm_ptr), false); env->CallVoidMethod(static_cast(userdata), java_after_callback_function, before_pointer, after_pointer, did_recover); realm_close(after_realm_ptr); + bool result = true; if (env->ExceptionCheck()) { std::string exception_message = get_exception_message(env); std::string message_template = "An error has occurred in the 'onAfter' callback: "; system_out_println(env, message_template.append(exception_message)); - return false; + result = false; } - - return true; + env->PopLocalFrame(NULL); + return result; } void @@ -974,20 +1057,20 @@ realm_sync_session_register_progress_notifier_wrapper( realm_sync_session_t* session, realm_sync_progress_direction_e direction, bool is_streaming, jobject callback ) { auto jenv = get_env(true); - jlong jresult = 0 ; - realm_sync_session_connection_state_notification_token_t *result = 0 ; - result = realm_sync_session_register_progress_notifier( - session, - realm_sync_session_progress_notifier_callback, - direction, - is_streaming, - static_cast(jenv->NewGlobalRef( - callback)), - [](void *userdata) { - get_env(true)->DeleteGlobalRef( - static_cast(userdata)); - } - ); + jlong jresult = 0; + realm_sync_session_connection_state_notification_token_t *result = + realm_sync_session_register_progress_notifier( + session, + realm_sync_session_progress_notifier_callback, + direction, + is_streaming, + static_cast(jenv->NewGlobalRef( + callback)), + [](void *userdata) { + get_env(true)->DeleteGlobalRef( + static_cast(userdata)); + } + ); if (!result) { bool exception_thrown = throw_last_error_as_java_exception(jenv); if (exception_thrown) {