summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-11-06 01:27:55 +0100
committerIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-11-25 03:14:10 +0100
commit3f645f980c5d7894f98075c29d1c65319be62be7 (patch)
treeefb9a10e4ad94b2e46c422633c299ae4b9f40e27 /modules
parentcdfef0c852d10cd14a275fffb8b3ad572b96074b (diff)
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<int>(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.
Diffstat (limited to 'modules')
-rw-r--r--modules/mono/editor/bindings_generator.cpp7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs53
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs200
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs93
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs1057
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs406
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs (renamed from modules/mono/glue/GodotSharp/GodotSharp/Variant.cs)8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj4
9 files changed, 556 insertions, 1274 deletions
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<T> FromVariantFunc(in godot_variant variant) =>
VariantUtils.ConvertToArrayObject<T>(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<in T, godot_variant> ConvertToVariantCallback;
- private static readonly unsafe delegate* managed<in godot_variant, T> ConvertToManagedCallback;
-
- // ReSharper restore StaticMemberInGenericType
-
static unsafe Array()
{
- VariantConversionCallbacks.GenericConversionCallbacks[typeof(Array<T>)] =
- (
- (IntPtr)(delegate* managed<in Array<T>, godot_variant>)&ToVariantFunc,
- (IntPtr)(delegate* managed<in godot_variant, Array<T>>)&FromVariantFunc
- );
-
- ConvertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
- ConvertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
- }
-
- 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<Array<T>>.ToVariantCb = &ToVariantFunc;
+ VariantUtils.GenericConversion<Array<T>>.FromVariantCb = &FromVariantFunc;
}
private readonly Array _underlyingArray;
@@ -539,8 +514,6 @@ namespace Godot.Collections
/// </summary>
public Array()
{
- ValidateVariantConversionCallbacks();
-
_underlyingArray = new Array();
}
@@ -551,8 +524,6 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array(IEnumerable<T> collection)
{
- ValidateVariantConversionCallbacks();
-
if (collection == null)
throw new ArgumentNullException(nameof(collection));
@@ -569,8 +540,6 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array(T[] array) : this()
{
- ValidateVariantConversionCallbacks();
-
if (array == null)
throw new ArgumentNullException(nameof(array));
@@ -586,8 +555,6 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
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<T>(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
/// </summary>
/// <param name="item">The item to search for.</param>
/// <returns>The index of the item, or -1 if not found.</returns>
- 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
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="item">The item to insert.</param>
- 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
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>The new size after adding the item.</returns>
- 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<T0>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
+ VariantUtils.ConvertTo<T0>(args[0])
);
ret = default;
@@ -73,8 +73,8 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 2);
((Action<T0, T1>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1])
);
ret = default;
@@ -93,9 +93,9 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 3);
((Action<T0, T1, T2>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2])
);
ret = default;
@@ -114,10 +114,10 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 4);
((Action<T0, T1, T2, T3>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3])
);
ret = default;
@@ -136,11 +136,11 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 5);
((Action<T0, T1, T2, T3, T4>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4])
);
ret = default;
@@ -159,12 +159,12 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 6);
((Action<T0, T1, T2, T3, T4, T5>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5])
);
ret = default;
@@ -183,13 +183,13 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 7);
((Action<T0, T1, T2, T3, T4, T5, T6>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
- VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6])
);
ret = default;
@@ -208,14 +208,14 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 8);
((Action<T0, T1, T2, T3, T4, T5, T6, T7>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
- VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
- VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7])
);
ret = default;
@@ -234,15 +234,15 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 9);
((Action<T0, T1, T2, T3, T4, T5, T6, T7, T8>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
- VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
- VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
- VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7]),
+ VariantUtils.ConvertTo<T8>(args[8])
);
ret = default;
@@ -265,7 +265,7 @@ public readonly partial struct Callable
TResult res = ((Func<TResult>)delegateObj)();
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -281,10 +281,10 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 1);
TResult res = ((Func<T0, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
+ VariantUtils.ConvertTo<T0>(args[0])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -300,11 +300,11 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 2);
TResult res = ((Func<T0, T1, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -320,12 +320,12 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 3);
TResult res = ((Func<T0, T1, T2, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -341,13 +341,13 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 4);
TResult res = ((Func<T0, T1, T2, T3, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -363,14 +363,14 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 5);
TResult res = ((Func<T0, T1, T2, T3, T4, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -386,15 +386,15 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 6);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -410,16 +410,16 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 7);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
- VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -435,17 +435,17 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 8);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
- VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
- VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@@ -461,18 +461,18 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 9);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>)delegateObj)(
- VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
- VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
- VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
- VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
- VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
- VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
- VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
- VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
- VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7]),
+ VariantUtils.ConvertTo<T8>(args[8])
);
- ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(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<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
VariantUtils.ConvertToDictionaryObject<TKey, TValue>(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<in TKey, godot_variant> ConvertKeyToVariantCallback;
- private static readonly unsafe delegate* managed<in godot_variant, TKey> ConvertKeyToManagedCallback;
- private static readonly unsafe delegate* managed<in TValue, godot_variant> ConvertValueToVariantCallback;
- private static readonly unsafe delegate* managed<in godot_variant, TValue> ConvertValueToManagedCallback;
-
- // ReSharper restore StaticMemberInGenericType
-
static unsafe Dictionary()
{
- VariantConversionCallbacks.GenericConversionCallbacks[typeof(Dictionary<TKey, TValue>)] =
- (
- (IntPtr)(delegate* managed<in Dictionary<TKey, TValue>, godot_variant>)&ToVariantFunc,
- (IntPtr)(delegate* managed<in godot_variant, Dictionary<TKey, TValue>>)&FromVariantFunc
- );
-
- ConvertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
- ConvertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
- ConvertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
- ConvertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
- }
-
- 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<Dictionary<TKey, TValue>>.ToVariantCb = &ToVariantFunc;
+ VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.FromVariantCb = &FromVariantFunc;
}
private readonly Dictionary _underlyingDict;
@@ -416,8 +381,6 @@ namespace Godot.Collections
/// </summary>
public Dictionary()
{
- ValidateVariantConversionCallbacks();
-
_underlyingDict = new Dictionary();
}
@@ -428,8 +391,6 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
- ValidateVariantConversionCallbacks();
-
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
@@ -446,8 +407,6 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
- ValidateVariantConversionCallbacks();
-
_underlyingDict = dictionary;
}
@@ -481,18 +440,18 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
/// <value>The value at the given <paramref name="key"/>.</value>
- 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<TValue>(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<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
- private unsafe KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
+ private KeyValuePair<TKey, TValue> 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<TKey, TValue>(
- ConvertKeyToManagedCallback(key),
- ConvertValueToManagedCallback(value));
+ VariantUtils.ConvertTo<TKey>(key),
+ VariantUtils.ConvertTo<TValue>(value));
}
}
@@ -562,15 +521,15 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
- 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
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
- 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 <see cref="Dictionary{TKey, TValue}"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
- 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
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
- public 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<TValue>(retValue) : default;
return found;
}
@@ -635,9 +594,9 @@ namespace Godot.Collections
/// </summary>
public void Clear() => _underlyingDict.Clear();
- unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
+ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> 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<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> 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<T>. 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<Type, (IntPtr ToVariant, IntPtr FromVariant)>
- GenericConversionCallbacks = new();
-
- [SuppressMessage("ReSharper", "RedundantNameQualifier")]
- internal static delegate*<in T, godot_variant> GetToVariantCallback<T>()
- {
- 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*<in T, godot_variant>)(delegate*<in bool, godot_variant>)
- &FromBool;
- }
-
- if (typeOfT == typeof(char))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in char, godot_variant>)
- &FromChar;
- }
-
- if (typeOfT == typeof(sbyte))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in sbyte, godot_variant>)
- &FromInt8;
- }
-
- if (typeOfT == typeof(short))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in short, godot_variant>)
- &FromInt16;
- }
-
- if (typeOfT == typeof(int))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in int, godot_variant>)
- &FromInt32;
- }
-
- if (typeOfT == typeof(long))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in long, godot_variant>)
- &FromInt64;
- }
-
- if (typeOfT == typeof(byte))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in byte, godot_variant>)
- &FromUInt8;
- }
-
- if (typeOfT == typeof(ushort))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in ushort, godot_variant>)
- &FromUInt16;
- }
-
- if (typeOfT == typeof(uint))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in uint, godot_variant>)
- &FromUInt32;
- }
-
- if (typeOfT == typeof(ulong))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in ulong, godot_variant>)
- &FromUInt64;
- }
-
- if (typeOfT == typeof(float))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in float, godot_variant>)
- &FromFloat;
- }
-
- if (typeOfT == typeof(double))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in double, godot_variant>)
- &FromDouble;
- }
-
- if (typeOfT == typeof(Vector2))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector2, godot_variant>)
- &FromVector2;
- }
-
- if (typeOfT == typeof(Vector2i))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector2i, godot_variant>)
- &FromVector2I;
- }
-
- if (typeOfT == typeof(Rect2))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Rect2, godot_variant>)
- &FromRect2;
- }
-
- if (typeOfT == typeof(Rect2i))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Rect2i, godot_variant>)
- &FromRect2I;
- }
-
- if (typeOfT == typeof(Transform2D))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Transform2D, godot_variant>)
- &FromTransform2D;
- }
-
- if (typeOfT == typeof(Vector3))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector3, godot_variant>)
- &FromVector3;
- }
-
- if (typeOfT == typeof(Vector3i))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector3i, godot_variant>)
- &FromVector3I;
- }
-
- if (typeOfT == typeof(Basis))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Basis, godot_variant>)
- &FromBasis;
- }
-
- if (typeOfT == typeof(Quaternion))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Quaternion, godot_variant>)
- &FromQuaternion;
- }
-
- if (typeOfT == typeof(Transform3D))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Transform3D, godot_variant>)
- &FromTransform3D;
- }
-
- if (typeOfT == typeof(Vector4))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector4, godot_variant>)
- &FromVector4;
- }
-
- if (typeOfT == typeof(Vector4i))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector4i, godot_variant>)
- &FromVector4I;
- }
-
- if (typeOfT == typeof(AABB))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in AABB, godot_variant>)
- &FromAabb;
- }
-
- if (typeOfT == typeof(Color))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Color, godot_variant>)
- &FromColor;
- }
-
- if (typeOfT == typeof(Plane))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Plane, godot_variant>)
- &FromPlane;
- }
-
- if (typeOfT == typeof(Callable))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Callable, godot_variant>)
- &FromCallable;
- }
-
- if (typeOfT == typeof(SignalInfo))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in SignalInfo, godot_variant>)
- &FromSignalInfo;
- }
-
- if (typeOfT.IsEnum)
- {
- var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
-
- switch (Type.GetTypeCode(enumUnderlyingType))
- {
- case TypeCode.SByte:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in sbyte, godot_variant>)
- &FromInt8;
- }
- case TypeCode.Int16:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in short, godot_variant>)
- &FromInt16;
- }
- case TypeCode.Int32:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in int, godot_variant>)
- &FromInt32;
- }
- case TypeCode.Int64:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in long, godot_variant>)
- &FromInt64;
- }
- case TypeCode.Byte:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in byte, godot_variant>)
- &FromUInt8;
- }
- case TypeCode.UInt16:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in ushort, godot_variant>)
- &FromUInt16;
- }
- case TypeCode.UInt32:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in uint, godot_variant>)
- &FromUInt32;
- }
- case TypeCode.UInt64:
- {
- return (delegate*<in T, godot_variant>)(delegate*<in ulong, godot_variant>)
- &FromUInt64;
- }
- default:
- return null;
- }
- }
-
- if (typeOfT == typeof(string))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in string, godot_variant>)
- &FromString;
- }
-
- if (typeOfT == typeof(byte[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in byte[], godot_variant>)
- &FromByteArray;
- }
-
- if (typeOfT == typeof(int[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in int[], godot_variant>)
- &FromInt32Array;
- }
-
- if (typeOfT == typeof(long[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in long[], godot_variant>)
- &FromInt64Array;
- }
-
- if (typeOfT == typeof(float[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in float[], godot_variant>)
- &FromFloatArray;
- }
-
- if (typeOfT == typeof(double[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in double[], godot_variant>)
- &FromDoubleArray;
- }
-
- if (typeOfT == typeof(string[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in string[], godot_variant>)
- &FromStringArray;
- }
-
- if (typeOfT == typeof(Vector2[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector2[], godot_variant>)
- &FromVector2Array;
- }
-
- if (typeOfT == typeof(Vector3[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Vector3[], godot_variant>)
- &FromVector3Array;
- }
-
- if (typeOfT == typeof(Color[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Color[], godot_variant>)
- &FromColorArray;
- }
-
- if (typeOfT == typeof(StringName[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in StringName[], godot_variant>)
- &FromStringNameArray;
- }
-
- if (typeOfT == typeof(NodePath[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in NodePath[], godot_variant>)
- &FromNodePathArray;
- }
-
- if (typeOfT == typeof(RID[]))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in RID[], godot_variant>)
- &FromRidArray;
- }
-
- if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Godot.Object, godot_variant>)
- &FromGodotObject;
- }
-
- if (typeOfT == typeof(StringName))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in StringName, godot_variant>)
- &FromStringName;
- }
-
- if (typeOfT == typeof(NodePath))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in NodePath, godot_variant>)
- &FromNodePath;
- }
-
- if (typeOfT == typeof(RID))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in RID, godot_variant>)
- &FromRid;
- }
-
- if (typeOfT == typeof(Godot.Collections.Dictionary))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Godot.Collections.Dictionary, godot_variant>)
- &FromGodotDictionary;
- }
-
- if (typeOfT == typeof(Godot.Collections.Array))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Godot.Collections.Array, godot_variant>)
- &FromGodotArray;
- }
-
- if (typeOfT == typeof(Variant))
- {
- return (delegate*<in T, godot_variant>)(delegate*<in Variant, godot_variant>)
- &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*<in T, godot_variant>)genericConversion.ToVariant;
- }
- }
- }
-
- return null;
- }
-
- [SuppressMessage("ReSharper", "RedundantNameQualifier")]
- internal static delegate*<in godot_variant, T> GetToManagedCallback<T>()
- {
- 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*<in godot_variant, T>)(delegate*<in godot_variant, bool>)
- &ToBool;
- }
-
- if (typeOfT == typeof(char))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, char>)
- &ToChar;
- }
-
- if (typeOfT == typeof(sbyte))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, sbyte>)
- &ToInt8;
- }
-
- if (typeOfT == typeof(short))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, short>)
- &ToInt16;
- }
-
- if (typeOfT == typeof(int))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, int>)
- &ToInt32;
- }
-
- if (typeOfT == typeof(long))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, long>)
- &ToInt64;
- }
-
- if (typeOfT == typeof(byte))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, byte>)
- &ToUInt8;
- }
-
- if (typeOfT == typeof(ushort))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ushort>)
- &ToUInt16;
- }
-
- if (typeOfT == typeof(uint))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, uint>)
- &ToUInt32;
- }
-
- if (typeOfT == typeof(ulong))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ulong>)
- &ToUInt64;
- }
-
- if (typeOfT == typeof(float))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, float>)
- &ToFloat;
- }
-
- if (typeOfT == typeof(double))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, double>)
- &ToDouble;
- }
-
- if (typeOfT == typeof(Vector2))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector2>)
- &ToVector2;
- }
-
- if (typeOfT == typeof(Vector2i))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector2i>)
- &ToVector2I;
- }
-
- if (typeOfT == typeof(Rect2))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Rect2>)
- &ToRect2;
- }
-
- if (typeOfT == typeof(Rect2i))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Rect2i>)
- &ToRect2I;
- }
-
- if (typeOfT == typeof(Transform2D))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Transform2D>)
- &ToTransform2D;
- }
-
- if (typeOfT == typeof(Vector3))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector3>)
- &ToVector3;
- }
-
- if (typeOfT == typeof(Vector3i))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector3i>)
- &ToVector3I;
- }
-
- if (typeOfT == typeof(Basis))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Basis>)
- &ToBasis;
- }
-
- if (typeOfT == typeof(Quaternion))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Quaternion>)
- &ToQuaternion;
- }
-
- if (typeOfT == typeof(Transform3D))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Transform3D>)
- &ToTransform3D;
- }
-
- if (typeOfT == typeof(Vector4))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector4>)
- &ToVector4;
- }
-
- if (typeOfT == typeof(Vector4i))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector4i>)
- &ToVector4I;
- }
-
- if (typeOfT == typeof(AABB))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, AABB>)
- &ToAabb;
- }
-
- if (typeOfT == typeof(Color))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Color>)
- &ToColor;
- }
-
- if (typeOfT == typeof(Plane))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Plane>)
- &ToPlane;
- }
-
- if (typeOfT == typeof(Callable))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Callable>)
- &ToCallable;
- }
-
- if (typeOfT == typeof(SignalInfo))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, SignalInfo>)
- &ToSignalInfo;
- }
-
- if (typeOfT.IsEnum)
- {
- var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
-
- switch (Type.GetTypeCode(enumUnderlyingType))
- {
- case TypeCode.SByte:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, sbyte>)
- &ToInt8;
- }
- case TypeCode.Int16:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, short>)
- &ToInt16;
- }
- case TypeCode.Int32:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, int>)
- &ToInt32;
- }
- case TypeCode.Int64:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, long>)
- &ToInt64;
- }
- case TypeCode.Byte:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, byte>)
- &ToUInt8;
- }
- case TypeCode.UInt16:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ushort>)
- &ToUInt16;
- }
- case TypeCode.UInt32:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, uint>)
- &ToUInt32;
- }
- case TypeCode.UInt64:
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ulong>)
- &ToUInt64;
- }
- default:
- return null;
- }
- }
-
- if (typeOfT == typeof(string))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, string>)
- &ToString;
- }
-
- if (typeOfT == typeof(byte[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, byte[]>)
- &ToByteArray;
- }
-
- if (typeOfT == typeof(int[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, int[]>)
- &ToInt32Array;
- }
-
- if (typeOfT == typeof(long[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, long[]>)
- &ToInt64Array;
- }
-
- if (typeOfT == typeof(float[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, float[]>)
- &ToFloatArray;
- }
-
- if (typeOfT == typeof(double[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, double[]>)
- &ToDoubleArray;
- }
-
- if (typeOfT == typeof(string[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, string[]>)
- &ToStringArray;
- }
-
- if (typeOfT == typeof(Vector2[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector2[]>)
- &ToVector2Array;
- }
-
- if (typeOfT == typeof(Vector3[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector3[]>)
- &ToVector3Array;
- }
-
- if (typeOfT == typeof(Color[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Color[]>)
- &ToColorArray;
- }
-
- if (typeOfT == typeof(StringName[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, StringName[]>)
- &ToStringNameArray;
- }
-
- if (typeOfT == typeof(NodePath[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, NodePath[]>)
- &ToNodePathArray;
- }
-
- if (typeOfT == typeof(RID[]))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, RID[]>)
- &ToRidArray;
- }
-
- if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Godot.Object>)
- &ToGodotObject;
- }
-
- if (typeOfT == typeof(StringName))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, StringName>)
- &ToStringName;
- }
-
- if (typeOfT == typeof(NodePath))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, NodePath>)
- &ToNodePath;
- }
-
- if (typeOfT == typeof(RID))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, RID>)
- &ToRid;
- }
-
- if (typeOfT == typeof(Godot.Collections.Dictionary))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Godot.Collections.Dictionary>)
- &ToGodotDictionary;
- }
-
- if (typeOfT == typeof(Godot.Collections.Array))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Godot.Collections.Array>)
- &ToGodotArray;
- }
-
- if (typeOfT == typeof(Variant))
- {
- return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Variant>)
- &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*<in godot_variant, T>)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<T>() => throw new InvalidOperationException(
+ $"The type is not supported for conversion to/from Variant: '{typeof(T).FullName}'");
+
+ internal static class GenericConversion<T>
+ {
+ public static unsafe godot_variant ToVariant(in T from) =>
+ ToVariantCb != null ? ToVariantCb(from) : throw UnsupportedType<T>();
+
+ public static unsafe T FromVariant(in godot_variant variant) =>
+ FromVariantCb != null ? FromVariantCb(variant) : throw UnsupportedType<T>();
+
+ // ReSharper disable once StaticMemberInGenericType
+ internal static unsafe delegate*<in T, godot_variant> ToVariantCb;
+
+ // ReSharper disable once StaticMemberInGenericType
+ internal static unsafe delegate*<in godot_variant, T> 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<TTo>(in T f) => Unsafe.As<T, TTo>(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<bool>(from));
+
+ if (typeof(T) == typeof(char))
+ return CreateFromInt(UnsafeAs<char>(from));
+
+ if (typeof(T) == typeof(sbyte))
+ return CreateFromInt(UnsafeAs<sbyte>(from));
+
+ if (typeof(T) == typeof(short))
+ return CreateFromInt(UnsafeAs<short>(from));
+
+ if (typeof(T) == typeof(int))
+ return CreateFromInt(UnsafeAs<int>(from));
+
+ if (typeof(T) == typeof(long))
+ return CreateFromInt(UnsafeAs<long>(from));
+
+ if (typeof(T) == typeof(byte))
+ return CreateFromInt(UnsafeAs<byte>(from));
+
+ if (typeof(T) == typeof(ushort))
+ return CreateFromInt(UnsafeAs<ushort>(from));
+
+ if (typeof(T) == typeof(uint))
+ return CreateFromInt(UnsafeAs<uint>(from));
+
+ if (typeof(T) == typeof(ulong))
+ return CreateFromInt(UnsafeAs<ulong>(from));
+
+ if (typeof(T) == typeof(float))
+ return CreateFromFloat(UnsafeAs<float>(from));
+
+ if (typeof(T) == typeof(double))
+ return CreateFromFloat(UnsafeAs<double>(from));
+
+ if (typeof(T) == typeof(Vector2))
+ return CreateFromVector2(UnsafeAs<Vector2>(from));
+
+ if (typeof(T) == typeof(Vector2i))
+ return CreateFromVector2i(UnsafeAs<Vector2i>(from));
+
+ if (typeof(T) == typeof(Rect2))
+ return CreateFromRect2(UnsafeAs<Rect2>(from));
+
+ if (typeof(T) == typeof(Rect2i))
+ return CreateFromRect2i(UnsafeAs<Rect2i>(from));
+
+ if (typeof(T) == typeof(Transform2D))
+ return CreateFromTransform2D(UnsafeAs<Transform2D>(from));
+
+ if (typeof(T) == typeof(Vector3))
+ return CreateFromVector3(UnsafeAs<Vector3>(from));
+
+ if (typeof(T) == typeof(Vector3i))
+ return CreateFromVector3i(UnsafeAs<Vector3i>(from));
+
+ if (typeof(T) == typeof(Basis))
+ return CreateFromBasis(UnsafeAs<Basis>(from));
+
+ if (typeof(T) == typeof(Quaternion))
+ return CreateFromQuaternion(UnsafeAs<Quaternion>(from));
+
+ if (typeof(T) == typeof(Transform3D))
+ return CreateFromTransform3D(UnsafeAs<Transform3D>(from));
+
+ if (typeof(T) == typeof(Vector4))
+ return CreateFromVector4(UnsafeAs<Vector4>(from));
+
+ if (typeof(T) == typeof(Vector4i))
+ return CreateFromVector4i(UnsafeAs<Vector4i>(from));
+
+ if (typeof(T) == typeof(AABB))
+ return CreateFromAABB(UnsafeAs<AABB>(from));
+
+ if (typeof(T) == typeof(Color))
+ return CreateFromColor(UnsafeAs<Color>(from));
+
+ if (typeof(T) == typeof(Plane))
+ return CreateFromPlane(UnsafeAs<Plane>(from));
+
+ if (typeof(T) == typeof(Callable))
+ return CreateFromCallable(UnsafeAs<Callable>(from));
+
+ if (typeof(T) == typeof(SignalInfo))
+ return CreateFromSignalInfo(UnsafeAs<SignalInfo>(from));
+
+ if (typeof(T) == typeof(string))
+ return CreateFromString(UnsafeAs<string>(from));
+
+ if (typeof(T) == typeof(byte[]))
+ return CreateFromPackedByteArray(UnsafeAs<byte[]>(from));
+
+ if (typeof(T) == typeof(int[]))
+ return CreateFromPackedInt32Array(UnsafeAs<int[]>(from));
+
+ if (typeof(T) == typeof(long[]))
+ return CreateFromPackedInt64Array(UnsafeAs<long[]>(from));
+
+ if (typeof(T) == typeof(float[]))
+ return CreateFromPackedFloat32Array(UnsafeAs<float[]>(from));
+
+ if (typeof(T) == typeof(double[]))
+ return CreateFromPackedFloat64Array(UnsafeAs<double[]>(from));
+
+ if (typeof(T) == typeof(string[]))
+ return CreateFromPackedStringArray(UnsafeAs<string[]>(from));
+
+ if (typeof(T) == typeof(Vector2[]))
+ return CreateFromPackedVector2Array(UnsafeAs<Vector2[]>(from));
+
+ if (typeof(T) == typeof(Vector3[]))
+ return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
+
+ if (typeof(T) == typeof(Color[]))
+ return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
+
+ if (typeof(T) == typeof(StringName[]))
+ return CreateFromSystemArrayOfStringName(UnsafeAs<StringName[]>(from));
+
+ if (typeof(T) == typeof(NodePath[]))
+ return CreateFromSystemArrayOfNodePath(UnsafeAs<NodePath[]>(from));
+
+ if (typeof(T) == typeof(RID[]))
+ return CreateFromSystemArrayOfRID(UnsafeAs<RID[]>(from));
+
+ if (typeof(T) == typeof(StringName))
+ return CreateFromStringName(UnsafeAs<StringName>(from));
+
+ if (typeof(T) == typeof(NodePath))
+ return CreateFromNodePath(UnsafeAs<NodePath>(from));
+
+ if (typeof(T) == typeof(RID))
+ return CreateFromRID(UnsafeAs<RID>(from));
+
+ if (typeof(T) == typeof(Godot.Collections.Dictionary))
+ return CreateFromDictionary(UnsafeAs<Godot.Collections.Dictionary>(from));
+
+ if (typeof(T) == typeof(Godot.Collections.Array))
+ return CreateFromArray(UnsafeAs<Godot.Collections.Array>(from));
+
+ if (typeof(T) == typeof(Variant))
+ return NativeFuncs.godotsharp_variant_new_copy((godot_variant)UnsafeAs<Variant>(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<Godot.Object>(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<T>()` works and is optimized away.
+ // We don't need to know whether it's signed or unsigned.
+
+ if (Unsafe.SizeOf<T>() == 1)
+ return CreateFromInt(UnsafeAs<sbyte>(from));
+
+ if (Unsafe.SizeOf<T>() == 2)
+ return CreateFromInt(UnsafeAs<short>(from));
+
+ if (Unsafe.SizeOf<T>() == 4)
+ return CreateFromInt(UnsafeAs<int>(from));
+
+ if (Unsafe.SizeOf<T>() == 8)
+ return CreateFromInt(UnsafeAs<long>(from));
+
+ throw UnsupportedType<T>();
+ }
+
+ return GenericConversion<T>.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>(TFrom f) => Unsafe.As<TFrom, T>(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<T>()` works and is optimized away.
+ // We don't need to know whether it's signed or unsigned.
+
+ if (Unsafe.SizeOf<T>() == 1)
+ return UnsafeAsT(ConvertToInt8(variant));
+
+ if (Unsafe.SizeOf<T>() == 2)
+ return UnsafeAsT(ConvertToInt16(variant));
+
+ if (Unsafe.SizeOf<T>() == 4)
+ return UnsafeAsT(ConvertToInt32(variant));
+
+ if (Unsafe.SizeOf<T>() == 8)
+ return UnsafeAsT(ConvertToInt64(variant));
+
+ throw UnsupportedType<T>();
+ }
+
+ return GenericConversion<T>.FromVariant(variant);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
index d354509dbf..237a4da364 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
@@ -121,6 +121,14 @@ public partial struct Variant : IDisposable
}
[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<T>(NativeVar.DangerousSelfRef);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AsBool() =>
VariantUtils.ConvertToBool((godot_variant)NativeVar);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index e3fb254f49..85f7e36639 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -101,9 +101,9 @@
<Compile Include="Core\NativeInterop\InteropUtils.cs" />
<Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
<Compile Include="Core\NativeInterop\NativeVariantPtrArgs.cs" />
- <Compile Include="Core\NativeInterop\VariantConversionCallbacks.cs" />
<Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" />
<Compile Include="Core\NativeInterop\VariantUtils.cs" />
+ <Compile Include="Core\NativeInterop\VariantUtils.generic.cs" />
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
<Compile Include="Core\Object.exceptions.cs" />
@@ -123,6 +123,7 @@
<Compile Include="Core\StringName.cs" />
<Compile Include="Core\Transform2D.cs" />
<Compile Include="Core\Transform3D.cs" />
+ <Compile Include="Core\Variant.cs" />
<Compile Include="Core\Vector2.cs" />
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
@@ -131,7 +132,6 @@
<Compile Include="Core\Vector4i.cs" />
<Compile Include="GlobalUsings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Variant.cs" />
</ItemGroup>
<!--
We import a props file with auto-generated includes. This works well with Rider.