From 3afeb28560c3b10419287538162cded631994ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Mon, 28 Nov 2022 18:17:02 +0100 Subject: [PATCH 1/3] C#: Remove `ConvertVariantToManagedObject` Its two usages were: - The Array `ICollection.CopyTo` implementation. It's possible that this class shouldn't be implementing the non-generic `ICollection`, but this commit doesn't change that. The new implementation stores the elements as boxed `Variant` values. - The `Variant.Obj` property. I'm not sure if this property's existence is justified, but for now I rewrote it as a simpler version of `ConvertVariantToManagedObject`. --- .../glue/GodotSharp/GodotSharp/Core/Array.cs | 4 +- .../Core/NativeInterop/Marshaling.cs | 105 ------------------ .../GodotSharp/GodotSharp/Core/Variant.cs | 56 ++++++++-- 3 files changed, 49 insertions(+), 116 deletions(-) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 1b9f63c22d9..130776499bf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -418,8 +418,8 @@ namespace Godot.Collections { for (int i = 0; i < count; i++) { - object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]); - array.SetValue(obj, index); + object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]); + array.SetValue(boxedVariant, index); index++; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index d2a80d0e922..fd1d723c050 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -584,111 +584,6 @@ namespace Godot.NativeInterop return false; } - public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var) - { - switch (p_var.Type) - { - case Variant.Type.Bool: - return p_var.Bool.ToBool(); - case Variant.Type.Int: - return p_var.Int; - case Variant.Type.Float: - { -#if REAL_T_IS_DOUBLE - return p_var.Float; -#else - return (float)p_var.Float; -#endif - } - case Variant.Type.String: - return ConvertStringToManaged(p_var.String); - case Variant.Type.Vector2: - return p_var.Vector2; - case Variant.Type.Vector2i: - return p_var.Vector2i; - case Variant.Type.Rect2: - return p_var.Rect2; - case Variant.Type.Rect2i: - return p_var.Rect2i; - case Variant.Type.Vector3: - return p_var.Vector3; - case Variant.Type.Vector3i: - return p_var.Vector3i; - case Variant.Type.Transform2d: - return *p_var.Transform2D; - case Variant.Type.Vector4: - return p_var.Vector4; - case Variant.Type.Vector4i: - return p_var.Vector4i; - case Variant.Type.Plane: - return p_var.Plane; - case Variant.Type.Quaternion: - return p_var.Quaternion; - case Variant.Type.Aabb: - return *p_var.AABB; - case Variant.Type.Basis: - return *p_var.Basis; - case Variant.Type.Transform3d: - return *p_var.Transform3D; - case Variant.Type.Projection: - return *p_var.Projection; - case Variant.Type.Color: - return p_var.Color; - case Variant.Type.StringName: - { - // The Variant owns the value, so we need to make a copy - return StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName)); - } - case Variant.Type.NodePath: - { - // The Variant owns the value, so we need to make a copy - return NodePath.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath)); - } - case Variant.Type.Rid: - return p_var.RID; - case Variant.Type.Object: - return InteropUtils.UnmanagedGetManaged(p_var.Object); - case Variant.Type.Callable: - return ConvertCallableToManaged(p_var.Callable); - case Variant.Type.Signal: - return ConvertSignalToManaged(p_var.Signal); - case Variant.Type.Dictionary: - { - // The Variant owns the value, so we need to make a copy - return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary)); - } - case Variant.Type.Array: - { - // The Variant owns the value, so we need to make a copy - return Collections.Array.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_array_new_copy(p_var.Array)); - } - case Variant.Type.PackedByteArray: - return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); - case Variant.Type.PackedInt32Array: - return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); - case Variant.Type.PackedInt64Array: - return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); - case Variant.Type.PackedFloat32Array: - return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var); - case Variant.Type.PackedFloat64Array: - return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var); - case Variant.Type.PackedStringArray: - return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var); - case Variant.Type.PackedVector2Array: - return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var); - case Variant.Type.PackedVector3Array: - return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var); - case Variant.Type.PackedColorArray: - return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); - default: - return null; - } - } - // String public static unsafe godot_string ConvertStringToNative(string? p_mono_string) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs index 237a4da3642..32d9598554b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs @@ -109,16 +109,54 @@ public partial struct Variant : IDisposable public override string ToString() => AsString(); - public object? Obj - { - get + public object? Obj => + _obj ??= NativeVar.DangerousSelfRef.Type switch { - if (_obj == null) - _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar); - - return _obj; - } - } + Type.Bool => AsBool(), + Type.Int => AsInt64(), +#if REAL_T_IS_DOUBLE + Type.Float => AsDouble(), +#else + Type.Float => AsSingle(), +#endif + Type.String => AsString(), + Type.Vector2 => AsVector2(), + Type.Vector2i => AsVector2i(), + Type.Rect2 => AsRect2(), + Type.Rect2i => AsRect2i(), + Type.Vector3 => AsVector3(), + Type.Vector3i => AsVector3i(), + Type.Transform2d => AsTransform2D(), + Type.Vector4 => AsVector4(), + Type.Vector4i => AsVector4i(), + Type.Plane => AsPlane(), + Type.Quaternion => AsQuaternion(), + Type.Aabb => AsAABB(), + Type.Basis => AsBasis(), + Type.Transform3d => AsTransform3D(), + Type.Projection => AsProjection(), + Type.Color => AsColor(), + Type.StringName => AsStringName(), + Type.NodePath => AsNodePath(), + Type.Rid => AsRID(), + Type.Object => AsGodotObject(), + Type.Callable => AsCallable(), + Type.Signal => AsSignalInfo(), + Type.Dictionary => AsGodotDictionary(), + Type.Array => AsGodotArray(), + Type.PackedByteArray => AsByteArray(), + Type.PackedInt32Array => AsInt32Array(), + Type.PackedInt64Array => AsInt64Array(), + Type.PackedFloat32Array => AsFloat32Array(), + Type.PackedFloat64Array => AsFloat64Array(), + Type.PackedStringArray => AsStringArray(), + Type.PackedVector2Array => AsVector2Array(), + Type.PackedVector3Array => AsVector3Array(), + Type.PackedColorArray => AsColorArray(), + Type.Nil => null, + Type.Max or _ => + throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"), + }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant From<[MustBeVariant] T>(in T from) => From f86c6b6ac47ecf3e75c74cb94fd65efa6ea2eb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Tue, 29 Nov 2022 01:01:36 +0100 Subject: [PATCH 2/3] C#: Replace most conversions between Variant and System.Object This commit replaces most usages of `ConvertManagedObjectToVariant` and `ConvertVariantToManagedObjectOfType`, by using the `Godot.Variant` struct instead of `System.Object`. The most notable change is to the `GetGodotPropertyDefaultValues` method that's generated for scripts. The dictionary it returns now stores `Godot.Variant` values. Remaining usages are: - The `DelegateUtils` class, for the serialization of closure display classes during assembly reloading by the editor. These display classes are compiler generated classes to store values captured by a closure. Since it's generated by the compiler, the only way we have to access the fields is through reflection. This leads to using `System.Object`. - Converting parameters when invoking constructors from the engine. This will be replaced with source generators in the future. - Legacy support for old `GetGodotPropertyDefaultValues` return values. We need to keep supporting the old version of this generated method for some time. Otherwise, if loading a project built with the previous version, it could lead to the loss of exported property values. Ideally, we should remove this legacy support before a stable release. --- .../Godot.SourceGenerators/MarshalUtils.cs | 4 +- .../ScriptPropertyDefValGenerator.cs | 11 +- .../GodotTools/GodotTools/GodotSharpEditor.cs | 2 +- .../GodotTools/Internals/Globals.cs | 28 +- .../Core/Bridge/ScriptManagerBridge.cs | 59 ++- .../GodotSharp/Core/DelegateUtils.cs | 275 +++++++++++- .../Core/NativeInterop/Marshaling.cs | 412 +----------------- .../NativeInterop/VariantUtils.generic.cs | 2 + 8 files changed, 350 insertions(+), 443 deletions(-) diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs index 4fdd40f638c..6b48f9135b6 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs @@ -368,7 +368,7 @@ namespace Godot.SourceGenerators source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"), MarshalType.Enum => source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), - ")", VariantUtils, ".ConvertToInt32(", inputExpr, ")"), + ")", VariantUtils, ".ConvertToInt64(", inputExpr, ")"), MarshalType.ByteArray => source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"), MarshalType.Int32Array => @@ -491,7 +491,7 @@ namespace Godot.SourceGenerators MarshalType.SignalInfo => source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"), MarshalType.Enum => - source.Append(VariantUtils, ".CreateFromInt((int)", inputExpr, ")"), + source.Append(VariantUtils, ".CreateFromInt((long)", inputExpr, ")"), MarshalType.ByteArray => source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"), MarshalType.Int32Array => diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 63339b34871..501717bf5c7 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -174,7 +174,8 @@ namespace Godot.SourceGenerators } else { - var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors.Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault(); + var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors + .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault(); if (propertyGet != null) { if (propertyGet.ExpressionBody != null) @@ -200,7 +201,8 @@ namespace Godot.SourceGenerators { var returns = propertyGet.DescendantNodes().OfType(); if (returns.Count() == 1) - {// Generate only single return + { + // Generate only single return var returnStatementSyntax = returns.Single(); if (returnStatementSyntax.Expression is IdentifierNameSyntax identifierNameSyntax) { @@ -277,7 +279,8 @@ namespace Godot.SourceGenerators { source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - string dictionaryType = "System.Collections.Generic.Dictionary"; + string dictionaryType = + "global::System.Collections.Generic.Dictionary"; source.Append("#if TOOLS\n"); source.Append(" internal new static "); @@ -304,7 +307,7 @@ namespace Godot.SourceGenerators source.Append(" values.Add(PropertyName."); source.Append(exportedMember.Name); source.Append(", "); - source.Append(defaultValueLocalName); + source.AppendManagedToVariantExpr(defaultValueLocalName, exportedMember.Type); source.Append(");\n"); } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 89364d1c020..54969920cbf 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -455,7 +455,7 @@ namespace GodotTools _menuPopup.IdPressed += _MenuOptionPressed; // External editor settings - EditorDef("mono/editor/external_editor", ExternalEditorId.None); + EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None)); string settingsHintStr = "Disabled"; diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index acb7cc3ab0e..45ae7eb86b4 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -1,3 +1,4 @@ +using Godot; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -8,30 +9,31 @@ namespace GodotTools.Internals { public static float EditorScale => Internal.godot_icall_Globals_EditorScale(); - public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) + // ReSharper disable once UnusedMethodReturnValue.Global + public static Variant GlobalDef(string setting, Variant defaultValue, bool restartIfChanged = false) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); - using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue); - Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); - using (result) - return Marshaling.ConvertVariantToManagedObject(result); + using godot_variant defaultValueIn = defaultValue.CopyNativeVariant(); + Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, + out godot_variant result); + return Variant.CreateTakingOwnershipOfDisposableValue(result); } - public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) + // ReSharper disable once UnusedMethodReturnValue.Global + public static Variant EditorDef(string setting, Variant defaultValue, bool restartIfChanged = false) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); - using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue); - Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); - using (result) - return Marshaling.ConvertVariantToManagedObject(result); + using godot_variant defaultValueIn = defaultValue.CopyNativeVariant(); + Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, + out godot_variant result); + return Variant.CreateTakingOwnershipOfDisposableValue(result); } - public static object EditorShortcut(string setting) + public static Variant EditorShortcut(string setting) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result); - using (result) - return Marshaling.ConvertVariantToManagedObject(result); + return Variant.CreateTakingOwnershipOfDisposableValue(result); } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index d6fad391b6d..e6a8054ae26 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -159,7 +159,7 @@ namespace Godot.Bridge for (int i = 0; i < paramCount; i++) { - invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType( + invokeParams[i] = DelegateUtils.RuntimeTypeConversionHelper.ConvertToObjectOfType( *args[i], parameters[i].ParameterType); } @@ -832,7 +832,8 @@ namespace Godot.Bridge } else { - interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_info)))!; + interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc( + (nuint)length, (nuint)sizeof(godotsharp_property_info)))!; } try @@ -858,8 +859,8 @@ namespace Godot.Bridge addPropInfoFunc(scriptPtr, ¤tClassName, interopProperties, length); - // We're borrowing the StringName's without making an owning copy, so the - // managed collection needs to be kept alive until `addPropInfoFunc` returns. + // We're borrowing the native value of the StringName entries. + // The dictionary needs to be kept alive until `addPropInfoFunc` returns. GC.KeepAlive(properties); } finally @@ -884,12 +885,7 @@ namespace Godot.Bridge { // Careful with padding... public godot_string_name Name; // Not owned - public godot_variant Value; - - public void Dispose() - { - Value.Dispose(); - } + public godot_variant Value; // Not owned } [UnmanagedCallersOnly] @@ -928,10 +924,35 @@ namespace Godot.Bridge if (getGodotPropertyDefaultValuesMethod == null) return; - var defaultValues = (Dictionary?) - getGodotPropertyDefaultValuesMethod.Invoke(null, null); + var defaultValuesObj = getGodotPropertyDefaultValuesMethod.Invoke(null, null); - if (defaultValues == null || defaultValues.Count <= 0) + if (defaultValuesObj == null) + return; + + Dictionary defaultValues; + + if (defaultValuesObj is Dictionary defaultValuesLegacy) + { + // We have to support this for some time, otherwise this could cause data loss for projects + // built with previous releases. Ideally, we should remove this before Godot 4.0 stable. + + if (defaultValuesLegacy.Count <= 0) + return; + + defaultValues = new(); + + foreach (var pair in defaultValuesLegacy) + { + defaultValues[pair.Key] = Variant.CreateTakingOwnershipOfDisposableValue( + DelegateUtils.RuntimeTypeConversionHelper.ConvertToVariant(pair.Value)); + } + } + else + { + defaultValues = (Dictionary)defaultValuesObj; + } + + if (defaultValues.Count <= 0) return; int length = defaultValues.Count; @@ -952,7 +973,8 @@ namespace Godot.Bridge } else { - interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!; + interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc( + (nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!; } try @@ -963,7 +985,7 @@ namespace Godot.Bridge godotsharp_property_def_val_pair interopProperty = new() { Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned - Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value) + Value = (godot_variant)defaultValuePair.Value.NativeVar // Not owned }; interopDefaultValues[i] = interopProperty; @@ -973,15 +995,12 @@ namespace Godot.Bridge addDefValFunc(scriptPtr, interopDefaultValues, length); - // We're borrowing the StringName's without making an owning copy, so the - // managed collection needs to be kept alive until `addDefValFunc` returns. + // We're borrowing the native value of the StringName and Variant entries. + // The dictionary needs to be kept alive until `addDefValFunc` returns. GC.KeepAlive(defaultValues); } finally { - for (int i = 0; i < length; i++) - interopDefaultValues[i].Dispose(); - if (!useStack) NativeMemory.Free(interopDefaultValues); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index d19e0c08f20..a3cfecfaa62 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -186,7 +186,7 @@ namespace Godot writer.Write(field.Name); var fieldValue = field.GetValue(target); - using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue); + using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue); byte[] valueBuffer = VarToBytes(fieldValueVariant); writer.Write(valueBuffer.Length); writer.Write(valueBuffer); @@ -443,7 +443,14 @@ namespace Godot FieldInfo? fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public); - fieldInfo?.SetValue(recreatedTarget, GD.BytesToVar(valueBuffer)); + + if (fieldInfo != null) + { + var variantValue = GD.BytesToVar(valueBuffer); + object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType( + (godot_variant)variantValue.NativeVar, fieldInfo.FieldType); + fieldInfo.SetValue(recreatedTarget, managedValue); + } } @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo, @@ -537,5 +544,269 @@ namespace Godot return type; } + + internal static class RuntimeTypeConversionHelper + { + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + public static godot_variant ConvertToVariant(object? obj) + { + if (obj == null) + return default; + + switch (obj) + { + case bool @bool: + return VariantUtils.CreateFrom(@bool); + case char @char: + return VariantUtils.CreateFrom(@char); + case sbyte int8: + return VariantUtils.CreateFrom(int8); + case short int16: + return VariantUtils.CreateFrom(int16); + case int int32: + return VariantUtils.CreateFrom(int32); + case long int64: + return VariantUtils.CreateFrom(int64); + case byte uint8: + return VariantUtils.CreateFrom(uint8); + case ushort uint16: + return VariantUtils.CreateFrom(uint16); + case uint uint32: + return VariantUtils.CreateFrom(uint32); + case ulong uint64: + return VariantUtils.CreateFrom(uint64); + case float @float: + return VariantUtils.CreateFrom(@float); + case double @double: + return VariantUtils.CreateFrom(@double); + case Vector2 vector2: + return VariantUtils.CreateFrom(vector2); + case Vector2i vector2I: + return VariantUtils.CreateFrom(vector2I); + case Rect2 rect2: + return VariantUtils.CreateFrom(rect2); + case Rect2i rect2I: + return VariantUtils.CreateFrom(rect2I); + case Transform2D transform2D: + return VariantUtils.CreateFrom(transform2D); + case Vector3 vector3: + return VariantUtils.CreateFrom(vector3); + case Vector3i vector3I: + return VariantUtils.CreateFrom(vector3I); + case Vector4 vector4: + return VariantUtils.CreateFrom(vector4); + case Vector4i vector4I: + return VariantUtils.CreateFrom(vector4I); + case Basis basis: + return VariantUtils.CreateFrom(basis); + case Quaternion quaternion: + return VariantUtils.CreateFrom(quaternion); + case Transform3D transform3d: + return VariantUtils.CreateFrom(transform3d); + case Projection projection: + return VariantUtils.CreateFrom(projection); + case AABB aabb: + return VariantUtils.CreateFrom(aabb); + case Color color: + return VariantUtils.CreateFrom(color); + case Plane plane: + return VariantUtils.CreateFrom(plane); + case Callable callable: + return VariantUtils.CreateFrom(callable); + case SignalInfo signalInfo: + return VariantUtils.CreateFrom(signalInfo); + case string @string: + return VariantUtils.CreateFrom(@string); + case byte[] byteArray: + return VariantUtils.CreateFrom(byteArray); + case int[] int32Array: + return VariantUtils.CreateFrom(int32Array); + case long[] int64Array: + return VariantUtils.CreateFrom(int64Array); + case float[] floatArray: + return VariantUtils.CreateFrom(floatArray); + case double[] doubleArray: + return VariantUtils.CreateFrom(doubleArray); + case string[] stringArray: + return VariantUtils.CreateFrom(stringArray); + case Vector2[] vector2Array: + return VariantUtils.CreateFrom(vector2Array); + case Vector3[] vector3Array: + return VariantUtils.CreateFrom(vector3Array); + case Color[] colorArray: + return VariantUtils.CreateFrom(colorArray); + case StringName[] stringNameArray: + return VariantUtils.CreateFrom(stringNameArray); + case NodePath[] nodePathArray: + return VariantUtils.CreateFrom(nodePathArray); + case RID[] ridArray: + return VariantUtils.CreateFrom(ridArray); + case Godot.Object[] godotObjectArray: + return VariantUtils.CreateFrom(godotObjectArray); + case StringName stringName: + return VariantUtils.CreateFrom(stringName); + case NodePath nodePath: + return VariantUtils.CreateFrom(nodePath); + case RID rid: + return VariantUtils.CreateFrom(rid); + case Collections.Dictionary godotDictionary: + return VariantUtils.CreateFrom(godotDictionary); + case Collections.Array godotArray: + return VariantUtils.CreateFrom(godotArray); + case Variant variant: + return VariantUtils.CreateFrom(variant); + case Godot.Object godotObject: + return VariantUtils.CreateFrom(godotObject); + case Enum @enum: + return VariantUtils.CreateFrom(Convert.ToInt64(@enum)); + case Collections.IGenericGodotDictionary godotDictionary: + return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary); + case Collections.IGenericGodotArray godotArray: + return VariantUtils.CreateFrom(godotArray.UnderlyingArray); + } + + GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + + obj.GetType().FullName + "."); + return new godot_variant(); + } + + private delegate object? ConvertToSystemObjectFunc(in godot_variant managed); + + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + // ReSharper disable once RedundantNameQualifier + private static readonly System.Collections.Generic.Dictionary + ToSystemObjectFuncByType = new() + { + [typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(SignalInfo)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(StringName[])] = + (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) => + VariantUtils.ConvertTo(variant), + [typeof(Godot.Collections.Array)] = + (in godot_variant variant) => VariantUtils.ConvertTo(variant), + [typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo(variant), + }; + + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + public static object? ConvertToObjectOfType(in godot_variant variant, Type type) + { + if (ToSystemObjectFuncByType.TryGetValue(type, out var func)) + return func(variant); + + if (typeof(Godot.Object).IsAssignableFrom(type)) + return Convert.ChangeType(VariantUtils.ConvertTo(variant), type); + + if (type.IsEnum) + { + var enumUnderlyingType = type.GetEnumUnderlyingType(); + + switch (Type.GetTypeCode(enumUnderlyingType)) + { + case TypeCode.SByte: + return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant)); + case TypeCode.Int16: + return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant)); + case TypeCode.Int32: + return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant)); + case TypeCode.Int64: + return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant)); + case TypeCode.Byte: + return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant)); + case TypeCode.UInt16: + return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant)); + case TypeCode.UInt32: + return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant)); + case TypeCode.UInt64: + return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant)); + default: + { + GD.PushError( + "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); + return null; + } + } + } + + if (type.IsGenericType) + { + var genericTypeDef = type.GetGenericTypeDefinition(); + + if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>)) + { + var ctor = type.GetConstructor(BindingFlags.Default, + new[] { typeof(Godot.Collections.Dictionary) }); + + if (ctor == null) + throw new InvalidOperationException("Dictionary constructor not found"); + + return ctor.Invoke(new object?[] + { + VariantUtils.ConvertTo(variant) + }); + } + + if (genericTypeDef == typeof(Godot.Collections.Array<>)) + { + var ctor = type.GetConstructor(BindingFlags.Default, + new[] { typeof(Godot.Collections.Array) }); + + if (ctor == null) + throw new InvalidOperationException("Array constructor not found"); + + return ctor.Invoke(new object?[] + { + VariantUtils.ConvertTo(variant) + }); + } + } + + GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}."); + return null; + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index fd1d723c050..6176093bc15 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -1,5 +1,7 @@ using System; using System.Runtime.InteropServices; +using Godot.Collections; +using Array = System.Array; // ReSharper disable InconsistentNaming @@ -148,6 +150,15 @@ namespace Godot.NativeInterop { if (typeof(Godot.Object).IsAssignableFrom(type)) return Variant.Type.Object; + + // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections + // because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode. + + if (typeof(IGenericGodotDictionary).IsAssignableFrom(type)) + return Variant.Type.Dictionary; + + if (typeof(IGenericGodotArray).IsAssignableFrom(type)) + return Variant.Type.Array; } else if (type == typeof(Variant)) { @@ -183,407 +194,6 @@ namespace Godot.NativeInterop return Variant.Type.Nil; } - /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ - public static godot_variant ConvertManagedObjectToVariant(object? p_obj) - { - if (p_obj == null) - return new godot_variant(); - - switch (p_obj) - { - case bool @bool: - return VariantUtils.CreateFromBool(@bool); - case char @char: - return VariantUtils.CreateFromInt(@char); - case sbyte @int8: - return VariantUtils.CreateFromInt(@int8); - case short @int16: - return VariantUtils.CreateFromInt(@int16); - case int @int32: - return VariantUtils.CreateFromInt(@int32); - case long @int64: - return VariantUtils.CreateFromInt(@int64); - case byte @uint8: - return VariantUtils.CreateFromInt(@uint8); - case ushort @uint16: - return VariantUtils.CreateFromInt(@uint16); - case uint @uint32: - return VariantUtils.CreateFromInt(@uint32); - case ulong @uint64: - return VariantUtils.CreateFromInt(@uint64); - case float @float: - return VariantUtils.CreateFromFloat(@float); - case double @double: - return VariantUtils.CreateFromFloat(@double); - case Vector2 @vector2: - return VariantUtils.CreateFromVector2(@vector2); - case Vector2i @vector2i: - return VariantUtils.CreateFromVector2i(@vector2i); - case Rect2 @rect2: - return VariantUtils.CreateFromRect2(@rect2); - case Rect2i @rect2i: - return VariantUtils.CreateFromRect2i(@rect2i); - case Transform2D @transform2D: - return VariantUtils.CreateFromTransform2D(@transform2D); - case Vector3 @vector3: - return VariantUtils.CreateFromVector3(@vector3); - case Vector3i @vector3i: - return VariantUtils.CreateFromVector3i(@vector3i); - case Vector4 @vector4: - return VariantUtils.CreateFromVector4(@vector4); - case Vector4i @vector4i: - return VariantUtils.CreateFromVector4i(@vector4i); - case Basis @basis: - return VariantUtils.CreateFromBasis(@basis); - case Quaternion @quaternion: - return VariantUtils.CreateFromQuaternion(@quaternion); - case Transform3D @transform3d: - return VariantUtils.CreateFromTransform3D(@transform3d); - case Projection @projection: - return VariantUtils.CreateFromProjection(@projection); - case AABB @aabb: - return VariantUtils.CreateFromAABB(@aabb); - case Color @color: - return VariantUtils.CreateFromColor(@color); - case Plane @plane: - return VariantUtils.CreateFromPlane(@plane); - case Callable @callable: - return VariantUtils.CreateFromCallable(@callable); - case SignalInfo @signalInfo: - return VariantUtils.CreateFromSignalInfo(@signalInfo); - case Enum @enum: - return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); - case string @string: - return VariantUtils.CreateFromString(@string); - case byte[] byteArray: - return VariantUtils.CreateFromPackedByteArray(byteArray); - case int[] int32Array: - return VariantUtils.CreateFromPackedInt32Array(int32Array); - case long[] int64Array: - return VariantUtils.CreateFromPackedInt64Array(int64Array); - case float[] floatArray: - return VariantUtils.CreateFromPackedFloat32Array(floatArray); - case double[] doubleArray: - return VariantUtils.CreateFromPackedFloat64Array(doubleArray); - case string[] stringArray: - return VariantUtils.CreateFromPackedStringArray(stringArray); - case Vector2[] vector2Array: - return VariantUtils.CreateFromPackedVector2Array(vector2Array); - case Vector3[] vector3Array: - return VariantUtils.CreateFromPackedVector3Array(vector3Array); - case Color[] colorArray: - return VariantUtils.CreateFromPackedColorArray(colorArray); - case StringName[] stringNameArray: - return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray); - case NodePath[] nodePathArray: - return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray); - case RID[] ridArray: - return VariantUtils.CreateFromSystemArrayOfRID(ridArray); - case Godot.Object[] godotObjectArray: - return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray); - case Godot.Object godotObject: - return VariantUtils.CreateFromGodotObject(godotObject); - case StringName stringName: - return VariantUtils.CreateFromStringName(stringName); - case NodePath nodePath: - return VariantUtils.CreateFromNodePath(nodePath); - case RID rid: - return VariantUtils.CreateFromRID(rid); - case Collections.Dictionary godotDictionary: - return VariantUtils.CreateFromDictionary(godotDictionary); - case Collections.Array godotArray: - return VariantUtils.CreateFromArray(godotArray); - case Collections.IGenericGodotDictionary godotDictionary: - return VariantUtils.CreateFromDictionary(godotDictionary.UnderlyingDictionary); - case Collections.IGenericGodotArray godotArray: - return VariantUtils.CreateFromArray(godotArray.UnderlyingArray); - case Variant variant: - return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar); - } - - GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + - p_obj.GetType().FullName + "."); - return new godot_variant(); - } - - public static object? ConvertVariantToManagedObjectOfType(in godot_variant p_var, Type type) - { - // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant. - switch (Type.GetTypeCode(type)) - { - case TypeCode.Boolean: - return VariantUtils.ConvertToBool(p_var); - case TypeCode.Char: - return VariantUtils.ConvertToChar(p_var); - case TypeCode.SByte: - return VariantUtils.ConvertToInt8(p_var); - case TypeCode.Int16: - return VariantUtils.ConvertToInt16(p_var); - case TypeCode.Int32: - return VariantUtils.ConvertToInt32(p_var); - case TypeCode.Int64: - return VariantUtils.ConvertToInt64(p_var); - case TypeCode.Byte: - return VariantUtils.ConvertToUInt8(p_var); - case TypeCode.UInt16: - return VariantUtils.ConvertToUInt16(p_var); - case TypeCode.UInt32: - return VariantUtils.ConvertToUInt32(p_var); - case TypeCode.UInt64: - return VariantUtils.ConvertToUInt64(p_var); - case TypeCode.Single: - return VariantUtils.ConvertToFloat32(p_var); - case TypeCode.Double: - return VariantUtils.ConvertToFloat64(p_var); - case TypeCode.String: - return VariantUtils.ConvertToStringObject(p_var); - default: - { - if (type == typeof(Vector2)) - return VariantUtils.ConvertToVector2(p_var); - - if (type == typeof(Vector2i)) - return VariantUtils.ConvertToVector2i(p_var); - - if (type == typeof(Rect2)) - return VariantUtils.ConvertToRect2(p_var); - - if (type == typeof(Rect2i)) - return VariantUtils.ConvertToRect2i(p_var); - - if (type == typeof(Transform2D)) - return VariantUtils.ConvertToTransform2D(p_var); - - if (type == typeof(Vector3)) - return VariantUtils.ConvertToVector3(p_var); - - if (type == typeof(Vector3i)) - return VariantUtils.ConvertToVector3i(p_var); - - if (type == typeof(Vector4)) - return VariantUtils.ConvertToVector4(p_var); - - if (type == typeof(Vector4i)) - return VariantUtils.ConvertToVector4i(p_var); - - if (type == typeof(Basis)) - return VariantUtils.ConvertToBasis(p_var); - - if (type == typeof(Quaternion)) - return VariantUtils.ConvertToQuaternion(p_var); - - if (type == typeof(Transform3D)) - return VariantUtils.ConvertToTransform3D(p_var); - - if (type == typeof(Projection)) - return VariantUtils.ConvertToProjection(p_var); - - if (type == typeof(AABB)) - return VariantUtils.ConvertToAABB(p_var); - - if (type == typeof(Color)) - return VariantUtils.ConvertToColor(p_var); - - if (type == typeof(Plane)) - return VariantUtils.ConvertToPlane(p_var); - - if (type == typeof(Callable)) - return VariantUtils.ConvertToCallableManaged(p_var); - - if (type == typeof(SignalInfo)) - return VariantUtils.ConvertToSignalInfo(p_var); - - if (type.IsEnum) - { - var enumUnderlyingType = type.GetEnumUnderlyingType(); - switch (Type.GetTypeCode(enumUnderlyingType)) - { - case TypeCode.SByte: - return VariantUtils.ConvertToInt8(p_var); - case TypeCode.Int16: - return VariantUtils.ConvertToInt16(p_var); - case TypeCode.Int32: - return VariantUtils.ConvertToInt32(p_var); - case TypeCode.Int64: - return VariantUtils.ConvertToInt64(p_var); - case TypeCode.Byte: - return VariantUtils.ConvertToUInt8(p_var); - case TypeCode.UInt16: - return VariantUtils.ConvertToUInt16(p_var); - case TypeCode.UInt32: - return VariantUtils.ConvertToUInt32(p_var); - case TypeCode.UInt64: - return VariantUtils.ConvertToUInt64(p_var); - default: - { - GD.PushError( - "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + - type.FullName + " : " + enumUnderlyingType.FullName + "."); - return null; - } - } - } - - if (type.IsArray || type.IsSZArray) - { - return ConvertVariantToSystemArrayOfType(p_var, type); - } - else if (type.IsGenericType) - { - if (typeof(Godot.Object).IsAssignableFrom(type)) - { - var godotObject = VariantUtils.ConvertToGodotObject(p_var); - - if (!type.IsInstanceOfType(godotObject)) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`."); - return null; - } - - return godotObject; - } - - return null; - } - else if (type == typeof(Variant)) - { - return Variant.CreateCopyingBorrowed(p_var); - } - - if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res)) - return res; - - break; - } - } - - GD.PushError("Attempted to convert Variant to unsupported type. Name: " + - type.FullName + "."); - return null; - } - - private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type) - { - if (type == typeof(byte[])) - return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); - - if (type == typeof(int[])) - return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); - - if (type == typeof(long[])) - return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); - - if (type == typeof(float[])) - return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var); - - if (type == typeof(double[])) - return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var); - - if (type == typeof(string[])) - return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var); - - if (type == typeof(Vector2[])) - return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var); - - if (type == typeof(Vector3[])) - return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var); - - if (type == typeof(Color[])) - return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); - - if (type == typeof(StringName[])) - return VariantUtils.ConvertToSystemArrayOfStringName(p_var); - - if (type == typeof(NodePath[])) - return VariantUtils.ConvertToSystemArrayOfNodePath(p_var); - - if (type == typeof(RID[])) - return VariantUtils.ConvertToSystemArrayOfRID(p_var); - - if (typeof(Godot.Object[]).IsAssignableFrom(type)) - return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type); - - GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + - type.GetElementType()!.FullName + "."); - return null; - } - - private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type, - out object? res) - { - if (typeof(Godot.Object).IsAssignableFrom(type)) - { - if (p_var.Type == Variant.Type.Nil) - { - res = null; - return true; - } - - if (p_var.Type != Variant.Type.Object) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" Variant type is `{p_var.Type}`; expected `{p_var.Object}`."); - res = null; - return true; - } - - var godotObjectPtr = VariantUtils.ConvertToGodotObjectPtr(p_var); - - if (godotObjectPtr == IntPtr.Zero) - { - res = null; - return true; - } - - var godotObject = InteropUtils.UnmanagedGetManaged(godotObjectPtr); - - if (!type.IsInstanceOfType(godotObject)) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`."); - res = null; - return false; - } - - res = godotObject; - return true; - } - - if (typeof(StringName) == type) - { - res = VariantUtils.ConvertToStringNameObject(p_var); - return true; - } - - if (typeof(NodePath) == type) - { - res = VariantUtils.ConvertToNodePathObject(p_var); - return true; - } - - if (typeof(RID) == type) - { - res = VariantUtils.ConvertToRID(p_var); - return true; - } - - if (typeof(Collections.Dictionary) == type) - { - res = VariantUtils.ConvertToDictionaryObject(p_var); - return true; - } - - if (typeof(Collections.Array) == type) - { - res = VariantUtils.ConvertToArrayObject(p_var); - return true; - } - - res = null; - return false; - } - // String public static unsafe godot_string ConvertStringToNative(string? p_mono_string) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs index 694da6db775..80ef2a1ea1d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs @@ -4,6 +4,8 @@ using System.Runtime.CompilerServices; namespace Godot.NativeInterop; +#nullable enable + public partial class VariantUtils { private static Exception UnsupportedType() => new InvalidOperationException( From 17b2838f39c634324710166d2f36458906ecaf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Thu, 1 Dec 2022 01:45:11 +0100 Subject: [PATCH 3/3] C#: Cleanup Variant marshaling code in source/bindings generators This change aims to reduce the number of places that need to be changed when adding or editing a Godot type to the bindings. Since the addition of `Variant.From/As` and `VariantUtils.CreateFrom/ConvertTo`, we can now replace a lot of the previous code in the bindings generator and the source generators that specify these conversions for each type manually. The only exceptions are the generic Godot collections (`Array` and `Dictionary`) which still use the old version, as that one cannot be matched by our new conversion methods (limitation in the language with generics, forcing us to use delegate pointers). The cleanup applies to: - Bindings generator: - `TypeInterface.cs_variant_to_managed` - `TypeInterface.cs_managed_to_variant` - Source generators: - `MarshalUtils.AppendNativeVariantToManagedExpr` - `MarshalUtils.AppendManagedToNativeVariantExpr` - `MarshalUtils.AppendVariantToManagedExpr` - `MarshalUtils.AppendManagedToVariantExpr` --- .../ExtensionMethods.cs | 9 +- .../Godot.SourceGenerators/GodotMemberData.cs | 6 +- .../Godot.SourceGenerators/MarshalUtils.cs | 374 ++---------------- .../ScriptMethodsGenerator.cs | 8 +- .../ScriptPropertiesGenerator.cs | 14 +- .../ScriptPropertyDefValGenerator.cs | 3 +- .../ScriptSerializationGenerator.cs | 6 +- .../ScriptSignalsGenerator.cs | 8 +- modules/mono/editor/bindings_generator.cpp | 61 +-- modules/mono/editor/bindings_generator.h | 9 +- 10 files changed, 83 insertions(+), 415 deletions(-) diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs index 7008fb638f7..d67e57edc20 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs @@ -268,8 +268,9 @@ namespace Godot.SourceGenerators if (parameters.Length > paramTypes.Length) return null; // Ignore incompatible method - return new GodotMethodData(method, paramTypes, parameters - .Select(p => p.Type).ToImmutableArray(), retType, retSymbol); + return new GodotMethodData(method, paramTypes, + parameters.Select(p => p.Type).ToImmutableArray(), + retType != null ? (retType.Value, retSymbol) : null); } public static IEnumerable WhereHasGodotCompatibleSignature( @@ -330,10 +331,10 @@ namespace Godot.SourceGenerators public static string Path(this Location location) => location.SourceTree?.GetLineSpan(location.SourceSpan).Path - ?? location.GetLineSpan().Path; + ?? location.GetLineSpan().Path; public static int StartLine(this Location location) => location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line - ?? location.GetLineSpan().StartLinePosition.Line; + ?? location.GetLineSpan().StartLinePosition.Line; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs index abd8079922d..0760ea11bb7 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs @@ -6,20 +6,18 @@ namespace Godot.SourceGenerators public readonly struct GodotMethodData { public GodotMethodData(IMethodSymbol method, ImmutableArray paramTypes, - ImmutableArray paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol) + ImmutableArray paramTypeSymbols, (MarshalType MarshalType, ITypeSymbol TypeSymbol)? retType) { Method = method; ParamTypes = paramTypes; ParamTypeSymbols = paramTypeSymbols; RetType = retType; - RetSymbol = retSymbol; } public IMethodSymbol Method { get; } public ImmutableArray ParamTypes { get; } public ImmutableArray ParamTypeSymbols { get; } - public MarshalType? RetType { get; } - public ITypeSymbol? RetSymbol { get; } + public (MarshalType MarshalType, ITypeSymbol TypeSymbol)? RetType { get; } } public readonly struct GodotSignalDelegateData diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs index 6b48f9135b6..5b3f677f871 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs @@ -304,240 +304,33 @@ namespace Godot.SourceGenerators { return marshalType switch { - MarshalType.Boolean => - source.Append(VariantUtils, ".ConvertToBool(", inputExpr, ")"), - MarshalType.Char => - source.Append("(char)", VariantUtils, ".ConvertToUInt16(", inputExpr, ")"), - MarshalType.SByte => - source.Append(VariantUtils, ".ConvertToInt8(", inputExpr, ")"), - MarshalType.Int16 => - source.Append(VariantUtils, ".ConvertToInt16(", inputExpr, ")"), - MarshalType.Int32 => - source.Append(VariantUtils, ".ConvertToInt32(", inputExpr, ")"), - MarshalType.Int64 => - source.Append(VariantUtils, ".ConvertToInt64(", inputExpr, ")"), - MarshalType.Byte => - source.Append(VariantUtils, ".ConvertToUInt8(", inputExpr, ")"), - MarshalType.UInt16 => - source.Append(VariantUtils, ".ConvertToUInt16(", inputExpr, ")"), - MarshalType.UInt32 => - source.Append(VariantUtils, ".ConvertToUInt32(", inputExpr, ")"), - MarshalType.UInt64 => - source.Append(VariantUtils, ".ConvertToUInt64(", inputExpr, ")"), - MarshalType.Single => - source.Append(VariantUtils, ".ConvertToFloat32(", inputExpr, ")"), - MarshalType.Double => - source.Append(VariantUtils, ".ConvertToFloat64(", inputExpr, ")"), - MarshalType.String => - source.Append(VariantUtils, ".ConvertToStringObject(", inputExpr, ")"), - MarshalType.Vector2 => - source.Append(VariantUtils, ".ConvertToVector2(", inputExpr, ")"), - MarshalType.Vector2i => - source.Append(VariantUtils, ".ConvertToVector2i(", inputExpr, ")"), - MarshalType.Rect2 => - source.Append(VariantUtils, ".ConvertToRect2(", inputExpr, ")"), - MarshalType.Rect2i => - source.Append(VariantUtils, ".ConvertToRect2i(", inputExpr, ")"), - MarshalType.Transform2D => - source.Append(VariantUtils, ".ConvertToTransform2D(", inputExpr, ")"), - MarshalType.Vector3 => - source.Append(VariantUtils, ".ConvertToVector3(", inputExpr, ")"), - MarshalType.Vector3i => - source.Append(VariantUtils, ".ConvertToVector3i(", inputExpr, ")"), - MarshalType.Basis => - source.Append(VariantUtils, ".ConvertToBasis(", inputExpr, ")"), - MarshalType.Quaternion => - source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"), - MarshalType.Transform3D => - source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"), - MarshalType.Vector4 => - source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"), - MarshalType.Vector4i => - source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"), - MarshalType.Projection => - source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"), - MarshalType.AABB => - source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"), - MarshalType.Color => - source.Append(VariantUtils, ".ConvertToColor(", inputExpr, ")"), - MarshalType.Plane => - source.Append(VariantUtils, ".ConvertToPlane(", inputExpr, ")"), - MarshalType.Callable => - source.Append(VariantUtils, ".ConvertToCallableManaged(", inputExpr, ")"), - MarshalType.SignalInfo => - source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"), - MarshalType.Enum => - source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), - ")", VariantUtils, ".ConvertToInt64(", inputExpr, ")"), - MarshalType.ByteArray => - source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"), - MarshalType.Int32Array => - source.Append(VariantUtils, ".ConvertAsPackedInt32ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Int64Array => - source.Append(VariantUtils, ".ConvertAsPackedInt64ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Float32Array => - source.Append(VariantUtils, ".ConvertAsPackedFloat32ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Float64Array => - source.Append(VariantUtils, ".ConvertAsPackedFloat64ArrayToSystemArray(", inputExpr, ")"), - MarshalType.StringArray => - source.Append(VariantUtils, ".ConvertAsPackedStringArrayToSystemArray(", inputExpr, ")"), - MarshalType.Vector2Array => - source.Append(VariantUtils, ".ConvertAsPackedVector2ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Vector3Array => - source.Append(VariantUtils, ".ConvertAsPackedVector3ArrayToSystemArray(", inputExpr, ")"), - MarshalType.ColorArray => - source.Append(VariantUtils, ".ConvertAsPackedColorArrayToSystemArray(", inputExpr, ")"), - MarshalType.GodotObjectOrDerivedArray => - source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<", - ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), - MarshalType.SystemArrayOfStringName => - source.Append(VariantUtils, ".ConvertToSystemArrayOfStringName(", inputExpr, ")"), - MarshalType.SystemArrayOfNodePath => - source.Append(VariantUtils, ".ConvertToSystemArrayOfNodePath(", inputExpr, ")"), - MarshalType.SystemArrayOfRID => - source.Append(VariantUtils, ".ConvertToSystemArrayOfRID(", inputExpr, ")"), - MarshalType.Variant => - source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"), - MarshalType.GodotObjectOrDerived => - source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), - ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"), - MarshalType.StringName => - source.Append(VariantUtils, ".ConvertToStringNameObject(", inputExpr, ")"), - MarshalType.NodePath => - source.Append(VariantUtils, ".ConvertToNodePathObject(", inputExpr, ")"), - MarshalType.RID => - source.Append(VariantUtils, ".ConvertToRID(", inputExpr, ")"), - MarshalType.GodotDictionary => - source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"), - MarshalType.GodotArray => - source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"), + // For generic Godot collections, VariantUtils.ConvertTo is slower, so we need this special case MarshalType.GodotGenericDictionary => source.Append(VariantUtils, ".ConvertToDictionaryObject<", ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ", - ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), + ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", + inputExpr, ")"), MarshalType.GodotGenericArray => source.Append(VariantUtils, ".ConvertToArrayObject<", - ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), - _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type") + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", + inputExpr, ")"), + _ => source.Append(VariantUtils, ".ConvertTo<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), }; } - public static StringBuilder AppendManagedToNativeVariantExpr( - this StringBuilder source, string inputExpr, MarshalType marshalType) + public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source, + string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType) { return marshalType switch { - MarshalType.Boolean => - source.Append(VariantUtils, ".CreateFromBool(", inputExpr, ")"), - MarshalType.Char => - source.Append(VariantUtils, ".CreateFromInt((ushort)", inputExpr, ")"), - MarshalType.SByte => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Int16 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Int32 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Int64 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Byte => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.UInt16 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.UInt32 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.UInt64 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Single => - source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"), - MarshalType.Double => - source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"), - MarshalType.String => - source.Append(VariantUtils, ".CreateFromString(", inputExpr, ")"), - MarshalType.Vector2 => - source.Append(VariantUtils, ".CreateFromVector2(", inputExpr, ")"), - MarshalType.Vector2i => - source.Append(VariantUtils, ".CreateFromVector2i(", inputExpr, ")"), - MarshalType.Rect2 => - source.Append(VariantUtils, ".CreateFromRect2(", inputExpr, ")"), - MarshalType.Rect2i => - source.Append(VariantUtils, ".CreateFromRect2i(", inputExpr, ")"), - MarshalType.Transform2D => - source.Append(VariantUtils, ".CreateFromTransform2D(", inputExpr, ")"), - MarshalType.Vector3 => - source.Append(VariantUtils, ".CreateFromVector3(", inputExpr, ")"), - MarshalType.Vector3i => - source.Append(VariantUtils, ".CreateFromVector3i(", inputExpr, ")"), - MarshalType.Basis => - source.Append(VariantUtils, ".CreateFromBasis(", inputExpr, ")"), - MarshalType.Quaternion => - source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"), - MarshalType.Transform3D => - source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"), - MarshalType.Vector4 => - source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"), - MarshalType.Vector4i => - source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"), - MarshalType.Projection => - source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"), - MarshalType.AABB => - source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"), - MarshalType.Color => - source.Append(VariantUtils, ".CreateFromColor(", inputExpr, ")"), - MarshalType.Plane => - source.Append(VariantUtils, ".CreateFromPlane(", inputExpr, ")"), - MarshalType.Callable => - source.Append(VariantUtils, ".CreateFromCallable(", inputExpr, ")"), - MarshalType.SignalInfo => - source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"), - MarshalType.Enum => - source.Append(VariantUtils, ".CreateFromInt((long)", inputExpr, ")"), - MarshalType.ByteArray => - source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"), - MarshalType.Int32Array => - source.Append(VariantUtils, ".CreateFromPackedInt32Array(", inputExpr, ")"), - MarshalType.Int64Array => - source.Append(VariantUtils, ".CreateFromPackedInt64Array(", inputExpr, ")"), - MarshalType.Float32Array => - source.Append(VariantUtils, ".CreateFromPackedFloat32Array(", inputExpr, ")"), - MarshalType.Float64Array => - source.Append(VariantUtils, ".CreateFromPackedFloat64Array(", inputExpr, ")"), - MarshalType.StringArray => - source.Append(VariantUtils, ".CreateFromPackedStringArray(", inputExpr, ")"), - MarshalType.Vector2Array => - source.Append(VariantUtils, ".CreateFromPackedVector2Array(", inputExpr, ")"), - MarshalType.Vector3Array => - source.Append(VariantUtils, ".CreateFromPackedVector3Array(", inputExpr, ")"), - MarshalType.ColorArray => - source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"), - MarshalType.GodotObjectOrDerivedArray => - source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"), - MarshalType.SystemArrayOfStringName => - source.Append(VariantUtils, ".CreateFromSystemArrayOfStringName(", inputExpr, ")"), - MarshalType.SystemArrayOfNodePath => - source.Append(VariantUtils, ".CreateFromSystemArrayOfNodePath(", inputExpr, ")"), - MarshalType.SystemArrayOfRID => - source.Append(VariantUtils, ".CreateFromSystemArrayOfRID(", inputExpr, ")"), - MarshalType.Variant => - source.Append(inputExpr, ".CopyNativeVariant()"), - MarshalType.GodotObjectOrDerived => - source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"), - MarshalType.StringName => - source.Append(VariantUtils, ".CreateFromStringName(", inputExpr, ")"), - MarshalType.NodePath => - source.Append(VariantUtils, ".CreateFromNodePath(", inputExpr, ")"), - MarshalType.RID => - source.Append(VariantUtils, ".CreateFromRID(", inputExpr, ")"), - MarshalType.GodotDictionary => - source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"), - MarshalType.GodotArray => - source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"), + // For generic Godot collections, VariantUtils.CreateFrom is slower, so we need this special case MarshalType.GodotGenericDictionary => source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"), MarshalType.GodotGenericArray => source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"), - _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type") + _ => source.Append(VariantUtils, ".CreateFrom<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), }; } @@ -546,137 +339,30 @@ namespace Godot.SourceGenerators { return marshalType switch { - MarshalType.Boolean => source.Append(inputExpr, ".AsBool()"), - MarshalType.Char => source.Append(inputExpr, ".AsChar()"), - MarshalType.SByte => source.Append(inputExpr, ".AsSByte()"), - MarshalType.Int16 => source.Append(inputExpr, ".AsInt16()"), - MarshalType.Int32 => source.Append(inputExpr, ".AsInt32()"), - MarshalType.Int64 => source.Append(inputExpr, ".AsInt64()"), - MarshalType.Byte => source.Append(inputExpr, ".AsByte()"), - MarshalType.UInt16 => source.Append(inputExpr, ".AsUInt16()"), - MarshalType.UInt32 => source.Append(inputExpr, ".AsUInt32()"), - MarshalType.UInt64 => source.Append(inputExpr, ".AsUInt64()"), - MarshalType.Single => source.Append(inputExpr, ".AsSingle()"), - MarshalType.Double => source.Append(inputExpr, ".AsDouble()"), - MarshalType.String => source.Append(inputExpr, ".AsString()"), - MarshalType.Vector2 => source.Append(inputExpr, ".AsVector2()"), - MarshalType.Vector2i => source.Append(inputExpr, ".AsVector2i()"), - MarshalType.Rect2 => source.Append(inputExpr, ".AsRect2()"), - MarshalType.Rect2i => source.Append(inputExpr, ".AsRect2i()"), - MarshalType.Transform2D => source.Append(inputExpr, ".AsTransform2D()"), - MarshalType.Vector3 => source.Append(inputExpr, ".AsVector3()"), - MarshalType.Vector3i => source.Append(inputExpr, ".AsVector3i()"), - MarshalType.Basis => source.Append(inputExpr, ".AsBasis()"), - MarshalType.Quaternion => source.Append(inputExpr, ".AsQuaternion()"), - MarshalType.Transform3D => source.Append(inputExpr, ".AsTransform3D()"), - MarshalType.Vector4 => source.Append(inputExpr, ".AsVector4()"), - MarshalType.Vector4i => source.Append(inputExpr, ".AsVector4i()"), - MarshalType.Projection => source.Append(inputExpr, ".AsProjection()"), - MarshalType.AABB => source.Append(inputExpr, ".AsAABB()"), - MarshalType.Color => source.Append(inputExpr, ".AsColor()"), - MarshalType.Plane => source.Append(inputExpr, ".AsPlane()"), - MarshalType.Callable => source.Append(inputExpr, ".AsCallable()"), - MarshalType.SignalInfo => source.Append(inputExpr, ".AsSignalInfo()"), - MarshalType.Enum => - source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsInt64()"), - MarshalType.ByteArray => source.Append(inputExpr, ".AsByteArray()"), - MarshalType.Int32Array => source.Append(inputExpr, ".AsInt32Array()"), - MarshalType.Int64Array => source.Append(inputExpr, ".AsInt64Array()"), - MarshalType.Float32Array => source.Append(inputExpr, ".AsFloat32Array()"), - MarshalType.Float64Array => source.Append(inputExpr, ".AsFloat64Array()"), - MarshalType.StringArray => source.Append(inputExpr, ".AsStringArray()"), - MarshalType.Vector2Array => source.Append(inputExpr, ".AsVector2Array()"), - MarshalType.Vector3Array => source.Append(inputExpr, ".AsVector3Array()"), - MarshalType.ColorArray => source.Append(inputExpr, ".AsColorArray()"), - MarshalType.GodotObjectOrDerivedArray => source.Append(inputExpr, ".AsGodotObjectArray<", - ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"), - MarshalType.SystemArrayOfStringName => source.Append(inputExpr, ".AsSystemArrayOfStringName()"), - MarshalType.SystemArrayOfNodePath => source.Append(inputExpr, ".AsSystemArrayOfNodePath()"), - MarshalType.SystemArrayOfRID => source.Append(inputExpr, ".AsSystemArrayOfRID()"), - MarshalType.Variant => source.Append(inputExpr), - MarshalType.GodotObjectOrDerived => source.Append("(", - typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsGodotObject()"), - MarshalType.StringName => source.Append(inputExpr, ".AsStringName()"), - MarshalType.NodePath => source.Append(inputExpr, ".AsNodePath()"), - MarshalType.RID => source.Append(inputExpr, ".AsRID()"), - MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"), - MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"), - MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<", - ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ", - ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"), - MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<", - ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"), - _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type") + // For generic Godot collections, Variant.As is slower, so we need this special case + MarshalType.GodotGenericDictionary => + source.Append(inputExpr, ".AsGodotDictionary<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ", + ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"), + MarshalType.GodotGenericArray => + source.Append(inputExpr, ".AsGodotArray<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"), + _ => source.Append(inputExpr, ".As<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">()") }; } public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source, - string inputExpr, MarshalType marshalType) + string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType) { - switch (marshalType) + return marshalType switch { - case MarshalType.Boolean: - case MarshalType.Char: - case MarshalType.SByte: - case MarshalType.Int16: - case MarshalType.Int32: - case MarshalType.Int64: - case MarshalType.Byte: - case MarshalType.UInt16: - case MarshalType.UInt32: - case MarshalType.UInt64: - case MarshalType.Single: - case MarshalType.Double: - case MarshalType.String: - case MarshalType.Vector2: - case MarshalType.Vector2i: - case MarshalType.Rect2: - case MarshalType.Rect2i: - case MarshalType.Transform2D: - case MarshalType.Vector3: - case MarshalType.Vector3i: - case MarshalType.Basis: - case MarshalType.Quaternion: - case MarshalType.Transform3D: - case MarshalType.Vector4: - case MarshalType.Vector4i: - case MarshalType.Projection: - case MarshalType.AABB: - case MarshalType.Color: - case MarshalType.Plane: - case MarshalType.Callable: - case MarshalType.SignalInfo: - case MarshalType.ByteArray: - case MarshalType.Int32Array: - case MarshalType.Int64Array: - case MarshalType.Float32Array: - case MarshalType.Float64Array: - case MarshalType.StringArray: - case MarshalType.Vector2Array: - case MarshalType.Vector3Array: - case MarshalType.ColorArray: - case MarshalType.GodotObjectOrDerivedArray: - case MarshalType.SystemArrayOfStringName: - case MarshalType.SystemArrayOfNodePath: - case MarshalType.SystemArrayOfRID: - case MarshalType.GodotObjectOrDerived: - case MarshalType.StringName: - case MarshalType.NodePath: - case MarshalType.RID: - case MarshalType.GodotDictionary: - case MarshalType.GodotArray: - case MarshalType.GodotGenericDictionary: - case MarshalType.GodotGenericArray: - return source.Append("Variant.CreateFrom(", inputExpr, ")"); - case MarshalType.Enum: - return source.Append("Variant.CreateFrom((long)", inputExpr, ")"); - case MarshalType.Variant: - return source.Append(inputExpr); - default: - throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type"); - } + // For generic Godot collections, Variant.From is slower, so we need this special case + MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray => + source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"), + _ => source.Append("global::Godot.Variant.From<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")") + }; } } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 2f51018293c..f79909589ef 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -135,7 +135,8 @@ namespace Godot.SourceGenerators source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - source.Append($" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n"); + source.Append( + $" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -297,7 +298,7 @@ namespace Godot.SourceGenerators if (method.RetType != null) { - returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty); + returnVal = DeterminePropertyInfo(method.RetType.Value.MarshalType, name: string.Empty); } else { @@ -391,7 +392,8 @@ namespace Godot.SourceGenerators { source.Append(" ret = "); - source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value); + source.AppendManagedToNativeVariantExpr("callRet", + method.RetType.Value.TypeSymbol, method.RetType.Value.MarshalType); source.Append(";\n"); source.Append(" return true;\n"); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index 252f162b0c8..6c4206a575d 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -124,7 +124,8 @@ namespace Godot.SourceGenerators source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - source.Append($" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n"); + source.Append( + $" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -199,14 +200,14 @@ namespace Godot.SourceGenerators foreach (var property in godotClassProperties) { GeneratePropertyGetter(property.PropertySymbol.Name, - property.Type, source, isFirstEntry); + property.PropertySymbol.Type, property.Type, source, isFirstEntry); isFirstEntry = false; } foreach (var field in godotClassFields) { GeneratePropertyGetter(field.FieldSymbol.Name, - field.Type, source, isFirstEntry); + field.FieldSymbol.Type, field.Type, source, isFirstEntry); isFirstEntry = false; } @@ -303,6 +304,7 @@ namespace Godot.SourceGenerators private static void GeneratePropertyGetter( string propertyMemberName, + ITypeSymbol propertyTypeSymbol, MarshalType propertyMarshalType, StringBuilder source, bool isFirstEntry @@ -317,7 +319,8 @@ namespace Godot.SourceGenerators .Append(propertyMemberName) .Append(") {\n") .Append(" value = ") - .AppendManagedToNativeVariantExpr("this." + propertyMemberName, propertyMarshalType) + .AppendManagedToNativeVariantExpr("this." + propertyMemberName, + propertyTypeSymbol, propertyMarshalType) .Append(";\n") .Append(" return true;\n") .Append(" }\n"); @@ -376,7 +379,8 @@ namespace Godot.SourceGenerators if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1) hintString = attr.ConstructorArguments[1].Value?.ToString(); - yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, propertyUsage.Value, true); + yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, + propertyUsage.Value, true); } } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 501717bf5c7..aa9dd9583e8 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -307,7 +307,8 @@ namespace Godot.SourceGenerators source.Append(" values.Add(PropertyName."); source.Append(exportedMember.Name); source.Append(", "); - source.AppendManagedToVariantExpr(defaultValueLocalName, exportedMember.Type); + source.AppendManagedToVariantExpr(defaultValueLocalName, + exportedMember.TypeSymbol, exportedMember.Type); source.Append(");\n"); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index ed877cbd178..821f3af75f0 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -162,7 +162,8 @@ namespace Godot.SourceGenerators source.Append(" info.AddProperty(PropertyName.") .Append(propertyName) .Append(", ") - .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type) + .AppendManagedToVariantExpr(string.Concat("this.", propertyName), + property.PropertySymbol.Type, property.Type) .Append(");\n"); } @@ -175,7 +176,8 @@ namespace Godot.SourceGenerators source.Append(" info.AddProperty(PropertyName.") .Append(fieldName) .Append(", ") - .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type) + .AppendManagedToVariantExpr(string.Concat("this.", fieldName), + field.FieldSymbol.Type, field.Type) .Append(");\n"); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index 119cc9d4f0d..ba6c10aa312 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -176,7 +176,8 @@ namespace Godot.SourceGenerators source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - source.Append($" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n"); + source.Append( + $" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -236,7 +237,8 @@ namespace Godot.SourceGenerators .Append(signalName) .Append(";\n"); - source.Append($" /// \n"); + source.Append( + $" /// \n"); source.Append(" public event ") .Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()) @@ -351,7 +353,7 @@ namespace Godot.SourceGenerators if (invokeMethodData.RetType != null) { - returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty); + returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value.MarshalType, name: string.Empty); } else { diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 91855067765..9f0bc3fbe3c 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2837,9 +2837,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted); itype.memory_own = itype.is_ref_counted; - itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToGodotObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromGodotObject(%0)"; - itype.c_out = "%5return "; itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n"; @@ -3218,8 +3215,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() { enum_itype.cname = StringName(enum_itype.name); enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name; TypeInterface::postsetup_enum_type(enum_itype); - enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)"; - enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)"; enum_types.insert(enum_itype.cname, enum_itype); } @@ -3448,16 +3443,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { TypeInterface itype; -#define INSERT_STRUCT_TYPE(m_type) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_type_in = #m_type "*"; \ - itype.c_type_out = itype.cs_type; \ - itype.cs_in_expr = "&%0"; \ - itype.cs_in_expr_is_unsafe = true; \ - itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \ - itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \ - builtin_types.insert(itype.cname, itype); \ +#define INSERT_STRUCT_TYPE(m_type) \ + { \ + itype = TypeInterface::create_value_type(String(#m_type)); \ + itype.c_type_in = #m_type "*"; \ + itype.c_type_out = itype.cs_type; \ + itype.cs_in_expr = "&%0"; \ + itype.cs_in_expr_is_unsafe = true; \ + builtin_types.insert(itype.cname, itype); \ } INSERT_STRUCT_TYPE(Vector2) @@ -3488,8 +3481,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type; itype.c_arg_in = "&%s"; itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToBool(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromBool(%0)"; builtin_types.insert(itype.cname, itype); // Integer types @@ -3510,8 +3501,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = itype.name; \ itype.c_type_out = itype.name; \ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \ - itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)"; \ - itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)"; \ builtin_types.insert(itype.cname, itype); \ } @@ -3547,8 +3536,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = itype.proxy_name; itype.c_type_out = itype.proxy_name; itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat32(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)"; builtin_types.insert(itype.cname, itype); // double @@ -3562,8 +3549,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = itype.proxy_name; itype.c_type_out = itype.proxy_name; itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat64(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)"; builtin_types.insert(itype.cname, itype); } @@ -3581,8 +3566,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = true; itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToStringObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromString(%0)"; builtin_types.insert(itype.cname, itype); // StringName @@ -3601,8 +3584,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n"; itype.c_type_is_disposable_struct = false; // [c_out] takes ownership itype.c_ret_needs_default_initialization = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToStringNameObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromStringName(%0)"; builtin_types.insert(itype.cname, itype); // NodePath @@ -3620,8 +3601,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = false; // [c_out] takes ownership itype.c_ret_needs_default_initialization = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToNodePathObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromNodePath(%0)"; builtin_types.insert(itype.cname, itype); // RID @@ -3634,8 +3613,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type = itype.cs_type; itype.c_type_in = itype.c_type; itype.c_type_out = itype.c_type; - itype.cs_variant_to_managed = "VariantUtils.ConvertToRID(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromRID(%0)"; builtin_types.insert(itype.cname, itype); // Variant @@ -3652,8 +3629,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.cs_type; 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 @@ -3666,8 +3641,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = "in " + itype.cs_type; itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToCallableManaged(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromCallable(%0)"; builtin_types.insert(itype.cname, itype); // Signal @@ -3684,8 +3657,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = "in " + itype.cs_type; itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToSignalInfo(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromSignalInfo(%0)"; builtin_types.insert(itype.cname, itype); // VarArg (fictitious type to represent variable arguments) @@ -3715,8 +3686,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = itype.proxy_name; \ itype.c_type_out = itype.proxy_name; \ itype.c_type_is_disposable_struct = true; \ - itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)"; \ - itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \ builtin_types.insert(itype.name, itype); \ } @@ -3752,8 +3721,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = false; // [c_out] takes ownership itype.c_ret_needs_default_initialization = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)"; builtin_types.insert(itype.cname, itype); // Array_@generic @@ -3761,6 +3728,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Array_@generic"; itype.cname = itype.name; itype.cs_out = "%5return new %2(%0(%1));"; + // For generic Godot collections, Variant.From/As is slower, so we need this special case + itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)"; + itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)"; builtin_types.insert(itype.cname, itype); // Dictionary @@ -3778,8 +3748,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = false; // [c_out] takes ownership itype.c_ret_needs_default_initialization = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)"; builtin_types.insert(itype.cname, itype); // Dictionary_@generic @@ -3787,6 +3755,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Dictionary_@generic"; itype.cname = itype.name; itype.cs_out = "%5return new %2(%0(%1));"; + // For generic Godot collections, Variant.From/As is slower, so we need this special case + itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)"; + itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) @@ -3852,8 +3823,6 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.cname = ienum.cname; enum_itype.proxy_name = enum_itype.name; TypeInterface::postsetup_enum_type(enum_itype); - enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)"; - enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)"; enum_types.insert(enum_itype.cname, enum_itype); int prefix_length = _determine_enum_prefix(ienum); @@ -3886,8 +3855,6 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.cname = enum_cname; enum_itype.proxy_name = enum_itype.name; TypeInterface::postsetup_enum_type(enum_itype); - enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)"; - enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)"; enum_types.insert(enum_itype.cname, enum_itype); } } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index a479c443689..6d172f4fb89 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -209,7 +209,7 @@ class BindingsGenerator { String name; StringName cname; - int type_parameter_count; + int type_parameter_count = 0; /** * Identifier name of the base class. @@ -514,7 +514,12 @@ class BindingsGenerator { static void postsetup_enum_type(TypeInterface &r_enum_itype); - TypeInterface() {} + TypeInterface() { + static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)"; + static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)"; + cs_variant_to_managed = default_cs_variant_to_managed; + cs_managed_to_variant = default_cs_managed_to_variant; + } }; struct InternalCall {