C#: Revert marshalling of IDictionary/IEnumerable implementing types

Added marshalling for `System.Collections.Generic.List<T>` and
`System.Collections.Generic.Dictionary<TKey, TValue>`.
This commit is contained in:
Ignacio Etcheverry 2020-04-23 02:19:32 +02:00
parent 28f0b15c9d
commit 20b9dbb1d5
13 changed files with 328 additions and 500 deletions

View file

@ -15,10 +15,7 @@ namespace Godot.Collections
public override bool IsInvalid public override bool IsInvalid
{ {
get get { return handle == IntPtr.Zero; }
{
return handle == IntPtr.Zero;
}
} }
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
@ -43,7 +40,8 @@ namespace Godot.Collections
if (collection == null) if (collection == null)
throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
MarshalUtils.EnumerableToArray(collection, GetPtr()); foreach (object element in collection)
Add(element);
} }
internal Array(ArraySafeHandle handle) internal Array(ArraySafeHandle handle)
@ -272,14 +270,8 @@ namespace Godot.Collections
public T this[int index] public T this[int index]
{ {
get get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
{ set { objectArray[index] = value; }
return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass);
}
set
{
objectArray[index] = value;
}
} }
public int IndexOf(T item) public int IndexOf(T item)
@ -301,18 +293,12 @@ namespace Godot.Collections
public int Count public int Count
{ {
get get { return objectArray.Count; }
{
return objectArray.Count;
}
} }
public bool IsReadOnly public bool IsReadOnly
{ {
get get { return objectArray.IsReadOnly; }
{
return objectArray.IsReadOnly;
}
} }
public void Add(T item) public void Add(T item)

View file

@ -15,10 +15,7 @@ namespace Godot.Collections
public override bool IsInvalid public override bool IsInvalid
{ {
get get { return handle == IntPtr.Zero; }
{
return handle == IntPtr.Zero;
}
} }
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
@ -45,7 +42,8 @@ namespace Godot.Collections
if (dictionary == null) if (dictionary == null)
throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr()); foreach (DictionaryEntry entry in dictionary)
Add(entry.Key, entry.Value);
} }
internal Dictionary(DictionarySafeHandle handle) internal Dictionary(DictionarySafeHandle handle)
@ -330,14 +328,8 @@ namespace Godot.Collections
public TValue this[TKey key] public TValue this[TKey key]
{ {
get get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
{ set { objectDict[key] = value; }
return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass);
}
set
{
objectDict[key] = value;
}
} }
public ICollection<TKey> Keys public ICollection<TKey> Keys
@ -385,18 +377,12 @@ namespace Godot.Collections
public int Count public int Count
{ {
get get { return objectDict.Count; }
{
return objectDict.Count;
}
} }
public bool IsReadOnly public bool IsReadOnly
{ {
get get { return objectDict.IsReadOnly; }
{
return objectDict.IsReadOnly;
}
} }
public void Add(KeyValuePair<TKey, TValue> item) public void Add(KeyValuePair<TKey, TValue> item)
@ -440,7 +426,8 @@ namespace Godot.Collections
public bool Remove(KeyValuePair<TKey, TValue> item) public bool Remove(KeyValuePair<TKey, TValue> item)
{ {
return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
;
} }
// IEnumerable<KeyValuePair<TKey, TValue>> // IEnumerable<KeyValuePair<TKey, TValue>>

View file

@ -16,10 +16,8 @@ namespace Godot
/// <exception cref="System.InvalidOperationException"> /// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
/// </exception> /// </exception>
static bool TypeIsGenericArray(Type type) static bool TypeIsGenericArray(Type type) =>
{ type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
}
/// <summary> /// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
@ -28,10 +26,20 @@ namespace Godot
/// <exception cref="System.InvalidOperationException"> /// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
/// </exception> /// </exception>
static bool TypeIsGenericDictionary(Type type) static bool TypeIsGenericDictionary(Type type) =>
{ type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
} static bool TypeIsSystemGenericList(Type type) =>
type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>);
static bool TypeIsSystemGenericDictionary(Type type) =>
type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>);
static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
static void ArrayGetElementType(Type arrayType, out Type elementType) static void ArrayGetElementType(Type arrayType, out Type elementType)
{ {
@ -45,105 +53,6 @@ namespace Godot
valueType = genericArgs[1]; valueType = genericArgs[1];
} }
static bool GenericIEnumerableIsAssignableFromType(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
return true;
foreach (var interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
return true;
}
Type baseType = type.BaseType;
if (baseType == null)
return false;
return GenericIEnumerableIsAssignableFromType(baseType);
}
static bool GenericIDictionaryIsAssignableFromType(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
return true;
foreach (var interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
return true;
}
Type baseType = type.BaseType;
if (baseType == null)
return false;
return GenericIDictionaryIsAssignableFromType(baseType);
}
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) static Type MakeGenericArrayType(Type elemType)
{ {
return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
@ -153,60 +62,5 @@ namespace Godot
{ {
return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, 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
internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr)
{
if (enumerable is ICollection collection)
{
int count = collection.Count;
object[] tempArray = new object[count];
collection.CopyTo(tempArray, 0);
for (int i = 0; i < count; i++)
{
Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]);
}
}
else
{
foreach (object element in enumerable)
{
Array.godot_icall_Array_Add(godotArrayPtr, element);
}
}
}
internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr)
{
foreach (DictionaryEntry entry in dictionary)
{
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);
}
}
} }
} }

View file

@ -84,6 +84,7 @@ void CachedData::clear_corlib_cache() {
class_IntPtr = nullptr; class_IntPtr = nullptr;
class_System_Collections_IEnumerable = nullptr; class_System_Collections_IEnumerable = nullptr;
class_System_Collections_ICollection = nullptr;
class_System_Collections_IDictionary = nullptr; class_System_Collections_IDictionary = nullptr;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -171,22 +172,18 @@ void CachedData::clear_godot_api_cache() {
methodthunk_MarshalUtils_TypeIsGenericArray.nullify(); methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify(); methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify();
methodthunk_MarshalUtils_TypeIsSystemGenericList.nullify();
methodthunk_MarshalUtils_TypeIsSystemGenericDictionary.nullify();
methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
methodthunk_MarshalUtils_ArrayGetElementType.nullify(); methodthunk_MarshalUtils_ArrayGetElementType.nullify();
methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify();
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType.nullify();
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType.nullify();
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info.nullify();
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info.nullify();
methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); methodthunk_MarshalUtils_MakeGenericArrayType.nullify();
methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify();
methodthunk_MarshalUtils_EnumerableToArray.nullify();
methodthunk_MarshalUtils_IDictionaryToDictionary.nullify();
methodthunk_MarshalUtils_GenericIDictionaryToDictionary.nullify();
// End of MarshalUtils methods // End of MarshalUtils methods
task_scheduler_handle = Ref<MonoGCHandleRef>(); task_scheduler_handle = Ref<MonoGCHandleRef>();
@ -213,6 +210,7 @@ void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
CACHE_CLASS_AND_CHECK(System_Collections_ICollection, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "ICollection"));
CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary")); CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -297,22 +295,18 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericList, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericList", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericDictionary", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1));
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, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 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)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, GODOT_API_CLASS(MarshalUtils)->get_method("EnumerableToArray", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("IDictionaryToDictionary", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryToDictionary", 2));
// End of MarshalUtils methods // End of MarshalUtils methods
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED

View file

@ -58,6 +58,7 @@ struct CachedData {
GDMonoClass *class_IntPtr; // System.IntPtr GDMonoClass *class_IntPtr; // System.IntPtr
GDMonoClass *class_System_Collections_IEnumerable; GDMonoClass *class_System_Collections_IEnumerable;
GDMonoClass *class_System_Collections_ICollection;
GDMonoClass *class_System_Collections_IDictionary; GDMonoClass *class_System_Collections_IDictionary;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -141,22 +142,18 @@ struct CachedData {
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray; GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary; GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericList;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericDictionary;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info;
GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType; GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType;
GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType; GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType;
GDMonoMethodThunk<MonoObject *, Array *> methodthunk_MarshalUtils_EnumerableToArray;
GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_IDictionaryToDictionary;
GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_GenericIDictionaryToDictionary;
// End of MarshalUtils methods // End of MarshalUtils methods
Ref<MonoGCHandleRef> task_scheduler_handle; Ref<MonoGCHandleRef> task_scheduler_handle;
@ -186,14 +183,6 @@ inline void clear_godot_api_cache() {
cached_data.clear_godot_api_cache(); cached_data.clear_godot_api_cache();
} }
_FORCE_INLINE_ bool tools_godot_api_check() {
#ifdef TOOLS_ENABLED
return cached_data.godot_api_cache_updated;
#else
return true; // Assume it's updated if this was called, otherwise it's a bug
#endif
}
} // namespace GDMonoCache } // namespace GDMonoCache
#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class) #define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class)

View file

@ -31,6 +31,7 @@
#include "gd_mono_class.h" #include "gd_mono_class.h"
#include <mono/metadata/attrdefs.h> #include <mono/metadata/attrdefs.h>
#include <mono/metadata/debug-helpers.h>
#include "gd_mono_assembly.h" #include "gd_mono_assembly.h"
#include "gd_mono_cache.h" #include "gd_mono_cache.h"
@ -55,7 +56,11 @@ String GDMonoClass::get_full_name() const {
return get_full_name(mono_class); return get_full_name(mono_class);
} }
MonoType *GDMonoClass::get_mono_type() { String GDMonoClass::get_type_desc() const {
return GDMonoUtils::get_type_desc(get_mono_type());
}
MonoType *GDMonoClass::get_mono_type() const {
// Careful, you cannot compare two MonoType*. // Careful, you cannot compare two MonoType*.
// There is mono_metadata_type_equal, how is this different from comparing two MonoClass*? // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*?
return get_mono_type(mono_class); return get_mono_type(mono_class);
@ -264,6 +269,12 @@ bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr()); return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
} }
bool GDMonoClass::has_public_parameterless_ctor() {
GDMonoMethod *ctor = get_method(".ctor", 0);
return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC;
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count); MethodKey key = MethodKey(p_name, p_params_count);
@ -328,6 +339,9 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class); MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc); mono_method_desc_free(desc);
if (!method)
return nullptr;
ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr); ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr);
return get_method(method); return get_method(method);

View file

@ -31,8 +31,6 @@
#ifndef GD_MONO_CLASS_H #ifndef GD_MONO_CLASS_H
#define GD_MONO_CLASS_H #define GD_MONO_CLASS_H
#include <mono/metadata/debug-helpers.h>
#include "core/map.h" #include "core/map.h"
#include "core/ustring.h" #include "core/ustring.h"
@ -107,7 +105,8 @@ public:
static MonoType *get_mono_type(MonoClass *p_mono_class); static MonoType *get_mono_type(MonoClass *p_mono_class);
String get_full_name() const; String get_full_name() const;
MonoType *get_mono_type(); String get_type_desc() const;
MonoType *get_mono_type() const;
uint32_t get_flags() const; uint32_t get_flags() const;
bool is_static() const; bool is_static() const;
@ -137,6 +136,7 @@ public:
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
bool implements_interface(GDMonoClass *p_interface); bool implements_interface(GDMonoClass *p_interface);
bool has_public_parameterless_ctor();
GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0); GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method); GDMonoMethod *get_method(MonoMethod *p_raw_method);

View file

@ -360,56 +360,22 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break; break;
} }
if (CACHED_CLASS(Dictionary) == type_class) { // Godot.Collections.Dictionary or IDictionary
if (CACHED_CLASS(Dictionary) == type_class || type_class == CACHED_CLASS(System_Collections_IDictionary)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
if (CACHED_CLASS(Array) == type_class) { // Godot.Collections.Array or ICollection or IEnumerable
if (CACHED_CLASS(Array) == type_class ||
type_class == CACHED_CLASS(System_Collections_ICollection) ||
type_class == CACHED_CLASS(System_Collections_IEnumerable)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), 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))) {
if (GDMonoCache::tools_godot_api_check()) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
break;
} else {
MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array());
mono_field_set_value(p_object, mono_field, managed);
break;
}
}
ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'."); ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'.");
} break; } break;
@ -542,52 +508,62 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
// Godot.Collections.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
// Godot.Collections.Array<T>
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
// The order in which we check the following interfaces is very important (dictionaries and generics first) // System.Collections.Generic.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
MonoReflectionType *key_reftype, *value_reftype; MonoReflectionType *key_reftype = nullptr;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { MonoReflectionType *value_reftype = nullptr;
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); MonoObject *managed = GDMonoMarshal::Dictionary_to_system_generic_dict(p_value.operator Dictionary(),
type.type_class, key_reftype, value_reftype);
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { // System.Collections.Generic.List<T>
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
MonoReflectionType *elem_reftype = nullptr;
GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
MonoObject *managed = GDMonoMarshal::Array_to_system_generic_list(p_value.operator Array(),
type.type_class, elem_reftype);
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
// IDictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
MonoReflectionType *key_reftype;
MonoReflectionType *value_reftype;
GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), godot_dict_class);
mono_field_set_value(p_object, mono_field, managed);
break;
}
// ICollection<T> or IEnumerable<T>
if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
MonoReflectionType *elem_reftype; MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
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(), godot_array_class);
if (GDMonoCache::tools_godot_api_check()) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} else {
MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array());
mono_field_set_value(p_object, mono_field, managed);
break;
}
} }
} break; } break;

View file

@ -188,23 +188,14 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
return Variant::ARRAY; return Variant::ARRAY;
} }
// The order in which we check the following interfaces is very important (dictionaries and generics first) // IDictionary
if (p_type.type_class == CACHED_CLASS(System_Collections_IDictionary)) {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
return Variant::DICTIONARY; return Variant::DICTIONARY;
} }
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { // ICollection or IEnumerable
return Variant::DICTIONARY; if (p_type.type_class == CACHED_CLASS(System_Collections_ICollection) ||
} p_type.type_class == CACHED_CLASS(System_Collections_IEnumerable)) {
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
return Variant::ARRAY;
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY; return Variant::ARRAY;
} }
} break; } break;
@ -218,27 +209,33 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
// Godot.Collections.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
return Variant::DICTIONARY; return Variant::DICTIONARY;
} }
// Godot.Collections.Array<T>
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
return Variant::ARRAY; return Variant::ARRAY;
} }
// The order in which we check the following interfaces is very important (dictionaries and generics first) // System.Collections.Generic.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype))
return Variant::DICTIONARY;
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY; return Variant::DICTIONARY;
} }
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) // System.Collections.Generic.List<T>
if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
return Variant::ARRAY; return Variant::ARRAY;
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { // IDictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
return Variant::DICTIONARY;
}
// ICollection<T> or IEnumerable<T>
if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
return Variant::ARRAY; return Variant::ARRAY;
} }
} break; } break;
@ -266,7 +263,10 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) { if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype) ||
GDMonoUtils::Marshal::type_is_system_generic_list(array_reftype) ||
GDMonoUtils::Marshal::type_is_generic_icollection(array_reftype) ||
GDMonoUtils::Marshal::type_is_generic_ienumerable(array_reftype)) {
MonoReflectionType *elem_reftype; MonoReflectionType *elem_reftype;
GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype);
@ -274,12 +274,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
r_elem_type = ManagedType::from_reftype(elem_reftype); r_elem_type = ManagedType::from_reftype(elem_reftype);
return true; return true;
} }
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; } break;
default: { default: {
} break; } break;
@ -293,7 +287,9 @@ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, Ma
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type()); MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type());
if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) { if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype) ||
GDMonoUtils::Marshal::type_is_system_generic_dictionary(dict_reftype) ||
GDMonoUtils::Marshal::type_is_generic_idictionary(dict_reftype)) {
MonoReflectionType *key_reftype; MonoReflectionType *key_reftype;
MonoReflectionType *value_reftype; MonoReflectionType *value_reftype;
@ -303,13 +299,6 @@ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, Ma
r_value_type = ManagedType::from_reftype(value_reftype); r_value_type = ManagedType::from_reftype(value_reftype);
return true; 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; } break;
default: { default: {
} break; } break;
@ -615,41 +604,17 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::create_managed_from(p_var->operator RID()); return GDMonoUtils::create_managed_from(p_var->operator RID());
} }
if (CACHED_CLASS(Dictionary) == type_class) { // Godot.Collections.Dictionary or IDictionary
if (CACHED_CLASS(Dictionary) == type_class || CACHED_CLASS(System_Collections_IDictionary) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
} }
if (CACHED_CLASS(Array) == type_class) { // Godot.Collections.Array or ICollection or IEnumerable
if (CACHED_CLASS(Array) == type_class ||
CACHED_CLASS(System_Collections_ICollection) == type_class ||
CACHED_CLASS(System_Collections_IEnumerable) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); 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(mono_domain_get(), 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))) {
if (GDMonoCache::tools_godot_api_check()) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
} else {
return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
}
}
} break; } break;
case MONO_TYPE_OBJECT: { case MONO_TYPE_OBJECT: {
// Variant // Variant
@ -770,38 +735,48 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
// Godot.Collections.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
} }
// Godot.Collections.Array<T>
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); 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) // System.Collections.Generic.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
MonoReflectionType *key_reftype, *value_reftype; MonoReflectionType *key_reftype = nullptr;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { MonoReflectionType *value_reftype = nullptr;
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); return Dictionary_to_system_generic_dict(p_var->operator Dictionary(), p_type.type_class, key_reftype, value_reftype);
} }
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { // System.Collections.Generic.List<T>
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
MonoReflectionType *elem_reftype = nullptr;
GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
return Array_to_system_generic_list(p_var->operator Array(), p_type.type_class, elem_reftype);
} }
// IDictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
MonoReflectionType *key_reftype;
MonoReflectionType *value_reftype;
GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), godot_dict_class);
}
// ICollection<T> or IEnumerable<T>
if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
MonoReflectionType *elem_reftype; MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
return GDMonoUtils::create_managed_from(p_var->operator Array(), GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
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(), godot_array_class);
if (GDMonoCache::tools_godot_api_check()) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
} else {
return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
}
} }
} break; } break;
} break; } break;
@ -976,13 +951,7 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return ptr ? Variant(*ptr) : Variant(); return ptr ? Variant(*ptr) : Variant();
} }
if (CACHED_CLASS(Array) == type_class) { // Godot.Collections.Dictionary
MonoException *exc = nullptr;
Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant();
}
if (CACHED_CLASS(Dictionary) == type_class) { if (CACHED_CLASS(Dictionary) == type_class) {
MonoException *exc = nullptr; MonoException *exc = nullptr;
Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc);
@ -990,30 +959,19 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return ptr ? Variant(*ptr) : Variant(); return ptr ? Variant(*ptr) : Variant();
} }
// The order in which we check the following interfaces is very important (dictionaries and generics first) // Godot.Collections.Array
if (CACHED_CLASS(Array) == type_class) {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); MonoException *exc = nullptr;
Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc);
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { UNHANDLED_EXCEPTION(exc);
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); return ptr ? Variant(*ptr) : Variant();
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
}
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
} }
} break; } break;
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
// Godot.Collections.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
MonoException *exc = nullptr; MonoException *exc = nullptr;
MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
@ -1021,6 +979,7 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return *unbox<Dictionary *>(ret); return *unbox<Dictionary *>(ret);
} }
// Godot.Collections.Array<T>
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
MonoException *exc = nullptr; MonoException *exc = nullptr;
MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
@ -1028,22 +987,19 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return *unbox<Array *>(ret); return *unbox<Array *>(ret);
} }
// The order in which we check the following interfaces is very important (dictionaries and generics first) // System.Collections.Generic.Dictionary<TKey, TValue>
if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { MonoReflectionType *key_reftype = nullptr;
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); MonoReflectionType *value_reftype = nullptr;
GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
return system_generic_dict_to_Dictionary(p_obj, p_type.type_class, key_reftype, value_reftype);
} }
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { // System.Collections.Generic.List<T>
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
} MonoReflectionType *elem_reftype = nullptr;
GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { return system_generic_list_to_Array(p_obj, p_type.type_class, elem_reftype);
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
} }
} break; } break;
} }
@ -1100,6 +1056,80 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
} }
} }
MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
CRASH_COND(ctor == nullptr);
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
ERR_FAIL_NULL_V(mono_object, nullptr);
GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
MonoObject *godot_dict = GDMonoUtils::create_managed_from(p_dict, godot_dict_class);
void *ctor_args[1] = { godot_dict };
MonoException *exc = nullptr;
ctor->invoke_raw(mono_object, ctor_args, &exc);
UNHANDLED_EXCEPTION(exc);
return mono_object;
}
Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true);
CRASH_COND(godot_dict_ctor == nullptr);
MonoObject *godot_dict = mono_object_new(mono_domain_get(), godot_dict_class->get_mono_ptr());
ERR_FAIL_NULL_V(godot_dict, Dictionary());
void *ctor_args[1] = { p_obj };
MonoException *exc = nullptr;
godot_dict_ctor->invoke_raw(godot_dict, ctor_args, &exc);
UNHANDLED_EXCEPTION(exc);
exc = nullptr;
MonoObject *ret = godot_dict_class->get_method("GetPtr")->invoke(godot_dict, &exc);
UNHANDLED_EXCEPTION(exc);
return *unbox<Dictionary *>(ret);
}
MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) {
GDMonoClass *elem_class = ManagedType::from_reftype(p_elem_reftype).type_class;
String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + elem_class->get_type_desc() + ">)";
GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
CRASH_COND(ctor == nullptr);
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
ERR_FAIL_NULL_V(mono_object, nullptr);
void *ctor_args[1] = { Array_to_mono_array(p_array, elem_class) };
MonoException *exc = nullptr;
ctor->invoke_raw(mono_object, ctor_args, &exc);
UNHANDLED_EXCEPTION(exc);
return mono_object;
}
Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) {
GDMonoMethod *to_array = p_class->get_method("ToArray", 0);
CRASH_COND(to_array == nullptr);
MonoException *exc = nullptr;
MonoArray *mono_array = (MonoArray *)to_array->invoke_raw(p_obj, nullptr, &exc);
UNHANDLED_EXCEPTION(exc);
return mono_array_to_Array(mono_array);
}
MonoArray *Array_to_mono_array(const Array &p_array) { MonoArray *Array_to_mono_array(const Array &p_array) {
int length = p_array.size(); int length = p_array.size();
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length); MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length);

View file

@ -123,6 +123,14 @@ Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_ty
/// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead. /// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead.
String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc); String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc);
// System.Collections.Generic
MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
// Array // Array
MonoArray *Array_to_mono_array(const Array &p_array); MonoArray *Array_to_mono_array(const Array &p_array);

View file

@ -30,13 +30,14 @@
#include "gd_mono_method.h" #include "gd_mono_method.h"
#include <mono/metadata/attrdefs.h>
#include <mono/metadata/debug-helpers.h>
#include "gd_mono_cache.h" #include "gd_mono_cache.h"
#include "gd_mono_class.h" #include "gd_mono_class.h"
#include "gd_mono_marshal.h" #include "gd_mono_marshal.h"
#include "gd_mono_utils.h" #include "gd_mono_utils.h"
#include <mono/metadata/attrdefs.h>
void GDMonoMethod::_update_signature() { void GDMonoMethod::_update_signature() {
// Apparently MonoMethodSignature needs not to be freed. // Apparently MonoMethodSignature needs not to be freed.
// mono_method_signature caches the result, we don't need to cache it ourselves. // mono_method_signature caches the result, we don't need to cache it ourselves.

View file

@ -30,6 +30,7 @@
#include "gd_mono_utils.h" #include "gd_mono_utils.h"
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/exception.h> #include <mono/metadata/exception.h>
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
@ -358,6 +359,14 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain; return domain;
} }
String get_type_desc(MonoType *p_type) {
return mono_type_full_name(p_type);
}
String get_type_desc(MonoReflectionType *p_reftype) {
return get_type_desc(mono_reflection_type_get_type(p_reftype));
}
String get_exception_name_and_message(MonoException *p_exc) { String get_exception_name_and_message(MonoException *p_exc) {
String res; String res;
@ -584,6 +593,46 @@ bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
return (bool)res; return (bool)res;
} }
bool type_is_system_generic_list(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool type_is_generic_icollection(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
MonoException *exc = nullptr; MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc);
@ -596,65 +645,6 @@ void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoRefl
UNHANDLED_EXCEPTION(exc); UNHANDLED_EXCEPTION(exc);
} }
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info).invoke(p_reftype, r_elem_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
NO_GLUE_RET(false);
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info).invoke(p_reftype, r_key_reftype, r_value_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
Array enumerable_to_array(MonoObject *p_enumerable) {
NO_GLUE_RET(Array());
Array result;
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray).invoke(p_enumerable, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
}
Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
NO_GLUE_RET(Dictionary());
Dictionary result;
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary).invoke(p_idictionary, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
}
Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
NO_GLUE_RET(Dictionary());
Dictionary result;
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary).invoke(p_generic_idictionary, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
}
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
NO_GLUE_RET(nullptr); NO_GLUE_RET(nullptr);
MonoException *exc = nullptr; MonoException *exc = nullptr;

View file

@ -52,22 +52,18 @@ namespace Marshal {
bool type_is_generic_array(MonoReflectionType *p_reftype); bool type_is_generic_array(MonoReflectionType *p_reftype);
bool type_is_generic_dictionary(MonoReflectionType *p_reftype); bool type_is_generic_dictionary(MonoReflectionType *p_reftype);
bool type_is_system_generic_list(MonoReflectionType *p_reftype);
bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype);
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 array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_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); void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
bool 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_array_type(MonoReflectionType *p_elem_reftype);
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_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 } // namespace Marshal
_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
@ -115,6 +111,9 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class);
MonoDomain *create_domain(const String &p_friendly_name); MonoDomain *create_domain(const String &p_friendly_name);
String get_type_desc(MonoType *p_type);
String get_type_desc(MonoReflectionType *p_reftype);
String get_exception_name_and_message(MonoException *p_exc); String get_exception_name_and_message(MonoException *p_exc);
void set_exception_message(MonoException *p_exc, String message); void set_exception_message(MonoException *p_exc, String message);