Skip to content

Commit 0622cee

Browse files
committed
Implement has_java_method(...) for JavaClassWrapper and JNISingleton
1 parent d7382aa commit 0622cee

File tree

8 files changed

+132
-28
lines changed

8 files changed

+132
-28
lines changed

doc/classes/JNISingleton.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,13 @@
99
<tutorials>
1010
<link title="Creating Android plugins">$DOCS_URL/tutorials/platform/android/android_plugin.html#doc-android-plugin</link>
1111
</tutorials>
12+
<methods>
13+
<method name="has_java_method" qualifiers="const">
14+
<return type="bool" />
15+
<param index="0" name="method" type="StringName" />
16+
<description>
17+
Returns [code]true[/code] if the given [param method] name exists in the JNISingleton's Java methods.
18+
</description>
19+
</method>
20+
</methods>
1221
</class>

doc/classes/JavaClass.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,12 @@
2929
Returns a [JavaClass] representing the Java parent class of this class.
3030
</description>
3131
</method>
32+
<method name="has_java_method" qualifiers="const">
33+
<return type="bool" />
34+
<param index="0" name="method" type="StringName" />
35+
<description>
36+
Returns [code]true[/code] if the given [param method] name exists in the object's Java methods.
37+
</description>
38+
</method>
3239
</methods>
3340
</class>

doc/classes/JavaObject.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,12 @@
1717
Returns the [JavaClass] that this object is an instance of.
1818
</description>
1919
</method>
20+
<method name="has_java_method" qualifiers="const">
21+
<return type="bool" />
22+
<param index="0" name="method" type="StringName" />
23+
<description>
24+
Returns [code]true[/code] if the given [param method] name exists in the object's Java methods.
25+
</description>
26+
</method>
2027
</methods>
2128
</class>

platform/android/api/api.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ void JavaClass::_bind_methods() {
6262
ClassDB::bind_method(D_METHOD("get_java_class_name"), &JavaClass::get_java_class_name);
6363
ClassDB::bind_method(D_METHOD("get_java_method_list"), &JavaClass::get_java_method_list);
6464
ClassDB::bind_method(D_METHOD("get_java_parent_class"), &JavaClass::get_java_parent_class);
65+
ClassDB::bind_method(D_METHOD("has_java_method", "method"), &JavaClass::has_java_method);
6566
}
6667

6768
void JavaObject::_bind_methods() {
6869
ClassDB::bind_method(D_METHOD("get_java_class"), &JavaObject::get_java_class);
70+
ClassDB::bind_method(D_METHOD("has_java_method", "method"), &JavaObject::has_java_method);
6971
}
7072

7173
void JavaClassWrapper::_bind_methods() {
@@ -94,6 +96,10 @@ Ref<JavaClass> JavaClass::get_java_parent_class() const {
9496
return Ref<JavaClass>();
9597
}
9698

99+
bool JavaClass::has_java_method(const StringName &) const {
100+
return false;
101+
}
102+
97103
JavaClass::JavaClass() {
98104
}
99105

@@ -108,6 +114,10 @@ Ref<JavaClass> JavaObject::get_java_class() const {
108114
return Ref<JavaClass>();
109115
}
110116

117+
bool JavaObject::has_java_method(const StringName &) const {
118+
return false;
119+
}
120+
111121
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
112122

113123
Ref<JavaClass> JavaClassWrapper::_wrap(const String &, bool) {

platform/android/api/java_class_wrapper.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ class JavaClass : public RefCounted {
200200
String get_java_class_name() const;
201201
TypedArray<Dictionary> get_java_method_list() const;
202202
Ref<JavaClass> get_java_parent_class() const;
203+
bool has_java_method(const StringName &p_method) const;
203204

204205
#ifdef ANDROID_ENABLED
205206
virtual String to_string() override;
@@ -226,6 +227,7 @@ class JavaObject : public RefCounted {
226227
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
227228

228229
Ref<JavaClass> get_java_class() const;
230+
bool has_java_method(const StringName &p_method) const;
229231

230232
#ifdef ANDROID_ENABLED
231233
virtual String to_string() override;
@@ -244,7 +246,7 @@ class JavaClassWrapper : public Object {
244246
#ifdef ANDROID_ENABLED
245247
RBMap<String, Ref<JavaClass>> class_cache;
246248
friend class JavaClass;
247-
jmethodID Class_getDeclaredConstructors;
249+
jmethodID Class_getConstructors;
248250
jmethodID Class_getDeclaredMethods;
249251
jmethodID Class_getFields;
250252
jmethodID Class_getName;
@@ -272,7 +274,7 @@ class JavaClassWrapper : public Object {
272274

273275
Ref<JavaObject> exception;
274276

275-
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_private_methods_access);
277+
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_non_public_methods_access);
276278

277279
static JavaClassWrapper *singleton;
278280

platform/android/api/jni_singleton.h

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,60 @@ class JNISingleton : public Object {
4646
RBMap<StringName, MethodData> method_map;
4747
Ref<JavaObject> wrapped_object;
4848

49+
protected:
50+
static void _bind_methods() {
51+
ClassDB::bind_method(D_METHOD("has_java_method", "method"), &JNISingleton::has_java_method);
52+
}
53+
4954
public:
5055
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
51-
if (wrapped_object.is_valid()) {
52-
RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
53-
54-
// Check the method we're looking for is in the JNISingleton map and that
55-
// the arguments match.
56-
bool call_error = !E || E->get().argtypes.size() != p_argcount;
57-
if (!call_error) {
58-
for (int i = 0; i < p_argcount; i++) {
59-
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
60-
call_error = true;
61-
break;
56+
// Godot methods take precedence.
57+
Variant ret = Object::callp(p_method, p_args, p_argcount, r_error);
58+
if (r_error.error == Callable::CallError::CALL_OK) {
59+
return ret;
60+
}
61+
62+
// Check the method we're looking for is in the JNISingleton map.
63+
RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
64+
if (E) {
65+
if (wrapped_object.is_valid()) {
66+
// Check that the arguments match.
67+
int method_arg_count = E->get().argtypes.size();
68+
if (p_argcount < method_arg_count) {
69+
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
70+
} else if (p_argcount > method_arg_count) {
71+
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
72+
} else {
73+
// Check the arguments are valid.
74+
bool arguments_valid = true;
75+
for (int i = 0; i < p_argcount; i++) {
76+
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
77+
arguments_valid = false;
78+
break;
79+
}
6280
}
63-
}
64-
}
6581

66-
if (!call_error) {
67-
return wrapped_object->callp(p_method, p_args, p_argcount, r_error);
82+
if (arguments_valid) {
83+
return wrapped_object->callp(p_method, p_args, p_argcount, r_error);
84+
} else {
85+
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
86+
}
87+
}
88+
} else {
89+
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
6890
}
6991
}
70-
71-
return Object::callp(p_method, p_args, p_argcount, r_error);
92+
return Variant();
7293
}
7394

7495
Ref<JavaObject> get_wrapped_object() const {
7596
return wrapped_object;
7697
}
7798

99+
bool has_java_method(const StringName &p_method) const {
100+
return method_map.has(p_method);
101+
}
102+
78103
void add_method(const StringName &p_name, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
79104
MethodData md;
80105
md.argtypes = p_args;

platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) {
5151
* Provides access to the host [android.app.Activity] to GDScript
5252
*/
5353
@UsedByGodot
54-
override fun getActivity() = super.getActivity()
54+
public override fun getActivity() = super.getActivity()
5555

5656
/**
5757
* Utility method used to create [Runnable] from Godot [Callable].

platform/android/java_class_wrapper.cpp

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
3737
HashMap<StringName, List<MethodInfo>>::Iterator M = methods.find(p_method);
3838
if (!M) {
39+
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
3940
return false;
4041
}
4142

@@ -756,15 +757,19 @@ bool JavaClass::_get(const StringName &p_name, Variant &r_ret) const {
756757
}
757758

758759
Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
759-
Variant ret;
760+
// Godot methods take precedence.
761+
Variant ret = RefCounted::callp(p_method, p_args, p_argcount, r_error);
762+
if (r_error.error == Callable::CallError::CALL_OK) {
763+
return ret;
764+
}
760765

761766
String method = (p_method == java_constructor_name) ? "<init>" : p_method;
762767
bool found = _call_method(nullptr, method, p_args, p_argcount, r_error, ret);
763768
if (found) {
764769
return ret;
765770
}
766771

767-
return RefCounted::callp(p_method, p_args, p_argcount, r_error);
772+
return Variant();
768773
}
769774

770775
String JavaClass::get_java_class_name() const {
@@ -858,6 +863,11 @@ String JavaClass::to_string() {
858863
return "<JavaClass:" + java_class_name + ">";
859864
}
860865

866+
bool JavaClass::has_java_method(const StringName &p_method) const {
867+
String method = (p_method == java_constructor_name) ? "<init>" : p_method;
868+
return methods.has(method);
869+
}
870+
861871
JavaClass::JavaClass() {
862872
}
863873

@@ -873,10 +883,15 @@ JavaClass::~JavaClass() {
873883
/////////////////////
874884

875885
Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
886+
// Godot methods take precedence.
887+
Variant ret = RefCounted::callp(p_method, p_args, p_argcount, r_error);
888+
if (r_error.error == Callable::CallError::CALL_OK) {
889+
return ret;
890+
}
891+
876892
if (instance) {
877893
Ref<JavaClass> c = base_class;
878894
while (c.is_valid()) {
879-
Variant ret;
880895
bool found = c->_call_method(this, p_method, p_args, p_argcount, r_error, ret);
881896
if (found) {
882897
return ret;
@@ -885,7 +900,7 @@ Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, in
885900
}
886901
}
887902

888-
return RefCounted::callp(p_method, p_args, p_argcount, r_error);
903+
return Variant();
889904
}
890905

891906
Ref<JavaClass> JavaObject::get_java_class() const {
@@ -899,6 +914,19 @@ String JavaObject::to_string() {
899914
return RefCounted::to_string();
900915
}
901916

917+
bool JavaObject::has_java_method(const StringName &p_method) const {
918+
if (instance) {
919+
Ref<JavaClass> c = base_class;
920+
while (c.is_valid()) {
921+
if (c->has_java_method(p_method)) {
922+
return true;
923+
}
924+
c = c->get_java_parent_class();
925+
}
926+
}
927+
return false;
928+
}
929+
902930
JavaObject::JavaObject() {
903931
}
904932

@@ -1461,7 +1489,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
14611489
return false;
14621490
}
14631491

1464-
Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_private_methods_access) {
1492+
Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_non_public_methods_access) {
14651493
String class_name_dots = p_class.replace_char('/', '.');
14661494
if (class_cache.has(class_name_dots)) {
14671495
return class_cache[class_name_dots];
@@ -1473,10 +1501,18 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
14731501
jclass bclass = jni_find_class(env, class_name_dots.replace_char('.', '/').utf8().get_data());
14741502
ERR_FAIL_NULL_V_MSG(bclass, Ref<JavaClass>(), vformat("Java class '%s' not found.", p_class));
14751503

1476-
jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredConstructors);
1504+
jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getConstructors);
1505+
if (env->ExceptionCheck()) {
1506+
env->ExceptionDescribe();
1507+
env->ExceptionClear();
1508+
}
14771509
ERR_FAIL_NULL_V(constructors, Ref<JavaClass>());
14781510

14791511
jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredMethods);
1512+
if (env->ExceptionCheck()) {
1513+
env->ExceptionDescribe();
1514+
env->ExceptionClear();
1515+
}
14801516
ERR_FAIL_NULL_V(methods, Ref<JavaClass>());
14811517

14821518
Ref<JavaClass> java_class = memnew(JavaClass);
@@ -1515,8 +1551,9 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
15151551
Vector<String> params;
15161552

15171553
jint mods = env->CallIntMethod(obj, is_constructor ? Constructor_getModifiers : Method_getModifiers);
1554+
bool is_public = (mods & 0x0001) != 0; // java.lang.reflect.Modifier.PUBLIC
15181555

1519-
if (!(mods & 0x0001) && (is_constructor || !p_allow_private_methods_access)) {
1556+
if (!is_public && (is_constructor || !p_allow_non_public_methods_access)) {
15201557
env->DeleteLocalRef(obj);
15211558
continue; //not public bye
15221559
}
@@ -1627,6 +1664,13 @@ Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva
16271664
mi.method = env->GetMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
16281665
}
16291666

1667+
if (env->ExceptionCheck()) {
1668+
// Exceptions may be thrown when trying to access hidden methods; write the exception to the logs and continue.
1669+
env->ExceptionDescribe();
1670+
env->ExceptionClear();
1671+
continue;
1672+
}
1673+
16301674
ERR_CONTINUE(!mi.method);
16311675

16321676
java_class->methods[str_method].push_back(mi);
@@ -1700,7 +1744,7 @@ JavaClassWrapper::JavaClassWrapper() {
17001744
ERR_FAIL_NULL(env);
17011745

17021746
jclass bclass = jni_find_class(env, "java/lang/Class");
1703-
Class_getDeclaredConstructors = env->GetMethodID(bclass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;");
1747+
Class_getConstructors = env->GetMethodID(bclass, "getConstructors", "()[Ljava/lang/reflect/Constructor;");
17041748
Class_getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
17051749
Class_getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;");
17061750
Class_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");

0 commit comments

Comments
 (0)