Skip to content

Commit

Permalink
Improve the capabilities of JavaClassWrapper
Browse files Browse the repository at this point in the history
- Add support for passing `Callable` parameter
- Code cleanup
  • Loading branch information
m4gr3d committed Nov 20, 2024
1 parent a0cd8f1 commit 4bd068a
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 72 deletions.
5 changes: 5 additions & 0 deletions platform/android/api/java_class_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class JavaClass : public RefCounted {
ARG_TYPE_FLOAT,
ARG_TYPE_DOUBLE,
ARG_TYPE_STRING, //special case
ARG_TYPE_CALLABLE,
ARG_TYPE_CLASS,
ARG_ARRAY_BIT = 1 << 16,
ARG_NUMBER_CLASS_BIT = 1 << 17,
Expand Down Expand Up @@ -125,6 +126,9 @@ class JavaClass : public RefCounted {
case ARG_TYPE_STRING:
r_type = Variant::STRING;
break;
case ARG_TYPE_CALLABLE:
r_type = Variant::CALLABLE;
break;
case ARG_TYPE_CLASS:
r_type = Variant::OBJECT;
break;
Expand Down Expand Up @@ -166,6 +170,7 @@ class JavaClass : public RefCounted {
r_type = Variant::PACKED_STRING_ARRAY;
break;
case ARG_ARRAY_BIT | ARG_TYPE_CLASS:
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE:
r_type = Variant::ARRAY;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion platform/android/dir_access_jandroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

#include "dir_access_jandroid.h"

#include "string_android.h"
#include "jni_utils.h"
#include "thread_jandroid.h"

#include "core/string/print_string.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**************************************************************************/
/* Callable.kt */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

package org.godotengine.godot.variant

import androidx.annotation.Keep

/**
* Android version of a Godot built-in Callable type representing a method or a standalone function.
*/
@Keep
class Callable private constructor(private val nativeCallablePointer: Long) {

companion object {
@JvmStatic
fun call(godotObjectId: Long, methodName: String, vararg methodParameters: Any): Any? {
return nativeCallObject(godotObjectId, methodName, methodParameters)
}

@JvmStatic
fun callDeferred(godotObjectId: Long, methodName: String, vararg methodParameters: Any) {
nativeCallObjectDeferred(godotObjectId, methodName, methodParameters)
}

@JvmStatic
private external fun nativeCall(pointer: Long, params: Array<out Any>): Any?

@JvmStatic
private external fun nativeCallObject(godotObjectId: Long, methodName: String, params: Array<out Any>): Any?

@JvmStatic
private external fun nativeCallObjectDeferred(godotObjectId: Long, methodName: String, params: Array<out Any>)
}

internal fun call(vararg params: Any): Any? {
if (nativeCallablePointer == 0L) {
return null
}

return nativeCall(nativeCallablePointer, params)
}

private fun getNativePointer() = nativeCallablePointer
}
50 changes: 49 additions & 1 deletion platform/android/java_class_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -101,6 +101,11 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
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;
Expand Down Expand Up @@ -271,6 +276,11 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
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()) {
Expand Down Expand Up @@ -380,6 +390,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;
Expand Down Expand Up @@ -706,6 +729,9 @@ 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 == "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;";
Expand Down Expand Up @@ -793,6 +819,10 @@ 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_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);
Expand Down Expand Up @@ -1113,6 +1143,24 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
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;
}
Expand Down
2 changes: 1 addition & 1 deletion platform/android/java_godot_io_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#ifndef JAVA_GODOT_IO_WRAPPER_H
#define JAVA_GODOT_IO_WRAPPER_H

#include "string_android.h"
#include "jni_utils.h"

#include "core/math/rect2i.h"
#include "core/variant/typed_array.h"
Expand Down
1 change: 0 additions & 1 deletion platform/android/java_godot_lib_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
#include "net_socket_android.h"
#include "os_android.h"
#include "plugin/godot_plugin_jni.h"
#include "string_android.h"
#include "thread_jandroid.h"
#include "tts_android.h"

Expand Down
2 changes: 1 addition & 1 deletion platform/android/java_godot_view_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#ifndef JAVA_GODOT_VIEW_WRAPPER_H
#define JAVA_GODOT_VIEW_WRAPPER_H

#include "string_android.h"
#include "jni_utils.h"

#include "core/math/vector2.h"

Expand Down
1 change: 0 additions & 1 deletion platform/android/java_godot_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#define JAVA_GODOT_WRAPPER_H

#include "java_godot_view_wrapper.h"
#include "string_android.h"

#include "core/math/color.h"
#include "core/templates/list.h"
Expand Down
79 changes: 44 additions & 35 deletions platform/android/jni_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,39 @@

#include "api/java_class_wrapper.h"

jobject callable_to_jcallable(JNIEnv *p_env, const Variant *p_callable) {
ERR_FAIL_NULL_V(p_env, nullptr);
ERR_FAIL_NULL_V(p_callable, nullptr);
if (p_callable->get_type() != Variant::CALLABLE) {
return nullptr;
}

jclass bclass = p_env->FindClass("org/godotengine/godot/variant/Callable");
jmethodID ctor = p_env->GetMethodID(bclass, "<init>", "(J)V");
jobject jcallable = p_env->NewObject(bclass, ctor, reinterpret_cast<int64_t>(p_callable));
p_env->DeleteLocalRef(bclass);

return jcallable;
}

Callable jcallable_to_callable(JNIEnv *p_env, jobject p_jcallable_obj) {
ERR_FAIL_NULL_V(p_env, Callable());

Callable *callable = nullptr;
jclass callable_class = p_env->FindClass("org/godotengine/godot/variant/Callable");
if (callable_class && p_env->IsInstanceOf(p_jcallable_obj, callable_class)) {
jmethodID get_native_pointer = p_env->GetMethodID(callable_class, "getNativePointer", "()J");
jlong native_callable = p_env->CallLongMethod(p_jcallable_obj, get_native_pointer);

callable = reinterpret_cast<Callable *>(native_callable);
}

p_env->DeleteLocalRef(callable_class);

ERR_FAIL_NULL_V(callable, Callable());
return *callable;
}

jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) {
jvalret v;

Expand Down Expand Up @@ -100,6 +133,12 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a

} break;

case Variant::CALLABLE: {
jobject jcallable = callable_to_jcallable(env, p_arg);
v.val.l = jcallable;
v.obj = jcallable;
} break;

case Variant::DICTIONARY: {
Dictionary dict = *p_arg;
jclass dclass = env->FindClass("org/godotengine/godot/Dictionary");
Expand Down Expand Up @@ -370,6 +409,10 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
return ret;
}

if (name == "org.godotengine.godot.variant.Callable") {
return jcallable_to_callable(env, obj);
}

Ref<JavaObject> generic_object(memnew(JavaObject(JavaClassWrapper::get_singleton()->wrap(name), obj)));

env->DeleteLocalRef(c);
Expand All @@ -396,6 +439,7 @@ Variant::Type get_jni_type(const String &p_type) {
{ "[D", Variant::PACKED_FLOAT64_ARRAY },
{ "[Ljava.lang.String;", Variant::PACKED_STRING_ARRAY },
{ "org.godotengine.godot.Dictionary", Variant::DICTIONARY },
{ "org.godotengine.godot.variant.Callable", Variant::CALLABLE },
{ nullptr, Variant::NIL }
};

Expand All @@ -411,38 +455,3 @@ Variant::Type get_jni_type(const String &p_type) {

return Variant::OBJECT;
}

String get_jni_sig(const String &p_type) {
static struct {
const char *name;
const char *sig;
} _type_to_vtype[] = {
{ "void", "V" },
{ "boolean", "Z" },
{ "int", "I" },
{ "long", "J" },
{ "float", "F" },
{ "double", "D" },
{ "java.lang.String", "Ljava/lang/String;" },
{ "org.godotengine.godot.Dictionary", "Lorg/godotengine/godot/Dictionary;" },
{ "[I", "[I" },
{ "[J", "[J" },
{ "[B", "[B" },
{ "[F", "[F" },
{ "[D", "[D" },
{ "[Ljava.lang.String;", "[Ljava/lang/String;" },
{ nullptr, "V" }
};

int idx = 0;

while (_type_to_vtype[idx].name) {
if (p_type == _type_to_vtype[idx].name) {
return _type_to_vtype[idx].sig;
}

idx++;
}

return "L" + p_type.replace(".", "/") + ";";
}
Loading

0 comments on commit 4bd068a

Please sign in to comment.