Browse Source

Fix JNI local reference table overflow when wrapping Java class with large method counts

Fredia Huya-Kouadio 1 month ago
parent
commit
35fda7f857

+ 15 - 53
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();
 	JNIEnv *env = get_jni_env();
 	ERR_FAIL_NULL_V(env, false);
 	ERR_FAIL_NULL_V(env, false);
 
 
+	env->PushLocalFrame(p_argcount);
+
 	MethodInfo *method = nullptr;
 	MethodInfo *method = nullptr;
 	for (MethodInfo &E : M->value) {
 	for (MethodInfo &E : M->value) {
 		if (!p_instance && !E._static && !E._constructor) {
 		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());
 		argv = (jvalue *)alloca(sizeof(jvalue) * method->param_types.size());
 	}
 	}
 
 
-	List<jobject> to_free;
 	for (int i = 0; i < method->param_types.size(); i++) {
 	for (int i = 0; i < method->param_types.size(); i++) {
 		switch (method->param_types[i]) {
 		switch (method->param_types[i]) {
 			case ARG_TYPE_VOID: {
 			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]);
 				val.z = (bool)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE: {
 				jclass bclass = jni_find_class(env, "java/lang/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]);
 				val.b = (int)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR: {
 				jclass bclass = jni_find_class(env, "java/lang/Character");
 				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]);
 				val.c = (int)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_SHORT: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_SHORT: {
 				jclass bclass = jni_find_class(env, "java/lang/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]);
 				val.s = (int)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_INT: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_INT: {
 				jclass bclass = jni_find_class(env, "java/lang/Integer");
 				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]);
 				val.i = (int)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_LONG: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_LONG: {
 				jclass bclass = jni_find_class(env, "java/lang/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]);
 				val.j = (int64_t)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT: {
 				jclass bclass = jni_find_class(env, "java/lang/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]);
 				val.f = (float)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE: {
 			case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE: {
 				jclass bclass = jni_find_class(env, "java/lang/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]);
 				val.d = (double)(*p_args[i]);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				argv[i].l = obj;
 				argv[i].l = obj;
-				to_free.push_back(obj);
 			} break;
 			} break;
 			case ARG_TYPE_STRING:
 			case ARG_TYPE_STRING:
 			case ARG_TYPE_CHARSEQUENCE: {
 			case ARG_TYPE_CHARSEQUENCE: {
 				String s = *p_args[i];
 				String s = *p_args[i];
 				jstring jStr = env->NewStringUTF(s.utf8().get_data());
 				jstring jStr = env->NewStringUTF(s.utf8().get_data());
 				argv[i].l = jStr;
 				argv[i].l = jStr;
-				to_free.push_back(jStr);
 			} break;
 			} break;
 			case ARG_TYPE_CALLABLE: {
 			case ARG_TYPE_CALLABLE: {
 				jobject jcallable = callable_to_jcallable(env, *p_args[i]);
 				jobject jcallable = callable_to_jcallable(env, *p_args[i]);
 				argv[i].l = jcallable;
 				argv[i].l = jcallable;
-				to_free.push_back(jcallable);
 			} break;
 			} break;
 			case ARG_TYPE_CLASS: {
 			case ARG_TYPE_CLASS: {
 				if (p_args[i]->get_type() == Variant::DICTIONARY) {
 				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 {
 				} else {
 					Ref<JavaObject> jo = *p_args[i];
 					Ref<JavaObject> jo = *p_args[i];
 					if (jo.is_valid()) {
 					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);
 					env->SetBooleanArrayRegion(a, j, 1, &val);
 				}
 				}
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
-
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
 			case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
 				jbyteArray a = nullptr;
 				jbyteArray a = nullptr;
@@ -440,8 +429,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				}
 				}
 
 
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
-
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
 			case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
 				jcharArray a = nullptr;
 				jcharArray a = nullptr;
@@ -462,7 +449,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				}
 				}
 
 
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
 
 
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
 			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;
 				argv[i].l = a;
-				to_free.push_back(a);
 
 
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_INT: {
 			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;
 				argv[i].l = a;
-				to_free.push_back(a);
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
 			case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
 				jlongArray a = nullptr;
 				jlongArray a = nullptr;
@@ -524,8 +508,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				}
 				}
 
 
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
-
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
 			case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
 				jfloatArray a = nullptr;
 				jfloatArray a = nullptr;
@@ -544,8 +526,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				}
 				}
 
 
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
-
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
 			case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
 				jdoubleArray a = nullptr;
 				jdoubleArray a = nullptr;
@@ -564,8 +544,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				}
 				}
 
 
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
-
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_STRING:
 			case ARG_ARRAY_BIT | ARG_TYPE_STRING:
 			case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
 			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];
 						String s = arr[j];
 						jstring jStr = env->NewStringUTF(s.utf8().get_data());
 						jstring jStr = env->NewStringUTF(s.utf8().get_data());
 						env->SetObjectArrayElement(a, j, jStr);
 						env->SetObjectArrayElement(a, j, jStr);
-						to_free.push_back(jStr);
 					}
 					}
 				} else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) {
 				} else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) {
 					PackedStringArray arr = *p_args[i];
 					PackedStringArray arr = *p_args[i];
@@ -587,12 +564,10 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 						String s = arr[j];
 						String s = arr[j];
 						jstring jStr = env->NewStringUTF(s.utf8().get_data());
 						jstring jStr = env->NewStringUTF(s.utf8().get_data());
 						env->SetObjectArrayElement(a, j, jStr);
 						env->SetObjectArrayElement(a, j, jStr);
-						to_free.push_back(jStr);
 					}
 					}
 				}
 				}
 
 
 				argv[i].l = a;
 				argv[i].l = a;
-				to_free.push_back(a);
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
 			case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
 				Array arr = *p_args[i];
 				Array arr = *p_args[i];
@@ -601,11 +576,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 					Variant callable = arr[j];
 					Variant callable = arr[j];
 					jobject jcallable = callable_to_jcallable(env, callable);
 					jobject jcallable = callable_to_jcallable(env, callable);
 					env->SetObjectArrayElement(jarr, j, jcallable);
 					env->SetObjectArrayElement(jarr, j, jcallable);
-					to_free.push_back(jcallable);
 				}
 				}
 
 
 				argv[i].l = jarr;
 				argv[i].l = jarr;
-				to_free.push_back(jarr);
 			} break;
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
 			case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
 				String cn = method->param_sigs[i].operator String();
 				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;
 					argv[i].l = jarr;
-					to_free.push_back(jarr);
 				}
 				}
 			} break;
 			} 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;
 					r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
 					success = false;
 					success = false;
 				}
 				}
-				env->DeleteLocalRef(obj);
 			}
 			}
 
 
 		} break;
 		} break;
 	}
 	}
 
 
-	for (jobject &E : to_free) {
-		env->DeleteLocalRef(E);
-	}
-
 	jobject exception = env->ExceptionOccurred();
 	jobject exception = env->ExceptionOccurred();
 	if (exception) {
 	if (exception) {
 		env->ExceptionClear();
 		env->ExceptionClear();
 
 
 		jclass java_class = env->GetObjectClass(exception);
 		jclass java_class = env->GetObjectClass(exception);
 		Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
 		Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
-		env->DeleteLocalRef(java_class);
 
 
 		JavaClassWrapper::singleton->exception.instantiate(java_class_wrapped, exception);
 		JavaClassWrapper::singleton->exception.instantiate(java_class_wrapped, exception);
-		env->DeleteLocalRef(exception);
 	} else {
 	} else {
 		JavaClassWrapper::singleton->exception.unref();
 		JavaClassWrapper::singleton->exception.unref();
 	}
 	}
 
 
+	env->PopLocalFrame(nullptr);
+
 	return success;
 	return success;
 }
 }
 
 
@@ -1522,22 +1489,16 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_p
 	java_class->_class = (jclass)env->NewGlobalRef(bclass);
 	java_class->_class = (jclass)env->NewGlobalRef(bclass);
 	class_cache[class_name_dots] = java_class;
 	class_cache[class_name_dots] = java_class;
 
 
-	LocalVector<jobject> methods_and_constructors;
 	int constructor_count = env->GetArrayLength(constructors);
 	int constructor_count = env->GetArrayLength(constructors);
 	int method_count = env->GetArrayLength(methods);
 	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;
 		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;
 		String str_method;
 		if (is_constructor) {
 		if (is_constructor) {
@@ -1671,9 +1632,9 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_p
 				continue;
 				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);
 		env->DeleteLocalRef(obj);
@@ -1720,6 +1681,7 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_p
 	}
 	}
 
 
 	env->DeleteLocalRef(fields);
 	env->DeleteLocalRef(fields);
+	env->DeleteLocalRef(bclass);
 
 
 	return java_class;
 	return java_class;
 }
 }

+ 2 - 2
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) {
 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
 #ifdef TOOLS_ENABLED
 	if (EditorSettings::get_singleton() != nullptr) {
 	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");
 	WARN_PRINT("Access to the Editor Settings Project Metadata is only available on Editor builds");
 #endif
 #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) {
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setEditorProjectMetadata(JNIEnv *env, jclass clazz, jstring p_section, jstring p_key, jobject p_data) {

+ 55 - 54
platform/android/jni_utils.cpp

@@ -86,15 +86,16 @@ String charsequence_to_string(JNIEnv *p_env, jobject p_charsequence) {
 	return result;
 	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) {
 	if (p_depth > Variant::MAX_RECURSION_DEPTH) {
 		ERR_PRINT("Variant is too deep! Bailing.");
 		ERR_PRINT("Variant is too deep! Bailing.");
-		v.val.i = 0;
-		return v;
+		value.i = 0;
+		return value;
 	}
 	}
 
 
+	env->PushLocalFrame(2);
 	switch (p_type) {
 	switch (p_type) {
 		case Variant::BOOL: {
 		case Variant::BOOL: {
 			if (force_jobject) {
 			if (force_jobject) {
@@ -103,11 +104,10 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
 				jvalue val;
 				jvalue val;
 				val.z = (bool)(*p_arg);
 				val.z = (bool)(*p_arg);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
-				v.val.l = obj;
-				v.obj = obj;
+				value.l = obj;
 				env->DeleteLocalRef(bclass);
 				env->DeleteLocalRef(bclass);
 			} else {
 			} else {
-				v.val.z = *p_arg;
+				value.z = *p_arg;
 			}
 			}
 		} break;
 		} break;
 		case Variant::INT: {
 		case Variant::INT: {
@@ -117,12 +117,11 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
 				jvalue val;
 				jvalue val;
 				val.i = (int)(*p_arg);
 				val.i = (int)(*p_arg);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
-				v.val.l = obj;
-				v.obj = obj;
+				value.l = obj;
 				env->DeleteLocalRef(bclass);
 				env->DeleteLocalRef(bclass);
 
 
 			} else {
 			} else {
-				v.val.i = *p_arg;
+				value.i = *p_arg;
 			}
 			}
 		} break;
 		} break;
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
@@ -132,19 +131,17 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
 				jvalue val;
 				jvalue val;
 				val.d = (double)(*p_arg);
 				val.d = (double)(*p_arg);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
 				jobject obj = env->NewObjectA(bclass, ctor, &val);
-				v.val.l = obj;
-				v.obj = obj;
+				value.l = obj;
 				env->DeleteLocalRef(bclass);
 				env->DeleteLocalRef(bclass);
 
 
 			} else {
 			} else {
-				v.val.f = *p_arg;
+				value.f = *p_arg;
 			}
 			}
 		} break;
 		} break;
 		case Variant::STRING: {
 		case Variant::STRING: {
 			String s = *p_arg;
 			String s = *p_arg;
 			jstring jStr = env->NewStringUTF(s.utf8().get_data());
 			jstring jStr = env->NewStringUTF(s.utf8().get_data());
-			v.val.l = jStr;
-			v.obj = jStr;
+			value.l = jStr;
 		} break;
 		} break;
 		case Variant::PACKED_STRING_ARRAY: {
 		case Variant::PACKED_STRING_ARRAY: {
 			Vector<String> sarray = *p_arg;
 			Vector<String> 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->SetObjectArrayElement(arr, j, str);
 				env->DeleteLocalRef(str);
 				env->DeleteLocalRef(str);
 			}
 			}
-			v.val.l = arr;
-			v.obj = arr;
+			value.l = arr;
 
 
 		} break;
 		} break;
 
 
 		case Variant::CALLABLE: {
 		case Variant::CALLABLE: {
 			jobject jcallable = callable_to_jcallable(env, *p_arg);
 			jobject jcallable = callable_to_jcallable(env, *p_arg);
-			v.val.l = jcallable;
-			v.obj = jcallable;
+			value.l = jcallable;
 		} break;
 		} break;
 
 
 		case Variant::DICTIONARY: {
 		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++) {
 			for (int j = 0; j < keys.size(); j++) {
 				Variant var = dict[keys[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(jvalues);
 			env->DeleteLocalRef(dclass);
 			env->DeleteLocalRef(dclass);
 
 
-			v.val.l = jdict;
-			v.obj = jdict;
+			value.l = jdict;
 		} break;
 		} break;
 
 
 		case Variant::ARRAY: {
 		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++) {
 			for (int j = 0; j < array.size(); j++) {
 				Variant var = array[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;
 		} break;
 
 
 		case Variant::PACKED_INT32_ARRAY: {
 		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());
 			jintArray arr = env->NewIntArray(array.size());
 			const int *r = array.ptr();
 			const int *r = array.ptr();
 			env->SetIntArrayRegion(arr, 0, array.size(), r);
 			env->SetIntArrayRegion(arr, 0, array.size(), r);
-			v.val.l = arr;
-			v.obj = arr;
+			value.l = arr;
 
 
 		} break;
 		} break;
 		case Variant::PACKED_INT64_ARRAY: {
 		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());
 			jlongArray arr = env->NewLongArray(array.size());
 			const int64_t *r = array.ptr();
 			const int64_t *r = array.ptr();
 			env->SetLongArrayRegion(arr, 0, array.size(), r);
 			env->SetLongArrayRegion(arr, 0, array.size(), r);
-			v.val.l = arr;
-			v.obj = arr;
+			value.l = arr;
 
 
 		} break;
 		} break;
 		case Variant::PACKED_BYTE_ARRAY: {
 		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());
 			jbyteArray arr = env->NewByteArray(array.size());
 			const uint8_t *r = array.ptr();
 			const uint8_t *r = array.ptr();
 			env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast<const signed char *>(r));
 			env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast<const signed char *>(r));
-			v.val.l = arr;
-			v.obj = arr;
+			value.l = arr;
 
 
 		} break;
 		} break;
 		case Variant::PACKED_FLOAT32_ARRAY: {
 		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());
 			jfloatArray arr = env->NewFloatArray(array.size());
 			const float *r = array.ptr();
 			const float *r = array.ptr();
 			env->SetFloatArrayRegion(arr, 0, array.size(), r);
 			env->SetFloatArrayRegion(arr, 0, array.size(), r);
-			v.val.l = arr;
-			v.obj = arr;
+			value.l = arr;
 
 
 		} break;
 		} break;
 		case Variant::PACKED_FLOAT64_ARRAY: {
 		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());
 			jdoubleArray arr = env->NewDoubleArray(array.size());
 			const double *r = array.ptr();
 			const double *r = array.ptr();
 			env->SetDoubleArrayRegion(arr, 0, array.size(), r);
 			env->SetDoubleArrayRegion(arr, 0, array.size(), r);
-			v.val.l = arr;
-			v.obj = arr;
+			value.l = arr;
 
 
 		} break;
 		} break;
 		case Variant::OBJECT: {
 		case Variant::OBJECT: {
 			Ref<JavaObject> generic_object = *p_arg;
 			Ref<JavaObject> generic_object = *p_arg;
 			if (generic_object.is_valid()) {
 			if (generic_object.is_valid()) {
 				jobject obj = env->NewLocalRef(generic_object->get_instance());
 				jobject obj = env->NewLocalRef(generic_object->get_instance());
-				v.val.l = obj;
-				v.obj = obj;
+				value.l = obj;
 			} else {
 			} else {
-				v.val.i = 0;
+				value.i = 0;
 			}
 			}
 		} break;
 		} break;
 
 
 		default: {
 		default: {
-			v.val.i = 0;
+			value.i = 0;
 		} break;
 		} break;
 	}
 	}
-	return v;
+	value.l = env->PopLocalFrame(value.l);
+	return value;
 }
 }
 
 
 String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
 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);
 	String name = jstring_to_string(clsName, env);
 	env->DeleteLocalRef(clsName);
 	env->DeleteLocalRef(clsName);
+	env->DeleteLocalRef(cclass);
 
 
 	return name;
 	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");
 		jclass nclass = jni_find_class(env, "java/lang/Number");
 		jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
 		jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
 		jlong ret = env->CallLongMethod(obj, longValue);
 		jlong ret = env->CallLongMethod(obj, longValue);
+		env->DeleteLocalRef(nclass);
 		return ret;
 		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");
 		jclass nclass = jni_find_class(env, "java/lang/Number");
 		jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
 		jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
 		double ret = env->CallDoubleMethod(obj, doubleValue);
 		double ret = env->CallDoubleMethod(obj, doubleValue);
+		env->DeleteLocalRef(nclass);
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -558,6 +549,11 @@ void setup_android_class_loader() {
 		android_class_loader = nullptr;
 		android_class_loader = nullptr;
 		ERR_FAIL_MSG("Failed to find method ID for ClassLoader::loadClass.");
 		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() {
 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_env, nullptr);
 	ERR_FAIL_NULL_V(p_class_name, nullptr);
 	ERR_FAIL_NULL_V(p_class_name, nullptr);
 
 
+	jobject class_object = nullptr;
 	if (!android_class_loader || !load_class_method) {
 	if (!android_class_loader || !load_class_method) {
 		ERR_PRINT("Android ClassLoader is not initialized. Falling back to FindClass.");
 		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));
 	ERR_FAIL_NULL_V_MSG(class_object, nullptr, vformat("Failed to find Java class: \"%s\".", p_class_name));
 	return static_cast<jclass>(class_object);
 	return static_cast<jclass>(class_object);
 }
 }

+ 1 - 7
platform/android/jni_utils.h

@@ -38,13 +38,7 @@
 
 
 #include <jni.h>
 #include <jni.h>
 
 
-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);
 String _get_class_name(JNIEnv *env, jclass cls, bool *array);
 
 

+ 4 - 4
platform/android/variant/callable_jni.cpp

@@ -91,8 +91,8 @@ JNIEXPORT jobject JNICALL Java_org_godotengine_godot_variant_Callable_nativeCall
 		Callable::CallError err;
 		Callable::CallError err;
 		Variant result;
 		Variant result;
 		callable.callp(argptrs, count, result, err);
 		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.
 	// 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);
 	Callable callable = _generate_callable(p_env, p_object_id, p_method_name, p_parameters);
 	if (callable.is_valid()) {
 	if (callable.is_valid()) {
 		Variant result = callable.call();
 		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 {
 	} else {
 		return nullptr;
 		return nullptr;
 	}
 	}