Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

C#: Revert marshalling of IDictionary/IEnumerable implementing types #38141

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 7 additions & 21 deletions modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)

public override bool IsInvalid
{
get
{
return handle == IntPtr.Zero;
}
get { return handle == IntPtr.Zero; }
}

protected override bool ReleaseHandle()
Expand All @@ -43,7 +40,8 @@ public Array(IEnumerable collection) : this()
if (collection == 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)
Expand Down Expand Up @@ -272,14 +270,8 @@ public Error Resize(int newSize)

public T this[int index]
{
get
{
return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass);
}
set
{
objectArray[index] = value;
}
get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
set { objectArray[index] = value; }
}

public int IndexOf(T item)
Expand All @@ -301,18 +293,12 @@ public void RemoveAt(int index)

public int Count
{
get
{
return objectArray.Count;
}
get { return objectArray.Count; }
}

public bool IsReadOnly
{
get
{
return objectArray.IsReadOnly;
}
get { return objectArray.IsReadOnly; }
}

public void Add(T item)
Expand Down
31 changes: 9 additions & 22 deletions modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)

public override bool IsInvalid
{
get
{
return handle == IntPtr.Zero;
}
get { return handle == IntPtr.Zero; }
}

protected override bool ReleaseHandle()
Expand All @@ -45,7 +42,8 @@ public Dictionary(IDictionary dictionary) : this()
if (dictionary == 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)
Expand Down Expand Up @@ -330,14 +328,8 @@ public Dictionary<TKey, TValue> Duplicate(bool deep = false)

public TValue this[TKey key]
{
get
{
return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass);
}
set
{
objectDict[key] = value;
}
get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
set { objectDict[key] = value; }
}

public ICollection<TKey> Keys
Expand Down Expand Up @@ -385,18 +377,12 @@ public bool TryGetValue(TKey key, out TValue value)

public int Count
{
get
{
return objectDict.Count;
}
get { return objectDict.Count; }
}

public bool IsReadOnly
{
get
{
return objectDict.IsReadOnly;
}
get { return objectDict.IsReadOnly; }
}

public void Add(KeyValuePair<TKey, TValue> item)
Expand Down Expand Up @@ -440,7 +426,8 @@ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)

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>>
Expand Down
178 changes: 16 additions & 162 deletions modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ static class MarshalUtils
/// <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<>);
}
static bool TypeIsGenericArray(Type type) =>
type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);

/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
Expand All @@ -28,10 +26,20 @@ static bool TypeIsGenericArray(Type type)
/// <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 bool TypeIsGenericDictionary(Type type) =>
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)
{
Expand All @@ -45,105 +53,6 @@ static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, ou
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)
{
return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
Expand All @@ -153,60 +62,5 @@ 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

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);
}
}
}
}
Loading