From 613751a742bd439c968551f25f25e1bb25a289a4 Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Thu, 2 Dec 2021 20:05:05 +0100 Subject: [PATCH] Fix get_all_delegates method for generic classes If the class is generic, we must get its generic type definition and use it to retrieve the delegates. --- .../GodotSharp/GodotSharp/Core/MarshalUtils.cs | 12 ++++++++++++ modules/mono/mono_gd/gd_mono_cache.cpp | 4 ++++ modules/mono/mono_gd/gd_mono_cache.h | 2 ++ modules/mono/mono_gd/gd_mono_class.cpp | 14 ++++++++++++-- modules/mono/mono_gd/gd_mono_utils.cpp | 6 ++++++ modules/mono/mono_gd/gd_mono_utils.h | 2 ++ 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index 3051bcedc789..ee4d0eed08ea 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -79,6 +79,18 @@ private static bool TypeIsSystemGenericDictionary(Type type) => /// private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); + /// + /// Returns the generic type definition of . + /// + /// + /// Thrown when the given is not a generic type. + /// That is, returns . + /// + private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition) + { + genericTypeDefinition = type.GetGenericTypeDefinition(); + } + /// /// Gets the element type for the given . /// diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index c7677d044c01..1294b001d70e 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -167,6 +167,8 @@ void CachedData::clear_godot_api_cache() { methodthunk_MarshalUtils_TypeIsGenericICollection.nullify(); methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify(); + methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify(); + methodthunk_MarshalUtils_ArrayGetElementType.nullify(); methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); @@ -279,6 +281,8 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3)); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index fae0d55dc8f4..d775e56e9b22 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -138,6 +138,8 @@ struct CachedData { GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericICollection; GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericIDictionary; + GDMonoMethodThunk methodthunk_MarshalUtils_GetGenericTypeDefinition; + GDMonoMethodThunk methodthunk_MarshalUtils_ArrayGetElementType; GDMonoMethodThunk methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index d0cb7162ee02..df715983fae9 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -438,12 +438,22 @@ const Vector &GDMonoClass::get_all_properties() { } const Vector &GDMonoClass::get_all_delegates() { - if (delegates_fetched) + if (delegates_fetched) { return delegates_list; + } + + // If the class is generic we must use the generic type definition. + MonoClass *klass = mono_class; + if (mono_type_get_type(get_mono_type()) == MONO_TYPE_GENERICINST) { + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), get_mono_type()); + GDMonoUtils::Marshal::get_generic_type_definition(reftype, &reftype); + MonoType *type = mono_reflection_type_get_type(reftype); + klass = mono_class_from_mono_type(type); + } void *iter = NULL; MonoClass *raw_class = NULL; - while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != NULL) { + while ((raw_class = mono_class_get_nested_types(klass, &iter)) != NULL) { if (mono_class_is_delegate(raw_class)) { StringName name = String::utf8(mono_class_get_name(raw_class)); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 1552b70bd019..63c7de0e35c5 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -587,6 +587,12 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) { return (bool)res; } +void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) { + MonoException *exc = nullptr; + CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc); + UNHANDLED_EXCEPTION(exc); +} + void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { MonoException *exc = NULL; CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 5a0c36d63909..22998f54e1d2 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -62,6 +62,8 @@ bool type_is_generic_ienumerable(MonoReflectionType *p_reftype); bool type_is_generic_icollection(MonoReflectionType *p_reftype); bool type_is_generic_idictionary(MonoReflectionType *p_reftype); +void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype); + void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);