From 3f645f980c5d7894f98075c29d1c65319be62be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 6 Nov 2022 01:27:55 +0100 Subject: C#: Optimize Variant conversion callbacks These callbacks are used for marshaling by callables and generic Godot collections. C# generics don't support specialization the way C++ templates do. I knew NativeAOT could optimize away many type checks when the types are known at compile time, but I didn't trust the JIT would do as good a job, so I initially went with cached function pointers. Well, it turns out the JIT is also very good at optimizing in this scenario, so I'm changing the methods to do the conversion directly, rather than returning a function pointer for the conversion. The methods were moved to `VariantUtils`, and were renamed from `GetFromVariantCallback/GetToVariantCallback` to `ConvertTo/CreateFrom`. The new implementation looks like it goes through many `if` checks at runtime to find the right branch for the type, but in practice it works pretty much like template specialization. The JIT only generates code for the relevant branch. Together with inlining, the result is very close or the same as doing the conversion manually: ```cs godot_variant variant; int foo = variant.Int; int bar = VariantUtils.ConvertTo(variant); ``` If the type is a generic Godot collection, the conversion still goes through a function pointer call. The new code happens to be much shorter as well, with the file going from 1057 lines to 407. Side note: `Variant.cs` was mistakenly created in the wrong folder, so I moved it to the `Core` folder. --- modules/mono/editor/bindings_generator.cpp | 7 +- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 53 +- .../GodotSharp/Core/Callable.generics.cs | 200 ++-- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 93 +- .../NativeInterop/VariantConversionCallbacks.cs | 1057 -------------------- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 2 +- .../Core/NativeInterop/VariantUtils.generic.cs | 406 ++++++++ .../glue/GodotSharp/GodotSharp/Core/Variant.cs | 899 +++++++++++++++++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 4 +- modules/mono/glue/GodotSharp/GodotSharp/Variant.cs | 891 ----------------- 10 files changed, 1447 insertions(+), 2165 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Variant.cs diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index b90321b586..82b5f478e1 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 f1b46e293b..e3b7ac297d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -495,35 +495,10 @@ namespace Godot.Collections 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 @@ namespace Godot.Collections /// public Array() { - ValidateVariantConversionCallbacks(); - _underlyingArray = new Array(); } @@ -551,8 +524,6 @@ namespace Godot.Collections /// A new Godot Array. public Array(IEnumerable collection) { - ValidateVariantConversionCallbacks(); - if (collection == null) throw new ArgumentNullException(nameof(collection)); @@ -569,8 +540,6 @@ namespace Godot.Collections /// A new Godot Array. public Array(T[] array) : this() { - ValidateVariantConversionCallbacks(); - if (array == null) throw new ArgumentNullException(nameof(array)); @@ -586,8 +555,6 @@ namespace Godot.Collections /// The untyped array to construct from. public Array(Array array) { - ValidateVariantConversionCallbacks(); - _underlyingArray = array; } @@ -665,7 +632,7 @@ namespace Godot.Collections get { _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); - return ConvertToManagedCallback(borrowElem); + return VariantUtils.ConvertTo(borrowElem); } set { @@ -675,7 +642,7 @@ namespace Godot.Collections 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 @@ namespace Godot.Collections /// /// 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 @@ namespace Godot.Collections /// /// 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 @@ namespace Godot.Collections /// /// 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 6c6a104019..ff385da1c9 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 @@ public readonly partial struct Callable ThrowIfArgCountMismatch(args, 1); ((Action)delegateObj)( - VariantConversionCallbacks.GetToManagedCallback()(args[0]) + VariantUtils.ConvertTo(args[0]) ); ret = default; @@ -73,8 +73,8 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable TResult res = ((Func)delegateObj)(); - ret = VariantConversionCallbacks.GetToVariantCallback()(res); + ret = VariantUtils.CreateFrom(res); } return CreateWithUnsafeTrampoline(func, &Trampoline); @@ -281,10 +281,10 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 @@ public readonly partial struct Callable 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 f8793332a0..f14790a218 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -362,45 +362,10 @@ namespace Godot.Collections 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 @@ namespace Godot.Collections /// public Dictionary() { - ValidateVariantConversionCallbacks(); - _underlyingDict = new Dictionary(); } @@ -428,8 +391,6 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary(IDictionary dictionary) { - ValidateVariantConversionCallbacks(); - if (dictionary == null) throw new ArgumentNullException(nameof(dictionary)); @@ -446,8 +407,6 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary(Dictionary dictionary) { - ValidateVariantConversionCallbacks(); - _underlyingDict = dictionary; } @@ -481,18 +440,18 @@ namespace Godot.Collections /// 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 @@ namespace Godot.Collections } 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 @@ namespace Godot.Collections 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 @@ namespace Godot.Collections using (value) { return new KeyValuePair( - ConvertKeyToManagedCallback(key), - ConvertValueToManagedCallback(value)); + VariantUtils.ConvertTo(key), + VariantUtils.ConvertTo(value)); } } @@ -562,15 +521,15 @@ namespace Godot.Collections /// /// 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 @@ namespace Godot.Collections /// /// 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 @@ namespace Godot.Collections /// 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 @@ namespace Godot.Collections /// 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 @@ namespace Godot.Collections /// 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 @@ namespace Godot.Collections 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 @@ namespace Godot.Collections } } - 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 @@ namespace Godot.Collections 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 4b3db0c01a..0000000000 --- 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 57f9ec7d95..ba8e7a6c65 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 @@ using Godot.Collections; 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 0000000000..db84175e17 --- /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/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs new file mode 100644 index 0000000000..237a4da364 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs @@ -0,0 +1,899 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Godot.NativeInterop; + +namespace Godot; + +#nullable enable + +[SuppressMessage("ReSharper", "RedundantNameQualifier")] +public partial struct Variant : IDisposable +{ + internal godot_variant.movable NativeVar; + private object? _obj; + private Disposer? _disposer; + + private sealed class Disposer : IDisposable + { + private godot_variant.movable _native; + + private WeakReference? _weakReferenceToSelf; + + public Disposer(in godot_variant.movable nativeVar) + { + _native = nativeVar; + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + } + + ~Disposer() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + _native.DangerousSelfRef.Dispose(); + + if (_weakReferenceToSelf != null) + { + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + } + } + } + + private Variant(in godot_variant nativeVar) + { + NativeVar = (godot_variant.movable)nativeVar; + _obj = null; + + switch (nativeVar.Type) + { + case Type.Nil: + case Type.Bool: + case Type.Int: + case Type.Float: + case Type.Vector2: + case Type.Vector2i: + case Type.Rect2: + case Type.Rect2i: + case Type.Vector3: + case Type.Vector3i: + case Type.Vector4: + case Type.Vector4i: + case Type.Plane: + case Type.Quaternion: + case Type.Color: + case Type.Rid: + _disposer = null; + break; + default: + { + _disposer = new Disposer(NativeVar); + break; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // Explicit name to make it very clear + public static Variant CreateTakingOwnershipOfDisposableValue(in godot_variant nativeValueToOwn) => + new(nativeValueToOwn); + + // Explicit name to make it very clear + public static Variant CreateCopyingBorrowed(in godot_variant nativeValueToOwn) => + new(NativeFuncs.godotsharp_variant_new_copy(nativeValueToOwn)); + + /// + /// Constructs a new from this instance. + /// The caller is responsible of disposing the new instance to avoid memory leaks. + /// + public godot_variant CopyNativeVariant() => + NativeFuncs.godotsharp_variant_new_copy((godot_variant)NativeVar); + + public void Dispose() + { + _disposer?.Dispose(); + NativeVar = default; + _obj = null; + } + + // TODO: Consider renaming Variant.Type to VariantType and this property to Type. VariantType would also avoid ambiguity with System.Type. + public Type VariantType => NativeVar.DangerousSelfRef.Type; + + public override string ToString() => AsString(); + + public object? Obj + { + get + { + if (_obj == null) + _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar); + + return _obj; + } + } + + [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); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public char AsChar() => + (char)VariantUtils.ConvertToUInt16((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte AsSByte() => + VariantUtils.ConvertToInt8((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short AsInt16() => + VariantUtils.ConvertToInt16((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int AsInt32() => + VariantUtils.ConvertToInt32((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long AsInt64() => + VariantUtils.ConvertToInt64((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte AsByte() => + VariantUtils.ConvertToUInt8((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort AsUInt16() => + VariantUtils.ConvertToUInt16((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint AsUInt32() => + VariantUtils.ConvertToUInt32((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong AsUInt64() => + VariantUtils.ConvertToUInt64((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float AsSingle() => + VariantUtils.ConvertToFloat32((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double AsDouble() => + VariantUtils.ConvertToFloat64((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string AsString() => + VariantUtils.ConvertToStringObject((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 AsVector2() => + VariantUtils.ConvertToVector2((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i AsVector2i() => + VariantUtils.ConvertToVector2i((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rect2 AsRect2() => + VariantUtils.ConvertToRect2((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rect2i AsRect2i() => + VariantUtils.ConvertToRect2i((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Transform2D AsTransform2D() => + VariantUtils.ConvertToTransform2D((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 AsVector3() => + VariantUtils.ConvertToVector3((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i AsVector3i() => + VariantUtils.ConvertToVector3i((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Basis AsBasis() => + VariantUtils.ConvertToBasis((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Quaternion AsQuaternion() => + VariantUtils.ConvertToQuaternion((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Transform3D AsTransform3D() => + VariantUtils.ConvertToTransform3D((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 AsVector4() => + VariantUtils.ConvertToVector4((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4i AsVector4i() => + VariantUtils.ConvertToVector4i((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Projection AsProjection() => + VariantUtils.ConvertToProjection((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AABB AsAABB() => + VariantUtils.ConvertToAABB((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Color AsColor() => + VariantUtils.ConvertToColor((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Plane AsPlane() => + VariantUtils.ConvertToPlane((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Callable AsCallable() => + VariantUtils.ConvertToCallableManaged((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SignalInfo AsSignalInfo() => + VariantUtils.ConvertToSignalInfo((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] AsByteArray() => + VariantUtils.ConvertAsPackedByteArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int[] AsInt32Array() => + VariantUtils.ConvertAsPackedInt32ArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long[] AsInt64Array() => + VariantUtils.ConvertAsPackedInt64ArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float[] AsFloat32Array() => + VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double[] AsFloat64Array() => + VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string[] AsStringArray() => + VariantUtils.ConvertAsPackedStringArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2[] AsVector2Array() => + VariantUtils.ConvertAsPackedVector2ArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3[] AsVector3Array() => + VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Color[] AsColorArray() => + VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] AsGodotObjectArray() + 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); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NodePath[] AsSystemArrayOfNodePath() => + VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RID[] AsSystemArrayOfRID() => + VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Godot.Object AsGodotObject() => + VariantUtils.ConvertToGodotObject((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringName AsStringName() => + VariantUtils.ConvertToStringNameObject((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NodePath AsNodePath() => + VariantUtils.ConvertToNodePathObject((godot_variant)NativeVar); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RID AsRID() => + VariantUtils.ConvertToRID((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); + + // Explicit conversion operators to supported types + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator bool(Variant from) => from.AsBool(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator char(Variant from) => from.AsChar(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator sbyte(Variant from) => from.AsSByte(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator short(Variant from) => from.AsInt16(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator int(Variant from) => from.AsInt32(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator long(Variant from) => from.AsInt64(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator byte(Variant from) => from.AsByte(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator ushort(Variant from) => from.AsUInt16(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator uint(Variant from) => from.AsUInt32(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator ulong(Variant from) => from.AsUInt64(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator float(Variant from) => from.AsSingle(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator double(Variant from) => from.AsDouble(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator string(Variant from) => from.AsString(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector2(Variant from) => from.AsVector2(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector2i(Variant from) => from.AsVector2i(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Rect2(Variant from) => from.AsRect2(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Rect2i(Variant from) => from.AsRect2i(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Transform2D(Variant from) => from.AsTransform2D(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector3(Variant from) => from.AsVector3(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector3i(Variant from) => from.AsVector3i(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Basis(Variant from) => from.AsBasis(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Quaternion(Variant from) => from.AsQuaternion(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Transform3D(Variant from) => from.AsTransform3D(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector4(Variant from) => from.AsVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector4i(Variant from) => from.AsVector4i(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Projection(Variant from) => from.AsProjection(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator AABB(Variant from) => from.AsAABB(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Color(Variant from) => from.AsColor(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Plane(Variant from) => from.AsPlane(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Callable(Variant from) => from.AsCallable(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator SignalInfo(Variant from) => from.AsSignalInfo(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator byte[](Variant from) => from.AsByteArray(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator int[](Variant from) => from.AsInt32Array(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator long[](Variant from) => from.AsInt64Array(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator float[](Variant from) => from.AsFloat32Array(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator double[](Variant from) => from.AsFloat64Array(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator string[](Variant from) => from.AsStringArray(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector2[](Variant from) => from.AsVector2Array(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Vector3[](Variant from) => from.AsVector3Array(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Color[](Variant from) => from.AsColorArray(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Godot.Object(Variant from) => from.AsGodotObject(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator StringName(Variant from) => from.AsStringName(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator NodePath(Variant from) => from.AsNodePath(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator RID(Variant from) => from.AsRID(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Collections.Dictionary(Variant from) => from.AsGodotDictionary(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Collections.Array(Variant from) => from.AsGodotArray(); + + // While we provide implicit conversion operators, normal methods are still needed for + // casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc). + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(bool from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(char from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(sbyte from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(short from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(int from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(long from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(byte from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(ushort from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(uint from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(ulong from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(float from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(double from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(string from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector2 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector2i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Rect2 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Rect2i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Transform2D from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector3 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector3i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Basis from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Quaternion from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Transform3D from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector4 from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Vector4i from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Projection from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(AABB from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Color from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Plane from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Callable from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(SignalInfo from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [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; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Span from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Godot.Object from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(StringName from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(NodePath from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(RID from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Collections.Dictionary from) => from; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Variant CreateFrom(Collections.Array from) => from; + + // Implicit conversion operators + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(bool from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(char from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(sbyte from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(short from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(int from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(long from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(byte from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(ushort from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(uint from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(ulong from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(float from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(double from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(string from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromString(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector2 from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector2i from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2i(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Rect2 from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Rect2i from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2i(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Transform2D from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform2D(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector3 from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector3i from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3i(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Basis from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBasis(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Quaternion from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromQuaternion(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Transform3D from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector4 from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector4i from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Projection from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(AABB from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Color from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromColor(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Plane from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPlane(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Callable from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromCallable(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(SignalInfo from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSignalInfo(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(byte[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(int[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(long[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(float[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(double[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(string[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector2[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Vector3[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Color[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Godot.Object[] from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(StringName[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(NodePath[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(RID[] from) => + (Variant)from.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedByteArray(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt32Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt64Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat32Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat64Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedStringArray(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector2Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Span from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Godot.Object from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromGodotObject(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(StringName from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromStringName(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(NodePath from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromNodePath(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(RID from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRID(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Collections.Dictionary from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Collections.Array from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from)); +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index e3fb254f49..85f7e36639 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 @@ -