You've already forked godot
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:
@@ -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<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->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<const signed char *>(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<JavaObject> 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<jclass>(class_object);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user