From 5387f0d596dde3583e1e6c4c856ceba7ac046825 Mon Sep 17 00:00:00 2001 From: Anish Kumar Date: Wed, 14 Jan 2026 13:16:52 +0530 Subject: [PATCH] Android: Fix crash when emitting signal with java.util.HashMap --- .../org/godotengine/godot/Dictionary.java | 21 ++++++++- platform/android/java_class_wrapper.cpp | 4 +- platform/android/jni_utils.cpp | 43 ++++++++++++++----- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/platform/android/java/lib/src/main/java/org/godotengine/godot/Dictionary.java b/platform/android/java/lib/src/main/java/org/godotengine/godot/Dictionary.java index 699e19fcf0..83bfa7d1a2 100644 --- a/platform/android/java/lib/src/main/java/org/godotengine/godot/Dictionary.java +++ b/platform/android/java/lib/src/main/java/org/godotengine/godot/Dictionary.java @@ -31,11 +31,16 @@ package org.godotengine.godot; import java.util.HashMap; +import java.util.Map; import java.util.Set; -public class Dictionary extends HashMap { - protected String[] keys_cache; +public final class Dictionary extends HashMap { + private String[] keys_cache; + /** + * @deprecated Use {@link java.util.HashMap#keySet()} instead. + */ + @Deprecated public String[] get_keys() { String[] ret = new String[size()]; int i = 0; @@ -48,6 +53,10 @@ public class Dictionary extends HashMap { return ret; } + /** + * @deprecated Use {@link java.util.HashMap#values()} instead. + */ + @Deprecated public Object[] get_values() { Object[] ret = new Object[size()]; int i = 0; @@ -60,10 +69,18 @@ public class Dictionary extends HashMap { return ret; } + /** + * @deprecated Use {@link java.util.HashMap#putAll(Map)} instead. + */ + @Deprecated public void set_keys(String[] keys) { keys_cache = keys; } + /** + * @deprecated Use {@link java.util.HashMap#putAll(Map)} instead. + */ + @Deprecated public void set_values(Object[] vals) { int i = 0; for (String key : keys_cache) { diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 06bbd2a2e8..051775f043 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -1078,7 +1078,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va if (java_class_wrapped.is_valid()) { String cn = java_class_wrapped->get_java_class_name(); - if (cn == "org.godotengine.godot.Dictionary" || cn == "java.util.HashMap") { + if (cn == "org.godotengine.godot.Dictionary") { var = _jobject_to_variant(env, obj); } else { Ref ret = Ref(memnew(JavaObject(java_class_wrapped, obj))); @@ -1442,7 +1442,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va if (java_class_wrapped.is_valid()) { String cn = java_class_wrapped->get_java_class_name(); - if (cn == "org.godotengine.godot.Dictionary" || cn == "java.util.HashMap") { + if (cn == "org.godotengine.godot.Dictionary") { ret[i] = _jobject_to_variant(env, obj); } else { Ref java_obj_wrapped = Ref(memnew(JavaObject(java_class_wrapped, obj))); diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp index fe2415f2e7..c0e48d6b28 100644 --- a/platform/android/jni_utils.cpp +++ b/platform/android/jni_utils.cpp @@ -428,25 +428,46 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj, int p_depth) { return varr; } - if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") { + if (name == "org.godotengine.godot.Dictionary") { Dictionary ret; jclass oclass = c; - jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;"); - jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys); - PackedStringArray keys = _jobject_to_variant(env, arr, p_depth + 1); - env->DeleteLocalRef(arr); + jmethodID entrySet = env->GetMethodID(oclass, "entrySet", "()Ljava/util/Set;"); + jobject entrySetObj = env->CallObjectMethod(obj, entrySet); - jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;"); - arr = (jobjectArray)env->CallObjectMethod(obj, get_values); + jclass setClass = env->GetObjectClass(entrySetObj); + jmethodID iterator = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); + jobject iteratorObj = env->CallObjectMethod(entrySetObj, iterator); - Array vals = _jobject_to_variant(env, arr, p_depth + 1); - env->DeleteLocalRef(arr); + jclass iteratorClass = env->GetObjectClass(iteratorObj); + jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); + jmethodID next = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); - for (int i = 0; i < keys.size(); i++) { - ret[keys[i]] = vals[i]; + jclass entryClass = jni_find_class(env, "java/util/Map$Entry"); + jmethodID getKey = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); + jmethodID getValue = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); + + while (env->CallBooleanMethod(iteratorObj, hasNext)) { + jobject entry = env->CallObjectMethod(iteratorObj, next); + + jobject jkey = env->CallObjectMethod(entry, getKey); + jobject jvalue = env->CallObjectMethod(entry, getValue); + + Variant key = _jobject_to_variant(env, jkey, p_depth + 1); + Variant value = _jobject_to_variant(env, jvalue, p_depth + 1); + ret[key] = value; + + env->DeleteLocalRef(entry); + env->DeleteLocalRef(jkey); + env->DeleteLocalRef(jvalue); } + env->DeleteLocalRef(entrySetObj); + env->DeleteLocalRef(iteratorObj); + env->DeleteLocalRef(setClass); + env->DeleteLocalRef(iteratorClass); + env->DeleteLocalRef(entryClass); + return ret; }