1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

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

This commit is contained in:
Fredia Huya-Kouadio
2025-08-16 22:09:20 -07:00
parent 0622cee189
commit 35fda7f857
5 changed files with 77 additions and 120 deletions

View File

@@ -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); if (mi.method) {
java_class->methods[str_method].push_back(mi);
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;
} }

View File

@@ -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) {

View File

@@ -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) { jvalue _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject, int p_depth) {
jvalret v; 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; value.i = 0;
return v; 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; value.l = obj;
v.obj = 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; value.l = obj;
v.obj = 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; value.l = obj;
v.obj = 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; value.l = jStr;
v.obj = 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; value.l = arr;
v.obj = 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; value.l = jcallable;
v.obj = 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); jvalue valret = _variant_to_jvalue(env, var.get_type(), &var, true, p_depth + 1);
env->SetObjectArrayElement(jvalues, j, valret.val.l); env->SetObjectArrayElement(jvalues, j, valret.l);
if (valret.obj) { if (valret.l) {
env->DeleteLocalRef(valret.obj); 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; value.l = jdict;
v.obj = 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); jvalue valret = _variant_to_jvalue(env, var.get_type(), &var, true, p_depth + 1);
env->SetObjectArrayElement(arr, j, valret.val.l); env->SetObjectArrayElement(arr, j, valret.l);
if (valret.obj) { if (valret.l) {
env->DeleteLocalRef(valret.obj); env->DeleteLocalRef(valret.l);
} }
} }
v.val.l = arr; value.l = arr;
v.obj = 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; value.l = arr;
v.obj = 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; value.l = arr;
v.obj = 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; value.l = arr;
v.obj = 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; value.l = arr;
v.obj = 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; value.l = arr;
v.obj = 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; value.l = obj;
v.obj = 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);
} }

View File

@@ -38,13 +38,7 @@
#include <jni.h> #include <jni.h>
struct jvalret { jvalue _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false, int p_depth = 0);
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);
String _get_class_name(JNIEnv *env, jclass cls, bool *array); String _get_class_name(JNIEnv *env, jclass cls, bool *array);

View File

@@ -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); jvalue jresult = _variant_to_jvalue(p_env, result.get_type(), &result, true);
ret = jresult.obj; 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); jvalue jresult = _variant_to_jvalue(p_env, result.get_type(), &result, true);
return jresult.obj; return jresult.l;
} else { } else {
return nullptr; return nullptr;
} }