diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs index a5b4cb81f60..ac9f59aa99b 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs @@ -99,5 +99,11 @@ namespace Godot.SourceGenerators.Sample [Export] private Godot.Collections.Array field_GodotArray = new() { "foo", 10, Vector2.Up, Colors.Chocolate }; + + [Export] private Godot.Collections.Dictionary field_GodotGenericDictionary = + new() { { "foo", true }, { "bar", false } }; + + [Export] private Godot.Collections.Array field_GodotGenericArray = + new() { 0, 1, 2, 3, 4, 5, 6 }; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs index eb35c88260b..4a0e8075f07 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs @@ -99,5 +99,11 @@ namespace Godot.SourceGenerators.Sample [Export] private Godot.Collections.Array property_GodotArray { get; set; } = new() { "foo", 10, Vector2.Up, Colors.Chocolate }; + + [Export] private Godot.Collections.Dictionary property_GodotGenericDictionary { get; set; } = + new() { { "foo", true }, { "bar", false } }; + + [Export] private Godot.Collections.Array property_GodotGenericArray { get; set; } = + new() { 0, 1, 2, 3, 4, 5, 6 }; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs index 3f767c8a5f7..15f5803bf07 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs @@ -67,5 +67,7 @@ namespace Godot.SourceGenerators RID, GodotDictionary, GodotArray, + GodotGenericDictionary, + GodotGenericArray, } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs index 43000377968..ca84518c0c2 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs @@ -78,6 +78,8 @@ namespace Godot.SourceGenerators MarshalType.RID => VariantType.Rid, MarshalType.GodotDictionary => VariantType.Dictionary, MarshalType.GodotArray => VariantType.Array, + MarshalType.GodotGenericDictionary => VariantType.Dictionary, + MarshalType.GodotGenericArray => VariantType.Array, _ => null }; @@ -214,13 +216,17 @@ namespace Godot.SourceGenerators _ => null }; case "Collections" - when !(type is INamedTypeSymbol { IsGenericType: true }) && - type.ContainingNamespace.FullQualifiedName() == - "Godot.Collections": + when type.ContainingNamespace.FullQualifiedName() == "Godot.Collections": return type switch { - { Name: "Dictionary" } => MarshalType.GodotDictionary, - { Name: "Array" } => MarshalType.GodotArray, + { Name: "Dictionary" } => + type is INamedTypeSymbol { IsGenericType: false } ? + MarshalType.GodotDictionary : + MarshalType.GodotGenericDictionary, + { Name: "Array" } => + type is INamedTypeSymbol { IsGenericType: false } ? + MarshalType.GodotArray : + MarshalType.GodotGenericArray, _ => null }; } @@ -283,6 +289,10 @@ namespace Godot.SourceGenerators string c, string d, string e, string f, string g) => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g); + private static StringBuilder Append(this StringBuilder source, string a, string b, + string c, string d, string e, string f, string g, string h) + => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g).Append(h); + private const string VariantUtils = "global::Godot.NativeInterop.VariantUtils"; public static StringBuilder AppendNativeVariantToManagedExpr(this StringBuilder source, @@ -397,6 +407,13 @@ namespace Godot.SourceGenerators source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"), MarshalType.GodotArray => source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"), + MarshalType.GodotGenericDictionary => + source.Append(VariantUtils, ".ConvertToDictionaryObject<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ", + ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">(", inputExpr, ")"), + MarshalType.GodotGenericArray => + source.Append(VariantUtils, ".ConvertToArrayObject<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">(", inputExpr, ")"), _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, "Received unexpected marshal type") }; @@ -511,6 +528,10 @@ namespace Godot.SourceGenerators source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"), MarshalType.GodotArray => source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"), + MarshalType.GodotGenericDictionary => + source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"), + MarshalType.GodotGenericArray => + source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"), _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, "Received unexpected marshal type") }; @@ -576,6 +597,11 @@ namespace Godot.SourceGenerators MarshalType.RID => source.Append(inputExpr, ".AsRID()"), MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"), MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"), + MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ", + ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">()"), + MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">()"), _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, "Received unexpected marshal type") }; @@ -636,6 +662,8 @@ namespace Godot.SourceGenerators case MarshalType.RID: case MarshalType.GodotDictionary: case MarshalType.GodotArray: + case MarshalType.GodotGenericDictionary: + case MarshalType.GodotGenericArray: return source.Append("Variant.CreateFrom(", inputExpr, ")"); case MarshalType.Enum: return source.Append("Variant.CreateFrom((long)", inputExpr, ")"); diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index a8128be9098..96d1fc28bfb 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -58,7 +58,7 @@ namespace GodotTools.Build } // TODO Use List once we have proper serialization. - private Godot.Collections.Array _issues = new(); + private Godot.Collections.Array _issues = new(); private ItemList _issuesList; private PopupMenu _issuesListContextMenu; private TextEdit _buildLog; @@ -128,7 +128,7 @@ namespace GodotTools.Build if (issueIndex < 0 || issueIndex >= _issues.Count) throw new IndexOutOfRangeException("Issue index out of range"); - var issue = (BuildIssue)_issues[issueIndex]; + BuildIssue issue = _issues[issueIndex]; if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File)) return; @@ -162,7 +162,7 @@ namespace GodotTools.Build { for (int i = 0; i < _issues.Count; i++) { - var issue = (BuildIssue)_issues[i]; + BuildIssue issue = _issues[i]; if (!(issue.Warning ? WarningsVisible : ErrorsVisible)) continue; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index b879c95fa17..73d8f230819 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2891,13 +2891,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() { String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." + " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'."); } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) { -// TODO: Enable once generic Array is re-implemented -#if 0 imethod.return_type.cname = Variant::get_type_name(return_info.type) + "_@generic"; imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string)); -#else - imethod.return_type.cname = Variant::get_type_name(return_info.type); -#endif } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { imethod.return_type.cname = return_info.hint_string; } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { @@ -2928,13 +2923,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { -// TODO: Enable once generic Array is re-implemented -#if 0 iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic"; iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); -#else - iarg.type.cname = Variant::get_type_name(arginfo.type); -#endif } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { @@ -3041,13 +3031,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { -// TODO: Enable once generic Array is re-implemented -#if 0 iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic"; iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); -#else - iarg.type.cname = Variant::get_type_name(arginfo.type); -#endif } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs index a1b93e7daa3..bb482e0d6aa 100644 --- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs +++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs @@ -55,7 +55,7 @@ public partial class VisualShaderNode_CLASS_ : _BASE_ return 0; } - public override string _GetCode(Godot.Collections.Array inputVars, Godot.Collections.Array outputVars, Shader.Mode mode, VisualShader.Type type) + public override string _GetCode(Godot.Collections.Array inputVars, Godot.Collections.Array outputVars, Shader.Mode mode, VisualShader.Type type) { return ""; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 51d7d8195bd..81991c66264 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Godot.NativeInterop; @@ -424,13 +425,6 @@ namespace Godot.Collections } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal object GetAtAsType(int index, Type type) - { - GetVariantBorrowElementAt(index, out godot_variant borrowElem); - return Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, type); - } - // IEnumerable /// @@ -479,4 +473,357 @@ namespace Godot.Collections elem = NativeValue.DangerousSelfRef.Elements[index]; } } + + /// + /// 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 . + /// + /// The type of the array. + [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] + [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")] + public sealed class Array : + IList, + IReadOnlyList, + ICollection, + IEnumerable + { + // 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 unsafe delegate* managed _convertToVariantCallback; + private static unsafe delegate* managed _convertToManagedCallback; + + // ReSharper restore StaticMemberInGenericType + + static unsafe Array() + { + _convertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback(); + _convertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback(); + } + + private static unsafe void ValidateVariantConversionCallbacks() + { + if (_convertToVariantCallback == null || _convertToManagedCallback == null) + { + throw new InvalidOperationException( + $"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'"); + } + } + + private readonly Array _underlyingArray; + + internal ref godot_array.movable NativeValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _underlyingArray.NativeValue; + } + + /// + /// Constructs a new empty . + /// + public Array() + { + ValidateVariantConversionCallbacks(); + + _underlyingArray = new Array(); + } + + /// + /// Constructs a new from the given collection's elements. + /// + /// The collection of elements to construct from. + /// A new Godot Array. + public Array(IEnumerable collection) + { + ValidateVariantConversionCallbacks(); + + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + _underlyingArray = new Array(); + + foreach (T element in collection) + Add(element); + } + + /// + /// Constructs a new from the given items. + /// + /// The items to put in the new array. + /// A new Godot Array. + public Array(T[] array) : this() + { + ValidateVariantConversionCallbacks(); + + if (array == null) + throw new ArgumentNullException(nameof(array)); + + _underlyingArray = new Array(); + + foreach (T element in array) + Add(element); + } + + /// + /// Constructs a typed from an untyped . + /// + /// The untyped array to construct from. + public Array(Array array) + { + ValidateVariantConversionCallbacks(); + + _underlyingArray = array; + } + + // Explicit name to make it very clear + internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) + => new Array(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); + + /// + /// Converts this typed to an untyped . + /// + /// The typed array to convert. + public static explicit operator Array(Array from) + { + return from?._underlyingArray; + } + + /// + /// Duplicates this . + /// + /// If , performs a deep copy. + /// A new Godot Array. + public Array Duplicate(bool deep = false) + { + return new Array(_underlyingArray.Duplicate(deep)); + } + + /// + /// Resizes this to the given size. + /// + /// The new size of the array. + /// if successful, or an error code. + public Error Resize(int newSize) + { + return _underlyingArray.Resize(newSize); + } + + /// + /// Shuffles the contents of this into a random order. + /// + public void Shuffle() + { + _underlyingArray.Shuffle(); + } + + /// + /// Concatenates these two s. + /// + /// The first array. + /// The second array. + /// A new Godot Array with the contents of both arrays. + public static Array operator +(Array left, Array right) + { + if (left == null) + { + if (right == null) + return new Array(); + + return right.Duplicate(deep: false); + } + + if (right == null) + return left.Duplicate(deep: false); + + return new Array(left._underlyingArray + right._underlyingArray); + } + + // IList + + /// + /// Returns the value at the given . + /// + /// The value at the given . + public unsafe T this[int index] + { + get + { + _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); + return _convertToManagedCallback(borrowElem); + } + set + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException(nameof(index)); + var self = (godot_array)_underlyingArray.NativeValue; + godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); + godot_variant* itemPtr = &ptrw[index]; + (*itemPtr).Dispose(); + *itemPtr = _convertToVariantCallback(value); + } + } + + /// + /// Searches this for an item + /// and returns its index or -1 if not found. + /// + /// The item to search for. + /// The index of the item, or -1 if not found. + public unsafe int IndexOf(T item) + { + using var variantValue = _convertToVariantCallback(item); + var self = (godot_array)_underlyingArray.NativeValue; + return NativeFuncs.godotsharp_array_index_of(ref self, variantValue); + } + + /// + /// Inserts a new item at a given position in the . + /// 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. + /// + /// The index to insert at. + /// The item to insert. + public unsafe void Insert(int index, T item) + { + if (index < 0 || index > Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + using var variantValue = _convertToVariantCallback(item); + var self = (godot_array)_underlyingArray.NativeValue; + NativeFuncs.godotsharp_array_insert(ref self, index, variantValue); + } + + /// + /// Removes an element from this by index. + /// + /// The index of the element to remove. + public void RemoveAt(int index) + { + _underlyingArray.RemoveAt(index); + } + + // ICollection + + /// + /// Returns the number of elements in this . + /// This is also known as the size or length of the array. + /// + /// The number of elements. + public int Count => _underlyingArray.Count; + + bool ICollection.IsReadOnly => false; + + /// + /// Adds an item to the end of this . + /// This is the same as append or push_back in GDScript. + /// + /// The item to add. + /// The new size after adding the item. + public unsafe void Add(T item) + { + using var variantValue = _convertToVariantCallback(item); + var self = (godot_array)_underlyingArray.NativeValue; + _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); + } + + /// + /// Erases all items from this . + /// + public void Clear() + { + _underlyingArray.Clear(); + } + + /// + /// Checks if this contains the given item. + /// + /// The item to look for. + /// Whether or not this array contains the given item. + public bool Contains(T item) => IndexOf(item) != -1; + + /// + /// Copies the elements of this to the given + /// C# array, starting at the given index. + /// + /// The C# array to copy to. + /// The index to start at. + public void CopyTo(T[] 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] = this[i]; + arrayIndex++; + } + } + + /// + /// Removes the first occurrence of the specified value + /// from this . + /// + /// The value to remove. + /// A indicating success or failure. + public bool Remove(T item) + { + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; + } + + // IEnumerable + + /// + /// Gets an enumerator for this . + /// + /// An enumerator. + public IEnumerator GetEnumerator() + { + int count = _underlyingArray.Count; + + for (int i = 0; i < count; i++) + { + yield return this[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Converts this to a string. + /// + /// A string representation of this array. + public override string ToString() => _underlyingArray.ToString(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Array from) => Variant.CreateFrom(from); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Array(Variant from) => from.AsGodotArray(); + } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index c3d500119aa..fa8c94ed183 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot.Collections @@ -341,4 +343,380 @@ namespace Godot.Collections return Marshaling.ConvertStringToManaged(str); } } + + /// + /// 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 . + /// + /// The type of the dictionary's keys. + /// The type of the dictionary's values. + public class Dictionary : + IDictionary, + IReadOnlyDictionary + { + // 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 unsafe delegate* managed _convertKeyToVariantCallback; + private static unsafe delegate* managed _convertKeyToManagedCallback; + private static unsafe delegate* managed _convertValueToVariantCallback; + private static unsafe delegate* managed _convertValueToManagedCallback; + + // ReSharper restore StaticMemberInGenericType + + static unsafe Dictionary() + { + _convertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback(); + _convertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback(); + _convertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback(); + _convertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback(); + } + + private static unsafe void ValidateVariantConversionCallbacks() + { + if (_convertKeyToVariantCallback == null || _convertKeyToManagedCallback == null) + { + throw new InvalidOperationException( + $"The dictionary key type is not supported for conversion to Variant: '{typeof(TKey).FullName}'"); + } + + if (_convertValueToVariantCallback == null || _convertValueToManagedCallback == null) + { + throw new InvalidOperationException( + $"The dictionary value type is not supported for conversion to Variant: '{typeof(TValue).FullName}'"); + } + } + + private readonly Dictionary _underlyingDict; + + internal ref godot_dictionary.movable NativeValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _underlyingDict.NativeValue; + } + + /// + /// Constructs a new empty . + /// + public Dictionary() + { + ValidateVariantConversionCallbacks(); + + _underlyingDict = new Dictionary(); + } + + /// + /// Constructs a new from the given dictionary's elements. + /// + /// The dictionary to construct from. + /// A new Godot Dictionary. + public Dictionary(IDictionary dictionary) + { + ValidateVariantConversionCallbacks(); + + if (dictionary == null) + throw new ArgumentNullException(nameof(dictionary)); + + _underlyingDict = new Dictionary(); + + foreach (KeyValuePair entry in dictionary) + Add(entry.Key, entry.Value); + } + + /// + /// Constructs a new from the given dictionary's elements. + /// + /// The dictionary to construct from. + /// A new Godot Dictionary. + public Dictionary(Dictionary dictionary) + { + ValidateVariantConversionCallbacks(); + + _underlyingDict = dictionary; + } + + // Explicit name to make it very clear + internal static Dictionary CreateTakingOwnershipOfDisposableValue( + godot_dictionary nativeValueToOwn) + => new Dictionary(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); + + /// + /// Converts this typed to an untyped . + /// + /// The typed dictionary to convert. + public static explicit operator Dictionary(Dictionary from) + { + return from?._underlyingDict; + } + + /// + /// Duplicates this . + /// + /// If , performs a deep copy. + /// A new Godot Dictionary. + public Dictionary Duplicate(bool deep = false) + { + return new Dictionary(_underlyingDict.Duplicate(deep)); + } + + // IDictionary + + /// + /// Returns the value at the given . + /// + /// The value at the given . + public unsafe TValue this[TKey key] + { + get + { + using var variantKey = _convertKeyToVariantCallback(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 _convertValueToManagedCallback(value); + } + else + { + throw new KeyNotFoundException(); + } + } + set + { + using var variantKey = _convertKeyToVariantCallback(key); + using var variantValue = _convertValueToVariantCallback(value); + var self = (godot_dictionary)_underlyingDict.NativeValue; + NativeFuncs.godotsharp_dictionary_set_value(ref self, + variantKey, variantValue); + } + } + + /// + /// Gets the collection of keys in this . + /// + public ICollection Keys + { + get + { + godot_array keyArray; + var self = (godot_dictionary)_underlyingDict.NativeValue; + NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray); + return Array.CreateTakingOwnershipOfDisposableValue(keyArray); + } + } + + /// + /// Gets the collection of elements in this . + /// + public ICollection Values + { + get + { + godot_array valuesArray; + var self = (godot_dictionary)_underlyingDict.NativeValue; + NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray); + return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); + } + } + + IEnumerable IReadOnlyDictionary.Keys => Keys; + + IEnumerable IReadOnlyDictionary.Values => Values; + + private unsafe KeyValuePair 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( + _convertKeyToManagedCallback(key), + _convertValueToManagedCallback(value)); + } + } + + /// + /// Adds an object at key + /// to this . + /// + /// The key at which to add the object. + /// The object to add. + public unsafe void Add(TKey key, TValue value) + { + using var variantKey = _convertKeyToVariantCallback(key); + var self = (godot_dictionary)_underlyingDict.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 var variantValue = _convertValueToVariantCallback(value); + NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue); + } + + /// + /// Checks if this contains the given key. + /// + /// The key to look for. + /// Whether or not this dictionary contains the given key. + public unsafe bool ContainsKey(TKey key) + { + using var variantKey = _convertKeyToVariantCallback(key); + var self = (godot_dictionary)_underlyingDict.NativeValue; + return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool(); + } + + /// + /// Removes an element from this by key. + /// + /// The key of the element to remove. + public unsafe bool Remove(TKey key) + { + using var variantKey = _convertKeyToVariantCallback(key); + var self = (godot_dictionary)_underlyingDict.NativeValue; + return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); + } + + /// + /// Gets the object at the given . + /// + /// The key of the element to get. + /// The value at the given . + /// If an object was found for the given . + public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + using var variantKey = _convertKeyToVariantCallback(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 ? _convertValueToManagedCallback(retValue) : default; + + return found; + } + + // ICollection> + + /// + /// Returns the number of elements in this . + /// This is also known as the size or length of the dictionary. + /// + /// The number of elements. + public int Count => _underlyingDict.Count; + + bool ICollection>.IsReadOnly => false; + + void ICollection>.Add(KeyValuePair item) + => Add(item.Key, item.Value); + + /// + /// Erases all the items from this . + /// + public void Clear() => _underlyingDict.Clear(); + + unsafe bool ICollection>.Contains(KeyValuePair item) + { + using var variantKey = _convertKeyToVariantCallback(item.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) + { + if (!found) + return false; + + using var variantValue = _convertValueToVariantCallback(item.Value); + return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); + } + } + + /// + /// Copies the elements of this to the given + /// untyped C# array, starting at the given index. + /// + /// The array to copy to. + /// The index to start at. + public void CopyTo(KeyValuePair[] 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++; + } + } + + unsafe bool ICollection>.Remove(KeyValuePair item) + { + using var variantKey = _convertKeyToVariantCallback(item.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) + { + if (!found) + return false; + + using var variantValue = _convertValueToVariantCallback(item.Value); + if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) + { + return NativeFuncs.godotsharp_dictionary_remove_key( + ref self, variantKey).ToBool(); + } + + return false; + } + } + + // IEnumerable> + + /// + /// Gets an enumerator for this . + /// + /// An enumerator. + public IEnumerator> GetEnumerator() + { + for (int i = 0; i < Count; i++) + { + yield return GetKeyValuePair(i); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Converts this to a string. + /// + /// A string representation of this dictionary. + public override string ToString() => _underlyingDict.ToString(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Dictionary from) => Variant.CreateFrom(from); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Dictionary(Variant from) => from.AsGodotDictionary(); + } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs new file mode 100644 index 00000000000..2b5bf2e1429 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs @@ -0,0 +1,976 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Godot.NativeInterop; + +internal static unsafe class VariantConversionCallbacks +{ + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + internal static delegate* GetToVariantCallback() + { + static godot_variant FromBool(in bool @bool) => + VariantUtils.CreateFromBool(@bool); + + static godot_variant FromChar(in char @char) => + VariantUtils.CreateFromInt(@char); + + static godot_variant FromInt8(in sbyte @int8) => + VariantUtils.CreateFromInt(@int8); + + static godot_variant FromInt16(in short @int16) => + VariantUtils.CreateFromInt(@int16); + + static godot_variant FromInt32(in int @int32) => + VariantUtils.CreateFromInt(@int32); + + static godot_variant FromInt64(in long @int64) => + VariantUtils.CreateFromInt(@int64); + + static godot_variant FromUInt8(in byte @uint8) => + VariantUtils.CreateFromInt(@uint8); + + static godot_variant FromUInt16(in ushort @uint16) => + VariantUtils.CreateFromInt(@uint16); + + static godot_variant FromUInt32(in uint @uint32) => + VariantUtils.CreateFromInt(@uint32); + + static godot_variant FromUInt64(in ulong @uint64) => + VariantUtils.CreateFromInt(@uint64); + + static godot_variant FromFloat(in float @float) => + VariantUtils.CreateFromFloat(@float); + + static godot_variant FromDouble(in double @double) => + VariantUtils.CreateFromFloat(@double); + + static godot_variant FromVector2(in Vector2 @vector2) => + VariantUtils.CreateFromVector2(@vector2); + + static godot_variant FromVector2I(in Vector2i vector2I) => + VariantUtils.CreateFromVector2i(vector2I); + + static godot_variant FromRect2(in Rect2 @rect2) => + VariantUtils.CreateFromRect2(@rect2); + + static godot_variant FromRect2I(in Rect2i rect2I) => + VariantUtils.CreateFromRect2i(rect2I); + + static godot_variant FromTransform2D(in Transform2D @transform2D) => + VariantUtils.CreateFromTransform2D(@transform2D); + + static godot_variant FromVector3(in Vector3 @vector3) => + VariantUtils.CreateFromVector3(@vector3); + + static godot_variant FromVector3I(in Vector3i vector3I) => + VariantUtils.CreateFromVector3i(vector3I); + + static godot_variant FromBasis(in Basis @basis) => + VariantUtils.CreateFromBasis(@basis); + + static godot_variant FromQuaternion(in Quaternion @quaternion) => + VariantUtils.CreateFromQuaternion(@quaternion); + + static godot_variant FromTransform3D(in Transform3D @transform3d) => + VariantUtils.CreateFromTransform3D(@transform3d); + + static godot_variant FromAabb(in AABB @aabb) => + VariantUtils.CreateFromAABB(@aabb); + + static godot_variant FromColor(in Color @color) => + VariantUtils.CreateFromColor(@color); + + static godot_variant FromPlane(in Plane @plane) => + VariantUtils.CreateFromPlane(@plane); + + static godot_variant FromCallable(in Callable @callable) => + VariantUtils.CreateFromCallable(@callable); + + static godot_variant FromSignalInfo(in SignalInfo @signalInfo) => + VariantUtils.CreateFromSignalInfo(@signalInfo); + + static godot_variant FromString(in string @string) => + VariantUtils.CreateFromString(@string); + + static godot_variant FromByteArray(in byte[] byteArray) => + VariantUtils.CreateFromPackedByteArray(byteArray); + + static godot_variant FromInt32Array(in int[] int32Array) => + VariantUtils.CreateFromPackedInt32Array(int32Array); + + static godot_variant FromInt64Array(in long[] int64Array) => + VariantUtils.CreateFromPackedInt64Array(int64Array); + + static godot_variant FromFloatArray(in float[] floatArray) => + VariantUtils.CreateFromPackedFloat32Array(floatArray); + + static godot_variant FromDoubleArray(in double[] doubleArray) => + VariantUtils.CreateFromPackedFloat64Array(doubleArray); + + static godot_variant FromStringArray(in string[] stringArray) => + VariantUtils.CreateFromPackedStringArray(stringArray); + + static godot_variant FromVector2Array(in Vector2[] vector2Array) => + VariantUtils.CreateFromPackedVector2Array(vector2Array); + + static godot_variant FromVector3Array(in Vector3[] vector3Array) => + VariantUtils.CreateFromPackedVector3Array(vector3Array); + + static godot_variant FromColorArray(in Color[] colorArray) => + VariantUtils.CreateFromPackedColorArray(colorArray); + + static godot_variant FromStringNameArray(in StringName[] stringNameArray) => + VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray); + + static godot_variant FromNodePathArray(in NodePath[] nodePathArray) => + VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray); + + static godot_variant FromRidArray(in RID[] ridArray) => + VariantUtils.CreateFromSystemArrayOfRID(ridArray); + + static godot_variant FromGodotObject(in Godot.Object godotObject) => + VariantUtils.CreateFromGodotObject(godotObject); + + static godot_variant FromStringName(in StringName stringName) => + VariantUtils.CreateFromStringName(stringName); + + static godot_variant FromNodePath(in NodePath nodePath) => + VariantUtils.CreateFromNodePath(nodePath); + + static godot_variant FromRid(in RID rid) => + VariantUtils.CreateFromRID(rid); + + static godot_variant FromGodotDictionary(in Collections.Dictionary godotDictionary) => + VariantUtils.CreateFromDictionary(godotDictionary); + + static godot_variant FromGodotArray(in Collections.Array godotArray) => + VariantUtils.CreateFromArray(godotArray); + + static godot_variant FromVariant(in Variant variant) => + NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar); + + var typeOfT = typeof(T); + + if (typeOfT == typeof(bool)) + { + return (delegate* )(delegate* ) + &FromBool; + } + + if (typeOfT == typeof(char)) + { + return (delegate* )(delegate* ) + &FromChar; + } + + if (typeOfT == typeof(sbyte)) + { + return (delegate* )(delegate* ) + &FromInt8; + } + + if (typeOfT == typeof(short)) + { + return (delegate* )(delegate* ) + &FromInt16; + } + + if (typeOfT == typeof(int)) + { + return (delegate* )(delegate* ) + &FromInt32; + } + + if (typeOfT == typeof(long)) + { + return (delegate* )(delegate* ) + &FromInt64; + } + + if (typeOfT == typeof(byte)) + { + return (delegate* )(delegate* ) + &FromUInt8; + } + + if (typeOfT == typeof(ushort)) + { + return (delegate* )(delegate* ) + &FromUInt16; + } + + if (typeOfT == typeof(uint)) + { + return (delegate* )(delegate* ) + &FromUInt32; + } + + if (typeOfT == typeof(ulong)) + { + return (delegate* )(delegate* ) + &FromUInt64; + } + + if (typeOfT == typeof(float)) + { + return (delegate* )(delegate* ) + &FromFloat; + } + + if (typeOfT == typeof(double)) + { + return (delegate* )(delegate* ) + &FromDouble; + } + + if (typeOfT == typeof(Vector2)) + { + return (delegate* )(delegate* ) + &FromVector2; + } + + if (typeOfT == typeof(Vector2i)) + { + return (delegate* )(delegate* ) + &FromVector2I; + } + + if (typeOfT == typeof(Rect2)) + { + return (delegate* )(delegate* ) + &FromRect2; + } + + if (typeOfT == typeof(Rect2i)) + { + return (delegate* )(delegate* ) + &FromRect2I; + } + + if (typeOfT == typeof(Transform2D)) + { + return (delegate* )(delegate* ) + &FromTransform2D; + } + + if (typeOfT == typeof(Vector3)) + { + return (delegate* )(delegate* ) + &FromVector3; + } + + if (typeOfT == typeof(Vector3i)) + { + return (delegate* )(delegate* ) + &FromVector3I; + } + + if (typeOfT == typeof(Basis)) + { + return (delegate* )(delegate* ) + &FromBasis; + } + + if (typeOfT == typeof(Quaternion)) + { + return (delegate* )(delegate* ) + &FromQuaternion; + } + + if (typeOfT == typeof(Transform3D)) + { + return (delegate* )(delegate* ) + &FromTransform3D; + } + + if (typeOfT == typeof(AABB)) + { + return (delegate* )(delegate* ) + &FromAabb; + } + + if (typeOfT == typeof(Color)) + { + return (delegate* )(delegate* ) + &FromColor; + } + + if (typeOfT == typeof(Plane)) + { + return (delegate* )(delegate* ) + &FromPlane; + } + + if (typeOfT == typeof(Callable)) + { + return (delegate* )(delegate* ) + &FromCallable; + } + + if (typeOfT == typeof(SignalInfo)) + { + return (delegate* )(delegate* ) + &FromSignalInfo; + } + + if (typeOfT.IsEnum) + { + var enumUnderlyingType = typeOfT.GetEnumUnderlyingType(); + + switch (Type.GetTypeCode(enumUnderlyingType)) + { + case TypeCode.SByte: + { + return (delegate* )(delegate* ) + &FromInt8; + } + case TypeCode.Int16: + { + return (delegate* )(delegate* ) + &FromInt16; + } + case TypeCode.Int32: + { + return (delegate* )(delegate* ) + &FromInt32; + } + case TypeCode.Int64: + { + return (delegate* )(delegate* ) + &FromInt64; + } + case TypeCode.Byte: + { + return (delegate* )(delegate* ) + &FromUInt8; + } + case TypeCode.UInt16: + { + return (delegate* )(delegate* ) + &FromUInt16; + } + case TypeCode.UInt32: + { + return (delegate* )(delegate* ) + &FromUInt32; + } + case TypeCode.UInt64: + { + return (delegate* )(delegate* ) + &FromUInt64; + } + default: + return null; + } + } + + if (typeOfT == typeof(string)) + { + return (delegate* )(delegate* ) + &FromString; + } + + if (typeOfT == typeof(byte[])) + { + return (delegate* )(delegate* ) + &FromByteArray; + } + + if (typeOfT == typeof(int[])) + { + return (delegate* )(delegate* ) + &FromInt32Array; + } + + if (typeOfT == typeof(long[])) + { + return (delegate* )(delegate* ) + &FromInt64Array; + } + + if (typeOfT == typeof(float[])) + { + return (delegate* )(delegate* ) + &FromFloatArray; + } + + if (typeOfT == typeof(double[])) + { + return (delegate* )(delegate* ) + &FromDoubleArray; + } + + if (typeOfT == typeof(string[])) + { + return (delegate* )(delegate* ) + &FromStringArray; + } + + if (typeOfT == typeof(Vector2[])) + { + return (delegate* )(delegate* ) + &FromVector2Array; + } + + if (typeOfT == typeof(Vector3[])) + { + return (delegate* )(delegate* ) + &FromVector3Array; + } + + if (typeOfT == typeof(Color[])) + { + return (delegate* )(delegate* ) + &FromColorArray; + } + + if (typeOfT == typeof(StringName[])) + { + return (delegate* )(delegate* ) + &FromStringNameArray; + } + + if (typeOfT == typeof(NodePath[])) + { + return (delegate* )(delegate* ) + &FromNodePathArray; + } + + if (typeOfT == typeof(RID[])) + { + return (delegate* )(delegate* ) + &FromRidArray; + } + + if (typeof(Godot.Object).IsAssignableFrom(typeOfT)) + { + return (delegate* )(delegate* ) + &FromGodotObject; + } + + if (typeOfT == typeof(StringName)) + { + return (delegate* )(delegate* ) + &FromStringName; + } + + if (typeOfT == typeof(NodePath)) + { + return (delegate* )(delegate* ) + &FromNodePath; + } + + if (typeOfT == typeof(RID)) + { + return (delegate* )(delegate* ) + &FromRid; + } + + if (typeOfT == typeof(Godot.Collections.Dictionary)) + { + return (delegate* )(delegate* ) + &FromGodotDictionary; + } + + if (typeOfT == typeof(Godot.Collections.Array)) + { + return (delegate* )(delegate* ) + &FromGodotArray; + } + + if (typeOfT == typeof(Variant)) + { + return (delegate* )(delegate* ) + &FromVariant; + } + + return null; + } + + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + internal static delegate* GetToManagedCallback() + { + static bool ToBool(in godot_variant variant) => + VariantUtils.ConvertToBool(variant); + + static char ToChar(in godot_variant variant) => + VariantUtils.ConvertToChar(variant); + + static sbyte ToInt8(in godot_variant variant) => + VariantUtils.ConvertToInt8(variant); + + static short ToInt16(in godot_variant variant) => + VariantUtils.ConvertToInt16(variant); + + static int ToInt32(in godot_variant variant) => + VariantUtils.ConvertToInt32(variant); + + static long ToInt64(in godot_variant variant) => + VariantUtils.ConvertToInt64(variant); + + static byte ToUInt8(in godot_variant variant) => + VariantUtils.ConvertToUInt8(variant); + + static ushort ToUInt16(in godot_variant variant) => + VariantUtils.ConvertToUInt16(variant); + + static uint ToUInt32(in godot_variant variant) => + VariantUtils.ConvertToUInt32(variant); + + static ulong ToUInt64(in godot_variant variant) => + VariantUtils.ConvertToUInt64(variant); + + static float ToFloat(in godot_variant variant) => + VariantUtils.ConvertToFloat32(variant); + + static double ToDouble(in godot_variant variant) => + VariantUtils.ConvertToFloat64(variant); + + static Vector2 ToVector2(in godot_variant variant) => + VariantUtils.ConvertToVector2(variant); + + static Vector2i ToVector2I(in godot_variant variant) => + VariantUtils.ConvertToVector2i(variant); + + static Rect2 ToRect2(in godot_variant variant) => + VariantUtils.ConvertToRect2(variant); + + static Rect2i ToRect2I(in godot_variant variant) => + VariantUtils.ConvertToRect2i(variant); + + static Transform2D ToTransform2D(in godot_variant variant) => + VariantUtils.ConvertToTransform2D(variant); + + static Vector3 ToVector3(in godot_variant variant) => + VariantUtils.ConvertToVector3(variant); + + static Vector3i ToVector3I(in godot_variant variant) => + VariantUtils.ConvertToVector3i(variant); + + static Basis ToBasis(in godot_variant variant) => + VariantUtils.ConvertToBasis(variant); + + static Quaternion ToQuaternion(in godot_variant variant) => + VariantUtils.ConvertToQuaternion(variant); + + static Transform3D ToTransform3D(in godot_variant variant) => + VariantUtils.ConvertToTransform3D(variant); + + static AABB ToAabb(in godot_variant variant) => + VariantUtils.ConvertToAABB(variant); + + static Color ToColor(in godot_variant variant) => + VariantUtils.ConvertToColor(variant); + + static Plane ToPlane(in godot_variant variant) => + VariantUtils.ConvertToPlane(variant); + + static Callable ToCallable(in godot_variant variant) => + VariantUtils.ConvertToCallableManaged(variant); + + static SignalInfo ToSignalInfo(in godot_variant variant) => + VariantUtils.ConvertToSignalInfo(variant); + + static string ToString(in godot_variant variant) => + VariantUtils.ConvertToStringObject(variant); + + static byte[] ToByteArray(in godot_variant variant) => + VariantUtils.ConvertAsPackedByteArrayToSystemArray(variant); + + static int[] ToInt32Array(in godot_variant variant) => + VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(variant); + + static long[] ToInt64Array(in godot_variant variant) => + VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(variant); + + static float[] ToFloatArray(in godot_variant variant) => + VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(variant); + + static double[] ToDoubleArray(in godot_variant variant) => + VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(variant); + + static string[] ToStringArray(in godot_variant variant) => + VariantUtils.ConvertAsPackedStringArrayToSystemArray(variant); + + static Vector2[] ToVector2Array(in godot_variant variant) => + VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(variant); + + static Vector3[] ToVector3Array(in godot_variant variant) => + VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(variant); + + static Color[] ToColorArray(in godot_variant variant) => + VariantUtils.ConvertAsPackedColorArrayToSystemArray(variant); + + static StringName[] ToStringNameArray(in godot_variant variant) => + VariantUtils.ConvertToSystemArrayOfStringName(variant); + + static NodePath[] ToNodePathArray(in godot_variant variant) => + VariantUtils.ConvertToSystemArrayOfNodePath(variant); + + static RID[] ToRidArray(in godot_variant variant) => + VariantUtils.ConvertToSystemArrayOfRID(variant); + + static Godot.Object ToGodotObject(in godot_variant variant) => + VariantUtils.ConvertToGodotObject(variant); + + static StringName ToStringName(in godot_variant variant) => + VariantUtils.ConvertToStringNameObject(variant); + + static NodePath ToNodePath(in godot_variant variant) => + VariantUtils.ConvertToNodePathObject(variant); + + static RID ToRid(in godot_variant variant) => + VariantUtils.ConvertToRID(variant); + + static Collections.Dictionary ToGodotDictionary(in godot_variant variant) => + VariantUtils.ConvertToDictionaryObject(variant); + + static Collections.Array ToGodotArray(in godot_variant variant) => + VariantUtils.ConvertToArrayObject(variant); + + static Variant ToVariant(in godot_variant variant) => + Variant.CreateCopyingBorrowed(variant); + + var typeOfT = typeof(T); + + // ReSharper disable RedundantCast + // Rider is being stupid here. These casts are definitely needed. We get build errors without them. + + if (typeOfT == typeof(bool)) + { + return (delegate* )(delegate* ) + &ToBool; + } + + if (typeOfT == typeof(char)) + { + return (delegate* )(delegate* ) + &ToChar; + } + + if (typeOfT == typeof(sbyte)) + { + return (delegate* )(delegate* ) + &ToInt8; + } + + if (typeOfT == typeof(short)) + { + return (delegate* )(delegate* ) + &ToInt16; + } + + if (typeOfT == typeof(int)) + { + return (delegate* )(delegate* ) + &ToInt32; + } + + if (typeOfT == typeof(long)) + { + return (delegate* )(delegate* ) + &ToInt64; + } + + if (typeOfT == typeof(byte)) + { + return (delegate* )(delegate* ) + &ToUInt8; + } + + if (typeOfT == typeof(ushort)) + { + return (delegate* )(delegate* ) + &ToUInt16; + } + + if (typeOfT == typeof(uint)) + { + return (delegate* )(delegate* ) + &ToUInt32; + } + + if (typeOfT == typeof(ulong)) + { + return (delegate* )(delegate* ) + &ToUInt64; + } + + if (typeOfT == typeof(float)) + { + return (delegate* )(delegate* ) + &ToFloat; + } + + if (typeOfT == typeof(double)) + { + return (delegate* )(delegate* ) + &ToDouble; + } + + if (typeOfT == typeof(Vector2)) + { + return (delegate* )(delegate* ) + &ToVector2; + } + + if (typeOfT == typeof(Vector2i)) + { + return (delegate* )(delegate* ) + &ToVector2I; + } + + if (typeOfT == typeof(Rect2)) + { + return (delegate* )(delegate* ) + &ToRect2; + } + + if (typeOfT == typeof(Rect2i)) + { + return (delegate* )(delegate* ) + &ToRect2I; + } + + if (typeOfT == typeof(Transform2D)) + { + return (delegate* )(delegate* ) + &ToTransform2D; + } + + if (typeOfT == typeof(Vector3)) + { + return (delegate* )(delegate* ) + &ToVector3; + } + + if (typeOfT == typeof(Vector3i)) + { + return (delegate* )(delegate* ) + &ToVector3I; + } + + if (typeOfT == typeof(Basis)) + { + return (delegate* )(delegate* ) + &ToBasis; + } + + if (typeOfT == typeof(Quaternion)) + { + return (delegate* )(delegate* ) + &ToQuaternion; + } + + if (typeOfT == typeof(Transform3D)) + { + return (delegate* )(delegate* ) + &ToTransform3D; + } + + if (typeOfT == typeof(AABB)) + { + return (delegate* )(delegate* ) + &ToAabb; + } + + if (typeOfT == typeof(Color)) + { + return (delegate* )(delegate* ) + &ToColor; + } + + if (typeOfT == typeof(Plane)) + { + return (delegate* )(delegate* ) + &ToPlane; + } + + if (typeOfT == typeof(Callable)) + { + return (delegate* )(delegate* ) + &ToCallable; + } + + if (typeOfT == typeof(SignalInfo)) + { + return (delegate* )(delegate* ) + &ToSignalInfo; + } + + if (typeOfT.IsEnum) + { + var enumUnderlyingType = typeOfT.GetEnumUnderlyingType(); + + switch (Type.GetTypeCode(enumUnderlyingType)) + { + case TypeCode.SByte: + { + return (delegate* )(delegate* ) + &ToInt8; + } + case TypeCode.Int16: + { + return (delegate* )(delegate* ) + &ToInt16; + } + case TypeCode.Int32: + { + return (delegate* )(delegate* ) + &ToInt32; + } + case TypeCode.Int64: + { + return (delegate* )(delegate* ) + &ToInt64; + } + case TypeCode.Byte: + { + return (delegate* )(delegate* ) + &ToUInt8; + } + case TypeCode.UInt16: + { + return (delegate* )(delegate* ) + &ToUInt16; + } + case TypeCode.UInt32: + { + return (delegate* )(delegate* ) + &ToUInt32; + } + case TypeCode.UInt64: + { + return (delegate* )(delegate* ) + &ToUInt64; + } + default: + return null; + } + } + + if (typeOfT == typeof(string)) + { + return (delegate* )(delegate* ) + &ToString; + } + + if (typeOfT == typeof(byte[])) + { + return (delegate* )(delegate* ) + &ToByteArray; + } + + if (typeOfT == typeof(int[])) + { + return (delegate* )(delegate* ) + &ToInt32Array; + } + + if (typeOfT == typeof(long[])) + { + return (delegate* )(delegate* ) + &ToInt64Array; + } + + if (typeOfT == typeof(float[])) + { + return (delegate* )(delegate* ) + &ToFloatArray; + } + + if (typeOfT == typeof(double[])) + { + return (delegate* )(delegate* ) + &ToDoubleArray; + } + + if (typeOfT == typeof(string[])) + { + return (delegate* )(delegate* ) + &ToStringArray; + } + + if (typeOfT == typeof(Vector2[])) + { + return (delegate* )(delegate* ) + &ToVector2Array; + } + + if (typeOfT == typeof(Vector3[])) + { + return (delegate* )(delegate* ) + &ToVector3Array; + } + + if (typeOfT == typeof(Color[])) + { + return (delegate* )(delegate* ) + &ToColorArray; + } + + if (typeOfT == typeof(StringName[])) + { + return (delegate* )(delegate* ) + &ToStringNameArray; + } + + if (typeOfT == typeof(NodePath[])) + { + return (delegate* )(delegate* ) + &ToNodePathArray; + } + + if (typeOfT == typeof(RID[])) + { + return (delegate* )(delegate* ) + &ToRidArray; + } + + if (typeof(Godot.Object).IsAssignableFrom(typeOfT)) + { + return (delegate* )(delegate* ) + &ToGodotObject; + } + + if (typeOfT == typeof(StringName)) + { + return (delegate* )(delegate* ) + &ToStringName; + } + + if (typeOfT == typeof(NodePath)) + { + return (delegate* )(delegate* ) + &ToNodePath; + } + + if (typeOfT == typeof(RID)) + { + return (delegate* )(delegate* ) + &ToRid; + } + + if (typeOfT == typeof(Godot.Collections.Dictionary)) + { + return (delegate* )(delegate* ) + &ToGodotDictionary; + } + + if (typeOfT == typeof(Godot.Collections.Array)) + { + return (delegate* )(delegate* ) + &ToGodotArray; + } + + if (typeOfT == typeof(Variant)) + { + return (delegate* )(delegate* ) + &ToVariant; + } + + // ReSharper restore RedundantCast + + return null; + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 1ce89659399..491ccf904ef 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -238,6 +238,10 @@ namespace Godot.NativeInterop public static godot_variant CreateFromArray(Collections.Array? from) => from != null ? CreateFromArray((godot_array)from.NativeValue) : default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromArray(Array? from) + => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default; + public static godot_variant CreateFromDictionary(godot_dictionary from) { NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from); @@ -248,6 +252,10 @@ 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(Dictionary? from) + => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default; + public static godot_variant CreateFromStringName(godot_string_name from) { NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from); @@ -496,6 +504,10 @@ namespace Godot.NativeInterop public static Collections.Array ConvertToArrayObject(in godot_variant p_var) => Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Array ConvertToArrayObject(in godot_variant p_var) + => Array.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var)); + public static godot_dictionary ConvertToDictionary(in godot_variant p_var) => p_var.Type == Variant.Type.Dictionary ? NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) : @@ -505,6 +517,10 @@ namespace Godot.NativeInterop public static Dictionary ConvertToDictionaryObject(in godot_variant p_var) => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary ConvertToDictionaryObject(in godot_variant p_var) + => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var)); + public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index d1ff6ade8af..c7881c74042 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -94,6 +94,7 @@ + diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs index 7c4df291ac1..eb8b0611206 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs @@ -283,6 +283,14 @@ public partial struct Variant : IDisposable where T : Godot.Object => VariantUtils.ConvertToSystemArrayOfGodotObject((godot_variant)NativeVar); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Collections.Dictionary AsGodotDictionary() => + VariantUtils.ConvertToDictionaryObject((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Collections.Array AsGodotArray() => + VariantUtils.ConvertToArrayObject((godot_variant)NativeVar); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public StringName[] AsSystemArrayOfStringName() => VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar); @@ -594,6 +602,14 @@ public partial struct Variant : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Godot.Object[] from) => from; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Collections.Dictionary from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Collections.Array from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Span from) => from;