diff options
author | Ignacio Roldán Etcheverry <ignalfonsore@gmail.com> | 2022-07-28 17:41:50 +0200 |
---|---|---|
committer | Ignacio Roldán Etcheverry <ignalfonsore@gmail.com> | 2022-08-22 03:36:52 +0200 |
commit | 3123be2384c14f7dd156b1cc2d53d822002b837a (patch) | |
tree | 34358a0c1b1e2555b8df0a5c346e20e4d9ff6645 /modules/mono/glue | |
parent | 344f5028d48d4a5caf321abdf023c34f52aae0a4 (diff) |
C#: Array, Dictionary and marshaling refactoring
- Array and Dictionary now store `Variant` instead of `System.Object`.
- Removed generic Array and Dictionary.
They cause too much issues, heavily relying on reflection and
very limited by the lack of a generic specialization.
- Removed support for non-Godot collections.
Support for them also relied heavily on reflection for marshaling.
Support for them will likely be re-introduced in the future, but
it will have to rely on source generators instead of reflection.
- Reduced our use of reflection.
The remaining usages will be moved to source generators soon.
The only usage that I'm not sure yet how to replace is dynamic
invocation of delegates.
Diffstat (limited to 'modules/mono/glue')
16 files changed, 556 insertions, 1486 deletions
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 8ceb7ea882..51d7d8195b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Godot.NativeInterop; @@ -14,8 +13,8 @@ namespace Godot.Collections /// such as <see cref="System.Array"/> or <see cref="List{T}"/>. /// </summary> public sealed class Array : - IList<object>, - IReadOnlyList<object>, + IList<Variant>, + IReadOnlyList<Variant>, ICollection, IDisposable { @@ -37,22 +36,90 @@ namespace Godot.Collections /// </summary> /// <param name="collection">The collection of elements to construct from.</param> /// <returns>A new Godot Array.</returns> - public Array(IEnumerable collection) : this() + public Array(IEnumerable<Variant> collection) : this() { if (collection == null) throw new ArgumentNullException(nameof(collection)); - foreach (object element in collection) + foreach (Variant element in collection) Add(element); } - // TODO: This must be removed. Lots of silent mistakes as it takes pretty much anything. /// <summary> /// Constructs a new <see cref="Array"/> from the given objects. /// </summary> /// <param name="array">The objects to put in the new array.</param> /// <returns>A new Godot Array.</returns> - public Array(params object[] array) : this() + public Array(Variant[] array) : this() + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; + } + + public Array(Span<StringName> array) : this() + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; + } + + public Array(Span<NodePath> array) : this() + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; + } + + public Array(Span<RID> array) : this() + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; + } + + // We must use ReadOnlySpan instead of Span here as this can accept implicit conversions + // from derived types (e.g.: Node[]). Implicit conversion from Derived[] to Base[] are + // fine as long as the array is not mutated. However, Span does this type checking at + // instantiation, so it's not possible to use it even when not mutating anything. + // ReSharper disable once RedundantNameQualifier + public Array(ReadOnlySpan<Godot.Object> array) : this() { if (array == null) throw new ArgumentNullException(nameof(array)); @@ -170,15 +237,15 @@ namespace Godot.Collections } /// <summary> - /// Returns the object at the given <paramref name="index"/>. + /// Returns the item at the given <paramref name="index"/>. /// </summary> - /// <value>The object at the given <paramref name="index"/>.</value> - public unsafe object this[int index] + /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value> + public unsafe Variant this[int index] { get { GetVariantBorrowElementAt(index, out godot_variant borrowElem); - return Marshaling.ConvertVariantToManagedObject(borrowElem); + return Variant.CreateCopyingBorrowed(borrowElem); } set { @@ -186,29 +253,30 @@ namespace Godot.Collections throw new ArgumentOutOfRangeException(nameof(index)); var self = (godot_array)NativeValue; godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); - ptrw[index] = Marshaling.ConvertManagedObjectToVariant(value); + godot_variant* itemPtr = &ptrw[index]; + (*itemPtr).Dispose(); + *itemPtr = value.CopyNativeVariant(); } } /// <summary> - /// Adds an object to the end of this <see cref="Array"/>. + /// Adds an item to the end of this <see cref="Array"/>. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> - /// <param name="item">The object to add.</param> - /// <returns>The new size after adding the object.</returns> - public void Add(object item) + /// <param name="item">The <see cref="Variant"/> item to add.</param> + public void Add(Variant item) { - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); + godot_variant variantValue = (godot_variant)item.NativeVar; var self = (godot_array)NativeValue; _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); } /// <summary> - /// Checks if this <see cref="Array"/> contains the given object. + /// Checks if this <see cref="Array"/> contains the given item. /// </summary> - /// <param name="item">The item to look for.</param> - /// <returns>Whether or not this array contains the given object.</returns> - public bool Contains(object item) => IndexOf(item) != -1; + /// <param name="item">The <see cref="Variant"/> item to look for.</param> + /// <returns>Whether or not this array contains the given item.</returns> + public bool Contains(Variant item) => IndexOf(item) != -1; /// <summary> /// Erases all items from this <see cref="Array"/>. @@ -216,32 +284,32 @@ namespace Godot.Collections public void Clear() => Resize(0); /// <summary> - /// Searches this <see cref="Array"/> for an object + /// Searches this <see cref="Array"/> for an item /// and returns its index or -1 if not found. /// </summary> - /// <param name="item">The object to search for.</param> - /// <returns>The index of the object, or -1 if not found.</returns> - public int IndexOf(object item) + /// <param name="item">The <see cref="Variant"/> item to search for.</param> + /// <returns>The index of the item, or -1 if not found.</returns> + public int IndexOf(Variant item) { - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); + godot_variant variantValue = (godot_variant)item.NativeVar; var self = (godot_array)NativeValue; return NativeFuncs.godotsharp_array_index_of(ref self, variantValue); } /// <summary> - /// Inserts a new object at a given position in the array. + /// Inserts a new item at a given position in the array. /// The position must be a valid position of an existing item, /// or the position at the end of the array. /// Existing items will be moved to the right. /// </summary> /// <param name="index">The index to insert at.</param> - /// <param name="item">The object to insert.</param> - public void Insert(int index, object item) + /// <param name="item">The <see cref="Variant"/> item to insert.</param> + public void Insert(int index, Variant item) { if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); + godot_variant variantValue = (godot_variant)item.NativeVar; var self = (godot_array)NativeValue; NativeFuncs.godotsharp_array_insert(ref self, index, variantValue); } @@ -251,7 +319,7 @@ namespace Godot.Collections /// from this <see cref="Array"/>. /// </summary> /// <param name="item">The value to remove.</param> - public bool Remove(object item) + public bool Remove(Variant item) { int index = IndexOf(item); if (index >= 0) @@ -285,19 +353,19 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count => NativeValue.DangerousSelfRef.Size; - public bool IsSynchronized => false; + bool ICollection.IsSynchronized => false; - public object SyncRoot => false; + object ICollection.SyncRoot => false; - public bool IsReadOnly => false; + bool ICollection<Variant>.IsReadOnly => false; /// <summary> /// Copies the elements of this <see cref="Array"/> to the given - /// untyped C# array, starting at the given index. + /// <see cref="Variant"/> C# array, starting at the given index. /// </summary> /// <param name="array">The array to copy to.</param> /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(object[] array, int arrayIndex) + public void CopyTo(Variant[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -320,8 +388,7 @@ namespace Godot.Collections { for (int i = 0; i < count; i++) { - object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]); - array[arrayIndex] = obj; + array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]); arrayIndex++; } } @@ -364,37 +431,13 @@ namespace Godot.Collections return Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, type); } - internal void CopyToGeneric<T>(T[] array, int arrayIndex, Type type = null) - { - if (array == null) - throw new ArgumentNullException(nameof(array), "Value cannot be null."); - - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), - "Number was less than the array's lower bound in the first dimension."); - - var typeOfElements = type ?? typeof(T); - - int count = Count; - - if (array.Length < (arrayIndex + count)) - throw new ArgumentException( - "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); - - for (int i = 0; i < count; i++) - { - array[arrayIndex] = (T)GetAtAsType(i, typeOfElements); - arrayIndex++; - } - } - // IEnumerable /// <summary> /// Gets an enumerator for this <see cref="Array"/>. /// </summary> /// <returns>An enumerator.</returns> - public IEnumerator<object> GetEnumerator() + public IEnumerator<Variant> GetEnumerator() { int count = Count; @@ -436,289 +479,4 @@ namespace Godot.Collections elem = NativeValue.DangerousSelfRef.Elements[index]; } } - - internal interface IGenericGodotArray - { - Array UnderlyingArray { get; } - Type TypeOfElements { get; } - } - - // TODO: Now we should be able to avoid boxing - /// <summary> - /// Typed wrapper around Godot's Array class, an array of Variant - /// typed elements allocated in the engine in C++. Useful when - /// interfacing with the engine. Otherwise prefer .NET collections - /// such as arrays or <see cref="List{T}"/>. - /// </summary> - /// <typeparam name="T">The type of the array.</typeparam> - [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] - [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")] - public sealed class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>, IGenericGodotArray - { - private readonly Array _underlyingArray; - - internal ref godot_array.movable NativeValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _underlyingArray.NativeValue; - } - - // ReSharper disable StaticMemberInGenericType - // Warning is about unique static fields being created for each generic type combination: - // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html - // In our case this is exactly what we want. - private static readonly Type TypeOfElements = typeof(T); - // ReSharper restore StaticMemberInGenericType - - Array IGenericGodotArray.UnderlyingArray => _underlyingArray; - Type IGenericGodotArray.TypeOfElements => TypeOfElements; - - /// <summary> - /// Constructs a new empty <see cref="Array{T}"/>. - /// </summary> - public Array() - { - _underlyingArray = new Array(); - } - - /// <summary> - /// Constructs a new <see cref="Array{T}"/> from the given collection's elements. - /// </summary> - /// <param name="collection">The collection of elements to construct from.</param> - /// <returns>A new Godot Array.</returns> - public Array(IEnumerable<T> collection) - { - if (collection == null) - throw new ArgumentNullException(nameof(collection)); - - _underlyingArray = new Array(collection); - } - - /// <summary> - /// Constructs a new <see cref="Array{T}"/> from the given items. - /// </summary> - /// <param name="array">The items to put in the new array.</param> - /// <returns>A new Godot Array.</returns> - public Array(params T[] array) : this() - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - - _underlyingArray = new Array(array); - } - - /// <summary> - /// Constructs a typed <see cref="Array{T}"/> from an untyped <see cref="Array"/>. - /// </summary> - /// <param name="array">The untyped array to construct from.</param> - public Array(Array array) - { - _underlyingArray = array; - } - - // Explicit name to make it very clear - internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) - => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); - - /// <summary> - /// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>. - /// </summary> - /// <param name="from">The typed array to convert.</param> - public static explicit operator Array(Array<T> from) - { - return from?._underlyingArray; - } - - /// <summary> - /// Duplicates this <see cref="Array{T}"/>. - /// </summary> - /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> - /// <returns>A new Godot Array.</returns> - public Array<T> Duplicate(bool deep = false) - { - return new Array<T>(_underlyingArray.Duplicate(deep)); - } - - /// <summary> - /// Resizes this <see cref="Array{T}"/> to the given size. - /// </summary> - /// <param name="newSize">The new size of the array.</param> - /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> - public Error Resize(int newSize) - { - return _underlyingArray.Resize(newSize); - } - - /// <summary> - /// Shuffles the contents of this <see cref="Array{T}"/> into a random order. - /// </summary> - public void Shuffle() - { - _underlyingArray.Shuffle(); - } - - /// <summary> - /// Concatenates these two <see cref="Array{T}"/>s. - /// </summary> - /// <param name="left">The first array.</param> - /// <param name="right">The second array.</param> - /// <returns>A new Godot Array with the contents of both arrays.</returns> - public static Array<T> operator +(Array<T> left, Array<T> right) - { - if (left == null) - { - if (right == null) - return new Array<T>(); - - return right.Duplicate(deep: false); - } - - if (right == null) - return left.Duplicate(deep: false); - - return new Array<T>(left._underlyingArray + right._underlyingArray); - } - - // IList<T> - - /// <summary> - /// Returns the value at the given <paramref name="index"/>. - /// </summary> - /// <value>The value at the given <paramref name="index"/>.</value> - public T this[int index] - { - get => (T)_underlyingArray.GetAtAsType(index, TypeOfElements); - set => _underlyingArray[index] = value; - } - - /// <summary> - /// Searches this <see cref="Array{T}"/> for an item - /// and returns its index or -1 if not found. - /// </summary> - /// <param name="item">The item to search for.</param> - /// <returns>The index of the item, or -1 if not found.</returns> - public int IndexOf(T item) - { - return _underlyingArray.IndexOf(item); - } - - /// <summary> - /// Inserts a new item at a given position in the <see cref="Array{T}"/>. - /// The position must be a valid position of an existing item, - /// or the position at the end of the array. - /// Existing items will be moved to the right. - /// </summary> - /// <param name="index">The index to insert at.</param> - /// <param name="item">The item to insert.</param> - public void Insert(int index, T item) - { - _underlyingArray.Insert(index, item); - } - - /// <summary> - /// Removes an element from this <see cref="Array{T}"/> by index. - /// </summary> - /// <param name="index">The index of the element to remove.</param> - public void RemoveAt(int index) - { - _underlyingArray.RemoveAt(index); - } - - // ICollection<T> - - /// <summary> - /// Returns the number of elements in this <see cref="Array{T}"/>. - /// This is also known as the size or length of the array. - /// </summary> - /// <returns>The number of elements.</returns> - public int Count => _underlyingArray.Count; - - bool ICollection<T>.IsReadOnly => false; - - /// <summary> - /// Adds an item to the end of this <see cref="Array{T}"/>. - /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. - /// </summary> - /// <param name="item">The item to add.</param> - /// <returns>The new size after adding the item.</returns> - public void Add(T item) - { - _underlyingArray.Add(item); - } - - /// <summary> - /// Erases all items from this <see cref="Array{T}"/>. - /// </summary> - public void Clear() - { - _underlyingArray.Clear(); - } - - /// <summary> - /// Checks if this <see cref="Array{T}"/> contains the given item. - /// </summary> - /// <param name="item">The item to look for.</param> - /// <returns>Whether or not this array contains the given item.</returns> - public bool Contains(T item) - { - return _underlyingArray.Contains(item); - } - - /// <summary> - /// Copies the elements of this <see cref="Array{T}"/> to the given - /// C# array, starting at the given index. - /// </summary> - /// <param name="array">The C# array to copy to.</param> - /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(T[] array, int arrayIndex) => - _underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements); - - /// <summary> - /// Removes the first occurrence of the specified value - /// from this <see cref="Array{T}"/>. - /// </summary> - /// <param name="item">The value to remove.</param> - /// <returns>A <see langword="bool"/> indicating success or failure.</returns> - public bool Remove(T item) - { - int index = IndexOf(item); - if (index >= 0) - { - RemoveAt(index); - return true; - } - - return false; - } - - // IEnumerable<T> - - /// <summary> - /// Gets an enumerator for this <see cref="Array{T}"/>. - /// </summary> - /// <returns>An enumerator.</returns> - public IEnumerator<T> GetEnumerator() - { - int count = _underlyingArray.Count; - - for (int i = 0; i < count; i++) - { - yield return this[i]; - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// <summary> - /// Converts this <see cref="Array{T}"/> to a string. - /// </summary> - /// <returns>A string representation of this array.</returns> - public override string ToString() => _underlyingArray.ToString(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Array<T> from) => Variant.From(from); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Array<T>(Variant from) => from.AsGodotGenericArray<T>(); - } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 3636a08377..ae44f8f4ba 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -206,11 +206,8 @@ namespace Godot.Bridge // Save instance state - var info = new GodotSerializationInfo( - Collections.Dictionary<StringName, object>.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_dictionary_new_copy(*propertiesState)), - Collections.Dictionary<StringName, Collections.Array>.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_dictionary_new_copy(*signalEventsState))); + using var info = GodotSerializationInfo.CreateCopyingBorrowed( + *propertiesState, *signalEventsState); godotObject.SaveGodotObjectData(info); } @@ -236,11 +233,8 @@ namespace Godot.Bridge // Restore instance state - var info = new GodotSerializationInfo( - Collections.Dictionary<StringName, object>.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_dictionary_new_copy(*propertiesState)), - Collections.Dictionary<StringName, Collections.Array>.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_dictionary_new_copy(*signalEventsState))); + using var info = GodotSerializationInfo.CreateCopyingBorrowed( + *propertiesState, *signalEventsState); godotObject.RestoreGodotObjectData(info); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs index 53aeff8207..8f26967dcd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs @@ -1,35 +1,43 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Godot.NativeInterop; namespace Godot.Bridge; -public class GodotSerializationInfo +public class GodotSerializationInfo : IDisposable { - private readonly Collections.Dictionary<StringName, object> _properties = new(); - private readonly Collections.Dictionary<StringName, Collections.Array> _signalEvents = new(); + private readonly Collections.Dictionary _properties; + private readonly Collections.Dictionary _signalEvents; - internal GodotSerializationInfo() + public void Dispose() { + _properties?.Dispose(); + _signalEvents?.Dispose(); + + GC.SuppressFinalize(this); + } + + private GodotSerializationInfo(in godot_dictionary properties, in godot_dictionary signalEvents) + { + _properties = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(properties); + _signalEvents = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(signalEvents); } - internal GodotSerializationInfo( - Collections.Dictionary<StringName, object> properties, - Collections.Dictionary<StringName, Collections.Array> signalEvents - ) + internal static GodotSerializationInfo CreateCopyingBorrowed( + in godot_dictionary properties, in godot_dictionary signalEvents) { - _properties = properties; - _signalEvents = signalEvents; + return new(NativeFuncs.godotsharp_dictionary_new_copy(properties), + NativeFuncs.godotsharp_dictionary_new_copy(signalEvents)); } - public void AddProperty(StringName name, object value) + public void AddProperty(StringName name, Variant value) { _properties[name] = value; } - public bool TryGetProperty<T>(StringName name, [MaybeNullWhen(false)] out T value) + public bool TryGetProperty(StringName name, out Variant value) { - return _properties.TryGetValueAsType(name, out value); + return _properties.TryGetValue(name, out value); } public void AddSignalEventDelegate(StringName name, Delegate eventDelegate) @@ -49,9 +57,9 @@ public class GodotSerializationInfo public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value) where T : Delegate { - if (_signalEvents.TryGetValue(name, out Collections.Array serializedData)) + if (_signalEvents.TryGetValue(name, out Variant serializedData)) { - if (DelegateUtils.TryDeserializeDelegate(serializedData, out var eventDelegate)) + if (DelegateUtils.TryDeserializeDelegate(serializedData.AsGodotArray(), out var eventDelegate)) { value = eventDelegate as T; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs index 50260163bd..647ae436ff 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs @@ -11,10 +11,10 @@ public struct MethodInfo public MethodFlags Flags { get; init; } public int Id { get; init; } = 0; public List<PropertyInfo>? Arguments { get; init; } - public List<object>? DefaultArguments { get; init; } + public List<Variant>? DefaultArguments { get; init; } - public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags, List<PropertyInfo>? arguments, - List<object>? defaultArguments) + public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags, + List<PropertyInfo>? arguments, List<Variant>? defaultArguments) { Name = name; ReturnVal = returnVal; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 03094cbe81..0dc5ba7678 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -608,8 +608,8 @@ namespace Godot.Bridge methodParams.Add(new Collections.Dictionary() { { "name", param.Name }, - { "type", param.Type }, - { "usage", param.Usage } + { "type", (int)param.Type }, + { "usage", (int)param.Usage } }); } } @@ -628,7 +628,7 @@ namespace Godot.Bridge // RPC functions - Collections.Dictionary<string, Collections.Dictionary> rpcFunctions = new(); + Collections.Dictionary rpcFunctions = new(); top = scriptType; @@ -665,7 +665,7 @@ namespace Godot.Bridge } *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy( - (godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue); + (godot_dictionary)(rpcFunctions).NativeValue); // Event signals @@ -696,8 +696,8 @@ namespace Godot.Bridge signalParams.Add(new Collections.Dictionary() { { "name", param.Name }, - { "type", param.Type }, - { "usage", param.Usage } + { "type", (int)param.Type }, + { "usage", (int)param.Usage } }); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs index ef75ff446a..8d0e77d171 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs @@ -85,7 +85,7 @@ namespace Godot /// </summary> /// <param name="args">Arguments that will be passed to the method call.</param> /// <returns>The value returned by the method.</returns> - public unsafe object Call(params object[] args) + public unsafe Variant Call(params Variant[] args) { using godot_callable callable = Marshaling.ConvertCallableToNative(this); @@ -106,13 +106,13 @@ namespace Godot { for (int i = 0; i < argc; i++) { - varargs[i] = Marshaling.ConvertManagedObjectToVariant(args[i]); + varargs[i] = (godot_variant)args[i].NativeVar; argsPtr[i] = new IntPtr(&varargs[i]); } - using godot_variant ret = NativeFuncs.godotsharp_callable_call(callable, + godot_variant ret = NativeFuncs.godotsharp_callable_call(callable, (godot_variant**)argsPtr, argc, out _); - return Marshaling.ConvertVariantToManagedObject(ret); + return Variant.CreateTakingOwnershipOfDisposableValue(ret); } } @@ -121,7 +121,7 @@ namespace Godot /// Arguments can be passed and should match the method's signature. /// </summary> /// <param name="args">Arguments that will be passed to the method call.</param> - public unsafe void CallDeferred(params object[] args) + public unsafe void CallDeferred(params Variant[] args) { using godot_callable callable = Marshaling.ConvertCallableToNative(this); @@ -142,7 +142,7 @@ namespace Godot { for (int i = 0; i < argc; i++) { - varargs[i] = Marshaling.ConvertManagedObjectToVariant(args[i]); + varargs[i] = (godot_variant)args[i].NativeVar; argsPtr[i] = new IntPtr(&varargs[i]); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 48eec66182..266038a0af 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -99,7 +99,7 @@ namespace Godot if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer)) { - serializedData.Add(buffer); + serializedData.Add((Span<byte>)buffer); return true; } @@ -181,8 +181,18 @@ namespace Godot if (variantType == Variant.Type.Nil) return false; + static byte[] Var2Bytes(in godot_variant var) + { + NativeFuncs.godotsharp_var2bytes(var, false.ToGodotBool(), out var varBytes); + using (varBytes) + return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); + } + writer.Write(field.Name); - byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target)); + + var fieldValue = field.GetValue(target); + using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue); + byte[] valueBuffer = Var2Bytes(fieldValueVariant); writer.Write(valueBuffer.Length); writer.Write(valueBuffer); } @@ -320,9 +330,14 @@ namespace Godot internal static bool TryDeserializeDelegate(Collections.Array serializedData, [MaybeNullWhen(false)] out Delegate @delegate) { + @delegate = null; + if (serializedData.Count == 1) { - object elem = serializedData[0]; + var elem = serializedData[0].Obj; + + if (elem == null) + return false; if (elem is Collections.Array multiCastData) return TryDeserializeDelegate(multiCastData, out @delegate); @@ -330,12 +345,15 @@ namespace Godot return TryDeserializeSingleDelegate((byte[])elem, out @delegate); } - @delegate = null; - var delegates = new List<Delegate>(serializedData.Count); - foreach (object elem in serializedData) + foreach (Variant variantElem in serializedData) { + var elem = variantElem.Obj; + + if (elem == null) + continue; + if (elem is Collections.Array multiCastData) { if (TryDeserializeDelegate(multiCastData, out Delegate? oneDelegate)) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index a71ee1190e..c3d500119a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -2,10 +2,6 @@ using System; using System.Collections.Generic; using System.Collections; using Godot.NativeInterop; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Linq; -using System.Runtime.CompilerServices; namespace Godot.Collections { @@ -15,9 +11,8 @@ namespace Godot.Collections /// interfacing with the engine. /// </summary> public sealed class Dictionary : - IDictionary<object, object>, - IDictionary, - IReadOnlyDictionary<object, object>, + IDictionary<Variant, Variant>, + IReadOnlyDictionary<Variant, Variant>, IDisposable { internal godot_dictionary.movable NativeValue; @@ -33,20 +28,6 @@ namespace Godot.Collections _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } - /// <summary> - /// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements. - /// </summary> - /// <param name="dictionary">The dictionary to construct from.</param> - /// <returns>A new Godot Dictionary.</returns> - public Dictionary(IDictionary dictionary) : this() - { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); - - foreach (DictionaryEntry entry in dictionary) - Add(entry.Key, entry.Value); - } - private Dictionary(godot_dictionary nativeValueToOwn) { NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ? @@ -102,7 +83,7 @@ namespace Godot.Collections /// <summary> /// Gets the collection of keys in this <see cref="Dictionary"/>. /// </summary> - public ICollection<object> Keys + public ICollection<Variant> Keys { get { @@ -116,7 +97,7 @@ namespace Godot.Collections /// <summary> /// Gets the collection of elements in this <see cref="Dictionary"/>. /// </summary> - public ICollection<object> Values + public ICollection<Variant> Values { get { @@ -127,13 +108,9 @@ namespace Godot.Collections } } - IEnumerable<object> IReadOnlyDictionary<object, object>.Keys => Keys; - - IEnumerable<object> IReadOnlyDictionary<object, object>.Values => Values; - - ICollection IDictionary.Keys => Keys.ToList(); + IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Keys => Keys; - ICollection IDictionary.Values => Values.ToList(); + IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Values => Values; private (Array keys, Array values, int count) GetKeyValuePairs() { @@ -152,25 +129,20 @@ namespace Godot.Collections return (keys, values, count); } - bool IDictionary.IsFixedSize => false; - - bool IDictionary.IsReadOnly => false; - /// <summary> - /// Returns the object at the given <paramref name="key"/>. + /// Returns the value at the given <paramref name="key"/>. /// </summary> - /// <value>The object at the given <paramref name="key"/>.</value> - public object this[object key] + /// <value>The value at the given <paramref name="key"/>.</value> + public Variant this[Variant key] { get { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; - if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey, - out godot_variant value).ToBool()) + + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self, + (godot_variant)key.NativeVar, out godot_variant value).ToBool()) { - using (value) - return Marshaling.ConvertVariantToManagedObject(value); + return Variant.CreateTakingOwnershipOfDisposableValue(value); } else { @@ -179,33 +151,31 @@ namespace Godot.Collections } set { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); var self = (godot_dictionary)NativeValue; - NativeFuncs.godotsharp_dictionary_set_value(ref self, variantKey, variantValue); + NativeFuncs.godotsharp_dictionary_set_value(ref self, + (godot_variant)key.NativeVar, (godot_variant)value.NativeVar); } } /// <summary> - /// Adds an object <paramref name="value"/> at key <paramref name="key"/> + /// Adds an value <paramref name="value"/> at key <paramref name="key"/> /// to this <see cref="Dictionary"/>. /// </summary> - /// <param name="key">The key at which to add the object.</param> - /// <param name="value">The object to add.</param> - public void Add(object key, object value) + /// <param name="key">The key at which to add the value.</param> + /// <param name="value">The value to add.</param> + public void Add(Variant key, Variant value) { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); - + var variantKey = (godot_variant)key.NativeVar; var self = (godot_dictionary)NativeValue; if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool()) throw new ArgumentException("An element with the same key already exists", nameof(key)); - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + godot_variant variantValue = (godot_variant)value.NativeVar; NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue); } - void ICollection<KeyValuePair<object, object>>.Add(KeyValuePair<object, object> item) + void ICollection<KeyValuePair<Variant, Variant>>.Add(KeyValuePair<Variant, Variant> item) => Add(item.Key, item.Value); /// <summary> @@ -222,16 +192,15 @@ namespace Godot.Collections /// </summary> /// <param name="key">The key to look for.</param> /// <returns>Whether or not this dictionary contains the given key.</returns> - public bool ContainsKey(object key) + public bool ContainsKey(Variant key) { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; - return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool(); + return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool(); } - public bool Contains(KeyValuePair<object, object> item) + public bool Contains(KeyValuePair<Variant, Variant> item) { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); + godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey, out godot_variant retValue).ToBool(); @@ -241,30 +210,24 @@ namespace Godot.Collections if (!found) return false; - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + godot_variant variantValue = (godot_variant)item.Value.NativeVar; return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); } } - bool IDictionary.Contains(object key) - { - throw new NotImplementedException(); - } - /// <summary> /// Removes an element from this <see cref="Dictionary"/> by key. /// </summary> /// <param name="key">The key of the element to remove.</param> - public bool Remove(object key) + public bool Remove(Variant key) { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; - return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); + return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool(); } - public bool Remove(KeyValuePair<object, object> item) + public bool Remove(KeyValuePair<Variant, Variant> item) { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); + godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey, out godot_variant retValue).ToBool(); @@ -274,7 +237,7 @@ namespace Godot.Collections if (!found) return false; - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + godot_variant variantValue = (godot_variant)item.Value.NativeVar; if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) { return NativeFuncs.godotsharp_dictionary_remove_key( @@ -285,17 +248,6 @@ namespace Godot.Collections } } - void IDictionary.Remove(object key) - { - _ = Remove(key); - } - - // ICollection - - object ICollection.SyncRoot => this; - - bool ICollection.IsSynchronized => false; - /// <summary> /// Returns the number of elements in this <see cref="Dictionary"/>. /// This is also known as the size or length of the dictionary. @@ -310,19 +262,15 @@ namespace Godot.Collections } } - public bool IsReadOnly => false; + bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false; - public bool TryGetValue(object key, out object value) + public bool TryGetValue(Variant key, out Variant value) { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, - variantKey, out godot_variant retValue).ToBool(); + (godot_variant)key.NativeVar, out godot_variant retValue).ToBool(); - using (retValue) - { - value = found ? Marshaling.ConvertVariantToManagedObject(retValue) : default; - } + value = found ? Variant.CreateTakingOwnershipOfDisposableValue(retValue) : default; return found; } @@ -333,7 +281,7 @@ namespace Godot.Collections /// </summary> /// <param name="array">The array to copy to.</param> /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -355,35 +303,13 @@ namespace Godot.Collections } } - void ICollection.CopyTo(System.Array array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException(nameof(array), "Value cannot be null."); - - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), - "Number was less than the array's lower bound in the first dimension."); - - var (keys, values, count) = GetKeyValuePairs(); - - if (array.Length < (arrayIndex + count)) - throw new ArgumentException( - "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); - - for (int i = 0; i < count; i++) - { - array.SetValue(new DictionaryEntry(keys[i], values[i]), arrayIndex); - arrayIndex++; - } - } - // IEnumerable /// <summary> /// Gets an enumerator for this <see cref="Dictionary"/>. /// </summary> /// <returns>An enumerator.</returns> - public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + public IEnumerator<KeyValuePair<Variant, Variant>> GetEnumerator() { for (int i = 0; i < Count; i++) { @@ -393,84 +319,14 @@ namespace Godot.Collections IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - IDictionaryEnumerator IDictionary.GetEnumerator() => new DictionaryEnumerator(this); - - private class DictionaryEnumerator : IDictionaryEnumerator - { - private readonly Dictionary _dictionary; - private readonly int _count; - private int _index = -1; - private bool _dirty = true; - - private DictionaryEntry _entry; - - public DictionaryEnumerator(Dictionary dictionary) - { - _dictionary = dictionary; - _count = dictionary.Count; - } - - public object Current => Entry; - - public DictionaryEntry Entry - { - get - { - if (_dirty) - { - UpdateEntry(); - } - - return _entry; - } - } - - private void UpdateEntry() - { - _dirty = false; - var self = (godot_dictionary)_dictionary.NativeValue; - NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, _index, - out godot_variant key, - out godot_variant value); - using (key) - using (value) - { - // FIXME: DictionaryEntry keys cannot be null, but Godot dictionaries allow null keys - _entry = new DictionaryEntry(Marshaling.ConvertVariantToManagedObject(key)!, - Marshaling.ConvertVariantToManagedObject(value)); - } - } - - public object Key => Entry.Key; - - public object Value => Entry.Value; - - public bool MoveNext() - { - _index++; - _dirty = true; - return _index < _count; - } - - public void Reset() - { - _index = -1; - _dirty = true; - } - } - - private KeyValuePair<object, object> GetKeyValuePair(int index) + private KeyValuePair<Variant, Variant> GetKeyValuePair(int index) { var self = (godot_dictionary)NativeValue; NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index, out godot_variant key, out godot_variant value); - using (key) - using (value) - { - return new KeyValuePair<object, object>(Marshaling.ConvertVariantToManagedObject(key), - Marshaling.ConvertVariantToManagedObject(value)); - } + return new KeyValuePair<Variant, Variant>(Variant.CreateTakingOwnershipOfDisposableValue(key), + Variant.CreateTakingOwnershipOfDisposableValue(value)); } /// <summary> @@ -485,332 +341,4 @@ namespace Godot.Collections return Marshaling.ConvertStringToManaged(str); } } - - internal interface IGenericGodotDictionary - { - Dictionary UnderlyingDictionary { get; } - Type TypeOfKeys { get; } - Type TypeOfValues { get; } - } - - // TODO: Now we should be able to avoid boxing - - /// <summary> - /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant - /// typed elements allocated in the engine in C++. Useful when - /// interfacing with the engine. Otherwise prefer .NET collections - /// such as <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>. - /// </summary> - /// <typeparam name="TKey">The type of the dictionary's keys.</typeparam> - /// <typeparam name="TValue">The type of the dictionary's values.</typeparam> - public class Dictionary<TKey, TValue> : - IDictionary<TKey, TValue>, IGenericGodotDictionary - { - private readonly Dictionary _underlyingDict; - - internal ref godot_dictionary.movable NativeValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _underlyingDict.NativeValue; - } - - // ReSharper disable StaticMemberInGenericType - // Warning is about unique static fields being created for each generic type combination: - // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html - // In our case this is exactly what we want. - private static readonly Type TypeOfKeys = typeof(TKey); - - private static readonly Type TypeOfValues = typeof(TValue); - // ReSharper restore StaticMemberInGenericType - - Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict; - Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys; - Type IGenericGodotDictionary.TypeOfValues => TypeOfValues; - - /// <summary> - /// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - public Dictionary() - { - _underlyingDict = new Dictionary(); - } - - /// <summary> - /// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements. - /// </summary> - /// <param name="dictionary">The dictionary to construct from.</param> - /// <returns>A new Godot Dictionary.</returns> - public Dictionary(IDictionary<TKey, TValue> dictionary) - { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); - - _underlyingDict = new Dictionary(); - - foreach (KeyValuePair<TKey, TValue> entry in dictionary) - Add(entry.Key, entry.Value); - } - - /// <summary> - /// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements. - /// </summary> - /// <param name="dictionary">The dictionary to construct from.</param> - /// <returns>A new Godot Dictionary.</returns> - public Dictionary(Dictionary dictionary) - { - _underlyingDict = dictionary; - } - - // Explicit name to make it very clear - internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue( - godot_dictionary nativeValueToOwn) - => new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); - - /// <summary> - /// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>. - /// </summary> - /// <param name="from">The typed dictionary to convert.</param> - public static explicit operator Dictionary(Dictionary<TKey, TValue> from) - { - return from?._underlyingDict; - } - - /// <summary> - /// Duplicates this <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> - /// <returns>A new Godot Dictionary.</returns> - public Dictionary<TKey, TValue> Duplicate(bool deep = false) - { - return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep)); - } - - // IDictionary<TKey, TValue> - - /// <summary> - /// Returns the value at the given <paramref name="key"/>. - /// </summary> - /// <value>The value at the given <paramref name="key"/>.</value> - public TValue this[TKey key] - { - get - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); - var self = (godot_dictionary)_underlyingDict.NativeValue; - if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self, - variantKey, out godot_variant value).ToBool()) - { - using (value) - return (TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues); - } - else - { - throw new KeyNotFoundException(); - } - } - set => _underlyingDict[key] = value; - } - - /// <summary> - /// Gets the collection of keys in this <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - public ICollection<TKey> Keys - { - get - { - godot_array keyArray; - var self = (godot_dictionary)_underlyingDict.NativeValue; - NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray); - return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray); - } - } - - /// <summary> - /// Gets the collection of elements in this <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - public ICollection<TValue> Values - { - get - { - godot_array valuesArray; - var self = (godot_dictionary)_underlyingDict.NativeValue; - NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray); - return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray); - } - } - - private KeyValuePair<TKey, TValue> GetKeyValuePair(int index) - { - var self = (godot_dictionary)_underlyingDict.NativeValue; - NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index, - out godot_variant key, - out godot_variant value); - using (key) - using (value) - { - return new KeyValuePair<TKey, TValue>( - (TKey)Marshaling.ConvertVariantToManagedObjectOfType(key, TypeOfKeys), - (TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues)); - } - } - - /// <summary> - /// Adds an object <paramref name="value"/> at key <paramref name="key"/> - /// to this <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - /// <param name="key">The key at which to add the object.</param> - /// <param name="value">The object to add.</param> - public void Add(TKey key, TValue value) - { - _underlyingDict.Add(key, value); - } - - /// <summary> - /// Checks if this <see cref="Dictionary{TKey, TValue}"/> contains the given key. - /// </summary> - /// <param name="key">The key to look for.</param> - /// <returns>Whether or not this dictionary contains the given key.</returns> - public bool ContainsKey(TKey key) - { - return _underlyingDict.ContainsKey(key); - } - - /// <summary> - /// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key. - /// </summary> - /// <param name="key">The key of the element to remove.</param> - public bool Remove(TKey key) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); - var self = (godot_dictionary)_underlyingDict.NativeValue; - return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); - } - - /// <summary> - /// Gets the object at the given <paramref name="key"/>. - /// </summary> - /// <param name="key">The key of the element to get.</param> - /// <param name="value">The value at the given <paramref name="key"/>.</param> - /// <returns>If an object was found for the given <paramref name="key"/>.</returns> - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); - var self = (godot_dictionary)_underlyingDict.NativeValue; - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, - variantKey, out godot_variant retValue).ToBool(); - - using (retValue) - { - value = found ? - (TValue)Marshaling.ConvertVariantToManagedObjectOfType(retValue, TypeOfValues) : - default; - } - - return found; - } - - // TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace System.Object with a Variant type. - internal bool TryGetValueAsType<TValueCustom>(TKey key, [MaybeNullWhen(false)] out TValueCustom value) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); - var self = (godot_dictionary)_underlyingDict.NativeValue; - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, - variantKey, out godot_variant retValue).ToBool(); - - using (retValue) - { - value = found ? - (TValueCustom)Marshaling.ConvertVariantToManagedObjectOfType(retValue, typeof(TValueCustom)) : - default; - } - - return found; - } - - // ICollection<KeyValuePair<TKey, TValue>> - - /// <summary> - /// Returns the number of elements in this <see cref="Dictionary{TKey, TValue}"/>. - /// This is also known as the size or length of the dictionary. - /// </summary> - /// <returns>The number of elements.</returns> - public int Count => _underlyingDict.Count; - - bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false; - - void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) - { - _underlyingDict.Add(item.Key, item.Value); - } - - /// <summary> - /// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - public void Clear() - { - _underlyingDict.Clear(); - } - - bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) - => _underlyingDict.Contains(new(item.Key, item.Value)); - - /// <summary> - /// Copies the elements of this <see cref="Dictionary{TKey, TValue}"/> to the given - /// untyped C# array, starting at the given index. - /// </summary> - /// <param name="array">The array to copy to.</param> - /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException(nameof(array), "Value cannot be null."); - - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), - "Number was less than the array's lower bound in the first dimension."); - - int count = Count; - - if (array.Length < (arrayIndex + count)) - throw new ArgumentException( - "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); - - for (int i = 0; i < count; i++) - { - array[arrayIndex] = GetKeyValuePair(i); - arrayIndex++; - } - } - - bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) - => _underlyingDict.Remove(new(item.Key, item.Value)); - - // IEnumerable<KeyValuePair<TKey, TValue>> - - /// <summary> - /// Gets an enumerator for this <see cref="Dictionary{TKey, TValue}"/>. - /// </summary> - /// <returns>An enumerator.</returns> - public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() - { - for (int i = 0; i < Count; i++) - { - yield return GetKeyValuePair(i); - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// <summary> - /// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string. - /// </summary> - /// <returns>A string representation of this dictionary.</returns> - public override string ToString() => _underlyingDict.ToString(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.From(from); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotGenericDictionary<TKey, TValue>(); - } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs deleted file mode 100644 index fa8a1c6402..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Reflection; -using Godot.Collections; -using Godot.NativeInterop; - -namespace Godot -{ - public partial class SceneTree - { - /// <summary> - /// Returns a list of all nodes assigned to the given <paramref name="group"/>. - /// </summary> - /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> - public Array<T> GetNodesInGroup<T>(StringName group) where T : class - { - var array = GetNodesInGroup(group); - - if (array.Count == 0) - return new Array<T>(array); - - var typeOfT = typeof(T); - bool nativeBase = InternalIsClassNativeBase(typeOfT); - - if (nativeBase) - { - // Native type - var field = typeOfT.GetField("NativeName", - BindingFlags.DeclaredOnly | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic); - - var nativeName = (StringName)field!.GetValue(null); - var nativeNameSelf = (godot_string_name)nativeName!.NativeValue; - var inputSelf = (godot_array)array.NativeValue; - NativeFuncs.godotsharp_array_filter_godot_objects_by_native(nativeNameSelf, inputSelf, - out godot_array filteredArray); - return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray); - } - else - { - // Custom derived type - var inputSelf = (godot_array)array.NativeValue; - NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(inputSelf, - out godot_array filteredArray); - - var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray); - - // Re-use first array as its size is the same or greater than the filtered one - var resWrapped = new Array<T>(array); - - int j = 0; - for (int i = 0; i < filteredArrayWrapped.Count; i++) - { - if (filteredArrayWrapped[i] is T t) - { - resWrapped[j] = t; - j++; - } - } - - // Remove trailing elements, since this was re-used - resWrapped.Resize(j); - - return resWrapped; - } - } - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 206a8f1c0a..9348cc1d00 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -21,12 +21,11 @@ namespace Godot /// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param> /// <param name="allowObjects">If objects should be decoded.</param> /// <returns>The decoded <c>Variant</c>.</returns> - public static object Bytes2Var(byte[] bytes, bool allowObjects = false) + public static Variant Bytes2Var(Span<byte> bytes, bool allowObjects = false) { using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes); NativeFuncs.godotsharp_bytes2var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret); - using (ret) - return Marshaling.ConvertVariantToManagedObject(ret); + return Variant.CreateTakingOwnershipOfDisposableValue(ret); } /// <summary> @@ -44,12 +43,10 @@ namespace Godot /// </code> /// </example> /// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns> - public static object Convert(object what, Variant.Type type) + public static Variant Convert(Variant what, Variant.Type type) { - using var whatVariant = Marshaling.ConvertManagedObjectToVariant(what); - NativeFuncs.godotsharp_convert(whatVariant, (int)type, out godot_variant ret); - using (ret) - return Marshaling.ConvertVariantToManagedObject(ret); + NativeFuncs.godotsharp_convert((godot_variant)what.NativeVar, (int)type, out godot_variant ret); + return Variant.CreateTakingOwnershipOfDisposableValue(ret); } /// <summary> @@ -83,10 +80,9 @@ namespace Godot /// </example> /// <param name="var">Variable that will be hashed.</param> /// <returns>Hash of the variable passed.</returns> - public static int Hash(object var) + public static int Hash(Variant var) { - using var variant = Marshaling.ConvertManagedObjectToVariant(var); - return NativeFuncs.godotsharp_hash(variant); + return NativeFuncs.godotsharp_hash((godot_variant)var.NativeVar); } /// <summary> @@ -515,16 +511,16 @@ namespace Godot /// </summary> /// <param name="what">Arguments that will converted to string.</param> /// <returns>The string formed by the given arguments.</returns> - public static string Str(params object[] what) + public static string Str(params Variant[] what) { - using var whatGodotArray = Marshaling.ConvertSystemArrayToNativeGodotArray(what); - NativeFuncs.godotsharp_str(whatGodotArray, out godot_string ret); + using var whatGodot = new Godot.Collections.Array(what); + NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret); using (ret) return Marshaling.ConvertStringToManaged(ret); } /// <summary> - /// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value. + /// Converts a formatted string that was returned by <see cref="Var2Str(Variant)"/> to the original value. /// </summary> /// <example> /// <code> @@ -535,27 +531,25 @@ namespace Godot /// </example> /// <param name="str">String that will be converted to Variant.</param> /// <returns>The decoded <c>Variant</c>.</returns> - public static object Str2Var(string str) + public static Variant Str2Var(string str) { using var godotStr = Marshaling.ConvertStringToNative(str); NativeFuncs.godotsharp_str2var(godotStr, out godot_variant ret); - using (ret) - return Marshaling.ConvertVariantToManagedObject(ret); + return Variant.CreateTakingOwnershipOfDisposableValue(ret); } /// <summary> /// Encodes a <c>Variant</c> value to a byte array. /// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed /// (and can potentially include code). - /// Deserialization can be done with <see cref="Bytes2Var(byte[], bool)"/>. + /// Deserialization can be done with <see cref="Bytes2Var(Span{byte}, bool)"/>. /// </summary> /// <param name="var">Variant that will be encoded.</param> /// <param name="fullObjects">If objects should be serialized.</param> /// <returns>The <c>Variant</c> encoded as an array of bytes.</returns> - public static byte[] Var2Bytes(object var, bool fullObjects = false) + public static byte[] Var2Bytes(Variant var, bool fullObjects = false) { - using var variant = Marshaling.ConvertManagedObjectToVariant(var); - NativeFuncs.godotsharp_var2bytes(variant, fullObjects.ToGodotBool(), out var varBytes); + NativeFuncs.godotsharp_var2bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes); using (varBytes) return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); } @@ -577,10 +571,9 @@ namespace Godot /// </example> /// <param name="var">Variant that will be converted to string.</param> /// <returns>The <c>Variant</c> encoded as a string.</returns> - public static string Var2Str(object var) + public static string Var2Str(Variant var) { - using var variant = Marshaling.ConvertManagedObjectToVariant(var); - NativeFuncs.godotsharp_var2str(variant, out godot_string ret); + NativeFuncs.godotsharp_var2str((godot_variant)var.NativeVar, out godot_string ret); using (ret) return Marshaling.ConvertStringToManaged(ret); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 8802f229b3..eee19aea46 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -1,7 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; using System.Runtime.InteropServices; // ReSharper disable InconsistentNaming @@ -11,8 +8,6 @@ using System.Runtime.InteropServices; #nullable enable -// TODO: Consider removing support for IEnumerable - namespace Godot.NativeInterop { public static class Marshaling @@ -113,10 +108,10 @@ namespace Godot.NativeInterop if (type == typeof(byte[])) return Variant.Type.PackedByteArray; - if (type == typeof(Int32[])) + if (type == typeof(int[])) return Variant.Type.PackedInt32Array; - if (type == typeof(Int64[])) + if (type == typeof(long[])) return Variant.Type.PackedInt64Array; if (type == typeof(float[])) @@ -151,27 +146,6 @@ namespace Godot.NativeInterop } else if (type.IsGenericType) { - var genericTypeDefinition = type.GetGenericTypeDefinition(); - - if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) - return Variant.Type.Dictionary; - - if (genericTypeDefinition == typeof(Collections.Array<>)) - return Variant.Type.Array; - - if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) - return Variant.Type.Dictionary; - - if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) - return Variant.Type.Array; - - if (genericTypeDefinition == typeof(IDictionary<,>)) - return Variant.Type.Dictionary; - - if (genericTypeDefinition == typeof(ICollection<>) || - genericTypeDefinition == typeof(IEnumerable<>)) - return Variant.Type.Array; - if (typeof(Godot.Object).IsAssignableFrom(type)) return Variant.Type.Object; } @@ -194,15 +168,11 @@ namespace Godot.NativeInterop if (typeof(RID) == type) return Variant.Type.Rid; - if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + if (typeof(Collections.Dictionary) == type) return Variant.Type.Dictionary; - if (typeof(Collections.Array) == type || - typeof(System.Collections.ICollection) == type || - typeof(System.Collections.IEnumerable) == type) - { + if (typeof(Collections.Array) == type) return Variant.Type.Array; - } } break; @@ -214,7 +184,6 @@ namespace Godot.NativeInterop } /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ - public static godot_variant ConvertManagedObjectToVariant(object? p_obj) { if (p_obj == null) @@ -228,19 +197,19 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromInt(@char); case sbyte @int8: return VariantUtils.CreateFromInt(@int8); - case Int16 @int16: + case short @int16: return VariantUtils.CreateFromInt(@int16); - case Int32 @int32: + case int @int32: return VariantUtils.CreateFromInt(@int32); - case Int64 @int64: + case long @int64: return VariantUtils.CreateFromInt(@int64); case byte @uint8: return VariantUtils.CreateFromInt(@uint8); - case UInt16 @uint16: + case ushort @uint16: return VariantUtils.CreateFromInt(@uint16); - case UInt32 @uint32: + case uint @uint32: return VariantUtils.CreateFromInt(@uint32); - case UInt64 @uint64: + case ulong @uint64: return VariantUtils.CreateFromInt(@uint64); case float @float: return VariantUtils.CreateFromFloat(@float); @@ -288,9 +257,9 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromString(@string); case byte[] byteArray: return VariantUtils.CreateFromPackedByteArray(byteArray); - case Int32[] int32Array: + case int[] int32Array: return VariantUtils.CreateFromPackedInt32Array(int32Array); - case Int64[] int64Array: + case long[] int64Array: return VariantUtils.CreateFromPackedInt64Array(int64Array); case float[] floatArray: return VariantUtils.CreateFromPackedFloat32Array(floatArray); @@ -305,11 +274,11 @@ namespace Godot.NativeInterop case Color[] colorArray: return VariantUtils.CreateFromPackedColorArray(colorArray); case StringName[] stringNameArray: - return VariantUtils.CreateFromSystemArrayOfSupportedType(stringNameArray); + return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray); case NodePath[] nodePathArray: - return VariantUtils.CreateFromSystemArrayOfSupportedType(nodePathArray); + return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray); case RID[] ridArray: - return VariantUtils.CreateFromSystemArrayOfSupportedType(ridArray); + return VariantUtils.CreateFromSystemArrayOfRID(ridArray); case Godot.Object[] godotObjectArray: return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray); case Godot.Object godotObject: @@ -326,49 +295,6 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromArray(godotArray); case Variant variant: return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar); - case Collections.IGenericGodotDictionary genericGodotDictionary: - { - var godotDict = genericGodotDictionary.UnderlyingDictionary; - if (godotDict == null) - return new godot_variant(); - return VariantUtils.CreateFromDictionary(godotDict); - } - case Collections.IGenericGodotArray genericGodotArray: - { - var godotArray = genericGodotArray.UnderlyingArray; - if (godotArray == null) - return new godot_variant(); - return VariantUtils.CreateFromArray(godotArray); - } - default: - { - var type = p_obj.GetType(); - - if (type.IsGenericType) - { - var genericTypeDefinition = type.GetGenericTypeDefinition(); - - if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) - { - // TODO: Validate key and value types are compatible with Variant - var godotDict = new Collections.Dictionary(); - - foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj) - godotDict.Add(entry.Key, entry.Value); - - return VariantUtils.CreateFromDictionary(godotDict); - } - - if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) - { - // TODO: Validate element type is compatible with Variant - using var nativeGodotArray = ConvertICollectionToNativeGodotArray((ICollection)p_obj); - return VariantUtils.CreateFromArray(nativeGodotArray); - } - } - - break; - } } GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + @@ -495,11 +421,31 @@ namespace Godot.NativeInterop } if (type.IsArray || type.IsSZArray) + { return ConvertVariantToSystemArrayOfType(p_var, type); + } else if (type.IsGenericType) - return ConvertVariantToManagedObjectOfGenericType(p_var, type); + { + if (typeof(Godot.Object).IsAssignableFrom(type)) + { + var godotObject = VariantUtils.ConvertToGodotObject(p_var); + + if (!type.IsInstanceOfType(godotObject)) + { + GD.PushError("Invalid cast when marshaling Godot.Object type." + + $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`."); + return null; + } + + return godotObject; + } + + return null; + } else if (type == typeof(Variant)) + { return Variant.CreateCopyingBorrowed(p_var); + } if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res)) return res; @@ -518,10 +464,10 @@ namespace Godot.NativeInterop if (type == typeof(byte[])) return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); - if (type == typeof(Int32[])) + if (type == typeof(int[])) return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); - if (type == typeof(Int64[])) + if (type == typeof(long[])) return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); if (type == typeof(float[])) @@ -543,13 +489,13 @@ namespace Godot.NativeInterop return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); if (type == typeof(StringName[])) - return VariantUtils.ConvertToSystemArrayOfSupportedType<StringName>(p_var); + return VariantUtils.ConvertToSystemArrayOfStringName(p_var); if (type == typeof(NodePath[])) - return VariantUtils.ConvertToSystemArrayOfSupportedType<NodePath>(p_var); + return VariantUtils.ConvertToSystemArrayOfNodePath(p_var); if (type == typeof(RID[])) - return VariantUtils.ConvertToSystemArrayOfSupportedType<RID>(p_var); + return VariantUtils.ConvertToSystemArrayOfRID(p_var); if (typeof(Godot.Object[]).IsAssignableFrom(type)) return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type); @@ -618,15 +564,13 @@ namespace Godot.NativeInterop return true; } - if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + if (typeof(Collections.Dictionary) == type) { res = VariantUtils.ConvertToDictionaryObject(p_var); return true; } - if (typeof(Collections.Array) == type || - typeof(System.Collections.ICollection) == type || - typeof(System.Collections.IEnumerable) == type) + if (typeof(Collections.Array) == type) { res = VariantUtils.ConvertToArrayObject(p_var); return true; @@ -636,103 +580,6 @@ namespace Godot.NativeInterop return false; } - private static object? ConvertVariantToManagedObjectOfGenericType(in godot_variant p_var, Type type) - { - static object ConvertVariantToGenericGodotCollectionsDictionary(in godot_variant p_var, Type fullType) - { - var underlyingDict = VariantUtils.ConvertToDictionaryObject(p_var); - return Activator.CreateInstance(fullType, - BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] { underlyingDict }, null)!; - } - - static object ConvertVariantToGenericGodotCollectionsArray(in godot_variant p_var, Type fullType) - { - var underlyingArray = VariantUtils.ConvertToArrayObject(p_var); - return Activator.CreateInstance(fullType, - BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] { underlyingArray }, null)!; - } - - var genericTypeDefinition = type.GetGenericTypeDefinition(); - - if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) - return ConvertVariantToGenericGodotCollectionsDictionary(p_var, type); - - if (genericTypeDefinition == typeof(Collections.Array<>)) - return ConvertVariantToGenericGodotCollectionsArray(p_var, type); - - if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) - { - using var godotDictionary = VariantUtils.ConvertToDictionaryObject(p_var); - - var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type, - BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] - { - /* capacity: */ godotDictionary.Count - }, null)!; - - foreach (KeyValuePair<object, object> pair in godotDictionary) - dictionary.Add(pair.Key, pair.Value); - - return dictionary; - } - - if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) - { - using var godotArray = VariantUtils.ConvertToArrayObject(p_var); - - var list = (System.Collections.IList)Activator.CreateInstance(type, - BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] - { - /* capacity: */ godotArray.Count - }, null)!; - - foreach (object elem in godotArray) - list.Add(elem); - - return list; - } - - if (genericTypeDefinition == typeof(IDictionary<,>)) - { - var genericArgs = type.GetGenericArguments(); - var keyType = genericArgs[0]; - var valueType = genericArgs[1]; - var genericGodotDictionaryType = typeof(Collections.Dictionary<,>) - .MakeGenericType(keyType, valueType); - - return ConvertVariantToGenericGodotCollectionsDictionary(p_var, genericGodotDictionaryType); - } - - if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) - { - var elementType = type.GetGenericArguments()[0]; - var genericGodotArrayType = typeof(Collections.Array<>) - .MakeGenericType(elementType); - - return ConvertVariantToGenericGodotCollectionsArray(p_var, genericGodotArrayType); - } - - if (typeof(Godot.Object).IsAssignableFrom(type)) - { - var godotObject = VariantUtils.ConvertToGodotObject(p_var); - - if (!type.IsInstanceOfType(godotObject)) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`."); - return null; - } - - return godotObject; - } - - return null; - } - public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var) { switch (p_var.Type) @@ -949,20 +796,6 @@ namespace Godot.NativeInterop // Array - internal static T[] ConvertNativeGodotArrayToSystemArrayOfType<T>(in godot_array p_array) - { - var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_array_new_copy(p_array)); - - int length = array.Count; - var ret = new T[length]; - - // ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T> - array.CopyToGeneric(ret, 0); - - return ret; - } - internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array) where T : Godot.Object { @@ -972,12 +805,13 @@ namespace Godot.NativeInterop int length = array.Count; var ret = new T[length]; - // ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T> - array.CopyToGeneric(ret, 0); + for (int i = 0; i < length; i++) + ret[i] = (T)array[i].AsGodotObject(); return ret; } + // TODO: This needs reflection. Look for an alternative. internal static Godot.Object[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(in godot_array p_array, Type type) { @@ -987,93 +821,52 @@ namespace Godot.NativeInterop int length = array.Count; var ret = (Godot.Object[])Activator.CreateInstance(type, length)!; - // ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T> - array.CopyToGeneric(ret, 0, type.GetElementType()); + for (int i = 0; i < length; i++) + ret[i] = array[i].AsGodotObject(); return ret; } - public static godot_array ConvertSystemArrayToNativeGodotArray<T>(T[] p_array) + internal static StringName[] ConvertNativeGodotArrayToSystemArrayOfStringName(in godot_array p_array) { - int length = p_array.Length; - - if (length == 0) - return NativeFuncs.godotsharp_array_new(); + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); - using var array = new Collections.Array(); - array.Resize(length); + int length = array.Count; + var ret = new StringName[length]; for (int i = 0; i < length; i++) - array[i] = p_array[i]; - - var src = (godot_array)array.NativeValue; - return NativeFuncs.godotsharp_array_new_copy(src); - } - - public static godot_array ConvertICollectionToNativeGodotArray(ICollection p_array) - { - int length = p_array.Count; - - if (length == 0) - return NativeFuncs.godotsharp_array_new(); - - using var array = new Collections.Array(); - array.Resize(length); - - int i = 0; - foreach (var elem in p_array) - { - array[i] = elem; - i++; - } + ret[i] = array[i].AsStringName(); - var src = (godot_array)array.NativeValue; - return NativeFuncs.godotsharp_array_new_copy(src); + return ret; } - public static godot_array ConvertGenericICollectionToNativeGodotArray<T>(ICollection<T> p_array) + internal static NodePath[] ConvertNativeGodotArrayToSystemArrayOfNodePath(in godot_array p_array) { - int length = p_array.Count; - - if (length == 0) - return NativeFuncs.godotsharp_array_new(); + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); - var array = new Collections.Array<T>(); - using var underlyingArray = (Collections.Array)array; - array.Resize(length); + int length = array.Count; + var ret = new NodePath[length]; - int i = 0; - foreach (var elem in p_array) - { - array[i] = elem; - i++; - } + for (int i = 0; i < length; i++) + ret[i] = array[i].AsNodePath(); - var src = (godot_array)underlyingArray.NativeValue; - return NativeFuncs.godotsharp_array_new_copy(src); + return ret; } - public static godot_array ConvertIEnumerableToNativeGodotArray(IEnumerable p_array) + internal static RID[] ConvertNativeGodotArrayToSystemArrayOfRID(in godot_array p_array) { - using var array = new Collections.Array(); - - foreach (var elem in p_array) - array.Add(elem); - - var src = (godot_array)array.NativeValue; - return NativeFuncs.godotsharp_array_new_copy(src); - } + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); - public static godot_array ConvertGenericIEnumerableToNativeGodotArray<T>(IEnumerable<T> p_array) - { - var array = new Collections.Array<T>(); - using var underlyingArray = (Collections.Array)array; + int length = array.Count; + var ret = new RID[length]; - foreach (var elem in p_array) - array.Add(elem); + for (int i = 0; i < length; i++) + ret[i] = array[i].AsRID(); - var src = (godot_array)underlyingArray.NativeValue; - return NativeFuncs.godotsharp_array_new_copy(src); + return ret; } // PackedByteArray diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index fe0d7104ea..1ce8965939 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -210,21 +210,22 @@ namespace Godot.NativeInterop public static godot_variant CreateFromPackedColorArray(Span<Color> from) => CreateFromPackedColorArray(Marshaling.ConvertSystemArrayToNativePackedColorArray(from)); - public static godot_variant CreateFromSystemArrayOfSupportedType<T>(T[]? from) - { - if (from == null) - return default; // Nil - using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from); - return CreateFromArray(array); - } + public static godot_variant CreateFromSystemArrayOfStringName(Span<StringName> from) + => CreateFromArray(new Collections.Array(from)); + + public static godot_variant CreateFromSystemArrayOfNodePath(Span<NodePath> from) + => CreateFromArray(new Collections.Array(from)); + + public static godot_variant CreateFromSystemArrayOfRID(Span<RID> from) + => CreateFromArray(new Collections.Array(from)); // ReSharper disable once RedundantNameQualifier public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from) { if (from == null) return default; // Nil - using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from); - return CreateFromArray(array); + using var fromGodot = new Collections.Array(from); + return CreateFromArray((godot_array)fromGodot.NativeValue); } public static godot_variant CreateFromArray(godot_array from) @@ -237,45 +238,6 @@ namespace Godot.NativeInterop public static godot_variant CreateFromArray(Collections.Array? from) => from != null ? CreateFromArray((godot_array)from.NativeValue) : default; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - // ReSharper disable once RedundantNameQualifier - public static godot_variant CreateFromArray<T>(Collections.Array<T>? from) - => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default; - - public static godot_variant CreateFromSystemICollection(System.Collections.ICollection? from) - { - if (from == null) - return default; // Nil - using var nativeGodotArray = Marshaling.ConvertICollectionToNativeGodotArray(from); - return CreateFromArray(nativeGodotArray); - } - - public static godot_variant CreateFromSystemGenericICollection<T>( - System.Collections.Generic.ICollection<T>? from) - { - if (from == null) - return default; // Nil - using var nativeGodotArray = Marshaling.ConvertGenericICollectionToNativeGodotArray(from); - return CreateFromArray(nativeGodotArray); - } - - public static godot_variant CreateFromSystemIEnumerable(System.Collections.IEnumerable? from) - { - if (from == null) - return default; // Nil - using var nativeGodotArray = Marshaling.ConvertIEnumerableToNativeGodotArray(from); - return CreateFromArray(nativeGodotArray); - } - - public static godot_variant CreateFromSystemGenericIEnumerable<T>( - System.Collections.Generic.IEnumerable<T>? from) - { - if (from == null) - return default; // Nil - using var nativeGodotArray = Marshaling.ConvertGenericIEnumerableToNativeGodotArray(from); - return CreateFromArray(nativeGodotArray); - } - public static godot_variant CreateFromDictionary(godot_dictionary from) { NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from); @@ -286,51 +248,6 @@ namespace Godot.NativeInterop public static godot_variant CreateFromDictionary(Dictionary? from) => from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from) - => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default; - - public static godot_variant CreateFromSystemDictionary<TKey, TValue>( - System.Collections.Generic.Dictionary<TKey, TValue>? from) where TKey : notnull - { - if (from == null) - return default; // Nil - - var godotDict = new Dictionary(); - - foreach (var entry in from) - godotDict.Add(entry.Key, entry.Value); - - return CreateFromDictionary(godotDict); - } - - public static godot_variant CreateFromSystemIDictionary(System.Collections.IDictionary? from) - { - if (from == null) - return default; // Nil - - var godotDict = new Dictionary(); - - foreach (var entry in from) - godotDict.Add(entry, entry); - - return CreateFromDictionary(godotDict); - } - - public static godot_variant CreateFromSystemGenericIDictionary<TKey, TValue>( - System.Collections.Generic.IDictionary<TKey, TValue>? from) - { - if (from == null) - return default; // Nil - - var godotDict = new Dictionary<TKey, TValue>(); - - foreach (var entry in from) - godotDict.Add(entry.Key, entry.Value); - - return CreateFromDictionary(godotDict); - } - public static godot_variant CreateFromStringName(godot_string_name from) { NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from); @@ -381,17 +298,17 @@ namespace Godot.NativeInterop p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static Int16 ConvertToInt16(in godot_variant p_var) - => (Int16)(p_var.Type == Variant.Type.Int ? + public static short ConvertToInt16(in godot_variant p_var) + => (short)(p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static Int32 ConvertToInt32(in godot_variant p_var) - => (Int32)(p_var.Type == Variant.Type.Int ? + public static int ConvertToInt32(in godot_variant p_var) + => (int)(p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static Int64 ConvertToInt64(in godot_variant p_var) + public static long ConvertToInt64(in godot_variant p_var) => p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var); public static byte ConvertToUInt8(in godot_variant p_var) @@ -399,18 +316,18 @@ namespace Godot.NativeInterop p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static UInt16 ConvertToUInt16(in godot_variant p_var) - => (UInt16)(p_var.Type == Variant.Type.Int ? + public static ushort ConvertToUInt16(in godot_variant p_var) + => (ushort)(p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static UInt32 ConvertToUInt32(in godot_variant p_var) - => (UInt32)(p_var.Type == Variant.Type.Int ? + public static uint ConvertToUInt32(in godot_variant p_var) + => (uint)(p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static UInt64 ConvertToUInt64(in godot_variant p_var) - => (UInt64)(p_var.Type == Variant.Type.Int ? + public static ulong ConvertToUInt64(in godot_variant p_var) + => (ulong)(p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); @@ -642,10 +559,22 @@ namespace Godot.NativeInterop return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray); } - public static T[] ConvertToSystemArrayOfSupportedType<T>(in godot_variant p_var) + public static StringName[] ConvertToSystemArrayOfStringName(in godot_variant p_var) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Marshaling.ConvertNativeGodotArrayToSystemArrayOfStringName(godotArray); + } + + public static NodePath[] ConvertToSystemArrayOfNodePath(in godot_variant p_var) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray); + } + + public static RID[] ConvertToSystemArrayOfRID(in godot_variant p_var) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Marshaling.ConvertNativeGodotArrayToSystemArrayOfType<T>(godotArray); + return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRID(godotArray); } public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var) @@ -662,37 +591,5 @@ namespace Godot.NativeInterop using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type); } - - public static Array<T> ConvertToGenericArrayObject<T>(in godot_variant p_var) => - new(ConvertToArrayObject(p_var)); - - public static Dictionary<TKey, TValue> ConvertToGenericDictionaryObject<TKey, TValue>(in godot_variant p_var) => - new(ConvertToDictionaryObject(p_var)); - - public static System.Collections.Generic.List<T> ConvertToSystemGenericList<T>(in godot_variant p_var) - { - var godotArray = ConvertToArrayObject(p_var); - - var res = new System.Collections.Generic.List<T>(godotArray.Count); - - foreach (object elem in godotArray) - res.Add((T)elem); - - return res; - } - - public static System.Collections.Generic.Dictionary<TKey, TValue> - ConvertToSystemGenericDictionary<TKey, TValue>(in godot_variant p_var) - where TKey : notnull - { - var godotDictionary = ConvertToDictionaryObject(p_var); - - var res = new System.Collections.Generic.Dictionary<TKey, TValue>(godotDictionary.Count); - - foreach (System.Collections.Generic.KeyValuePair<object, object> pair in godotDictionary) - res.Add((TKey)pair.Key, (TValue)pair.Value); - - return res; - } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 04920ccfab..5cb678c280 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -187,18 +187,6 @@ namespace Godot return null; } - internal static bool InternalIsClassNativeBase(Type t) - { - // Check whether the type is declared in the GodotSharp or GodotSharpEditor assemblies - var typeAssembly = t.Assembly; - - if (typeAssembly == CachedType.Assembly) - return true; - - var typeAssemblyName = t.Assembly.GetName(); - return typeAssemblyName.Name == "GodotSharpEditor"; - } - // ReSharper disable once VirtualMemberNeverOverridden.Global protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 8ba3c403fa..96fb891086 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -4,10 +4,10 @@ using Godot.NativeInterop; namespace Godot { - public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> + public class SignalAwaiter : IAwaiter<Variant[]>, IAwaitable<Variant[]> { private bool _completed; - private object[] _result; + private Variant[] _result; private Action _continuation; public SignalAwaiter(Object source, StringName signal, Object target) @@ -26,9 +26,9 @@ namespace Godot _continuation = continuation; } - public object[] GetResult() => _result; + public Variant[] GetResult() => _result; - public IAwaiter<object[]> GetAwaiter() => this; + public IAwaiter<Variant[]> GetAwaiter() => this; [UnmanagedCallersOnly] internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount, @@ -48,10 +48,10 @@ namespace Godot awaiter._completed = true; - object[] signalArgs = new object[argCount]; + Variant[] signalArgs = new Variant[argCount]; for (int i = 0; i < argCount; i++) - signalArgs[i] = Marshaling.ConvertVariantToManagedObject(*args[i]); + signalArgs[i] = Variant.CreateCopyingBorrowed(*args[i]); awaiter._result = signalArgs; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 0a61069a1e..d1ff6ade8a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -78,7 +78,6 @@ <Compile Include="Core\Extensions\ObjectExtensions.cs" /> <Compile Include="Core\Extensions\PackedSceneExtensions.cs" /> <Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" /> - <Compile Include="Core\Extensions\SceneTreeExtensions.cs" /> <Compile Include="Core\GD.cs" /> <Compile Include="Core\GodotSynchronizationContext.cs" /> <Compile Include="Core\GodotTaskScheduler.cs" /> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs index c1c321829a..7c4df291ac 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs @@ -105,6 +105,8 @@ public partial struct Variant : IDisposable // TODO: Consider renaming Variant.Type to VariantType and this property to Type. VariantType would also avoid ambiguity with System.Type. public Type VariantType => NativeVar.DangerousSelfRef.Type; + public override string ToString() => AsString(); + public object? Obj { get @@ -116,8 +118,6 @@ public partial struct Variant : IDisposable } } - // TODO: Consider implicit operators - [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool AsBool() => VariantUtils.ConvertToBool((godot_variant)NativeVar); @@ -211,6 +211,18 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToTransform3D((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 AsVector4() => + VariantUtils.ConvertToVector4((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4i AsVector4i() => + VariantUtils.ConvertToVector4i((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Projection AsProjection() => + VariantUtils.ConvertToProjection((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public AABB AsAABB() => VariantUtils.ConvertToAABB((godot_variant)NativeVar); @@ -272,25 +284,16 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] AsSystemArrayOfSupportedType<T>() => - VariantUtils.ConvertToSystemArrayOfSupportedType<T>((godot_variant)NativeVar); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Collections.Dictionary<TKey, TValue> AsGodotGenericDictionary<TKey, TValue>() => - VariantUtils.ConvertToGenericDictionaryObject<TKey, TValue>((godot_variant)NativeVar); + public StringName[] AsSystemArrayOfStringName() => + VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Collections.Array<T> AsGodotGenericArray<T>() => - VariantUtils.ConvertToGenericArrayObject<T>((godot_variant)NativeVar); + public NodePath[] AsSystemArrayOfNodePath() => + VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public System.Collections.Generic.Dictionary<TKey, TValue> AsSystemGenericDictionary<TKey, TValue>() - where TKey : notnull => - VariantUtils.ConvertToSystemGenericDictionary<TKey, TValue>((godot_variant)NativeVar); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public System.Collections.Generic.List<T> AsSystemGenericList<T>() => - VariantUtils.ConvertToSystemGenericList<T>((godot_variant)NativeVar); + public RID[] AsSystemArrayOfRID() => + VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Godot.Object AsGodotObject() => @@ -388,6 +391,15 @@ public partial struct Variant : IDisposable public static explicit operator Transform3D(Variant from) => from.AsTransform3D(); [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector4(Variant from) => from.AsVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector4i(Variant from) => from.AsVector4i(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Projection(Variant from) => from.AsProjection(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator AABB(Variant from) => from.AsAABB(); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -430,6 +442,15 @@ public partial struct Variant : IDisposable public static explicit operator Color[](Variant from) => from.AsColorArray(); [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Godot.Object(Variant from) => from.AsGodotObject(); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -447,6 +468,161 @@ public partial struct Variant : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Collections.Array(Variant from) => from.AsGodotArray(); + // While we provide implicit conversion operators, normal methods are still needed for + // casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc). + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(bool from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(char from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(sbyte from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(short from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(int from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(long from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(byte from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(ushort from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(uint from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(ulong from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(float from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(double from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(string from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector2 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector2i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Rect2 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Rect2i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Transform2D from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector3 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector3i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Basis from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Quaternion from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Transform3D from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector4 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector4i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Projection from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(AABB from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Color from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Plane from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Callable from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(SignalInfo from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<byte> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<int> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<long> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<float> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<double> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<string> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<Vector2> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<Vector3> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<Color> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Godot.Object[] from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<StringName> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<NodePath> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span<RID> from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Godot.Object from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(StringName from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(NodePath from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(RID from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Collections.Dictionary from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Collections.Array from) => from; + + // Implicit conversion operators + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(bool from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from)); @@ -540,6 +716,18 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector4 from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector4i from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Projection from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(AABB from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from)); @@ -596,36 +784,20 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Godot.Object[]? from) => + public static implicit operator Variant(Godot.Object[] from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<T>(Collections.Array<T> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> from) - where TKey : notnull => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemDictionary(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<T>(System.Collections.Generic.List<T> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemICollection(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericIDictionary(from)); + public static implicit operator Variant(Span<StringName> from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<T>(System.Collections.Generic.ICollection<T> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericICollection(from)); + public static implicit operator Variant(Span<NodePath> from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From<T>(System.Collections.Generic.IEnumerable<T> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericIEnumerable(from)); + public static implicit operator Variant(Span<RID> from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Godot.Object from) => @@ -650,16 +822,4 @@ public partial struct Variant : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Collections.Array from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From(System.Collections.IDictionary from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemIDictionary(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From(System.Collections.ICollection from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemICollection(from)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant From(System.Collections.IEnumerable from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemIEnumerable(from)); } |