Explorar o código

C#: Add dedicated Variant struct, replacing System.Object

Ignacio Roldán Etcheverry %!s(int64=3) %!d(string=hai) anos
pai
achega
344f5028d4
Modificáronse 25 ficheiros con 776 adicións e 124 borrados
  1. 1 1
      modules/mono/csharp_script.cpp
  2. 1 2
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs
  3. 1 2
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs
  4. 1 1
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
  5. 1 2
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
  6. 9 15
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
  7. 1 1
      modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
  8. 3 3
      modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
  9. 1 1
      modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
  10. 1 1
      modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
  11. 18 23
      modules/mono/editor/bindings_generator.cpp
  12. 1 1
      modules/mono/editor/bindings_generator.h
  13. 1 1
      modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
  14. 1 1
      modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
  15. 6 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
  16. 3 3
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
  17. 7 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
  18. 6 49
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
  19. 3 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
  20. 38 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
  21. 0 14
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
  22. 0 2
      modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
  23. 1 0
      modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
  24. 665 0
      modules/mono/glue/GodotSharp/GodotSharp/Variant.cs
  25. 6 1
      modules/mono/glue/runtime_interop.cpp

+ 1 - 1
modules/mono/csharp_script.cpp

@@ -104,7 +104,7 @@ Error CSharpLanguage::execute_file(const String &p_path) {
 	return OK;
 }
 
-extern void *godotsharp_pinvoke_funcs[185];
+extern void *godotsharp_pinvoke_funcs[186];
 [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs;
 #ifdef TOOLS_ENABLED
 extern void *godotsharp_editor_pinvoke_funcs[30];

+ 1 - 2
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs

@@ -80,7 +80,6 @@ namespace Godot.SourceGenerators.Sample
         [Export] private Vector3[] field_Vector3Array = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
         [Export] private Color[] field_ColorArray = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
         [Export] private Godot.Object[] field_GodotObjectOrDerivedArray = { null };
-        [Export] private object[] field_SystemObjectArray = { 0, 1f, 2d, "foo", Vector3i.Up };
 
         // Generics
         [Export] private Godot.Collections.Dictionary<string, string> field_GodotGenericDictionary =
@@ -105,7 +104,7 @@ namespace Godot.SourceGenerators.Sample
             new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
 
         // Variant
-        [Export] private object field_SystemObject = "foo";
+        [Export] private Variant field_Variant = "foo";
 
         // Classes
         [Export] private Godot.Object field_GodotObjectOrDerived;

+ 1 - 2
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs

@@ -80,7 +80,6 @@ namespace Godot.SourceGenerators.Sample
         [Export] private Vector3[] property_Vector3Array { get; set; } = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
         [Export] private Color[] property_ColorArray { get; set; } = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
         [Export] private Godot.Object[] property_GodotObjectOrDerivedArray { get; set; } = { null };
-        [Export] private object[] property_SystemObjectArray { get; set; } = { 0, 1f, 2d, "foo", Vector3i.Up };
 
         // Generics
         [Export] private Godot.Collections.Dictionary<string, string> property_GodotGenericDictionary { get; set; } =
@@ -105,7 +104,7 @@ namespace Godot.SourceGenerators.Sample
             new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
 
         // Variant
-        [Export] private object property_SystemObject { get; set; } = "foo";
+        [Export] private Variant property_Variant { get; set; } = "foo";
 
         // Classes
         [Export] private Godot.Object property_GodotObjectOrDerived { get; set; }

+ 1 - 1
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs

@@ -30,7 +30,7 @@ namespace Godot.SourceGenerators.Sample
     {
         public partial class NesterClass : RefCounted
         {
-            public override object _Get(StringName property) => null;
+            public override Variant _Get(StringName property) => default;
         }
     }
 }

+ 1 - 2
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs

@@ -53,7 +53,6 @@ namespace Godot.SourceGenerators
         Vector3Array,
         ColorArray,
         GodotObjectOrDerivedArray,
-        SystemObjectArray,
         SystemArrayOfSupportedType,
 
         // Generics
@@ -66,7 +65,7 @@ namespace Godot.SourceGenerators
         GenericIEnumerable,
 
         // Variant
-        SystemObject,
+        Variant,
 
         // Classes
         GodotObjectOrDerived,

+ 9 - 15
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs

@@ -86,7 +86,6 @@ namespace Godot.SourceGenerators
                 MarshalType.Vector3Array => VariantType.PackedVector3Array,
                 MarshalType.ColorArray => VariantType.PackedColorArray,
                 MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
-                MarshalType.SystemObjectArray => VariantType.Array,
                 MarshalType.SystemArrayOfSupportedType => VariantType.Array,
                 MarshalType.GodotGenericDictionary => VariantType.Dictionary,
                 MarshalType.GodotGenericArray => VariantType.Array,
@@ -95,7 +94,7 @@ namespace Godot.SourceGenerators
                 MarshalType.GenericIDictionary => VariantType.Dictionary,
                 MarshalType.GenericICollection => VariantType.Array,
                 MarshalType.GenericIEnumerable => VariantType.Array,
-                MarshalType.SystemObject => VariantType.Nil,
+                MarshalType.Variant => VariantType.Nil,
                 MarshalType.GodotObjectOrDerived => VariantType.Object,
                 MarshalType.StringName => VariantType.StringName,
                 MarshalType.NodePath => VariantType.NodePath,
@@ -140,8 +139,6 @@ namespace Godot.SourceGenerators
                     return MarshalType.Double;
                 case SpecialType.System_String:
                     return MarshalType.String;
-                case SpecialType.System_Object:
-                    return MarshalType.SystemObject;
                 default:
                 {
                     var typeKind = type.TypeKind;
@@ -175,6 +172,7 @@ namespace Godot.SourceGenerators
                                 { Name: "RID" } => MarshalType.RID,
                                 { Name: "Callable" } => MarshalType.Callable,
                                 { Name: "SignalInfo" } => MarshalType.SignalInfo,
+                                { Name: "Variant" } => MarshalType.Variant,
                                 _ => null
                             };
                         }
@@ -198,8 +196,6 @@ namespace Godot.SourceGenerators
                                 return MarshalType.Float64Array;
                             case SpecialType.System_String:
                                 return MarshalType.StringArray;
-                            case SpecialType.System_Object:
-                                return MarshalType.SystemObjectArray;
                         }
 
                         if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
@@ -323,6 +319,9 @@ namespace Godot.SourceGenerators
             return null;
         }
 
+        private static StringBuilder Append(this StringBuilder source, string a, string b)
+            => source.Append(a).Append(b);
+
         private static StringBuilder Append(this StringBuilder source, string a, string b, string c)
             => source.Append(a).Append(b).Append(c);
 
@@ -346,7 +345,6 @@ namespace Godot.SourceGenerators
             string c, string d, string e, string f, string g, string h)
             => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g).Append(h);
 
-        private const string Marshaling = "global::Godot.NativeInterop.Marshaling";
         private const string VariantUtils = "global::Godot.NativeInterop.VariantUtils";
 
         public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
@@ -440,8 +438,6 @@ namespace Godot.SourceGenerators
                 MarshalType.GodotObjectOrDerivedArray =>
                     source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
                         ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedName(), ">(", inputExpr, ")"),
-                MarshalType.SystemObjectArray =>
-                    source.Append(VariantUtils, ".ConvertToSystemArrayOfVariant(", inputExpr, ")"),
                 MarshalType.SystemArrayOfSupportedType =>
                     source.Append(VariantUtils, ".ConvertToSystemArrayOfSupportedType<",
                         ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedName(), ">(", inputExpr, ")"),
@@ -466,8 +462,8 @@ namespace Godot.SourceGenerators
                 MarshalType.GenericICollection or MarshalType.GenericIEnumerable =>
                     source.Append(VariantUtils, ".ConvertToGenericArrayObject<",
                         ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">(", inputExpr, ")"),
-                MarshalType.SystemObject =>
-                    source.Append(Marshaling, ".ConvertVariantToManagedObject(", inputExpr, ")"),
+                MarshalType.Variant =>
+                    source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"),
                 MarshalType.GodotObjectOrDerived =>
                     source.Append("(", typeSymbol.FullQualifiedName(),
                         ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"),
@@ -579,8 +575,6 @@ namespace Godot.SourceGenerators
                     source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"),
                 MarshalType.GodotObjectOrDerivedArray =>
                     source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
-                MarshalType.SystemObjectArray =>
-                    source.Append(VariantUtils, ".CreateFromSystemArrayOfVariant(", inputExpr, ")"),
                 MarshalType.SystemArrayOfSupportedType =>
                     source.Append(VariantUtils, ".CreateFromSystemArrayOfSupportedType(", inputExpr, ")"),
                 MarshalType.GodotGenericDictionary =>
@@ -597,8 +591,8 @@ namespace Godot.SourceGenerators
                     source.Append(VariantUtils, ".CreateFromSystemGenericICollection(", inputExpr, ")"),
                 MarshalType.GenericIEnumerable =>
                     source.Append(VariantUtils, ".CreateFromSystemGenericIEnumerable(", inputExpr, ")"),
-                MarshalType.SystemObject =>
-                    source.Append(Marshaling, ".ConvertManagedObjectToVariant(", inputExpr, ")"),
+                MarshalType.Variant =>
+                    source.Append(inputExpr, ".CopyNativeVariant()"),
                 MarshalType.GodotObjectOrDerived =>
                     source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"),
                 MarshalType.StringName =>

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs

@@ -124,7 +124,7 @@ namespace GodotTools.Build
                 throw new IndexOutOfRangeException("Item list index out of range");
 
             // Get correct issue idx from issue list
-            int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx);
+            int issueIndex = (int)_issuesList.GetItemMetadata(idx);
 
             if (issueIndex < 0 || issueIndex >= _issues.Count)
                 throw new IndexOutOfRangeException("Issue index out of range");

+ 3 - 3
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -175,7 +175,7 @@ namespace GodotTools
         [UsedImplicitly]
         public Error OpenInExternalEditor(Script script, int line, int col)
         {
-            var editorId = (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor");
+            var editorId = (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor");
 
             switch (editorId)
             {
@@ -327,7 +327,7 @@ namespace GodotTools
         [UsedImplicitly]
         public bool OverridesExternalEditor()
         {
-            return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
+            return (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
         }
 
         public override bool _Build()
@@ -521,7 +521,7 @@ namespace GodotTools
                     // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
                     // will be freed after EditorSettings already was, and its device polling thread
                     // will try to access the EditorSettings singleton, resulting in null dereferencing.
-                    (_exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
+                    (_exportPluginWeak.GetRef().AsGodotObject() as ExportPlugin)?.Dispose();
 
                     _exportPluginWeak.Dispose();
                 }

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs

@@ -76,7 +76,7 @@ namespace GodotTools.Ides
 
         public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000)
         {
-            var editorId = (ExternalEditorId)GodotSharpEditor.Instance.GetEditorInterface()
+            var editorId = (ExternalEditorId)(int)GodotSharpEditor.Instance.GetEditorInterface()
                 .GetEditorSettings().GetSetting("mono/editor/external_editor");
             string editorIdentity = GetExternalEditorIdentity(editorId);
 

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs

@@ -22,7 +22,7 @@ namespace GodotTools.Ides.Rider
         public static void Initialize()
         {
             var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
-            var editor = (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor");
+            var editor = (ExternalEditorId)(int)editorSettings.GetSetting("mono/editor/external_editor");
             if (editor == ExternalEditorId.Rider)
             {
                 if (!editorSettings.HasSetting(EditorPathSettingName))

+ 18 - 23
modules/mono/editor/bindings_generator.cpp

@@ -105,8 +105,6 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
 #define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
 
 #define C_NS_MONOMARSHAL "Marshaling"
-#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL ".ConvertManagedObjectToVariant"
-#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL ".ConvertVariantToManagedObject"
 #define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".ConvertStringToNative"
 #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".ConvertStringToManaged"
 #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".ConvertSystemArrayToNative" #m_type
@@ -844,15 +842,12 @@ Error BindingsGenerator::_populate_method_icalls_table(const TypeInterface &p_it
 		}
 
 		// Get arguments information
-		int i = 0;
 		for (const ArgumentInterface &iarg : imethod.arguments) {
 			const TypeInterface *arg_type = _get_type_or_null(iarg.type);
 			ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
 
 			im_unique_sig += ",";
 			im_unique_sig += get_arg_unique_sig(*arg_type);
-
-			i++;
 		}
 
 		// godot_icall_{argc}_{icallcount}
@@ -1011,9 +1006,9 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
 
 			CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
 
-			_log("Declaring global enum '%s' inside static class '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
+			_log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
 
-			p_output.append("\npublic static partial class ");
+			p_output.append("\npublic partial struct ");
 			p_output.append(enum_class_name);
 			p_output.append("\n" OPEN_BLOCK);
 		}
@@ -2462,7 +2457,7 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
 
 			r_output << INDENT2 "using var variantSpanDisposer = new VariantSpanDisposer(varargs_span);\n";
 
-			r_output << INDENT2 "fixed (godot_variant* varargs = &MemoryMarshal.GetReference(varargs_span).DangerousSelfRef)\n"
+			r_output << INDENT2 "fixed (godot_variant.movable* varargs = &MemoryMarshal.GetReference(varargs_span))\n"
 					 << INDENT2 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = "
 								"&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n"
 					 << OPEN_BLOCK_L2;
@@ -2470,7 +2465,7 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
 			r_output << c_in_statements.as_string();
 
 			r_output << INDENT3 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
-					 << INDENT4 "varargs[i] = " C_METHOD_MANAGED_TO_VARIANT "(" << vararg_arg << "[i]);\n"
+					 << INDENT4 "varargs[i] = " << vararg_arg << "[i].NativeVar;\n"
 					 << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n"
 					 << CLOSE_BLOCK_L3;
 
@@ -3181,7 +3176,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 	switch (p_val.get_type()) {
 		case Variant::NIL:
 			// Either Object type or Variant
-			r_iarg.default_argument = "null";
+			r_iarg.default_argument = "default";
 			break;
 		// Atomic types
 		case Variant::BOOL:
@@ -3352,8 +3347,8 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 			break;
 	}
 
-	if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") {
-		r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+	if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "default") {
+		r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
 	}
 
 	return true;
@@ -3558,17 +3553,18 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype = TypeInterface();
 	itype.name = "Variant";
 	itype.cname = itype.name;
-	itype.proxy_name = "object";
+	itype.proxy_name = "Variant";
 	itype.cs_type = itype.proxy_name;
-	itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n";
-	itype.c_out = "%5return " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n";
+	itype.c_in = "%5%0 %1_in = (%0)%1.NativeVar;\n";
+	itype.c_out = "%5return Variant.CreateTakingOwnershipOfDisposableValue(%1);\n";
 	itype.c_arg_in = "&%s_in";
 	itype.c_type = "godot_variant";
 	itype.c_type_in = itype.cs_type;
 	itype.c_type_out = itype.cs_type;
-	itype.c_type_is_disposable_struct = true;
-	itype.cs_variant_to_managed = C_METHOD_MANAGED_FROM_VARIANT "(%0)";
-	itype.cs_managed_to_variant = C_METHOD_MANAGED_TO_VARIANT "(%0)";
+	itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+	itype.c_ret_needs_default_initialization = true;
+	itype.cs_variant_to_managed = "Variant.CreateCopyingBorrowed(%0)";
+	itype.cs_managed_to_variant = "%0.CopyNativeVariant()";
 	builtin_types.insert(itype.cname, itype);
 
 	// Callable
@@ -3607,14 +3603,13 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype = TypeInterface();
 	itype.name = "VarArg";
 	itype.cname = itype.name;
-	itype.proxy_name = "object[]";
-	itype.cs_type = "params object[]";
-	itype.cs_in_expr = "%0 ?? Array.Empty<object>()";
+	itype.proxy_name = "Variant[]";
+	itype.cs_type = "params Variant[]";
+	itype.cs_in_expr = "%0 ?? Array.Empty<Variant>()";
 	// c_type, c_in and c_arg_in are hard-coded in the generator.
 	// c_out and c_type_out are not applicable to VarArg.
 	itype.c_arg_in = "&%s_in";
-	itype.c_type_in = "object[]";
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToSystemArray(%0)";
+	itype.c_type_in = "Variant[]";
 	builtin_types.insert(itype.cname, itype);
 
 #define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t)                \

+ 1 - 1
modules/mono/editor/bindings_generator.h

@@ -240,7 +240,7 @@ class BindingsGenerator {
 		 * Determines whether the native return value of this type must be zero initialized
 		 * before its address is passed to ptrcall. This is required for types whose destructor
 		 * is called before being assigned the return value by `PtrToArg::encode`, e.g.:
-		 * Array, Dictionary, String, StringName.
+		 * Array, Dictionary, String, StringName, Variant.
 		 * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`.
 		 */
 		bool c_ret_needs_default_initialization = false;

+ 1 - 1
modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs

@@ -9,7 +9,7 @@ public partial class _CLASS_ : _BASE_
     public const float JumpVelocity = -400.0f;
 
     // Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
-    public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
+    public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
 
     public override void _PhysicsProcess(float delta)
     {

+ 1 - 1
modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs

@@ -9,7 +9,7 @@ public partial class _CLASS_ : _BASE_
     public const float JumpVelocity = 4.5f;
 
     // Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
-    public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
+    public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();
 
     public override void _PhysicsProcess(float delta)
     {

+ 6 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs

@@ -714,5 +714,11 @@ namespace Godot.Collections
         /// </summary>
         /// <returns>A string representation of this array.</returns>
         public override string ToString() => _underlyingArray.ToString();
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static implicit operator Variant(Array<T> from) => Variant.From(from);
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static explicit operator Array<T>(Variant from) => from.AsGodotGenericArray<T>();
     }
 }

+ 3 - 3
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs

@@ -63,7 +63,7 @@ namespace Godot.Bridge
                 var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
                     NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
 
-                object valueManaged = Marshaling.ConvertVariantToManagedObject(CustomUnsafe.AsRef(value));
+                Variant valueManaged = Variant.CreateCopyingBorrowed(*value);
 
                 return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
             }
@@ -94,9 +94,9 @@ namespace Godot.Bridge
                 var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
                     NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
 
-                object ret = godotObject._Get(nameManaged);
+                Variant ret = godotObject._Get(nameManaged);
 
-                if (ret == null)
+                if (ret.VariantType == Variant.Type.Nil)
                 {
                     *outRet = default;
                     return godot_bool.False;

+ 7 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs

@@ -5,6 +5,7 @@ using Godot.NativeInterop;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using System.Linq;
+using System.Runtime.CompilerServices;
 
 namespace Godot.Collections
 {
@@ -805,5 +806,11 @@ namespace Godot.Collections
         /// </summary>
         /// <returns>A string representation of this dictionary.</returns>
         public override string ToString() => _underlyingDict.ToString();
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.From(from);
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotGenericDictionary<TKey, TValue>();
     }
 }

+ 6 - 49
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs

@@ -148,9 +148,6 @@ namespace Godot.NativeInterop
 
                         if (typeof(Godot.Object[]).IsAssignableFrom(type))
                             return Variant.Type.Array;
-
-                        if (type == typeof(object[]))
-                            return Variant.Type.Array;
                     }
                     else if (type.IsGenericType)
                     {
@@ -178,7 +175,7 @@ namespace Godot.NativeInterop
                         if (typeof(Godot.Object).IsAssignableFrom(type))
                             return Variant.Type.Object;
                     }
-                    else if (type == typeof(object))
+                    else if (type == typeof(Variant))
                     {
                         r_nil_is_variant = true;
                         return Variant.Type.Nil;
@@ -315,16 +312,6 @@ namespace Godot.NativeInterop
                     return VariantUtils.CreateFromSystemArrayOfSupportedType(ridArray);
                 case Godot.Object[] godotObjectArray:
                     return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
-                case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[]
-                {
-                    // The pattern match for `object[]` catches arrays on any reference type,
-                    // so we need to check the actual type to make sure it's truly `object[]`.
-                    if (objectArray.GetType() == typeof(object[]))
-                        return VariantUtils.CreateFromSystemArrayOfVariant(objectArray);
-
-                    GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant.");
-                    return new godot_variant();
-                }
                 case Godot.Object godotObject:
                     return VariantUtils.CreateFromGodotObject(godotObject);
                 case StringName stringName:
@@ -337,6 +324,8 @@ namespace Godot.NativeInterop
                     return VariantUtils.CreateFromDictionary(godotDictionary);
                 case Collections.Array godotArray:
                     return VariantUtils.CreateFromArray(godotArray);
+                case Variant variant:
+                    return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
                 case Collections.IGenericGodotDictionary genericGodotDictionary:
                 {
                     var godotDict = genericGodotDictionary.UnderlyingDictionary;
@@ -509,8 +498,9 @@ namespace Godot.NativeInterop
                         return ConvertVariantToSystemArrayOfType(p_var, type);
                     else if (type.IsGenericType)
                         return ConvertVariantToManagedObjectOfGenericType(p_var, type);
-                    else if (type == typeof(object))
-                        return ConvertVariantToManagedObject(p_var);
+                    else if (type == typeof(Variant))
+                        return Variant.CreateCopyingBorrowed(p_var);
+
                     if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res))
                         return res;
 
@@ -564,9 +554,6 @@ namespace Godot.NativeInterop
             if (typeof(Godot.Object[]).IsAssignableFrom(type))
                 return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
 
-            if (type == typeof(object[]))
-                return VariantUtils.ConvertToSystemArrayOfVariant(p_var);
-
             GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " +
                          type.GetElementType()!.FullName + ".");
             return null;
@@ -962,19 +949,6 @@ namespace Godot.NativeInterop
 
         // Array
 
-        public static object[] ConvertNativeGodotArrayToSystemArray(in godot_array p_array)
-        {
-            var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
-                NativeFuncs.godotsharp_array_new_copy(p_array));
-
-            int length = array.Count;
-            var ret = new object[length];
-
-            array.CopyTo(ret, 0); // ConvertVariantToManagedObject handled by Collections.Array
-
-            return ret;
-        }
-
         internal static T[] ConvertNativeGodotArrayToSystemArrayOfType<T>(in godot_array p_array)
         {
             var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
@@ -1019,23 +993,6 @@ namespace Godot.NativeInterop
             return ret;
         }
 
-        public static godot_array ConvertSystemArrayToNativeGodotArray(object[] p_array)
-        {
-            int length = p_array.Length;
-
-            if (length == 0)
-                return NativeFuncs.godotsharp_array_new();
-
-            using var array = new Collections.Array();
-            array.Resize(length);
-
-            for (int i = 0; i < length; i++)
-                array[i] = p_array[i];
-
-            var src = (godot_array)array.NativeValue;
-            return NativeFuncs.godotsharp_array_new_copy(src);
-        }
-
         public static godot_array ConvertSystemArrayToNativeGodotArray<T>(T[] p_array)
         {
             int length = p_array.Length;

+ 3 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -199,6 +199,9 @@ namespace Godot.NativeInterop
         public static extern void
             godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s);
 
+        [DllImport(GodotDllName)]
+        public static extern void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src);
+
         [DllImport(GodotDllName)]
         public static extern void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np);
 

+ 38 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs

@@ -4,6 +4,44 @@ namespace Godot.NativeInterop
 {
     public static partial class NativeFuncs
     {
+        public static godot_variant godotsharp_variant_new_copy(in godot_variant src)
+        {
+            switch (src.Type)
+            {
+                case Variant.Type.Nil:
+                    return default;
+                case Variant.Type.Bool:
+                    return new godot_variant() { Bool = src.Bool };
+                case Variant.Type.Int:
+                    return new godot_variant() { Int = src.Int };
+                case Variant.Type.Float:
+                    return new godot_variant() { Float = src.Float };
+                case Variant.Type.Vector2:
+                    return new godot_variant() { Vector2 = src.Vector2 };
+                case Variant.Type.Vector2i:
+                    return new godot_variant() { Vector2i = src.Vector2i };
+                case Variant.Type.Rect2:
+                    return new godot_variant() { Rect2 = src.Rect2 };
+                case Variant.Type.Rect2i:
+                    return new godot_variant() { Rect2i = src.Rect2i };
+                case Variant.Type.Vector3:
+                    return new godot_variant() { Vector3 = src.Vector3 };
+                case Variant.Type.Vector3i:
+                    return new godot_variant() { Vector3i = src.Vector3i };
+                case Variant.Type.Plane:
+                    return new godot_variant() { Plane = src.Plane };
+                case Variant.Type.Quaternion:
+                    return new godot_variant() { Quaternion = src.Quaternion };
+                case Variant.Type.Color:
+                    return new godot_variant() { Color = src.Color };
+                case Variant.Type.Rid:
+                    return new godot_variant() { RID = src.RID };
+            }
+
+            godotsharp_variant_new_copy(out godot_variant ret, src);
+            return ret;
+        }
+
         public static godot_string_name godotsharp_string_name_new_copy(in godot_string_name src)
         {
             if (src.IsEmpty)

+ 0 - 14
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs

@@ -227,14 +227,6 @@ namespace Godot.NativeInterop
             return CreateFromArray(array);
         }
 
-        public static godot_variant CreateFromSystemArrayOfVariant(object[]? from)
-        {
-            if (from == null)
-                return default; // Nil
-            using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from);
-            return CreateFromArray(array);
-        }
-
         public static godot_variant CreateFromArray(godot_array from)
         {
             NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from);
@@ -671,12 +663,6 @@ namespace Godot.NativeInterop
             return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type);
         }
 
-        public static object[] ConvertToSystemArrayOfVariant(in godot_variant p_var)
-        {
-            using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
-            return Marshaling.ConvertNativeGodotArrayToSystemArray(godotArray);
-        }
-
         public static Array<T> ConvertToGenericArrayObject<T>(in godot_variant p_var) =>
             new(ConvertToArrayObject(p_var));
 

+ 0 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs

@@ -1,6 +1,4 @@
 using System;
-using System.Linq;
-using System.Reflection;
 using System.Runtime.InteropServices;
 using Godot.Bridge;
 using Godot.NativeInterop;

+ 1 - 0
modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj

@@ -124,6 +124,7 @@
     <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.

+ 665 - 0
modules/mono/glue/GodotSharp/GodotSharp/Variant.cs

@@ -0,0 +1,665 @@
+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 class Disposer : IDisposable
+    {
+        private godot_variant.movable _native;
+
+        private WeakReference<IDisposable>? _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);
+        }
+
+        public 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.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));
+
+    /// <summary>
+    /// Constructs a new <see cref="Godot.NativeInterop.godot_variant"/> from this instance.
+    /// The caller is responsible of disposing the new instance to avoid memory leaks.
+    /// </summary>
+    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 object? Obj
+    {
+        get
+        {
+            if (_obj == null)
+                _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar);
+
+            return _obj;
+        }
+    }
+
+    // TODO: Consider implicit operators
+
+    [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 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<T>()
+        where T : Godot.Object =>
+        VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public T[] AsSystemArrayOfSupportedType<T>() =>
+        VariantUtils.ConvertToSystemArrayOfSupportedType<T>((godot_variant)NativeVar);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public Collections.Dictionary<TKey, TValue> AsGodotGenericDictionary<TKey, TValue>() =>
+        VariantUtils.ConvertToGenericDictionaryObject<TKey, TValue>((godot_variant)NativeVar);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public Collections.Array<T> AsGodotGenericArray<T>() =>
+        VariantUtils.ConvertToGenericArrayObject<T>((godot_variant)NativeVar);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public System.Collections.Generic.Dictionary<TKey, TValue> AsSystemGenericDictionary<TKey, TValue>()
+        where TKey : notnull =>
+        VariantUtils.ConvertToSystemGenericDictionary<TKey, TValue>((godot_variant)NativeVar);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public System.Collections.Generic.List<T> AsSystemGenericList<T>() =>
+        VariantUtils.ConvertToSystemGenericList<T>((godot_variant)NativeVar);
+
+    [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 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 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();
+
+    [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(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(Span<byte> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedByteArray(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<int> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt32Array(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<long> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt64Array(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<float> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat32Array(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<double> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat64Array(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<string> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedStringArray(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<Vector2> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector2Array(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<Vector3> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<Color> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Godot.Object[]? from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<T>(Collections.Array<T> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> from)
+        where TKey : notnull => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemDictionary(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<T>(System.Collections.Generic.List<T> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemICollection(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericIDictionary(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<T>(System.Collections.Generic.ICollection<T> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericICollection(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From<T>(System.Collections.Generic.IEnumerable<T> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericIEnumerable(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));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From(System.Collections.IDictionary from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemIDictionary(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From(System.Collections.ICollection from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemICollection(from));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant From(System.Collections.IEnumerable from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemIEnumerable(from));
+}

+ 6 - 1
modules/mono/glue/runtime_interop.cpp

@@ -540,6 +540,10 @@ GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(MethodBind *p_method
 
 // variant.h
 
+GD_PINVOKE_EXPORT void godotsharp_variant_new_copy(godot_variant *r_dest, const Variant *p_src) {
+	memnew_placement(r_dest, Variant(*p_src));
+}
+
 GD_PINVOKE_EXPORT void godotsharp_variant_new_string_name(godot_variant *r_dest, const StringName *p_s) {
 	memnew_placement(r_dest, Variant(*p_s));
 }
@@ -1315,7 +1319,7 @@ GD_PINVOKE_EXPORT void godotsharp_object_to_string(Object *p_ptr, godot_string *
 #endif
 
 // We need this to prevent the functions from being stripped.
-void *godotsharp_pinvoke_funcs[185] = {
+void *godotsharp_pinvoke_funcs[186] = {
 	(void *)godotsharp_method_bind_get_method,
 	(void *)godotsharp_get_class_constructor,
 	(void *)godotsharp_engine_get_singleton,
@@ -1359,6 +1363,7 @@ void *godotsharp_pinvoke_funcs[185] = {
 	(void *)godotsharp_callable_call_deferred,
 	(void *)godotsharp_method_bind_ptrcall,
 	(void *)godotsharp_method_bind_call,
+	(void *)godotsharp_variant_new_copy,
 	(void *)godotsharp_variant_new_string_name,
 	(void *)godotsharp_variant_new_node_path,
 	(void *)godotsharp_variant_new_object,