Skip to content

Commit

Permalink
C#: Revert marshalling of IDictionary/IEnumerable implementing types
Browse files Browse the repository at this point in the history
Added marshalling for `System.Collections.Generic.List<T>` and
`System.Collections.Generic.Dictionary<TKey, TValue>`.
  • Loading branch information
neikeq committed Apr 23, 2020
1 parent 28f0b15 commit 20b9dbb
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 475 deletions.
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

0 comments on commit 20b9dbb

Please sign in to comment.