From 35fda7f857a8e55cc65959a310a4327bacf5f63a Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Sat, 16 Aug 2025 22:09:20 -0700 Subject: [PATCH] Fix JNI local reference table overflow when wrapping Java class with large method counts --- platform/android/java_class_wrapper.cpp | 68 +++----------- platform/android/java_godot_lib_jni.cpp | 4 +- platform/android/jni_utils.cpp | 109 +++++++++++----------- platform/android/jni_utils.h | 8 +- platform/android/variant/callable_jni.cpp | 8 +- 5 files changed, 77 insertions(+), 120 deletions(-) diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 78607c5565c..e2d223a6b18 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -43,6 +43,8 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, false); + env->PushLocalFrame(p_argcount); + MethodInfo *method = nullptr; for (MethodInfo &E : M->value) { if (!p_instance && !E._static && !E._constructor) { @@ -284,7 +286,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, argv = (jvalue *)alloca(sizeof(jvalue) * method->param_types.size()); } - List to_free; for (int i = 0; i < method->param_types.size(); i++) { switch (method->param_types[i]) { case ARG_TYPE_VOID: { @@ -323,7 +324,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.z = (bool)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE: { jclass bclass = jni_find_class(env, "java/lang/Byte"); @@ -332,7 +332,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.b = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR: { jclass bclass = jni_find_class(env, "java/lang/Character"); @@ -341,7 +340,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.c = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_SHORT: { jclass bclass = jni_find_class(env, "java/lang/Short"); @@ -350,7 +348,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.s = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_INT: { jclass bclass = jni_find_class(env, "java/lang/Integer"); @@ -359,7 +356,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.i = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_LONG: { jclass bclass = jni_find_class(env, "java/lang/Long"); @@ -368,7 +364,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.j = (int64_t)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT: { jclass bclass = jni_find_class(env, "java/lang/Float"); @@ -377,7 +372,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.f = (float)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE: { jclass bclass = jni_find_class(env, "java/lang/Double"); @@ -386,23 +380,20 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, val.d = (double)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; - to_free.push_back(obj); } break; case ARG_TYPE_STRING: case ARG_TYPE_CHARSEQUENCE: { String s = *p_args[i]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); argv[i].l = jStr; - to_free.push_back(jStr); } break; case ARG_TYPE_CALLABLE: { jobject jcallable = callable_to_jcallable(env, *p_args[i]); argv[i].l = jcallable; - to_free.push_back(jcallable); } break; case ARG_TYPE_CLASS: { if (p_args[i]->get_type() == Variant::DICTIONARY) { - argv[i].l = _variant_to_jvalue(env, Variant::DICTIONARY, p_args[i]).obj; + argv[i].l = _variant_to_jvalue(env, Variant::DICTIONARY, p_args[i]).l; } else { Ref jo = *p_args[i]; if (jo.is_valid()) { @@ -420,8 +411,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, env->SetBooleanArrayRegion(a, j, 1, &val); } argv[i].l = a; - to_free.push_back(a); - } break; case ARG_ARRAY_BIT | ARG_TYPE_BYTE: { jbyteArray a = nullptr; @@ -440,8 +429,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); - } break; case ARG_ARRAY_BIT | ARG_TYPE_CHAR: { jcharArray a = nullptr; @@ -462,7 +449,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); } break; case ARG_ARRAY_BIT | ARG_TYPE_SHORT: { @@ -485,7 +471,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); } break; case ARG_ARRAY_BIT | ARG_TYPE_INT: { @@ -505,7 +490,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); } break; case ARG_ARRAY_BIT | ARG_TYPE_LONG: { jlongArray a = nullptr; @@ -524,8 +508,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); - } break; case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: { jfloatArray a = nullptr; @@ -544,8 +526,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); - } break; case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: { jdoubleArray a = nullptr; @@ -564,8 +544,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = a; - to_free.push_back(a); - } break; case ARG_ARRAY_BIT | ARG_TYPE_STRING: case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: { @@ -578,7 +556,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, String s = arr[j]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); env->SetObjectArrayElement(a, j, jStr); - to_free.push_back(jStr); } } else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) { PackedStringArray arr = *p_args[i]; @@ -587,12 +564,10 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, String s = arr[j]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); env->SetObjectArrayElement(a, j, jStr); - to_free.push_back(jStr); } } argv[i].l = a; - to_free.push_back(a); } break; case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: { Array arr = *p_args[i]; @@ -601,11 +576,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, Variant callable = arr[j]; jobject jcallable = callable_to_jcallable(env, callable); env->SetObjectArrayElement(jarr, j, jcallable); - to_free.push_back(jcallable); } argv[i].l = jarr; - to_free.push_back(jarr); } break; case ARG_ARRAY_BIT | ARG_TYPE_CLASS: { String cn = method->param_sigs[i].operator String(); @@ -622,7 +595,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } argv[i].l = jarr; - to_free.push_back(jarr); } } break; } @@ -720,30 +692,25 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; success = false; } - env->DeleteLocalRef(obj); } } break; } - for (jobject &E : to_free) { - env->DeleteLocalRef(E); - } - jobject exception = env->ExceptionOccurred(); if (exception) { env->ExceptionClear(); jclass java_class = env->GetObjectClass(exception); Ref java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class); - env->DeleteLocalRef(java_class); JavaClassWrapper::singleton->exception.instantiate(java_class_wrapped, exception); - env->DeleteLocalRef(exception); } else { JavaClassWrapper::singleton->exception.unref(); } + env->PopLocalFrame(nullptr); + return success; } @@ -1522,22 +1489,16 @@ Ref JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_p java_class->_class = (jclass)env->NewGlobalRef(bclass); class_cache[class_name_dots] = java_class; - LocalVector methods_and_constructors; int constructor_count = env->GetArrayLength(constructors); int method_count = env->GetArrayLength(methods); - methods_and_constructors.resize(method_count + constructor_count); - for (int i = 0; i < constructor_count; i++) { - methods_and_constructors[i] = env->GetObjectArrayElement(constructors, i); - } - for (int i = 0; i < method_count; i++) { - methods_and_constructors[constructor_count + i] = env->GetObjectArrayElement(methods, i); - } - - for (int i = 0; i < (int)methods_and_constructors.size(); i++) { - jobject obj = methods_and_constructors[i]; - ERR_CONTINUE(!obj); + int methods_and_constructors_count = method_count + constructor_count; + for (int i = 0; i < methods_and_constructors_count; i++) { bool is_constructor = i < constructor_count; + jobject obj = is_constructor + ? env->GetObjectArrayElement(constructors, i) + : env->GetObjectArrayElement(methods, i - constructor_count); + ERR_CONTINUE(!obj); String str_method; if (is_constructor) { @@ -1671,9 +1632,9 @@ Ref JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_p continue; } - ERR_CONTINUE(!mi.method); - - java_class->methods[str_method].push_back(mi); + if (mi.method) { + java_class->methods[str_method].push_back(mi); + } } env->DeleteLocalRef(obj); @@ -1720,6 +1681,7 @@ Ref JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_p } env->DeleteLocalRef(fields); + env->DeleteLocalRef(bclass); return java_class; } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index c7a549f1a85..1643c9b329d 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -514,7 +514,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setEditorSetting(JNIE } JNIEXPORT jobject JNICALL Java_org_godotengine_godot_GodotLib_getEditorProjectMetadata(JNIEnv *env, jclass clazz, jstring p_section, jstring p_key, jobject p_default_value) { - jvalret result; + jvalue result; #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton() != nullptr) { @@ -528,7 +528,7 @@ JNIEXPORT jobject JNICALL Java_org_godotengine_godot_GodotLib_getEditorProjectMe WARN_PRINT("Access to the Editor Settings Project Metadata is only available on Editor builds"); #endif - return result.obj; + return result.l; } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setEditorProjectMetadata(JNIEnv *env, jclass clazz, jstring p_section, jstring p_key, jobject p_data) { diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp index 2bf1add4860..1753c7388a7 100644 --- a/platform/android/jni_utils.cpp +++ b/platform/android/jni_utils.cpp @@ -86,15 +86,16 @@ String charsequence_to_string(JNIEnv *p_env, jobject p_charsequence) { return result; } -jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject, int p_depth) { - jvalret v; +jvalue _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject, int p_depth) { + jvalue value; if (p_depth > Variant::MAX_RECURSION_DEPTH) { ERR_PRINT("Variant is too deep! Bailing."); - v.val.i = 0; - return v; + value.i = 0; + return value; } + env->PushLocalFrame(2); switch (p_type) { case Variant::BOOL: { if (force_jobject) { @@ -103,11 +104,10 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jvalue val; val.z = (bool)(*p_arg); jobject obj = env->NewObjectA(bclass, ctor, &val); - v.val.l = obj; - v.obj = obj; + value.l = obj; env->DeleteLocalRef(bclass); } else { - v.val.z = *p_arg; + value.z = *p_arg; } } break; case Variant::INT: { @@ -117,12 +117,11 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jvalue val; val.i = (int)(*p_arg); jobject obj = env->NewObjectA(bclass, ctor, &val); - v.val.l = obj; - v.obj = obj; + value.l = obj; env->DeleteLocalRef(bclass); } else { - v.val.i = *p_arg; + value.i = *p_arg; } } break; case Variant::FLOAT: { @@ -132,19 +131,17 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jvalue val; val.d = (double)(*p_arg); jobject obj = env->NewObjectA(bclass, ctor, &val); - v.val.l = obj; - v.obj = obj; + value.l = obj; env->DeleteLocalRef(bclass); } else { - v.val.f = *p_arg; + value.f = *p_arg; } } break; case Variant::STRING: { String s = *p_arg; jstring jStr = env->NewStringUTF(s.utf8().get_data()); - v.val.l = jStr; - v.obj = jStr; + value.l = jStr; } break; case Variant::PACKED_STRING_ARRAY: { Vector sarray = *p_arg; @@ -155,15 +152,13 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a env->SetObjectArrayElement(arr, j, str); env->DeleteLocalRef(str); } - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::CALLABLE: { jobject jcallable = callable_to_jcallable(env, *p_arg); - v.val.l = jcallable; - v.obj = jcallable; + value.l = jcallable; } break; case Variant::DICTIONARY: { @@ -191,10 +186,10 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a for (int j = 0; j < keys.size(); j++) { Variant var = dict[keys[j]]; - jvalret valret = _variant_to_jvalue(env, var.get_type(), &var, true, p_depth + 1); - env->SetObjectArrayElement(jvalues, j, valret.val.l); - if (valret.obj) { - env->DeleteLocalRef(valret.obj); + jvalue valret = _variant_to_jvalue(env, var.get_type(), &var, true, p_depth + 1); + env->SetObjectArrayElement(jvalues, j, valret.l); + if (valret.l) { + env->DeleteLocalRef(valret.l); } } @@ -204,8 +199,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a env->DeleteLocalRef(jvalues); env->DeleteLocalRef(dclass); - v.val.l = jdict; - v.obj = jdict; + value.l = jdict; } break; case Variant::ARRAY: { @@ -214,14 +208,13 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a for (int j = 0; j < array.size(); j++) { Variant var = array[j]; - jvalret valret = _variant_to_jvalue(env, var.get_type(), &var, true, p_depth + 1); - env->SetObjectArrayElement(arr, j, valret.val.l); - if (valret.obj) { - env->DeleteLocalRef(valret.obj); + jvalue valret = _variant_to_jvalue(env, var.get_type(), &var, true, p_depth + 1); + env->SetObjectArrayElement(arr, j, valret.l); + if (valret.l) { + env->DeleteLocalRef(valret.l); } } - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::PACKED_INT32_ARRAY: { @@ -229,8 +222,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jintArray arr = env->NewIntArray(array.size()); const int *r = array.ptr(); env->SetIntArrayRegion(arr, 0, array.size(), r); - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::PACKED_INT64_ARRAY: { @@ -238,8 +230,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jlongArray arr = env->NewLongArray(array.size()); const int64_t *r = array.ptr(); env->SetLongArrayRegion(arr, 0, array.size(), r); - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::PACKED_BYTE_ARRAY: { @@ -247,8 +238,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jbyteArray arr = env->NewByteArray(array.size()); const uint8_t *r = array.ptr(); env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast(r)); - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::PACKED_FLOAT32_ARRAY: { @@ -256,8 +246,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jfloatArray arr = env->NewFloatArray(array.size()); const float *r = array.ptr(); env->SetFloatArrayRegion(arr, 0, array.size(), r); - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::PACKED_FLOAT64_ARRAY: { @@ -265,26 +254,25 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a jdoubleArray arr = env->NewDoubleArray(array.size()); const double *r = array.ptr(); env->SetDoubleArrayRegion(arr, 0, array.size(), r); - v.val.l = arr; - v.obj = arr; + value.l = arr; } break; case Variant::OBJECT: { Ref generic_object = *p_arg; if (generic_object.is_valid()) { jobject obj = env->NewLocalRef(generic_object->get_instance()); - v.val.l = obj; - v.obj = obj; + value.l = obj; } else { - v.val.i = 0; + value.i = 0; } } break; default: { - v.val.i = 0; + value.i = 0; } break; } - return v; + value.l = env->PopLocalFrame(value.l); + return value; } String _get_class_name(JNIEnv *env, jclass cls, bool *array) { @@ -299,6 +287,7 @@ String _get_class_name(JNIEnv *env, jclass cls, bool *array) { } String name = jstring_to_string(clsName, env); env->DeleteLocalRef(clsName); + env->DeleteLocalRef(cclass); return name; } @@ -360,6 +349,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj, int p_depth) { jclass nclass = jni_find_class(env, "java/lang/Number"); jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J"); jlong ret = env->CallLongMethod(obj, longValue); + env->DeleteLocalRef(nclass); return ret; } @@ -400,6 +390,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj, int p_depth) { jclass nclass = jni_find_class(env, "java/lang/Number"); jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D"); double ret = env->CallDoubleMethod(obj, doubleValue); + env->DeleteLocalRef(nclass); return ret; } @@ -558,6 +549,11 @@ void setup_android_class_loader() { android_class_loader = nullptr; ERR_FAIL_MSG("Failed to find method ID for ClassLoader::loadClass."); } + + env->DeleteLocalRef(class_loader_class); + env->DeleteLocalRef(class_loader); + env->DeleteLocalRef(class_class); + env->DeleteLocalRef(known_class); } void cleanup_android_class_loader() { @@ -577,17 +573,22 @@ jclass jni_find_class(JNIEnv *p_env, const char *p_class_name) { ERR_FAIL_NULL_V(p_env, nullptr); ERR_FAIL_NULL_V(p_class_name, nullptr); + jobject class_object = nullptr; if (!android_class_loader || !load_class_method) { ERR_PRINT("Android ClassLoader is not initialized. Falling back to FindClass."); - return p_env->FindClass(p_class_name); + class_object = p_env->FindClass(p_class_name); + } else { + jstring java_class_name = p_env->NewStringUTF(p_class_name); + class_object = p_env->CallObjectMethod( + android_class_loader, + load_class_method, + java_class_name); + p_env->DeleteLocalRef(java_class_name); + } + if (p_env->ExceptionCheck()) { + p_env->ExceptionDescribe(); + p_env->ExceptionClear(); } - - jstring java_class_name = p_env->NewStringUTF(p_class_name); - jobject class_object = p_env->CallObjectMethod( - android_class_loader, - load_class_method, - java_class_name); - p_env->DeleteLocalRef(java_class_name); ERR_FAIL_NULL_V_MSG(class_object, nullptr, vformat("Failed to find Java class: \"%s\".", p_class_name)); return static_cast(class_object); } diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h index 803efe09288..bbb8c1d2dd5 100644 --- a/platform/android/jni_utils.h +++ b/platform/android/jni_utils.h @@ -38,13 +38,7 @@ #include -struct jvalret { - jobject obj; - jvalue val; - jvalret() { obj = nullptr; } -}; - -jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false, int p_depth = 0); +jvalue _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false, int p_depth = 0); String _get_class_name(JNIEnv *env, jclass cls, bool *array); diff --git a/platform/android/variant/callable_jni.cpp b/platform/android/variant/callable_jni.cpp index cab27fbebd9..646fd8e30a8 100644 --- a/platform/android/variant/callable_jni.cpp +++ b/platform/android/variant/callable_jni.cpp @@ -91,8 +91,8 @@ JNIEXPORT jobject JNICALL Java_org_godotengine_godot_variant_Callable_nativeCall Callable::CallError err; Variant result; callable.callp(argptrs, count, result, err); - jvalret jresult = _variant_to_jvalue(p_env, result.get_type(), &result, true); - ret = jresult.obj; + jvalue jresult = _variant_to_jvalue(p_env, result.get_type(), &result, true); + ret = jresult.l; } // Manually invoke the destructor to decrease the reference counts for the variant arguments. @@ -107,8 +107,8 @@ JNIEXPORT jobject JNICALL Java_org_godotengine_godot_variant_Callable_nativeCall Callable callable = _generate_callable(p_env, p_object_id, p_method_name, p_parameters); if (callable.is_valid()) { Variant result = callable.call(); - jvalret jresult = _variant_to_jvalue(p_env, result.get_type(), &result, true); - return jresult.obj; + jvalue jresult = _variant_to_jvalue(p_env, result.get_type(), &result, true); + return jresult.l; } else { return nullptr; }