mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
Merge pull request #107075 from m4gr3d/implement_javaclasswrapper_has_method
[Android] `JavaClassWrapper` bug fixes
This commit is contained in:
commit
3a16864c33
12 changed files with 210 additions and 149 deletions
|
@ -9,4 +9,13 @@
|
||||||
<tutorials>
|
<tutorials>
|
||||||
<link title="Creating Android plugins">$DOCS_URL/tutorials/platform/android/android_plugin.html#doc-android-plugin</link>
|
<link title="Creating Android plugins">$DOCS_URL/tutorials/platform/android/android_plugin.html#doc-android-plugin</link>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="has_java_method" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="method" type="StringName" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the given [param method] name exists in the JNISingleton's Java methods.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
</class>
|
</class>
|
||||||
|
|
|
@ -29,5 +29,12 @@
|
||||||
Returns a [JavaClass] representing the Java parent class of this class.
|
Returns a [JavaClass] representing the Java parent class of this class.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="has_java_method" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="method" type="StringName" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the given [param method] name exists in the object's Java methods.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
</class>
|
</class>
|
||||||
|
|
|
@ -17,5 +17,12 @@
|
||||||
Returns the [JavaClass] that this object is an instance of.
|
Returns the [JavaClass] that this object is an instance of.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="has_java_method" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="method" type="StringName" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the given [param method] name exists in the object's Java methods.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
</class>
|
</class>
|
||||||
|
|
|
@ -62,10 +62,12 @@ void JavaClass::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_java_class_name"), &JavaClass::get_java_class_name);
|
ClassDB::bind_method(D_METHOD("get_java_class_name"), &JavaClass::get_java_class_name);
|
||||||
ClassDB::bind_method(D_METHOD("get_java_method_list"), &JavaClass::get_java_method_list);
|
ClassDB::bind_method(D_METHOD("get_java_method_list"), &JavaClass::get_java_method_list);
|
||||||
ClassDB::bind_method(D_METHOD("get_java_parent_class"), &JavaClass::get_java_parent_class);
|
ClassDB::bind_method(D_METHOD("get_java_parent_class"), &JavaClass::get_java_parent_class);
|
||||||
|
ClassDB::bind_method(D_METHOD("has_java_method", "method"), &JavaClass::has_java_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaObject::_bind_methods() {
|
void JavaObject::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_java_class"), &JavaObject::get_java_class);
|
ClassDB::bind_method(D_METHOD("get_java_class"), &JavaObject::get_java_class);
|
||||||
|
ClassDB::bind_method(D_METHOD("has_java_method", "method"), &JavaObject::has_java_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaClassWrapper::_bind_methods() {
|
void JavaClassWrapper::_bind_methods() {
|
||||||
|
@ -94,6 +96,10 @@ Ref<JavaClass> JavaClass::get_java_parent_class() const {
|
||||||
return Ref<JavaClass>();
|
return Ref<JavaClass>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JavaClass::has_java_method(const StringName &) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
JavaClass::JavaClass() {
|
JavaClass::JavaClass() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +114,10 @@ Ref<JavaClass> JavaObject::get_java_class() const {
|
||||||
return Ref<JavaClass>();
|
return Ref<JavaClass>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JavaObject::has_java_method(const StringName &) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
|
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
|
||||||
|
|
||||||
Ref<JavaClass> JavaClassWrapper::_wrap(const String &, bool) {
|
Ref<JavaClass> JavaClassWrapper::_wrap(const String &, bool) {
|
||||||
|
|
|
@ -200,6 +200,7 @@ public:
|
||||||
String get_java_class_name() const;
|
String get_java_class_name() const;
|
||||||
TypedArray<Dictionary> get_java_method_list() const;
|
TypedArray<Dictionary> get_java_method_list() const;
|
||||||
Ref<JavaClass> get_java_parent_class() const;
|
Ref<JavaClass> get_java_parent_class() const;
|
||||||
|
bool has_java_method(const StringName &p_method) const;
|
||||||
|
|
||||||
#ifdef ANDROID_ENABLED
|
#ifdef ANDROID_ENABLED
|
||||||
virtual String to_string() override;
|
virtual String to_string() override;
|
||||||
|
@ -226,6 +227,7 @@ public:
|
||||||
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
|
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
|
||||||
|
|
||||||
Ref<JavaClass> get_java_class() const;
|
Ref<JavaClass> get_java_class() const;
|
||||||
|
bool has_java_method(const StringName &p_method) const;
|
||||||
|
|
||||||
#ifdef ANDROID_ENABLED
|
#ifdef ANDROID_ENABLED
|
||||||
virtual String to_string() override;
|
virtual String to_string() override;
|
||||||
|
@ -244,7 +246,7 @@ class JavaClassWrapper : public Object {
|
||||||
#ifdef ANDROID_ENABLED
|
#ifdef ANDROID_ENABLED
|
||||||
RBMap<String, Ref<JavaClass>> class_cache;
|
RBMap<String, Ref<JavaClass>> class_cache;
|
||||||
friend class JavaClass;
|
friend class JavaClass;
|
||||||
jmethodID Class_getDeclaredConstructors;
|
jmethodID Class_getConstructors;
|
||||||
jmethodID Class_getDeclaredMethods;
|
jmethodID Class_getDeclaredMethods;
|
||||||
jmethodID Class_getFields;
|
jmethodID Class_getFields;
|
||||||
jmethodID Class_getName;
|
jmethodID Class_getName;
|
||||||
|
@ -272,7 +274,7 @@ class JavaClassWrapper : public Object {
|
||||||
|
|
||||||
Ref<JavaObject> exception;
|
Ref<JavaObject> exception;
|
||||||
|
|
||||||
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_private_methods_access);
|
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_non_public_methods_access);
|
||||||
|
|
||||||
static JavaClassWrapper *singleton;
|
static JavaClassWrapper *singleton;
|
||||||
|
|
||||||
|
|
|
@ -46,35 +46,60 @@ class JNISingleton : public Object {
|
||||||
RBMap<StringName, MethodData> method_map;
|
RBMap<StringName, MethodData> method_map;
|
||||||
Ref<JavaObject> wrapped_object;
|
Ref<JavaObject> wrapped_object;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("has_java_method", "method"), &JNISingleton::has_java_method);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
||||||
if (wrapped_object.is_valid()) {
|
// Godot methods take precedence.
|
||||||
RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
|
Variant ret = Object::callp(p_method, p_args, p_argcount, r_error);
|
||||||
|
if (r_error.error == Callable::CallError::CALL_OK) {
|
||||||
// Check the method we're looking for is in the JNISingleton map and that
|
return ret;
|
||||||
// the arguments match.
|
|
||||||
bool call_error = !E || E->get().argtypes.size() != p_argcount;
|
|
||||||
if (!call_error) {
|
|
||||||
for (int i = 0; i < p_argcount; i++) {
|
|
||||||
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
|
|
||||||
call_error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!call_error) {
|
|
||||||
return wrapped_object->callp(p_method, p_args, p_argcount, r_error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object::callp(p_method, p_args, p_argcount, r_error);
|
// Check the method we're looking for is in the JNISingleton map.
|
||||||
|
RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
|
||||||
|
if (E) {
|
||||||
|
if (wrapped_object.is_valid()) {
|
||||||
|
// Check that the arguments match.
|
||||||
|
int method_arg_count = E->get().argtypes.size();
|
||||||
|
if (p_argcount < method_arg_count) {
|
||||||
|
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||||
|
} else if (p_argcount > method_arg_count) {
|
||||||
|
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||||
|
} else {
|
||||||
|
// Check the arguments are valid.
|
||||||
|
bool arguments_valid = true;
|
||||||
|
for (int i = 0; i < p_argcount; i++) {
|
||||||
|
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
|
||||||
|
arguments_valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_valid) {
|
||||||
|
return wrapped_object->callp(p_method, p_args, p_argcount, r_error);
|
||||||
|
} else {
|
||||||
|
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<JavaObject> get_wrapped_object() const {
|
Ref<JavaObject> get_wrapped_object() const {
|
||||||
return wrapped_object;
|
return wrapped_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_java_method(const StringName &p_method) const {
|
||||||
|
return method_map.has(p_method);
|
||||||
|
}
|
||||||
|
|
||||||
void add_method(const StringName &p_name, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
|
void add_method(const StringName &p_name, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
|
||||||
MethodData md;
|
MethodData md;
|
||||||
md.argtypes = p_args;
|
md.argtypes = p_args;
|
||||||
|
|
|
@ -51,7 +51,7 @@ class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) {
|
||||||
* Provides access to the host [android.app.Activity] to GDScript
|
* Provides access to the host [android.app.Activity] to GDScript
|
||||||
*/
|
*/
|
||||||
@UsedByGodot
|
@UsedByGodot
|
||||||
override fun getActivity() = super.getActivity()
|
public override fun getActivity() = super.getActivity()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility method used to create [Runnable] from Godot [Callable].
|
* Utility method used to create [Runnable] from Godot [Callable].
|
||||||
|
|
|
@ -36,12 +36,15 @@
|
||||||
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
|
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
|
||||||
HashMap<StringName, List<MethodInfo>>::Iterator M = methods.find(p_method);
|
HashMap<StringName, List<MethodInfo>>::Iterator M = methods.find(p_method);
|
||||||
if (!M) {
|
if (!M) {
|
||||||
|
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -283,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: {
|
||||||
|
@ -322,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");
|
||||||
|
@ -331,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");
|
||||||
|
@ -340,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");
|
||||||
|
@ -349,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");
|
||||||
|
@ -358,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");
|
||||||
|
@ -367,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");
|
||||||
|
@ -376,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");
|
||||||
|
@ -385,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()) {
|
||||||
|
@ -419,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;
|
||||||
|
@ -439,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;
|
||||||
|
@ -461,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: {
|
||||||
|
@ -484,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: {
|
||||||
|
@ -504,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;
|
||||||
|
@ -523,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;
|
||||||
|
@ -543,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;
|
||||||
|
@ -563,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: {
|
||||||
|
@ -577,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];
|
||||||
|
@ -586,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];
|
||||||
|
@ -600,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();
|
||||||
|
@ -621,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;
|
||||||
}
|
}
|
||||||
|
@ -719,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +724,11 @@ bool JavaClass::_get(const StringName &p_name, Variant &r_ret) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||||
Variant ret;
|
// Godot methods take precedence.
|
||||||
|
Variant ret = RefCounted::callp(p_method, p_args, p_argcount, r_error);
|
||||||
|
if (r_error.error == Callable::CallError::CALL_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
String method = (p_method == java_constructor_name) ? "<init>" : p_method;
|
String method = (p_method == java_constructor_name) ? "<init>" : p_method;
|
||||||
bool found = _call_method(nullptr, method, p_args, p_argcount, r_error, ret);
|
bool found = _call_method(nullptr, method, p_args, p_argcount, r_error, ret);
|
||||||
|
@ -764,7 +736,7 @@ Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RefCounted::callp(p_method, p_args, p_argcount, r_error);
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
String JavaClass::get_java_class_name() const {
|
String JavaClass::get_java_class_name() const {
|
||||||
|
@ -858,6 +830,11 @@ String JavaClass::to_string() {
|
||||||
return "<JavaClass:" + java_class_name + ">";
|
return "<JavaClass:" + java_class_name + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JavaClass::has_java_method(const StringName &p_method) const {
|
||||||
|
String method = (p_method == java_constructor_name) ? "<init>" : p_method;
|
||||||
|
return methods.has(method);
|
||||||
|
}
|
||||||
|
|
||||||
JavaClass::JavaClass() {
|
JavaClass::JavaClass() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,10 +850,15 @@ JavaClass::~JavaClass() {
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
||||||
Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||||
|
// Godot methods take precedence.
|
||||||
|
Variant ret = RefCounted::callp(p_method, p_args, p_argcount, r_error);
|
||||||
|
if (r_error.error == Callable::CallError::CALL_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (instance) {
|
if (instance) {
|
||||||
Ref<JavaClass> c = base_class;
|
Ref<JavaClass> c = base_class;
|
||||||
while (c.is_valid()) {
|
while (c.is_valid()) {
|
||||||
Variant ret;
|
|
||||||
bool found = c->_call_method(this, p_method, p_args, p_argcount, r_error, ret);
|
bool found = c->_call_method(this, p_method, p_args, p_argcount, r_error, ret);
|
||||||
if (found) {
|
if (found) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -885,7 +867,7 @@ Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RefCounted::callp(p_method, p_args, p_argcount, r_error);
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<JavaClass> JavaObject::get_java_class() const {
|
Ref<JavaClass> JavaObject::get_java_class() const {
|
||||||
|
@ -899,6 +881,19 @@ String JavaObject::to_string() {
|
||||||
return RefCounted::to_string();
|
return RefCounted::to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JavaObject::has_java_method(const StringName &p_method) const {
|
||||||
|
if (instance) {
|
||||||
|
Ref<JavaClass> c = base_class;
|
||||||
|
while (c.is_valid()) {
|
||||||
|
if (c->has_java_method(p_method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
c = c->get_java_parent_class();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
JavaObject::JavaObject() {
|
JavaObject::JavaObject() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1461,7 +1456,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_private_methods_access) {
|
Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_public_methods_access) {
|
||||||
String class_name_dots = p_class.replace_char('/', '.');
|
String class_name_dots = p_class.replace_char('/', '.');
|
||||||
if (class_cache.has(class_name_dots)) {
|
if (class_cache.has(class_name_dots)) {
|
||||||
return class_cache[class_name_dots];
|
return class_cache[class_name_dots];
|
||||||
|
@ -1473,10 +1468,18 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
|
||||||
jclass bclass = jni_find_class(env, class_name_dots.replace_char('.', '/').utf8().get_data());
|
jclass bclass = jni_find_class(env, class_name_dots.replace_char('.', '/').utf8().get_data());
|
||||||
ERR_FAIL_NULL_V_MSG(bclass, Ref<JavaClass>(), vformat("Java class '%s' not found.", p_class));
|
ERR_FAIL_NULL_V_MSG(bclass, Ref<JavaClass>(), vformat("Java class '%s' not found.", p_class));
|
||||||
|
|
||||||
jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredConstructors);
|
jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getConstructors);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
ERR_FAIL_NULL_V(constructors, Ref<JavaClass>());
|
ERR_FAIL_NULL_V(constructors, Ref<JavaClass>());
|
||||||
|
|
||||||
jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredMethods);
|
jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredMethods);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
ERR_FAIL_NULL_V(methods, Ref<JavaClass>());
|
ERR_FAIL_NULL_V(methods, Ref<JavaClass>());
|
||||||
|
|
||||||
Ref<JavaClass> java_class = memnew(JavaClass);
|
Ref<JavaClass> java_class = memnew(JavaClass);
|
||||||
|
@ -1486,22 +1489,16 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
|
||||||
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) {
|
||||||
|
@ -1515,8 +1512,9 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
|
||||||
Vector<String> params;
|
Vector<String> params;
|
||||||
|
|
||||||
jint mods = env->CallIntMethod(obj, is_constructor ? Constructor_getModifiers : Method_getModifiers);
|
jint mods = env->CallIntMethod(obj, is_constructor ? Constructor_getModifiers : Method_getModifiers);
|
||||||
|
bool is_public = (mods & 0x0001) != 0; // java.lang.reflect.Modifier.PUBLIC
|
||||||
|
|
||||||
if (!(mods & 0x0001) && (is_constructor || !p_allow_private_methods_access)) {
|
if (!is_public && (is_constructor || !p_allow_non_public_methods_access)) {
|
||||||
env->DeleteLocalRef(obj);
|
env->DeleteLocalRef(obj);
|
||||||
continue; //not public bye
|
continue; //not public bye
|
||||||
}
|
}
|
||||||
|
@ -1627,9 +1625,16 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
|
||||||
mi.method = env->GetMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
|
mi.method = env->GetMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_CONTINUE(!mi.method);
|
if (env->ExceptionCheck()) {
|
||||||
|
// Exceptions may be thrown when trying to access hidden methods; write the exception to the logs and continue.
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -1676,6 +1681,7 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
|
||||||
}
|
}
|
||||||
|
|
||||||
env->DeleteLocalRef(fields);
|
env->DeleteLocalRef(fields);
|
||||||
|
env->DeleteLocalRef(bclass);
|
||||||
|
|
||||||
return java_class;
|
return java_class;
|
||||||
}
|
}
|
||||||
|
@ -1700,7 +1706,7 @@ JavaClassWrapper::JavaClassWrapper() {
|
||||||
ERR_FAIL_NULL(env);
|
ERR_FAIL_NULL(env);
|
||||||
|
|
||||||
jclass bclass = jni_find_class(env, "java/lang/Class");
|
jclass bclass = jni_find_class(env, "java/lang/Class");
|
||||||
Class_getDeclaredConstructors = env->GetMethodID(bclass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;");
|
Class_getConstructors = env->GetMethodID(bclass, "getConstructors", "()[Ljava/lang/reflect/Constructor;");
|
||||||
Class_getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
|
Class_getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
|
||||||
Class_getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;");
|
Class_getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;");
|
||||||
Class_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
|
Class_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue