Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to get argument count of methods #87680

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,10 @@ bool ClassDB::class_has_method(const StringName &p_class, const StringName &p_me
return ::ClassDB::has_method(p_class, p_method, p_no_inheritance);
}

int ClassDB::class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) const {
return ::ClassDB::get_method_argument_count(p_class, p_method, nullptr, p_no_inheritance);
}

TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class, bool p_no_inheritance) const {
List<MethodInfo> methods;
::ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
Expand Down Expand Up @@ -1562,6 +1566,8 @@ void ClassDB::_bind_methods() {

::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::class_has_method, DEFVAL(false));

::ClassDB::bind_method(D_METHOD("class_get_method_argument_count", "class", "method", "no_inheritance"), &ClassDB::class_get_method_argument_count, DEFVAL(false));

::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));

::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false));
Expand Down
2 changes: 2 additions & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ class ClassDB : public Object {

bool class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;

int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;

TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;

PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
Expand Down
56 changes: 56 additions & 0 deletions core/extension/gdextension_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class CallableCustomExtension : public CallableCustom {

GDExtensionCallableCustomToString to_string_func;

GDExtensionCallableCustomGetArgumentCount get_argument_count_func;

uint32_t _hash;

static bool default_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
Expand Down Expand Up @@ -143,6 +145,21 @@ class CallableCustomExtension : public CallableCustom {
return object;
}

int get_argument_count(bool &r_is_valid) const override {
if (get_argument_count_func != nullptr) {
GDExtensionBool is_valid = false;

GDExtensionInt ret = get_argument_count_func(userdata, &is_valid);

if (is_valid) {
r_is_valid = true;
return ret;
}
}
r_is_valid = false;
return 0;
}

void *get_userdata(void *p_token) const {
return (p_token == token) ? userdata : nullptr;
}
Expand All @@ -157,6 +174,7 @@ class CallableCustomExtension : public CallableCustom {
r_call_error.expected = error.expected;
}

#ifndef DISABLE_DEPRECATED
CallableCustomExtension(GDExtensionCallableCustomInfo *p_info) {
userdata = p_info->callable_userdata;
token = p_info->token;
Expand All @@ -172,6 +190,35 @@ class CallableCustomExtension : public CallableCustom {

to_string_func = p_info->to_string_func;

get_argument_count_func = nullptr;

// Pre-calculate the hash.
if (p_info->hash_func != nullptr) {
_hash = p_info->hash_func(userdata);
} else {
_hash = hash_murmur3_one_64((uint64_t)call_func);
_hash = hash_murmur3_one_64((uint64_t)userdata, _hash);
}
}
#endif

CallableCustomExtension(GDExtensionCallableCustomInfo2 *p_info) {
userdata = p_info->callable_userdata;
token = p_info->token;

object = p_info->object_id;

call_func = p_info->call_func;
is_valid_func = p_info->is_valid_func;
free_func = p_info->free_func;

equal_func = p_info->equal_func;
less_than_func = p_info->less_than_func;

to_string_func = p_info->to_string_func;

get_argument_count_func = p_info->get_argument_count_func;

// Pre-calculate the hash.
if (p_info->hash_func != nullptr) {
_hash = p_info->hash_func(userdata);
Expand Down Expand Up @@ -1378,9 +1425,15 @@ static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExt
return script_instance_extension->instance;
}

#ifndef DISABLE_DEPRECATED
static void gdextension_callable_custom_create(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_custom_callable_info) {
memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
}
#endif

static void gdextension_callable_custom_create2(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo2 *p_custom_callable_info) {
memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
}

static void *gdextension_callable_custom_get_userdata(GDExtensionTypePtr p_callable, void *p_token) {
const Callable &callable = *reinterpret_cast<const Callable *>(p_callable);
Expand Down Expand Up @@ -1595,7 +1648,10 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
REGISTER_INTERFACE_FUNC(object_get_script_instance);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(callable_custom_create);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(callable_custom_create2);
REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
Expand Down
51 changes: 50 additions & 1 deletion core/extension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_user

typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);

typedef GDExtensionInt (*GDExtensionCallableCustomGetArgumentCount)(void *callable_userdata, GDExtensionBool *r_is_valid);

typedef struct {
/* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method.
*
Expand Down Expand Up @@ -471,7 +473,40 @@ typedef struct {
GDExtensionCallableCustomLessThan less_than_func;

GDExtensionCallableCustomToString to_string_func;
} GDExtensionCallableCustomInfo;
} GDExtensionCallableCustomInfo; // Deprecated. Use GDExtensionCallableCustomInfo2 instead.

typedef struct {
/* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method.
*
* `token` should point to an address that uniquely identifies the GDExtension (for example, the
* `GDExtensionClassLibraryPtr` passed to the entry symbol function.
*
* `hash_func`, `equal_func`, and `less_than_func` are optional. If not provided both `call_func` and
* `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes.
*
* The hash returned by `hash_func` is cached, `hash_func` will not be called more than once per callable.
*
* `is_valid_func` is necessary if the validity of the callable can change before destruction.
*
* `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed.
*/
void *callable_userdata;
void *token;

GDObjectInstanceID object_id;

GDExtensionCallableCustomCall call_func;
GDExtensionCallableCustomIsValid is_valid_func;
GDExtensionCallableCustomFree free_func;

GDExtensionCallableCustomHash hash_func;
GDExtensionCallableCustomEqual equal_func;
GDExtensionCallableCustomLessThan less_than_func;

GDExtensionCallableCustomToString to_string_func;

GDExtensionCallableCustomGetArgumentCount get_argument_count_func;
} GDExtensionCallableCustomInfo2;

/* SCRIPT INSTANCE EXTENSION */

Expand Down Expand Up @@ -2510,6 +2545,7 @@ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptIn
/**
* @name callable_custom_create
* @since 4.2
* @deprecated in Godot 4.3. Use `callable_custom_create2` instead.
*
* Creates a custom Callable object from a function pointer.
*
Expand All @@ -2520,6 +2556,19 @@ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptIn
*/
typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_callable_custom_info);

/**
* @name callable_custom_create2
* @since 4.3
*
* Creates a custom Callable object from a function pointer.
*
* Provided struct can be safely freed once the function returns.
*
* @param r_callable A pointer that will receive the new Callable.
* @param p_callable_custom_info The info required to construct a Callable.
*/
typedef void (*GDExtensionInterfaceCallableCustomCreate2)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo2 *p_callable_custom_info);

/**
* @name callable_custom_get_userdata
* @since 4.2
Expand Down
25 changes: 25 additions & 0 deletions core/object/callable_method_pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
return data.instance->get_instance_id();
}

virtual int get_argument_count(bool &r_is_valid) const {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
Expand Down Expand Up @@ -140,6 +145,11 @@ class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
return data.instance->get_instance_id();
}

virtual int get_argument_count(bool &r_is_valid) const {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
Expand Down Expand Up @@ -187,6 +197,11 @@ class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
return data.instance->get_instance_id();
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
Expand Down Expand Up @@ -238,6 +253,11 @@ class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase
return ObjectID();
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
r_return_value = Variant();
Expand Down Expand Up @@ -280,6 +300,11 @@ class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerB
return ObjectID();
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
Expand Down
25 changes: 25 additions & 0 deletions core/object/class_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,31 @@ bool ClassDB::has_method(const StringName &p_class, const StringName &p_method,
return false;
}

int ClassDB::get_method_argument_count(const StringName &p_class, const StringName &p_method, bool *r_is_valid, bool p_no_inheritance) {
OBJTYPE_RLOCK;

ClassInfo *type = classes.getptr(p_class);

while (type) {
MethodBind **method = type->method_map.getptr(p_method);
if (method && *method) {
if (r_is_valid) {
*r_is_valid = true;
}
return (*method)->get_argument_count();
}
if (p_no_inheritance) {
break;
}
type = type->inherits_ptr;
}

if (r_is_valid) {
*r_is_valid = false;
}
return 0;
}

void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) {
_bind_method_custom(p_class, p_method, false);
}
Expand Down
1 change: 1 addition & 0 deletions core/object/class_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ class ClassDB {
static void get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static void get_method_list_with_compatibility(const StringName &p_class, List<Pair<MethodInfo, uint32_t>> *p_methods_with_hash, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static bool get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static int get_method_argument_count(const StringName &p_class, const StringName &p_method, bool *r_is_valid = nullptr, bool p_no_inheritance = false);
static MethodBind *get_method(const StringName &p_class, const StringName &p_name);
static MethodBind *get_method_with_compatibility(const StringName &p_class, const StringName &p_name, uint64_t p_hash, bool *r_method_exists = nullptr, bool *r_is_deprecated = nullptr);
static Vector<uint32_t> get_method_compatibility_hashes(const StringName &p_class, const StringName &p_name);
Expand Down
55 changes: 55 additions & 0 deletions core/object/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,59 @@ bool Object::has_method(const StringName &p_method) const {
return false;
}

int Object::_get_method_argument_count_bind(const StringName &p_method) const {
return get_method_argument_count(p_method);
}

int Object::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
if (p_method == CoreStringNames::get_singleton()->_free) {
if (r_is_valid) {
*r_is_valid = true;
}
return 0;
}

if (script_instance) {
bool valid = false;
int ret = script_instance->get_method_argument_count(p_method, &valid);
if (valid) {
if (r_is_valid) {
*r_is_valid = true;
}
return ret;
}
}

{
bool valid = false;
int ret = ClassDB::get_method_argument_count(get_class_name(), p_method, &valid);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now delegates to the ClassDB case instead, keeping the code in one place

if (valid) {
if (r_is_valid) {
*r_is_valid = true;
}
return ret;
}
}

const Script *scr = Object::cast_to<Script>(this);
while (scr != nullptr) {
bool valid = false;
int ret = scr->get_script_method_argument_count(p_method, &valid);
if (valid) {
if (r_is_valid) {
*r_is_valid = true;
}
return ret;
}
scr = scr->get_base_script().ptr();
}
Comment on lines +725 to +736
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just calling it on the script itself and letting ScriptInstance handle the attached script as is done elsewhere


if (r_is_valid) {
*r_is_valid = false;
}
return 0;
}

Variant Object::getvar(const Variant &p_key, bool *r_valid) const {
if (r_valid) {
*r_valid = false;
Expand Down Expand Up @@ -1644,6 +1697,8 @@ void Object::_bind_methods() {

ClassDB::bind_method(D_METHOD("has_method", "method"), &Object::has_method);

ClassDB::bind_method(D_METHOD("get_method_argument_count", "method"), &Object::_get_method_argument_count_bind);

ClassDB::bind_method(D_METHOD("has_signal", "signal"), &Object::has_signal);
ClassDB::bind_method(D_METHOD("get_signal_list"), &Object::_get_signal_list);
ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
Expand Down
2 changes: 2 additions & 0 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ class Object {
Variant _get_bind(const StringName &p_name) const;
void _set_indexed_bind(const NodePath &p_name, const Variant &p_value);
Variant _get_indexed_bind(const NodePath &p_name) const;
int _get_method_argument_count_bind(const StringName &p_name) const;

_FORCE_INLINE_ void _construct_object(bool p_reference);

Expand Down Expand Up @@ -865,6 +866,7 @@ class Object {
Variant property_get_revert(const StringName &p_name) const;

bool has_method(const StringName &p_method) const;
int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const;
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Expand Down
Loading
Loading