diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index b90321b586955..82b5f478e10fc 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2274,7 +2274,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf p_output.append(");\n"); // Generate Callable trampoline for the delegate - p_output << MEMBER_BEGIN "private static unsafe void " << p_isignal.proxy_name << "Trampoline" + p_output << MEMBER_BEGIN "private static void " << p_isignal.proxy_name << "Trampoline" << "(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)\n" << INDENT1 "{\n" << INDENT2 "Callable.ThrowIfArgCountMismatch(args, " << itos(p_isignal.arguments.size()) << ");\n" @@ -2289,9 +2289,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf p_output << ","; } - // TODO: We don't need to use VariantConversionCallbacks. We have the type information so we can use [cs_variant_to_managed] and [cs_managed_to_variant]. - p_output << "\n" INDENT3 "VariantConversionCallbacks.GetToManagedCallback<" - << arg_type->cs_type << ">()(args[" << itos(idx) << "])"; + p_output << sformat(arg_type->cs_variant_to_managed, + "args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name); idx++; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index f1b46e293b48a..e3b7ac297da69 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -495,35 +495,10 @@ public sealed class Array<[MustBeVariant] T> : private static Array FromVariantFunc(in godot_variant variant) => VariantUtils.ConvertToArrayObject(variant); - // 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 unsafe delegate* managed ConvertToVariantCallback; - private static readonly unsafe delegate* managed ConvertToManagedCallback; - - // ReSharper restore StaticMemberInGenericType - static unsafe Array() { - VariantConversionCallbacks.GenericConversionCallbacks[typeof(Array)] = - ( - (IntPtr)(delegate* managed, godot_variant>)&ToVariantFunc, - (IntPtr)(delegate* managed>)&FromVariantFunc - ); - - 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}'."); - } + VariantUtils.GenericConversion>.ToVariantCb = &ToVariantFunc; + VariantUtils.GenericConversion>.FromVariantCb = &FromVariantFunc; } private readonly Array _underlyingArray; @@ -539,8 +514,6 @@ private static unsafe void ValidateVariantConversionCallbacks() /// public Array() { - ValidateVariantConversionCallbacks(); - _underlyingArray = new Array(); } @@ -551,8 +524,6 @@ public Array() /// A new Godot Array. public Array(IEnumerable collection) { - ValidateVariantConversionCallbacks(); - if (collection == null) throw new ArgumentNullException(nameof(collection)); @@ -569,8 +540,6 @@ public Array(IEnumerable collection) /// A new Godot Array. public Array(T[] array) : this() { - ValidateVariantConversionCallbacks(); - if (array == null) throw new ArgumentNullException(nameof(array)); @@ -586,8 +555,6 @@ public Array(T[] array) : this() /// The untyped array to construct from. public Array(Array array) { - ValidateVariantConversionCallbacks(); - _underlyingArray = array; } @@ -665,7 +632,7 @@ public void Shuffle() get { _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); - return ConvertToManagedCallback(borrowElem); + return VariantUtils.ConvertTo(borrowElem); } set { @@ -675,7 +642,7 @@ public void Shuffle() godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); godot_variant* itemPtr = &ptrw[index]; (*itemPtr).Dispose(); - *itemPtr = ConvertToVariantCallback(value); + *itemPtr = VariantUtils.CreateFrom(value); } } @@ -685,9 +652,9 @@ public void Shuffle() /// /// The item to search for. /// The index of the item, or -1 if not found. - public unsafe int IndexOf(T item) + public int IndexOf(T item) { - using var variantValue = ConvertToVariantCallback(item); + using var variantValue = VariantUtils.CreateFrom(item); var self = (godot_array)_underlyingArray.NativeValue; return NativeFuncs.godotsharp_array_index_of(ref self, variantValue); } @@ -700,12 +667,12 @@ public unsafe int IndexOf(T item) /// /// The index to insert at. /// The item to insert. - public unsafe void Insert(int index, T item) + public void Insert(int index, T item) { if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); - using var variantValue = ConvertToVariantCallback(item); + using var variantValue = VariantUtils.CreateFrom(item); var self = (godot_array)_underlyingArray.NativeValue; NativeFuncs.godotsharp_array_insert(ref self, index, variantValue); } @@ -736,9 +703,9 @@ public void RemoveAt(int index) /// /// The item to add. /// The new size after adding the item. - public unsafe void Add(T item) + public void Add(T item) { - using var variantValue = ConvertToVariantCallback(item); + using var variantValue = VariantUtils.CreateFrom(item); var self = (godot_array)_underlyingArray.NativeValue; _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs index 6c6a10401997c..ff385da1c9ab7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs @@ -54,7 +54,7 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 1); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]) + VariantUtils.ConvertTo(args[0]) ); ret = default; @@ -73,8 +73,8 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 2); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]) ); ret = default; @@ -93,9 +93,9 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 3); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]) ); ret = default; @@ -114,10 +114,10 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 4); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]) ); ret = default; @@ -136,11 +136,11 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 5); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]) ); ret = default; @@ -159,12 +159,12 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 6); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]) ); ret = default; @@ -183,13 +183,13 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 7); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]), - VariantConversionCallbacks.GetToManagedCallback()(args[6]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]), + VariantUtils.ConvertTo(args[6]) ); ret = default; @@ -208,14 +208,14 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 8); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]), - VariantConversionCallbacks.GetToManagedCallback()(args[6]), - VariantConversionCallbacks.GetToManagedCallback()(args[7]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]), + VariantUtils.ConvertTo(args[6]), + VariantUtils.ConvertTo(args[7]) ); ret = default; @@ -234,15 +234,15 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 9); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]), - VariantConversionCallbacks.GetToManagedCallback()(args[6]), - VariantConversionCallbacks.GetToManagedCallback()(args[7]), - VariantConversionCallbacks.GetToManagedCallback()(args[8]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]), + VariantUtils.ConvertTo(args[6]), + VariantUtils.ConvertTo(args[7]), + VariantUtils.ConvertTo(args[8]) ); ret = default; @@ -265,7 +265,7 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ TResult res = ((Func)delegateObj)(); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -281,10 +281,10 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 1); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]) + VariantUtils.ConvertTo(args[0]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -300,11 +300,11 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 2); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -320,12 +320,12 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 3); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -341,13 +341,13 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 4); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -363,14 +363,14 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 5); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -386,15 +386,15 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 6); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -410,16 +410,16 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 7); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]), - VariantConversionCallbacks.GetToManagedCallback()(args[6]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]), + VariantUtils.ConvertTo(args[6]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -435,17 +435,17 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 8); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]), - VariantConversionCallbacks.GetToManagedCallback()(args[6]), - VariantConversionCallbacks.GetToManagedCallback()(args[7]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]), + VariantUtils.ConvertTo(args[6]), + VariantUtils.ConvertTo(args[7]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -461,18 +461,18 @@ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_ ThrowIfArgCountMismatch(args, 9); TResult res = ((Func)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]), - VariantConversionCallbacks.GetToManagedCallback()(args[1]), - VariantConversionCallbacks.GetToManagedCallback()(args[2]), - VariantConversionCallbacks.GetToManagedCallback()(args[3]), - VariantConversionCallbacks.GetToManagedCallback()(args[4]), - VariantConversionCallbacks.GetToManagedCallback()(args[5]), - VariantConversionCallbacks.GetToManagedCallback()(args[6]), - VariantConversionCallbacks.GetToManagedCallback()(args[7]), - VariantConversionCallbacks.GetToManagedCallback()(args[8]) + VariantUtils.ConvertTo(args[0]), + VariantUtils.ConvertTo(args[1]), + VariantUtils.ConvertTo(args[2]), + VariantUtils.ConvertTo(args[3]), + VariantUtils.ConvertTo(args[4]), + VariantUtils.ConvertTo(args[5]), + VariantUtils.ConvertTo(args[6]), + VariantUtils.ConvertTo(args[7]), + VariantUtils.ConvertTo(args[8]) ); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index f8793332a0b65..f14790a218972 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -362,45 +362,10 @@ public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> : private static Dictionary FromVariantFunc(in godot_variant variant) => VariantUtils.ConvertToDictionaryObject(variant); - // 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 unsafe delegate* managed ConvertKeyToVariantCallback; - private static readonly unsafe delegate* managed ConvertKeyToManagedCallback; - private static readonly unsafe delegate* managed ConvertValueToVariantCallback; - private static readonly unsafe delegate* managed ConvertValueToManagedCallback; - - // ReSharper restore StaticMemberInGenericType - static unsafe Dictionary() { - VariantConversionCallbacks.GenericConversionCallbacks[typeof(Dictionary)] = - ( - (IntPtr)(delegate* managed, godot_variant>)&ToVariantFunc, - (IntPtr)(delegate* managed>)&FromVariantFunc - ); - - 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}'."); - } + VariantUtils.GenericConversion>.ToVariantCb = &ToVariantFunc; + VariantUtils.GenericConversion>.FromVariantCb = &FromVariantFunc; } private readonly Dictionary _underlyingDict; @@ -416,8 +381,6 @@ private static unsafe void ValidateVariantConversionCallbacks() /// public Dictionary() { - ValidateVariantConversionCallbacks(); - _underlyingDict = new Dictionary(); } @@ -428,8 +391,6 @@ public Dictionary() /// A new Godot Dictionary. public Dictionary(IDictionary dictionary) { - ValidateVariantConversionCallbacks(); - if (dictionary == null) throw new ArgumentNullException(nameof(dictionary)); @@ -446,8 +407,6 @@ public Dictionary(IDictionary dictionary) /// A new Godot Dictionary. public Dictionary(Dictionary dictionary) { - ValidateVariantConversionCallbacks(); - _underlyingDict = dictionary; } @@ -481,18 +440,18 @@ public Dictionary(Dictionary dictionary) /// Returns the value at the given . /// /// The value at the given . - public unsafe TValue this[TKey key] + public TValue this[TKey key] { get { - using var variantKey = ConvertKeyToVariantCallback(key); + using var variantKey = VariantUtils.CreateFrom(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); + return VariantUtils.ConvertTo(value); } else { @@ -501,8 +460,8 @@ public Dictionary(Dictionary dictionary) } set { - using var variantKey = ConvertKeyToVariantCallback(key); - using var variantValue = ConvertValueToVariantCallback(value); + using var variantKey = VariantUtils.CreateFrom(key); + using var variantValue = VariantUtils.CreateFrom(value); var self = (godot_dictionary)_underlyingDict.NativeValue; NativeFuncs.godotsharp_dictionary_set_value(ref self, variantKey, variantValue); @@ -541,7 +500,7 @@ public ICollection Values IEnumerable IReadOnlyDictionary.Values => Values; - private unsafe KeyValuePair GetKeyValuePair(int index) + private KeyValuePair GetKeyValuePair(int index) { var self = (godot_dictionary)_underlyingDict.NativeValue; NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index, @@ -551,8 +510,8 @@ public ICollection Values using (value) { return new KeyValuePair( - ConvertKeyToManagedCallback(key), - ConvertValueToManagedCallback(value)); + VariantUtils.ConvertTo(key), + VariantUtils.ConvertTo(value)); } } @@ -562,15 +521,15 @@ public ICollection Values /// /// The key at which to add the object. /// The object to add. - public unsafe void Add(TKey key, TValue value) + public void Add(TKey key, TValue value) { - using var variantKey = ConvertKeyToVariantCallback(key); + using var variantKey = VariantUtils.CreateFrom(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); + using var variantValue = VariantUtils.CreateFrom(value); NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue); } @@ -579,9 +538,9 @@ public unsafe void Add(TKey key, TValue value) /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public unsafe bool ContainsKey(TKey key) + public bool ContainsKey(TKey key) { - using var variantKey = ConvertKeyToVariantCallback(key); + using var variantKey = VariantUtils.CreateFrom(key); var self = (godot_dictionary)_underlyingDict.NativeValue; return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool(); } @@ -590,9 +549,9 @@ public unsafe bool ContainsKey(TKey key) /// Removes an element from this by key. /// /// The key of the element to remove. - public unsafe bool Remove(TKey key) + public bool Remove(TKey key) { - using var variantKey = ConvertKeyToVariantCallback(key); + using var variantKey = VariantUtils.CreateFrom(key); var self = (godot_dictionary)_underlyingDict.NativeValue; return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); } @@ -603,15 +562,15 @@ public unsafe bool Remove(TKey key) /// 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) + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { - using var variantKey = ConvertKeyToVariantCallback(key); + using var variantKey = VariantUtils.CreateFrom(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; + value = found ? VariantUtils.ConvertTo(retValue) : default; return found; } @@ -635,9 +594,9 @@ public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value /// public void Clear() => _underlyingDict.Clear(); - unsafe bool ICollection>.Contains(KeyValuePair item) + bool ICollection>.Contains(KeyValuePair item) { - using var variantKey = ConvertKeyToVariantCallback(item.Key); + using var variantKey = VariantUtils.CreateFrom(item.Key); var self = (godot_dictionary)_underlyingDict.NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey, out godot_variant retValue).ToBool(); @@ -647,7 +606,7 @@ public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value if (!found) return false; - using var variantValue = ConvertValueToVariantCallback(item.Value); + using var variantValue = VariantUtils.CreateFrom(item.Value); return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); } } @@ -680,9 +639,9 @@ public void CopyTo(KeyValuePair[] array, int arrayIndex) } } - unsafe bool ICollection>.Remove(KeyValuePair item) + bool ICollection>.Remove(KeyValuePair item) { - using var variantKey = ConvertKeyToVariantCallback(item.Key); + using var variantKey = VariantUtils.CreateFrom(item.Key); var self = (godot_dictionary)_underlyingDict.NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey, out godot_variant retValue).ToBool(); @@ -692,7 +651,7 @@ public void CopyTo(KeyValuePair[] array, int arrayIndex) if (!found) return false; - using var variantValue = ConvertValueToVariantCallback(item.Value); + using var variantValue = VariantUtils.CreateFrom(item.Value); if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) { return NativeFuncs.godotsharp_dictionary_remove_key( diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs deleted file mode 100644 index 4b3db0c01a1e8..0000000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs +++ /dev/null @@ -1,1057 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace Godot.NativeInterop; - -// TODO: Change VariantConversionCallbacks. Store the callback in a static field for quick repeated access, instead of checking every time. -internal static unsafe class VariantConversionCallbacks -{ - internal static System.Collections.Generic.Dictionary - GenericConversionCallbacks = new(); - - [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 FromVector4(in Vector4 @vector4) => - VariantUtils.CreateFromVector4(@vector4); - - static godot_variant FromVector4I(in Vector4i vector4I) => - VariantUtils.CreateFromVector4i(vector4I); - - 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(Vector4)) - { - return (delegate*)(delegate*) - &FromVector4; - } - - if (typeOfT == typeof(Vector4i)) - { - return (delegate*)(delegate*) - &FromVector4I; - } - - 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; - } - - // TODO: - // IsGenericType and GetGenericTypeDefinition don't work in NativeAOT's reflection-free mode. - // We could make the Godot collections implement an interface and use IsAssignableFrom instead. - // Or we could just skip the check and always look for a conversion callback for the type. - if (typeOfT.IsGenericType) - { - var genericTypeDef = typeOfT.GetGenericTypeDefinition(); - - if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>) || - genericTypeDef == typeof(Godot.Collections.Array<>)) - { - RuntimeHelpers.RunClassConstructor(typeOfT.TypeHandle); - - if (GenericConversionCallbacks.TryGetValue(typeOfT, out var genericConversion)) - { - return (delegate*)genericConversion.ToVariant; - } - } - } - - 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 Vector4 ToVector4(in godot_variant variant) => - VariantUtils.ConvertToVector4(variant); - - static Vector4i ToVector4I(in godot_variant variant) => - VariantUtils.ConvertToVector4i(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(Vector4)) - { - return (delegate*)(delegate*) - &ToVector4; - } - - if (typeOfT == typeof(Vector4i)) - { - return (delegate*)(delegate*) - &ToVector4I; - } - - 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; - } - - // TODO: - // IsGenericType and GetGenericTypeDefinition don't work in NativeAOT's reflection-free mode. - // We could make the Godot collections implement an interface and use IsAssignableFrom instead. - // Or we could just skip the check and always look for a conversion callback for the type. - if (typeOfT.IsGenericType) - { - var genericTypeDef = typeOfT.GetGenericTypeDefinition(); - - if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>) || - genericTypeDef == typeof(Godot.Collections.Array<>)) - { - RuntimeHelpers.RunClassConstructor(typeOfT.TypeHandle); - - if (GenericConversionCallbacks.TryGetValue(typeOfT, out var genericConversion)) - { - return (delegate*)genericConversion.FromVariant; - } - } - } - - // 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 57f9ec7d95d11..ba8e7a6c6553c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -8,7 +8,7 @@ namespace Godot.NativeInterop { - public static class VariantUtils + public static partial class VariantUtils { public static godot_variant CreateFromRID(RID from) => new() { Type = Variant.Type.Rid, RID = from }; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs new file mode 100644 index 0000000000000..db84175e17c0d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs @@ -0,0 +1,406 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Godot.NativeInterop; + +public partial class VariantUtils +{ + private static Exception UnsupportedType() => throw new InvalidOperationException( + $"The type is not supported for conversion to/from Variant: '{typeof(T).FullName}'"); + + internal static class GenericConversion + { + public static unsafe godot_variant ToVariant(in T from) => + ToVariantCb != null ? ToVariantCb(from) : throw UnsupportedType(); + + public static unsafe T FromVariant(in godot_variant variant) => + FromVariantCb != null ? FromVariantCb(variant) : throw UnsupportedType(); + + // ReSharper disable once StaticMemberInGenericType + internal static unsafe delegate* ToVariantCb; + + // ReSharper disable once StaticMemberInGenericType + internal static unsafe delegate* FromVariantCb; + + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + static GenericConversion() + { + RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + public static godot_variant CreateFrom<[MustBeVariant] T>(in T from) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static TTo UnsafeAs(in T f) => Unsafe.As(ref Unsafe.AsRef(f)); + + // `typeof(T) == typeof(X)` is optimized away. We cannot cache `typeof(T)` in a local variable, as it's not optimized when done like that. + + if (typeof(T) == typeof(bool)) + return CreateFromBool(UnsafeAs(from)); + + if (typeof(T) == typeof(char)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(sbyte)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(short)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(int)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(long)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(byte)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(ushort)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(uint)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(ulong)) + return CreateFromInt(UnsafeAs(from)); + + if (typeof(T) == typeof(float)) + return CreateFromFloat(UnsafeAs(from)); + + if (typeof(T) == typeof(double)) + return CreateFromFloat(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector2)) + return CreateFromVector2(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector2i)) + return CreateFromVector2i(UnsafeAs(from)); + + if (typeof(T) == typeof(Rect2)) + return CreateFromRect2(UnsafeAs(from)); + + if (typeof(T) == typeof(Rect2i)) + return CreateFromRect2i(UnsafeAs(from)); + + if (typeof(T) == typeof(Transform2D)) + return CreateFromTransform2D(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector3)) + return CreateFromVector3(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector3i)) + return CreateFromVector3i(UnsafeAs(from)); + + if (typeof(T) == typeof(Basis)) + return CreateFromBasis(UnsafeAs(from)); + + if (typeof(T) == typeof(Quaternion)) + return CreateFromQuaternion(UnsafeAs(from)); + + if (typeof(T) == typeof(Transform3D)) + return CreateFromTransform3D(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector4)) + return CreateFromVector4(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector4i)) + return CreateFromVector4i(UnsafeAs(from)); + + if (typeof(T) == typeof(AABB)) + return CreateFromAABB(UnsafeAs(from)); + + if (typeof(T) == typeof(Color)) + return CreateFromColor(UnsafeAs(from)); + + if (typeof(T) == typeof(Plane)) + return CreateFromPlane(UnsafeAs(from)); + + if (typeof(T) == typeof(Callable)) + return CreateFromCallable(UnsafeAs(from)); + + if (typeof(T) == typeof(SignalInfo)) + return CreateFromSignalInfo(UnsafeAs(from)); + + if (typeof(T) == typeof(string)) + return CreateFromString(UnsafeAs(from)); + + if (typeof(T) == typeof(byte[])) + return CreateFromPackedByteArray(UnsafeAs(from)); + + if (typeof(T) == typeof(int[])) + return CreateFromPackedInt32Array(UnsafeAs(from)); + + if (typeof(T) == typeof(long[])) + return CreateFromPackedInt64Array(UnsafeAs(from)); + + if (typeof(T) == typeof(float[])) + return CreateFromPackedFloat32Array(UnsafeAs(from)); + + if (typeof(T) == typeof(double[])) + return CreateFromPackedFloat64Array(UnsafeAs(from)); + + if (typeof(T) == typeof(string[])) + return CreateFromPackedStringArray(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector2[])) + return CreateFromPackedVector2Array(UnsafeAs(from)); + + if (typeof(T) == typeof(Vector3[])) + return CreateFromPackedVector3Array(UnsafeAs(from)); + + if (typeof(T) == typeof(Color[])) + return CreateFromPackedColorArray(UnsafeAs(from)); + + if (typeof(T) == typeof(StringName[])) + return CreateFromSystemArrayOfStringName(UnsafeAs(from)); + + if (typeof(T) == typeof(NodePath[])) + return CreateFromSystemArrayOfNodePath(UnsafeAs(from)); + + if (typeof(T) == typeof(RID[])) + return CreateFromSystemArrayOfRID(UnsafeAs(from)); + + if (typeof(T) == typeof(StringName)) + return CreateFromStringName(UnsafeAs(from)); + + if (typeof(T) == typeof(NodePath)) + return CreateFromNodePath(UnsafeAs(from)); + + if (typeof(T) == typeof(RID)) + return CreateFromRID(UnsafeAs(from)); + + if (typeof(T) == typeof(Godot.Collections.Dictionary)) + return CreateFromDictionary(UnsafeAs(from)); + + if (typeof(T) == typeof(Godot.Collections.Array)) + return CreateFromArray(UnsafeAs(from)); + + if (typeof(T) == typeof(Variant)) + return NativeFuncs.godotsharp_variant_new_copy((godot_variant)UnsafeAs(from).NativeVar); + + // More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away. + + // `typeof(X).IsAssignableFrom(typeof(T))` is optimized away + + if (typeof(Godot.Object).IsAssignableFrom(typeof(T))) + return CreateFromGodotObject(UnsafeAs(from)); + + // `typeof(T).IsValueType` is optimized away + // `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113 + // Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job! + + if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T))) + { + // `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away. + // Fortunately, `Unsafe.SizeOf()` works and is optimized away. + // We don't need to know whether it's signed or unsigned. + + if (Unsafe.SizeOf() == 1) + return CreateFromInt(UnsafeAs(from)); + + if (Unsafe.SizeOf() == 2) + return CreateFromInt(UnsafeAs(from)); + + if (Unsafe.SizeOf() == 4) + return CreateFromInt(UnsafeAs(from)); + + if (Unsafe.SizeOf() == 8) + return CreateFromInt(UnsafeAs(from)); + + throw UnsupportedType(); + } + + return GenericConversion.ToVariant(from); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + public static T ConvertTo<[MustBeVariant] T>(in godot_variant variant) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static T UnsafeAsT(TFrom f) => Unsafe.As(ref Unsafe.AsRef(f)); + + if (typeof(T) == typeof(bool)) + return UnsafeAsT(ConvertToBool(variant)); + + if (typeof(T) == typeof(char)) + return UnsafeAsT(ConvertToChar(variant)); + + if (typeof(T) == typeof(sbyte)) + return UnsafeAsT(ConvertToInt8(variant)); + + if (typeof(T) == typeof(short)) + return UnsafeAsT(ConvertToInt16(variant)); + + if (typeof(T) == typeof(int)) + return UnsafeAsT(ConvertToInt32(variant)); + + if (typeof(T) == typeof(long)) + return UnsafeAsT(ConvertToInt64(variant)); + + if (typeof(T) == typeof(byte)) + return UnsafeAsT(ConvertToUInt8(variant)); + + if (typeof(T) == typeof(ushort)) + return UnsafeAsT(ConvertToUInt16(variant)); + + if (typeof(T) == typeof(uint)) + return UnsafeAsT(ConvertToUInt32(variant)); + + if (typeof(T) == typeof(ulong)) + return UnsafeAsT(ConvertToUInt64(variant)); + + if (typeof(T) == typeof(float)) + return UnsafeAsT(ConvertToFloat32(variant)); + + if (typeof(T) == typeof(double)) + return UnsafeAsT(ConvertToFloat64(variant)); + + if (typeof(T) == typeof(Vector2)) + return UnsafeAsT(ConvertToVector2(variant)); + + if (typeof(T) == typeof(Vector2i)) + return UnsafeAsT(ConvertToVector2i(variant)); + + if (typeof(T) == typeof(Rect2)) + return UnsafeAsT(ConvertToRect2(variant)); + + if (typeof(T) == typeof(Rect2i)) + return UnsafeAsT(ConvertToRect2i(variant)); + + if (typeof(T) == typeof(Transform2D)) + return UnsafeAsT(ConvertToTransform2D(variant)); + + if (typeof(T) == typeof(Vector3)) + return UnsafeAsT(ConvertToVector3(variant)); + + if (typeof(T) == typeof(Vector3i)) + return UnsafeAsT(ConvertToVector3i(variant)); + + if (typeof(T) == typeof(Basis)) + return UnsafeAsT(ConvertToBasis(variant)); + + if (typeof(T) == typeof(Quaternion)) + return UnsafeAsT(ConvertToQuaternion(variant)); + + if (typeof(T) == typeof(Transform3D)) + return UnsafeAsT(ConvertToTransform3D(variant)); + + if (typeof(T) == typeof(Vector4)) + return UnsafeAsT(ConvertToVector4(variant)); + + if (typeof(T) == typeof(Vector4i)) + return UnsafeAsT(ConvertToVector4i(variant)); + + if (typeof(T) == typeof(AABB)) + return UnsafeAsT(ConvertToAABB(variant)); + + if (typeof(T) == typeof(Color)) + return UnsafeAsT(ConvertToColor(variant)); + + if (typeof(T) == typeof(Plane)) + return UnsafeAsT(ConvertToPlane(variant)); + + if (typeof(T) == typeof(Callable)) + return UnsafeAsT(ConvertToCallableManaged(variant)); + + if (typeof(T) == typeof(SignalInfo)) + return UnsafeAsT(ConvertToSignalInfo(variant)); + + if (typeof(T) == typeof(string)) + return UnsafeAsT(ConvertToStringObject(variant)); + + if (typeof(T) == typeof(byte[])) + return UnsafeAsT(ConvertAsPackedByteArrayToSystemArray(variant)); + + if (typeof(T) == typeof(int[])) + return UnsafeAsT(ConvertAsPackedInt32ArrayToSystemArray(variant)); + + if (typeof(T) == typeof(long[])) + return UnsafeAsT(ConvertAsPackedInt64ArrayToSystemArray(variant)); + + if (typeof(T) == typeof(float[])) + return UnsafeAsT(ConvertAsPackedFloat32ArrayToSystemArray(variant)); + + if (typeof(T) == typeof(double[])) + return UnsafeAsT(ConvertAsPackedFloat64ArrayToSystemArray(variant)); + + if (typeof(T) == typeof(string[])) + return UnsafeAsT(ConvertAsPackedStringArrayToSystemArray(variant)); + + if (typeof(T) == typeof(Vector2[])) + return UnsafeAsT(ConvertAsPackedVector2ArrayToSystemArray(variant)); + + if (typeof(T) == typeof(Vector3[])) + return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant)); + + if (typeof(T) == typeof(Color[])) + return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant)); + + if (typeof(T) == typeof(StringName[])) + return UnsafeAsT(ConvertToSystemArrayOfStringName(variant)); + + if (typeof(T) == typeof(NodePath[])) + return UnsafeAsT(ConvertToSystemArrayOfNodePath(variant)); + + if (typeof(T) == typeof(RID[])) + return UnsafeAsT(ConvertToSystemArrayOfRID(variant)); + + if (typeof(T) == typeof(StringName)) + return UnsafeAsT(ConvertToStringNameObject(variant)); + + if (typeof(T) == typeof(NodePath)) + return UnsafeAsT(ConvertToNodePathObject(variant)); + + if (typeof(T) == typeof(RID)) + return UnsafeAsT(ConvertToRID(variant)); + + if (typeof(T) == typeof(Godot.Collections.Dictionary)) + return UnsafeAsT(ConvertToDictionaryObject(variant)); + + if (typeof(T) == typeof(Godot.Collections.Array)) + return UnsafeAsT(ConvertToArrayObject(variant)); + + if (typeof(T) == typeof(Variant)) + return UnsafeAsT(Variant.CreateCopyingBorrowed(variant)); + + // More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away. + + // `typeof(X).IsAssignableFrom(typeof(T))` is optimized away + + if (typeof(Godot.Object).IsAssignableFrom(typeof(T))) + return (T)(object)ConvertToGodotObject(variant); + + // `typeof(T).IsValueType` is optimized away + // `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113 + // Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job! + + if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T))) + { + // `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away. + // Fortunately, `Unsafe.SizeOf()` works and is optimized away. + // We don't need to know whether it's signed or unsigned. + + if (Unsafe.SizeOf() == 1) + return UnsafeAsT(ConvertToInt8(variant)); + + if (Unsafe.SizeOf() == 2) + return UnsafeAsT(ConvertToInt16(variant)); + + if (Unsafe.SizeOf() == 4) + return UnsafeAsT(ConvertToInt32(variant)); + + if (Unsafe.SizeOf() == 8) + return UnsafeAsT(ConvertToInt64(variant)); + + throw UnsupportedType(); + } + + return GenericConversion.FromVariant(variant); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs similarity index 99% rename from modules/mono/glue/GodotSharp/GodotSharp/Variant.cs rename to modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs index d354509dbf4eb..237a4da364259 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs @@ -120,6 +120,14 @@ public void Dispose() } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant From<[MustBeVariant] T>(in T from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFrom(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T As<[MustBeVariant] T>() => + VariantUtils.ConvertTo(NativeVar.DangerousSelfRef); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool AsBool() => VariantUtils.ConvertToBool((godot_variant)NativeVar); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index e3fb254f49349..85f7e36639eff 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -101,9 +101,9 @@ - + @@ -123,6 +123,7 @@ + @@ -131,7 +132,6 @@ -