Переглянути джерело

Merge pull request #28974 from neikeq/moshi-moshi_git-desu-ka

C#: Marshalling support for IEnumerable<> and IDictionary<,>
Ignacio Roldán Etcheverry 6 роки тому
батько
коміт
f54becfee2

+ 91 - 45
modules/mono/csharp_script.cpp

@@ -2020,7 +2020,7 @@ bool CSharpScript::_update_exports() {
 			for (int i = fields.size() - 1; i >= 0; i--) {
 				GDMonoField *field = fields[i];
 
-				if (_get_member_export(top, field, prop_info, exported)) {
+				if (_get_member_export(field, prop_info, exported)) {
 					StringName name = field->get_name();
 
 					if (exported) {
@@ -2041,7 +2041,7 @@ bool CSharpScript::_update_exports() {
 			for (int i = properties.size() - 1; i >= 0; i--) {
 				GDMonoProperty *property = properties[i];
 
-				if (_get_member_export(top, property, prop_info, exported)) {
+				if (_get_member_export(property, prop_info, exported)) {
 					StringName name = property->get_name();
 
 					if (exported) {
@@ -2168,17 +2168,19 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve
  * Returns false if there was an error, otherwise true.
  * If there was an error, r_prop_info and r_exported are not assigned any value.
  */
-bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
+bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
 
-	StringName name = p_member->get_name();
+	// Goddammit, C++. All I wanted was some nested functions.
+#define MEMBER_FULL_QUALIFIED_NAME(m_member) \
+	(m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name())
 
 	if (p_member->is_static()) {
 		if (p_member->has_attribute(CACHED_CLASS(ExportAttribute)))
-			ERR_PRINTS("Cannot export member because it is static: " + p_class->get_full_name() + "." + name.operator String());
+			ERR_PRINTS("Cannot export member because it is static: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
 		return false;
 	}
 
-	if (member_info.has(name))
+	if (member_info.has(p_member->get_name()))
 		return false;
 
 	ManagedType type;
@@ -2191,19 +2193,22 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
 		CRASH_NOW();
 	}
 
-	GDMonoMarshal::ExportInfo export_info;
-	Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &export_info);
+	Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
 
 	if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
-		r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
+		r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
 		r_exported = false;
 		return true;
 	}
 
 	if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
 		GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
-		if (!property->has_getter() || !property->has_setter()) {
-			ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String());
+		if (!property->has_getter()) {
+			ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
+			return false;
+		}
+		if (!property->has_setter()) {
+			ERR_PRINTS("Set-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
 			return false;
 		}
 	}
@@ -2214,16 +2219,38 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
 	String hint_string;
 
 	if (variant_type == Variant::NIL) {
-		ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String());
+		ERR_PRINTS("Unknown exported member type: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
 		return false;
-	} else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) {
-		// TODO: Move to ExportInfo?
-		variant_type = Variant::INT;
-		hint = PROPERTY_HINT_ENUM;
+	}
+
+	int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
+
+	if (hint_res == -1) {
+		ERR_EXPLAIN("Error while trying to determine information about the exported member: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
+		ERR_FAIL_V(false);
+	}
+
+	if (hint_res == 0) {
+		hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
+		hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
+	}
+
+	r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
+	r_exported = true;
+
+	return true;
+
+#undef MEMBER_FULL_QUALIFIED_NAME
+}
+
+int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
+
+	if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
+		r_hint = PROPERTY_HINT_ENUM;
 
-		Vector<MonoClassField *> fields = type.type_class->get_enum_fields();
+		Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
 
-		MonoType *enum_basetype = mono_class_enum_basetype(type.type_class->get_mono_ptr());
+		MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr());
 
 		String name_only_hint_string;
 
@@ -2236,12 +2263,12 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
 			MonoClassField *field = fields[i];
 
 			if (i > 0) {
-				hint_string += ",";
+				r_hint_string += ",";
 				name_only_hint_string += ",";
 			}
 
 			String enum_field_name = mono_field_get_name(field);
-			hint_string += enum_field_name;
+			r_hint_string += enum_field_name;
 			name_only_hint_string += enum_field_name;
 
 			// TODO:
@@ -2251,54 +2278,73 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
 			MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL);
 
 			if (val_obj == NULL) {
-				ERR_PRINTS("Failed to get '" + enum_field_name + "' constant enum value of exported member: " +
-						   p_class->get_full_name() + "." + name.operator String());
-				return false;
+				ERR_EXPLAIN("Failed to get '" + enum_field_name + "' constant enum value");
+				ERR_FAIL_V(-1);
 			}
 
 			bool r_error;
 			uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
 			if (r_error) {
-				ERR_PRINTS("Failed to unbox '" + enum_field_name + "' constant enum value of exported member: " +
-						   p_class->get_full_name() + "." + name.operator String());
-				return false;
+				ERR_EXPLAIN("Failed to unbox '" + enum_field_name + "' constant enum value");
+				ERR_FAIL_V(-1);
 			}
 
 			if (val != (unsigned int)i) {
 				uses_default_values = false;
 			}
 
-			hint_string += ":";
-			hint_string += String::num_uint64(val);
+			r_hint_string += ":";
+			r_hint_string += String::num_uint64(val);
 		}
 
 		if (uses_default_values) {
 			// If we use the format NAME:VAL, that's what the editor displays.
 			// That's annoying if the user is not using custom values for the enum constants.
 			// This may not be needed in the future if the editor is changed to not display values.
-			hint_string = name_only_hint_string;
+			r_hint_string = name_only_hint_string;
 		}
-	} else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) {
-		GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(type.type_class);
+	} else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) {
+		GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
 		CRASH_COND(field_native_class == NULL);
 
-		hint = PROPERTY_HINT_RESOURCE_TYPE;
-		hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
-	} else if (variant_type == Variant::ARRAY && export_info.array.element_type != Variant::NIL) {
-		String elem_type_str = itos(export_info.array.element_type);
-		hint = PROPERTY_HINT_TYPE_STRING;
-		hint_string = elem_type_str + "/" + elem_type_str + ":" + export_info.array.element_native_name;
-	} else if (variant_type == Variant::DICTIONARY && export_info.dictionary.key_type != Variant::NIL && export_info.dictionary.value_type != Variant::NIL) {
-		// TODO: There is no hint for this yet
+		r_hint = PROPERTY_HINT_RESOURCE_TYPE;
+		r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
+	} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
+		// Nested arrays are not supported in the inspector
+
+		ManagedType elem_type;
+
+		if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type))
+			return 0;
+
+		Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type);
+
+		PropertyHint elem_hint = PROPERTY_HINT_NONE;
+		String elem_hint_string;
+
+		if (elem_variant_type == Variant::NIL) {
+			ERR_EXPLAIN("Unknown array element type");
+			ERR_FAIL_V(-1);
+		}
+
+		int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
+
+		if (hint_res == -1) {
+			ERR_EXPLAIN("Error while trying to determine information about the array element type");
+			ERR_FAIL_V(-1);
+		}
+
+		// Format: type/hint:hint_string
+		r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
+		r_hint = PROPERTY_HINT_TYPE_STRING;
+
+	} else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
+		// TODO: Dictionaries are not supported in the inspector
 	} else {
-		hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
-		hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
+		return 0;
 	}
 
-	r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
-	r_exported = true;
-
-	return true;
+	return 1;
 }
 #endif
 

+ 2 - 1
modules/mono/csharp_script.h

@@ -127,7 +127,8 @@ class CSharpScript : public Script {
 
 	bool _update_exports();
 #ifdef TOOLS_ENABLED
-	bool _get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
+	bool _get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
+	static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
 #endif
 
 	CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error);

+ 111 - 5
modules/mono/glue/Managed/Files/MarshalUtils.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections;
+using System.Collections.Generic;
 
 namespace Godot
 {
@@ -8,29 +9,113 @@ namespace Godot
 
     static class MarshalUtils
     {
+        /// <summary>
+        /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+        /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
+        /// </summary>
+        /// <exception cref="System.InvalidOperationException">
+        /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
+        /// </exception>
         static bool TypeIsGenericArray(Type type)
         {
             return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
         }
 
+        /// <summary>
+        /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+        /// is <see cref="Godot.Collections.Dictionary{T}"/>; otherwise returns <see langword="false"/>.
+        /// </summary>
+        /// <exception cref="System.InvalidOperationException">
+        /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
+        /// </exception>
         static bool TypeIsGenericDictionary(Type type)
         {
             return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
         }
 
-        static void ArrayGetElementType(Type type, out Type elementType)
+        static void ArrayGetElementType(Type arrayType, out Type elementType)
         {
-            elementType = type.GetGenericArguments()[0];
+            elementType = arrayType.GetGenericArguments()[0];
         }
 
-        static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType)
+        static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
         {
-            var genericArgs = type.GetGenericArguments();
-
+            var genericArgs = dictionaryType.GetGenericArguments();
             keyType = genericArgs[0];
             valueType = genericArgs[1];
         }
 
+        static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType)
+        {
+            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+            {
+                elementType = type.GetGenericArguments()[0];
+                return true;
+            }
+
+            foreach (var interfaceType in type.GetInterfaces())
+            {
+                if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+                {
+                    elementType = interfaceType.GetGenericArguments()[0];
+                    return true;
+                }
+            }
+
+            Type baseType = type.BaseType;
+
+            if (baseType == null)
+            {
+                elementType = null;
+                return false;
+            }
+
+            return GenericIEnumerableIsAssignableFromType(baseType, out elementType);
+        }
+
+        static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType)
+        {
+            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+            {
+                var genericArgs = type.GetGenericArguments();
+                keyType = genericArgs[0];
+                valueType = genericArgs[1];
+                return true;
+            }
+
+            foreach (var interfaceType in type.GetInterfaces())
+            {
+                if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+                {
+                    var genericArgs = interfaceType.GetGenericArguments();
+                    keyType = genericArgs[0];
+                    valueType = genericArgs[1];
+                    return true;
+                }
+            }
+
+            Type baseType = type.BaseType;
+
+            if (baseType == null)
+            {
+                keyType = null;
+                valueType = null;
+                return false;
+            }
+
+            return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType);
+        }
+
+        static Type MakeGenericArrayType(Type elemType)
+        {
+            return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
+        }
+
+        static Type MakeGenericDictionaryType(Type keyType, Type valueType)
+        {
+            return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
+        }
+
         // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue>
         // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized
 
@@ -64,5 +149,26 @@ namespace Godot
                 Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
             }
         }
+
+        internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr)
+        {
+#if DEBUG
+            if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType()))
+                throw new InvalidOperationException("The type does not implement IDictionary<,>");
+#endif
+
+            // TODO: Can we optimize this?
+
+            var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator();
+            var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator();
+
+            while (keys.MoveNext() && values.MoveNext())
+            {
+                object key = keys.Current;
+                object value = values.Current;
+
+                Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value);
+            }
+        }
     }
 }

+ 39 - 13
modules/mono/mono_gd/gd_mono_field.cpp

@@ -313,12 +313,32 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(),
+						GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
+				mono_field_set_value(p_object, mono_field, managed);
+				break;
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
 				mono_field_set_value(p_object, mono_field, managed);
 				break;
 			}
 
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(),
+						GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+				mono_field_set_value(p_object, mono_field, managed);
+				break;
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
 				mono_field_set_value(p_object, mono_field, managed);
@@ -432,26 +452,24 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 		case MONO_TYPE_GENERICINST: {
 			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
 
-			MonoException *exc = NULL;
-
-			GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
-			MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
-			UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
-			if (is_dict) {
+			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
 				mono_field_set_value(p_object, mono_field, managed);
 				break;
 			}
 
-			exc = NULL;
+			if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
+				mono_field_set_value(p_object, mono_field, managed);
+				break;
+			}
 
-			GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
-			MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
-			UNLIKELY_UNHANDLED_EXCEPTION(exc);
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 
-			if (is_array) {
-				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(),
+						GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
 				mono_field_set_value(p_object, mono_field, managed);
 				break;
 			}
@@ -462,6 +480,14 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(),
+						GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+				mono_field_set_value(p_object, mono_field, managed);
+				break;
+			}
+
 			if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
 				mono_field_set_value(p_object, mono_field, managed);

+ 4 - 2
modules/mono/mono_gd/gd_mono_field.h

@@ -47,9 +47,11 @@ class GDMonoField : public IMonoClassMember {
 	MonoCustomAttrInfo *attributes;
 
 public:
-	virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_FIELD; }
+	virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
 
-	virtual StringName get_name() GD_FINAL { return name; }
+	virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; }
+
+	virtual StringName get_name() const GD_FINAL { return name; }
 
 	virtual bool is_static() GD_FINAL;
 	virtual Visibility get_visibility() GD_FINAL;

+ 150 - 92
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -35,7 +35,7 @@
 
 namespace GDMonoMarshal {
 
-Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info) {
+Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 	switch (p_type.type_encoding) {
 		case MONO_TYPE_BOOLEAN:
 			return Variant::BOOL;
@@ -157,10 +157,24 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
 				return Variant::ARRAY;
 			}
 
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+				return Variant::DICTIONARY;
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
 				return Variant::DICTIONARY;
 			}
 
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+				return Variant::ARRAY;
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 				return Variant::ARRAY;
 			}
@@ -169,71 +183,96 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
 		case MONO_TYPE_GENERICINST: {
 			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
 
-			MonoException *exc = NULL;
-			GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
-			MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
-			UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
-			if (is_dict) {
-				if (r_export_info) {
-					MonoReflectionType *key_reftype;
-					MonoReflectionType *value_reftype;
+			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
+				return Variant::DICTIONARY;
+			}
 
-					exc = NULL;
-					invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes),
-							reftype, &key_reftype, &value_reftype, &exc);
-					UNLIKELY_UNHANDLED_EXCEPTION(exc);
+			if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+				return Variant::ARRAY;
+			}
 
-					ManagedType key_type = ManagedType::from_reftype(key_reftype);
-					ManagedType value_type = ManagedType::from_reftype(value_reftype);
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 
-					r_export_info->dictionary.key_type = managed_to_variant_type(key_type);
-					r_export_info->dictionary.key_native_name = NATIVE_GDMONOCLASS_NAME(key_type.type_class);
-					r_export_info->dictionary.value_type = managed_to_variant_type(value_type);
-					r_export_info->dictionary.value_native_name = NATIVE_GDMONOCLASS_NAME(value_type.type_class);
-				}
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype))
+				return Variant::DICTIONARY;
 
+			if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
 				return Variant::DICTIONARY;
 			}
 
-			exc = NULL;
-			GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
-			MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
-			UNLIKELY_UNHANDLED_EXCEPTION(exc);
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype))
+				return Variant::ARRAY;
 
-			if (is_array) {
-				if (r_export_info) {
-					MonoReflectionType *elem_reftype;
+			if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+				return Variant::ARRAY;
+			}
+		} break;
+
+		default: {
+		} break;
+	}
 
-					exc = NULL;
-					invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType),
-							reftype, &elem_reftype, &exc);
-					UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	// Unknown
+	return Variant::NIL;
+}
 
-					ManagedType elem_type = ManagedType::from_reftype(elem_reftype);
+bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
+	switch (p_array_type.type_encoding) {
+		case MONO_TYPE_GENERICINST: {
+			MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type());
 
-					r_export_info->array.element_type = managed_to_variant_type(elem_type);
-					r_export_info->array.element_native_name = NATIVE_GDMONOCLASS_NAME(elem_type.type_class);
-				}
+			if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) {
+				MonoReflectionType *elem_reftype;
 
-				return Variant::ARRAY;
-			}
+				GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype);
 
-			if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
-				return Variant::DICTIONARY;
+				r_elem_type = ManagedType::from_reftype(elem_reftype);
+				return true;
 			}
 
-			if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-				return Variant::ARRAY;
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) {
+				r_elem_type = ManagedType::from_reftype(elem_reftype);
+				return true;
 			}
 		} break;
+		default: {
+		} break;
+	}
+
+	return false;
+}
 
+bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
+	switch (p_dictionary_type.type_encoding) {
+		case MONO_TYPE_GENERICINST: {
+			MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type());
+
+			if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) {
+				MonoReflectionType *key_reftype;
+				MonoReflectionType *value_reftype;
+
+				GDMonoUtils::Marshal::dictionary_get_key_value_types(dict_reftype, &key_reftype, &value_reftype);
+
+				r_key_type = ManagedType::from_reftype(key_reftype);
+				r_value_type = ManagedType::from_reftype(value_reftype);
+				return true;
+			}
+
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) {
+				r_key_type = ManagedType::from_reftype(key_reftype);
+				r_value_type = ManagedType::from_reftype(value_reftype);
+				return true;
+			}
+		} break;
 		default: {
 		} break;
 	}
 
-	// Unknown
-	return Variant::NIL;
+	return false;
 }
 
 String mono_to_utf8_string(MonoString *p_mono_string) {
@@ -502,10 +541,26 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 				return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
 			}
 
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+				return GDMonoUtils::create_managed_from(p_var->operator Dictionary(),
+						GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
 				return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
 			}
 
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+				return GDMonoUtils::create_managed_from(p_var->operator Array(),
+						GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 				return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
 			}
@@ -603,28 +658,32 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 			case MONO_TYPE_GENERICINST: {
 				MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
 
-				MonoException *exc = NULL;
-				GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
-				MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
-				if (is_dict) {
+				if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 					return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
 				}
 
-				exc = NULL;
-				GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
-				MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
-				if (is_array) {
+				if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
 					return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
 				}
 
+				// The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+				MonoReflectionType *key_reftype, *value_reftype;
+				if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+					return GDMonoUtils::create_managed_from(p_var->operator Dictionary(),
+							GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
+				}
+
 				if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
 					return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
 				}
 
+				MonoReflectionType *elem_reftype;
+				if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+					return GDMonoUtils::create_managed_from(p_var->operator Array(),
+							GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+				}
+
 				if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 					return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
 				}
@@ -787,66 +846,64 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
 				return ptr ? Variant(*ptr) : Variant();
 			}
 
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+				return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
+			}
+
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
-				Dictionary dict;
-				MonoException *exc = NULL;
-				invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
-				return dict;
+				return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
+			}
+
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+				return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
 			}
 
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-				Array array;
-				MonoException *exc = NULL;
-				invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
-				return array;
+				return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
 			}
 		} break;
 
 		case MONO_TYPE_GENERICINST: {
 			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
 
-			MonoException *exc = NULL;
-
-			GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
-			MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
-			UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
-			if (is_dict) {
-				exc = NULL;
+			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
+				MonoException *exc = NULL;
 				MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
 				UNLIKELY_UNHANDLED_EXCEPTION(exc);
 				return *unbox<Dictionary *>(ret);
 			}
 
-			exc = NULL;
-
-			GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
-			MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
-			UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
-			if (is_array) {
-				exc = NULL;
+			if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+				MonoException *exc = NULL;
 				MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
 				UNLIKELY_UNHANDLED_EXCEPTION(exc);
 				return *unbox<Array *>(ret);
 			}
 
+			// The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+			MonoReflectionType *key_reftype, *value_reftype;
+			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+				return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
+			}
+
 			if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
-				Dictionary dict;
-				exc = NULL;
-				invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
-				return dict;
+				return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
+			}
+
+			MonoReflectionType *elem_reftype;
+			if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+				return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
 			}
 
 			if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-				Array array;
-				exc = NULL;
-				invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
-				return array;
+				return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
 			}
 		} break;
 	}
@@ -1075,4 +1132,5 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
 
 	return ret;
 }
+
 } // namespace GDMonoMarshal

+ 3 - 21
modules/mono/mono_gd/gd_mono_marshal.h

@@ -57,28 +57,10 @@ T unbox(MonoObject *p_obj) {
 #define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
 #define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
 
-// FIXME: Made this struct in a hurry. It could be done differently.
-struct ExportInfo {
-	struct ArrayInfo {
-		Variant::Type element_type;
-		String element_native_name;
-
-		ArrayInfo() :
-				element_type(Variant::NIL) {}
-	} array;
-	struct DictionaryInfo {
-		Variant::Type key_type;
-		String key_native_name;
-		Variant::Type value_type;
-		String value_native_name;
-
-		DictionaryInfo() :
-				key_type(Variant::NIL),
-				value_type(Variant::NIL) {}
-	} dictionary;
-};
+Variant::Type managed_to_variant_type(const ManagedType &p_type);
 
-Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info = NULL);
+bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
+bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
 
 // String
 

+ 4 - 0
modules/mono/mono_gd/gd_mono_method.cpp

@@ -74,6 +74,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
 	method_info = MethodInfo();
 }
 
+GDMonoClass *GDMonoMethod::get_enclosing_class() const {
+	return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method));
+}
+
 bool GDMonoMethod::is_static() {
 	return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC;
 }

+ 4 - 2
modules/mono/mono_gd/gd_mono_method.h

@@ -57,9 +57,11 @@ class GDMonoMethod : public IMonoClassMember {
 	MonoMethod *mono_method;
 
 public:
-	virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_METHOD; }
+	virtual GDMonoClass *get_enclosing_class() const GD_FINAL;
 
-	virtual StringName get_name() GD_FINAL { return name; }
+	virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; }
+
+	virtual StringName get_name() const GD_FINAL { return name; }
 
 	virtual bool is_static() GD_FINAL;
 

+ 4 - 2
modules/mono/mono_gd/gd_mono_property.h

@@ -47,9 +47,11 @@ class GDMonoProperty : public IMonoClassMember {
 	MonoCustomAttrInfo *attributes;
 
 public:
-	virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_PROPERTY; }
+	virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
 
-	virtual StringName get_name() GD_FINAL { return name; }
+	virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; }
+
+	virtual StringName get_name() const GD_FINAL { return name; }
 
 	virtual bool is_static() GD_FINAL;
 	virtual Visibility get_visibility() GD_FINAL;

+ 126 - 2
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -109,7 +109,7 @@ void MonoCache::clear_members() {
 	class_NodePath = NULL;
 	class_RID = NULL;
 	class_GodotObject = NULL;
-	class_GodotReference = NULL;
+	class_GodotResource = NULL;
 	class_Node = NULL;
 	class_Control = NULL;
 	class_Spatial = NULL;
@@ -151,12 +151,25 @@ void MonoCache::clear_members() {
 	methodthunk_SignalAwaiter_FailureCallback = NULL;
 	methodthunk_GodotTaskScheduler_Activate = NULL;
 
+	// Start of MarshalUtils methods
+
 	methodthunk_MarshalUtils_TypeIsGenericArray = NULL;
 	methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL;
+
 	methodthunk_MarshalUtils_ArrayGetElementType = NULL;
 	methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL;
+
+	methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL;
+	methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL;
+
+	methodthunk_MarshalUtils_MakeGenericArrayType = NULL;
+	methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL;
+
 	methodthunk_MarshalUtils_EnumerableToArray = NULL;
 	methodthunk_MarshalUtils_IDictionaryToDictionary = NULL;
+	methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL;
+
+	// End of MarshalUtils methods
 
 	task_scheduler_handle = Ref<MonoGCHandle>();
 }
@@ -217,7 +230,7 @@ void update_godot_api_cache() {
 	CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
 	CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
 	CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
-	CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference));
+	CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
 	CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
 	CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
 	CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
@@ -258,12 +271,28 @@ void update_godot_api_cache() {
 	CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0));
 	CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0));
 
+	// Start of MarshalUtils methods
+
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1));
+
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
+
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
+
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
+
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
+
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2));
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2));
+
+	// End of MarshalUtils methods
 
 #ifdef DEBUG_ENABLED
 	CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
@@ -727,4 +756,99 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
 	invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc);
 }
 
+namespace Marshal {
+
+MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) {
+	TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
+	MonoException *exc = NULL;
+	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return res;
+}
+
+MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) {
+	TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
+	MonoException *exc = NULL;
+	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return res;
+}
+
+void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
+	ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
+	MonoException *exc = NULL;
+	invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+}
+
+void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
+	DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
+	MonoException *exc = NULL;
+	invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+}
+
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
+	GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
+	MonoException *exc = NULL;
+	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return res;
+}
+
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
+	GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
+	MonoException *exc = NULL;
+	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return res;
+}
+
+Array enumerable_to_array(MonoObject *p_enumerable) {
+	Array result;
+	EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
+	MonoException *exc = NULL;
+	invoke_method_thunk(thunk, p_enumerable, &result, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return result;
+}
+
+Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
+	Dictionary result;
+	IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
+	MonoException *exc = NULL;
+	invoke_method_thunk(thunk, p_idictionary, &result, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return result;
+}
+
+Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
+	Dictionary result;
+	GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
+	MonoException *exc = NULL;
+	invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return result;
+}
+
+GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
+	MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
+	MonoException *exc = NULL;
+	MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
+}
+
+GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
+	MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
+	MonoException *exc = NULL;
+	MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
+	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
+}
+
+} // namespace Marshal
+
+// namespace Marshal
+
 } // namespace GDMonoUtils

+ 47 - 3
modules/mono/mono_gd/gd_mono_utils.h

@@ -60,10 +60,41 @@ typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, Mo
 
 typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **);
 typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **);
-typedef MonoBoolean (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
-typedef MonoBoolean (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+
+typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
+typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+
+typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
+typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+
+typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **);
+typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **);
+
 typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **);
 typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
+typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
+
+namespace Marshal {
+
+MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype);
+MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_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);
+
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
+
+GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
+GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
+
+Array enumerable_to_array(MonoObject *p_enumerable);
+Dictionary idictionary_to_dictionary(MonoObject *p_idictionary);
+Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary);
+
+} // namespace Marshal
+
+// End of MarshalUtils methods
 
 struct MonoCache {
 
@@ -114,7 +145,7 @@ struct MonoCache {
 	GDMonoClass *class_NodePath;
 	GDMonoClass *class_RID;
 	GDMonoClass *class_GodotObject;
-	GDMonoClass *class_GodotReference;
+	GDMonoClass *class_GodotResource;
 	GDMonoClass *class_Node;
 	GDMonoClass *class_Control;
 	GDMonoClass *class_Spatial;
@@ -156,12 +187,25 @@ struct MonoCache {
 	SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
 	GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
 
+	// Start of MarshalUtils methods
+
 	TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray;
 	TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary;
+
 	ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType;
 	DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
+
+	GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
+	GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
+
+	MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType;
+	MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType;
+
 	EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray;
 	IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary;
+	GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary;
+
+	// End of MarshalUtils methods
 
 	Ref<MonoGCHandle> task_scheduler_handle;
 

+ 4 - 2
modules/mono/mono_gd/i_mono_class_member.h

@@ -53,9 +53,11 @@ public:
 
 	virtual ~IMonoClassMember() {}
 
-	virtual MemberType get_member_type() = 0;
+	virtual GDMonoClass *get_enclosing_class() const = 0;
 
-	virtual StringName get_name() = 0;
+	virtual MemberType get_member_type() const = 0;
+
+	virtual StringName get_name() const = 0;
 
 	virtual bool is_static() = 0;