Additional fixes and improvements to JavaClassWrapper

- Fix crashing bug when invoking class constructor with parameters
- Add support for accessing class constants
- Add support for Godot Callable arguments. A Godot Callable can be wrapped by a Java Runnable to allow Java logic to run arbitrary Godot lambdas
- Automatically convert java.lang.CharSequence to Godot String as needed
- Code cleanup
This commit is contained in:
Fredia Huya-Kouadio 2024-11-20 10:10:17 -08:00
parent 6e2cf2aa7b
commit 23cea1b9d2
19 changed files with 526 additions and 126 deletions

View file

@ -30,7 +30,7 @@
#include "api/java_class_wrapper.h"
#include "string_android.h"
#include "jni_utils.h"
#include "thread_jandroid.h"
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
@ -96,11 +96,17 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
arg_expected = Variant::FLOAT;
}
} break;
case ARG_TYPE_STRING: {
case ARG_TYPE_STRING:
case ARG_TYPE_CHARSEQUENCE: {
if (!p_args[i]->is_string()) {
arg_expected = Variant::STRING;
}
} break;
case ARG_TYPE_CALLABLE: {
if (p_args[i]->get_type() != Variant::CALLABLE) {
arg_expected = Variant::CALLABLE;
}
} break;
case ARG_TYPE_CLASS: {
if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) {
arg_expected = Variant::OBJECT;
@ -265,12 +271,18 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
argv[i].l = obj;
to_free.push_back(obj);
} break;
case ARG_TYPE_STRING: {
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: {
Ref<JavaObject> jo = *p_args[i];
if (jo.is_valid()) {
@ -367,7 +379,8 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
to_free.push_back(a);
} break;
case ARG_ARRAY_BIT | ARG_TYPE_STRING: {
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
Array arr = *p_args[i];
jobjectArray a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
for (int j = 0; j < arr.size(); j++) {
@ -380,6 +393,19 @@ 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_CALLABLE: {
Array arr = *p_args[i];
jobjectArray jarr = env->NewObjectArray(arr.size(), env->FindClass("org/godotengine/godot/variant/Callable"), nullptr);
for (int j = 0; j < arr.size(); j++) {
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: {
argv[i].l = nullptr;
} break;
@ -463,7 +489,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
default: {
jobject obj;
if (method->_constructor) {
obj = env->NewObject(_class, method->method, argv);
obj = env->NewObjectA(_class, method->method, argv);
} else if (method->_static) {
obj = env->CallStaticObjectMethodA(_class, method->method, argv);
} else {
@ -491,6 +517,15 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
return success;
}
bool JavaClass::_get(const StringName &p_name, Variant &r_ret) const {
if (constant_map.has(p_name)) {
r_ret = constant_map[p_name];
return true;
}
return false;
}
Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
Variant ret;
@ -706,6 +741,12 @@ bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, St
} else if (str_type == "java.lang.String") {
t |= JavaClass::ARG_TYPE_STRING;
strsig += "Ljava/lang/String;";
} else if (str_type == "java.lang.CharSequence") {
t |= JavaClass::ARG_TYPE_CHARSEQUENCE;
strsig += "Ljava/lang/CharSequence;";
} else if (str_type == "org.godotengine.godot.variant.Callable") {
t |= JavaClass::ARG_TYPE_CALLABLE;
strsig += "Lorg/godotengine/godot/variant/Callable;";
} else if (str_type == "java.lang.Boolean") {
t |= JavaClass::ARG_TYPE_BOOLEAN | JavaClass::ARG_NUMBER_CLASS_BIT;
strsig += "Ljava/lang/Boolean;";
@ -793,6 +834,14 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
var = jstring_to_string((jstring)obj, env);
return true;
} break;
case ARG_TYPE_CHARSEQUENCE: {
var = charsequence_to_string(env, obj);
return true;
} break;
case ARG_TYPE_CALLABLE: {
var = jcallable_to_callable(env, obj);
return true;
} break;
case ARG_TYPE_CLASS: {
jclass java_class = env->GetObjectClass(obj);
Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
@ -1113,6 +1162,44 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
var = ret;
return true;
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(arr, i);
if (!o) {
ret.push_back(Variant());
} else {
String val = charsequence_to_string(env, o);
ret.push_back(val);
}
env->DeleteLocalRef(o);
}
var = ret;
return true;
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
Array ret;
jobjectArray jarr = (jobjectArray)obj;
int count = env->GetArrayLength(jarr);
for (int i = 0; i < count; i++) {
jobject o = env->GetObjectArrayElement(jarr, i);
if (!o) {
ret.push_back(Variant());
} else {
Callable callable = jcallable_to_callable(env, o);
ret.push_back(callable);
}
env->DeleteLocalRef(o);
}
var = ret;
return true;
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
} break;
}