summaryrefslogtreecommitdiff
path: root/modules/mono/glue/GodotSharp
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <remi@verschelde.fr>2022-08-22 10:07:21 +0200
committerGitHub <noreply@github.com>2022-08-22 10:07:21 +0200
commit8a1e5980116355024cd7a7ce0c15db7d4ecb200a (patch)
treefc40366fb5ad00f6b01e0b796ef85495433bd26f /modules/mono/glue/GodotSharp
parent847183093d2948601cdd82f50a333bdee2d6eef0 (diff)
parent9c34a02191bc6c560cc2a1bceea943f9bd8aebfa (diff)
Merge pull request #64089 from neikeq/dotnet6
Diffstat (limited to 'modules/mono/glue/GodotSharp')
-rw-r--r--modules/mono/glue/GodotSharp/.editorconfig8
-rw-r--r--modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml5
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs65
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs119
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs6
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj11
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs463
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj17
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/Main.cs270
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs64
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs666
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs13
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs253
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs87
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs89
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs1028
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs92
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs66
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs98
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs107
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs243
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs677
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs95
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs220
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs190
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs30
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs154
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs313
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs139
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs1091
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs96
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs1092
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs522
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs99
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs976
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs33
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs611
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs146
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs164
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs135
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs85
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs73
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs92
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs116
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj69
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Variant.cs841
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings4
87 files changed, 10734 insertions, 1615 deletions
diff --git a/modules/mono/glue/GodotSharp/.editorconfig b/modules/mono/glue/GodotSharp/.editorconfig
new file mode 100644
index 0000000000..d4e71b1bd9
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/.editorconfig
@@ -0,0 +1,8 @@
+[**/Generated/**.cs]
+# Validate parameter is non-null before using it
+# Useful for generated code, as it disables nullable
+dotnet_diagnostic.CA1062.severity = error
+# CA1069: Enums should not have duplicate values
+dotnet_diagnostic.CA1069.severity = none
+# CA1708: Identifiers should differ by more than case
+dotnet_diagnostic.CA1708.severity = none
diff --git a/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml b/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml
new file mode 100644
index 0000000000..2dc350d4f2
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml
@@ -0,0 +1,5 @@
+<assembly name="System.Runtime.InteropServices">
+ <member name="T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute">
+ <attribute ctor="M:JetBrains.Annotations.MeansImplicitUseAttribute.#ctor" />
+ </member>
+</assembly>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs
new file mode 100644
index 0000000000..686023a077
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs
@@ -0,0 +1,24 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal struct CallbacksData
+{
+ public CallbacksData(INamedTypeSymbol nativeTypeSymbol, INamedTypeSymbol funcStructSymbol)
+ {
+ NativeTypeSymbol = nativeTypeSymbol;
+ FuncStructSymbol = funcStructSymbol;
+ Methods = NativeTypeSymbol.GetMembers()
+ .Where(symbol => symbol is IMethodSymbol { IsPartialDefinition: true })
+ .Cast<IMethodSymbol>()
+ .ToImmutableArray();
+ }
+
+ public INamedTypeSymbol NativeTypeSymbol { get; }
+
+ public INamedTypeSymbol FuncStructSymbol { get; }
+
+ public ImmutableArray<IMethodSymbol> Methods { get; }
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs
new file mode 100644
index 0000000000..16e96c725a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs
@@ -0,0 +1,65 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal static class Common
+{
+ public static void ReportNonPartialUnmanagedCallbacksClass(
+ GeneratorExecutionContext context,
+ ClassDeclarationSyntax cds, INamedTypeSymbol symbol
+ )
+ {
+ string message =
+ "Missing partial modifier on declaration of type '" +
+ $"{symbol.FullQualifiedName()}' which has attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
+
+ string description = $"{message}. Classes with attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' " +
+ "must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0001",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ cds.GetLocation(),
+ cds.SyntaxTree.FilePath));
+ }
+
+ public static void ReportNonPartialUnmanagedCallbacksOuterClass(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax outerTypeDeclSyntax
+ )
+ {
+ var outerSymbol = context.Compilation
+ .GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
+ .GetDeclaredSymbol(outerTypeDeclSyntax);
+
+ string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
+ namedTypeSymbol.FullQualifiedName() :
+ "type not found";
+
+ string message =
+ $"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
+ $"which contains one or more subclasses with attribute " +
+ $"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
+
+ string description = $"{message}. Classes with attribute " +
+ $"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' and their " +
+ "containing types must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0002",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ outerTypeDeclSyntax.GetLocation(),
+ outerTypeDeclSyntax.SyntaxTree.FilePath));
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
new file mode 100644
index 0000000000..fac362479a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
@@ -0,0 +1,119 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal static class ExtensionMethods
+{
+ public static AttributeData? GetGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
+ => symbol.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false);
+
+ private static bool HasGenerateUnmanagedCallbacksAttribute(
+ this ClassDeclarationSyntax cds, Compilation compilation,
+ out INamedTypeSymbol? symbol
+ )
+ {
+ var sm = compilation.GetSemanticModel(cds.SyntaxTree);
+
+ var classTypeSymbol = sm.GetDeclaredSymbol(cds);
+ if (classTypeSymbol == null)
+ {
+ symbol = null;
+ return false;
+ }
+
+ if (!classTypeSymbol.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false))
+ {
+ symbol = null;
+ return false;
+ }
+
+ symbol = classTypeSymbol;
+ return true;
+ }
+
+ private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
+
+ public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
+ this IEnumerable<ClassDeclarationSyntax> source,
+ Compilation compilation
+ )
+ {
+ foreach (var cds in source)
+ {
+ if (cds.HasGenerateUnmanagedCallbacksAttribute(compilation, out var symbol))
+ yield return (cds, symbol!);
+ }
+ }
+
+ public static bool IsNested(this TypeDeclarationSyntax cds)
+ => cds.Parent is TypeDeclarationSyntax;
+
+ public static bool IsPartial(this TypeDeclarationSyntax cds)
+ => cds.Modifiers.Any(SyntaxKind.PartialKeyword);
+
+ public static bool AreAllOuterTypesPartial(
+ this TypeDeclarationSyntax cds,
+ out TypeDeclarationSyntax? typeMissingPartial
+ )
+ {
+ SyntaxNode? outerSyntaxNode = cds.Parent;
+
+ while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax)
+ {
+ if (!outerTypeDeclSyntax.IsPartial())
+ {
+ typeMissingPartial = outerTypeDeclSyntax;
+ return false;
+ }
+
+ outerSyntaxNode = outerSyntaxNode.Parent;
+ }
+
+ typeMissingPartial = null;
+ return true;
+ }
+
+ public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ string? keyword = namedTypeSymbol.DeclaringSyntaxReferences
+ .OfType<TypeDeclarationSyntax>().FirstOrDefault()?
+ .Keyword.Text;
+
+ return keyword ?? namedTypeSymbol.TypeKind switch
+ {
+ TypeKind.Interface => "interface",
+ TypeKind.Struct => "struct",
+ _ => "class"
+ };
+ }
+
+ private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
+ SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+ public static string FullQualifiedName(this ITypeSymbol symbol)
+ => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
+
+ public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
+ {
+ return symbol.IsGenericType ?
+ string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
+ symbol.Name;
+ }
+
+ public static string FullQualifiedName(this INamespaceSymbol symbol)
+ => symbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
+
+ public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
+ => qualifiedName
+ // AddSource() doesn't support angle brackets
+ .Replace("<", "(Of ")
+ .Replace(">", ")");
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs
new file mode 100644
index 0000000000..1bbb33f5a1
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs
@@ -0,0 +1,6 @@
+namespace Godot.SourceGenerators.Internal;
+
+internal static class GeneratorClasses
+{
+ public const string GenerateUnmanagedCallbacksAttr = "Godot.SourceGenerators.Internal.GenerateUnmanagedCallbacksAttribute";
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
new file mode 100644
index 0000000000..4d1a5bb76c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
new file mode 100644
index 0000000000..da578309bc
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
@@ -0,0 +1,463 @@
+using System.Text;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+[Generator]
+public class UnmanagedCallbacksGenerator : ISourceGenerator
+{
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForPostInitialization(ctx => { GenerateAttribute(ctx); });
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ INamedTypeSymbol[] unmanagedCallbacksClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectUnmanagedCallbacksClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialUnmanagedCallbacksOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialUnmanagedCallbacksClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ foreach (var symbol in unmanagedCallbacksClasses)
+ {
+ var attr = symbol.GetGenerateUnmanagedCallbacksAttribute();
+ if (attr == null || attr.ConstructorArguments.Length != 1)
+ {
+ // TODO: Report error or throw exception, this is an invalid case and should never be reached
+ System.Diagnostics.Debug.Fail("FAILED!");
+ continue;
+ }
+
+ var funcStructType = (INamedTypeSymbol?)attr.ConstructorArguments[0].Value;
+ if (funcStructType == null)
+ {
+ // TODO: Report error or throw exception, this is an invalid case and should never be reached
+ System.Diagnostics.Debug.Fail("FAILED!");
+ continue;
+ }
+
+ var data = new CallbacksData(symbol, funcStructType);
+ GenerateInteropMethodImplementations(context, data);
+ GenerateUnmanagedCallbacksStruct(context, data);
+ }
+ }
+
+ private void GenerateAttribute(GeneratorPostInitializationContext context)
+ {
+ string source = @"using System;
+
+namespace Godot.SourceGenerators.Internal
+{
+internal class GenerateUnmanagedCallbacksAttribute : Attribute
+{
+ public Type FuncStructType { get; }
+
+ public GenerateUnmanagedCallbacksAttribute(Type funcStructType)
+ {
+ FuncStructType = funcStructType;
+ }
+}
+}";
+
+ context.AddSource("GenerateUnmanagedCallbacksAttribute.generated",
+ SourceText.From(source, Encoding.UTF8));
+ }
+
+ private void GenerateInteropMethodImplementations(GeneratorExecutionContext context, CallbacksData data)
+ {
+ var symbol = data.NativeTypeSymbol;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+ bool isInnerClass = symbol.ContainingType != null;
+
+ var source = new StringBuilder();
+ var methodSource = new StringBuilder();
+ var methodCallArguments = new StringBuilder();
+ var methodSourceAfterCall = new StringBuilder();
+
+ source.Append(
+ @"using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
+
+");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append("\n{\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ source.Append($"unsafe partial class {symbol.Name}\n");
+ source.Append("{\n");
+ source.Append($" private static {data.FuncStructSymbol.FullQualifiedName()} _unmanagedCallbacks;\n\n");
+
+ foreach (var callback in data.Methods)
+ {
+ methodSource.Clear();
+ methodCallArguments.Clear();
+ methodSourceAfterCall.Clear();
+
+ source.Append(" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n");
+ source.Append($" {SyntaxFacts.GetText(callback.DeclaredAccessibility)} ");
+
+ if (callback.IsStatic)
+ source.Append("static ");
+
+ source.Append("partial ");
+ source.Append(callback.ReturnType.FullQualifiedName());
+ source.Append(' ');
+ source.Append(callback.Name);
+ source.Append('(');
+
+ for (int i = 0; i < callback.Parameters.Length; i++)
+ {
+ var parameter = callback.Parameters[i];
+
+ source.Append(parameter.ToDisplayString());
+ source.Append(' ');
+ source.Append(parameter.Name);
+
+ if (parameter.RefKind == RefKind.Out)
+ {
+ // Only assign default if the parameter won't be passed by-ref or copied later.
+ if (IsGodotInteropStruct(parameter.Type))
+ methodSource.Append($" {parameter.Name} = default;\n");
+ }
+
+ if (IsByRefParameter(parameter))
+ {
+ if (IsGodotInteropStruct(parameter.Type))
+ {
+ methodSource.Append(" ");
+ AppendCustomUnsafeAsPointer(methodSource, parameter, out string varName);
+ methodCallArguments.Append(varName);
+ }
+ else if (parameter.Type.IsValueType)
+ {
+ methodSource.Append(" ");
+ AppendCopyToStackAndGetPointer(methodSource, parameter, out string varName);
+ methodCallArguments.Append($"&{varName}");
+
+ if (parameter.RefKind is RefKind.Out or RefKind.Ref)
+ {
+ methodSourceAfterCall.Append($" {parameter.Name} = {varName};\n");
+ }
+ }
+ else
+ {
+ // If it's a by-ref param and we can't get the pointer
+ // just pass it by-ref and let it be pinned.
+ AppendRefKind(methodCallArguments, parameter.RefKind)
+ .Append(' ')
+ .Append(parameter.Name);
+ }
+ }
+ else
+ {
+ methodCallArguments.Append(parameter.Name);
+ }
+
+ if (i < callback.Parameters.Length - 1)
+ {
+ source.Append(", ");
+ methodCallArguments.Append(", ");
+ }
+ }
+
+ source.Append(")\n");
+ source.Append(" {\n");
+
+ source.Append(methodSource);
+ source.Append(" ");
+
+ if (!callback.ReturnsVoid)
+ {
+ if (methodSourceAfterCall.Length != 0)
+ source.Append($"{callback.ReturnType.FullQualifiedName()} ret = ");
+ else
+ source.Append("return ");
+ }
+
+ source.Append($"_unmanagedCallbacks.{callback.Name}(");
+ source.Append(methodCallArguments);
+ source.Append(");\n");
+
+ if (methodSourceAfterCall.Length != 0)
+ {
+ source.Append(methodSourceAfterCall);
+
+ if (!callback.ReturnsVoid)
+ source.Append(" return ret;\n");
+ }
+
+ source.Append(" }\n\n");
+ }
+
+ source.Append("}\n");
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ source.Append("\n}");
+
+ source.Append("\n\n#pragma warning restore CA1707\n");
+
+ context.AddSource($"{data.NativeTypeSymbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()}.generated",
+ SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private void GenerateUnmanagedCallbacksStruct(GeneratorExecutionContext context, CallbacksData data)
+ {
+ var symbol = data.FuncStructSymbol;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+ bool isInnerClass = symbol.ContainingType != null;
+
+ var source = new StringBuilder();
+
+ source.Append(
+ @"using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
+
+");
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append("\n{\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("[StructLayout(LayoutKind.Sequential)]\n");
+ source.Append($"unsafe partial struct {symbol.Name}\n{{\n");
+
+ foreach (var callback in data.Methods)
+ {
+ source.Append(" ");
+ source.Append(callback.DeclaredAccessibility == Accessibility.Public ? "public " : "internal ");
+
+ source.Append("delegate* unmanaged<");
+
+ foreach (var parameter in callback.Parameters)
+ {
+ if (IsByRefParameter(parameter))
+ {
+ if (IsGodotInteropStruct(parameter.Type) || parameter.Type.IsValueType)
+ {
+ AppendPointerType(source, parameter.Type);
+ }
+ else
+ {
+ // If it's a by-ref param and we can't get the pointer
+ // just pass it by-ref and let it be pinned.
+ AppendRefKind(source, parameter.RefKind)
+ .Append(' ')
+ .Append(parameter.Type.FullQualifiedName());
+ }
+ }
+ else
+ {
+ source.Append(parameter.Type.FullQualifiedName());
+ }
+
+ source.Append(", ");
+ }
+
+ source.Append(callback.ReturnType.FullQualifiedName());
+ source.Append($"> {callback.Name};\n");
+ }
+
+ source.Append("}\n");
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ source.Append("}\n");
+
+ source.Append("\n#pragma warning restore CA1707\n");
+
+ context.AddSource($"{symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()}.generated",
+ SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static bool IsGodotInteropStruct(ITypeSymbol type) =>
+ GodotInteropStructs.Contains(type.FullQualifiedName());
+
+ private static bool IsByRefParameter(IParameterSymbol parameter) =>
+ parameter.RefKind is RefKind.In or RefKind.Out or RefKind.Ref;
+
+ private static StringBuilder AppendRefKind(StringBuilder source, RefKind refKind) =>
+ refKind switch
+ {
+ RefKind.In => source.Append("in"),
+ RefKind.Out => source.Append("out"),
+ RefKind.Ref => source.Append("ref"),
+ _ => source,
+ };
+
+ private static void AppendPointerType(StringBuilder source, ITypeSymbol type)
+ {
+ source.Append(type.FullQualifiedName());
+ source.Append('*');
+ }
+
+ private static void AppendCustomUnsafeAsPointer(StringBuilder source, IParameterSymbol parameter,
+ out string varName)
+ {
+ varName = $"{parameter.Name}_ptr";
+
+ AppendPointerType(source, parameter.Type);
+ source.Append(' ');
+ source.Append(varName);
+ source.Append(" = ");
+
+ source.Append('(');
+ AppendPointerType(source, parameter.Type);
+ source.Append(')');
+
+ if (parameter.RefKind == RefKind.In)
+ source.Append("CustomUnsafe.ReadOnlyRefAsPointer(in ");
+ else
+ source.Append("CustomUnsafe.AsPointer(ref ");
+
+ source.Append(parameter.Name);
+
+ source.Append(");\n");
+ }
+
+ private static void AppendCopyToStackAndGetPointer(StringBuilder source, IParameterSymbol parameter,
+ out string varName)
+ {
+ varName = $"{parameter.Name}_copy";
+
+ source.Append(parameter.Type.FullQualifiedName());
+ source.Append(' ');
+ source.Append(varName);
+ if (parameter.RefKind is RefKind.In or RefKind.Ref)
+ {
+ source.Append(" = ");
+ source.Append(parameter.Name);
+ }
+
+ source.Append(";\n");
+ }
+
+ private static readonly string[] GodotInteropStructs =
+ {
+ "Godot.NativeInterop.godot_ref",
+ "Godot.NativeInterop.godot_variant_call_error",
+ "Godot.NativeInterop.godot_variant",
+ "Godot.NativeInterop.godot_string",
+ "Godot.NativeInterop.godot_string_name",
+ "Godot.NativeInterop.godot_node_path",
+ "Godot.NativeInterop.godot_signal",
+ "Godot.NativeInterop.godot_callable",
+ "Godot.NativeInterop.godot_array",
+ "Godot.NativeInterop.godot_dictionary",
+ "Godot.NativeInterop.godot_packed_byte_array",
+ "Godot.NativeInterop.godot_packed_int32_array",
+ "Godot.NativeInterop.godot_packed_int64_array",
+ "Godot.NativeInterop.godot_packed_float32_array",
+ "Godot.NativeInterop.godot_packed_float64_array",
+ "Godot.NativeInterop.godot_packed_string_array",
+ "Godot.NativeInterop.godot_packed_vector2_array",
+ "Godot.NativeInterop.godot_packed_vector3_array",
+ "Godot.NativeInterop.godot_packed_color_array",
+ };
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
new file mode 100644
index 0000000000..e720d3878c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+
+ <!-- To generate the .runtimeconfig.json file-->
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
new file mode 100644
index 0000000000..dad7464410
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+namespace GodotPlugins
+{
+ public static class Main
+ {
+ // IMPORTANT:
+ // Keeping strong references to the AssemblyLoadContext (our PluginLoadContext) prevents
+ // it from being unloaded. To avoid issues, we wrap the reference in this class, and mark
+ // all the methods that access it as non-inlineable. This way we prevent local references
+ // (either real or introduced by the JIT) to escape the scope of these methods due to
+ // inlining, which could keep the AssemblyLoadContext alive while trying to unload.
+ private sealed class PluginLoadContextWrapper
+ {
+ private PluginLoadContext? _pluginLoadContext;
+
+ public string? AssemblyLoadedPath
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get => _pluginLoadContext?.AssemblyLoadedPath;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static (Assembly, PluginLoadContextWrapper) CreateAndLoadFromAssemblyName(
+ AssemblyName assemblyName,
+ string pluginPath,
+ ICollection<string> sharedAssemblies,
+ AssemblyLoadContext mainLoadContext
+ )
+ {
+ var wrapper = new PluginLoadContextWrapper();
+ wrapper._pluginLoadContext = new PluginLoadContext(
+ pluginPath, sharedAssemblies, mainLoadContext);
+ var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
+ return (assembly, wrapper);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public WeakReference CreateWeakReference()
+ {
+ return new WeakReference(_pluginLoadContext, trackResurrection: true);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal void Unload()
+ {
+ _pluginLoadContext?.Unload();
+ _pluginLoadContext = null;
+ }
+ }
+
+ private static readonly List<AssemblyName> SharedAssemblies = new();
+ private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
+ private static Assembly? _editorApiAssembly;
+ private static PluginLoadContextWrapper? _projectLoadContext;
+
+ private static readonly AssemblyLoadContext MainLoadContext =
+ AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
+ AssemblyLoadContext.Default;
+
+ private static DllImportResolver? _dllImportResolver;
+
+ // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
+ [UnmanagedCallersOnly]
+ // ReSharper disable once UnusedMember.Local
+ private static unsafe godot_bool InitializeFromEngine(IntPtr godotDllHandle, godot_bool editorHint,
+ PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ _dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
+
+ SharedAssemblies.Add(CoreApiAssembly.GetName());
+ NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
+
+ AlcReloadCfg.Configure(alcReloadEnabled: editorHint.ToBool());
+ NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+
+ if (editorHint.ToBool())
+ {
+ _editorApiAssembly = Assembly.Load("GodotSharpEditor");
+ SharedAssemblies.Add(_editorApiAssembly.GetName());
+ NativeLibrary.SetDllImportResolver(_editorApiAssembly, _dllImportResolver);
+ }
+
+ *pluginsCallbacks = new()
+ {
+ LoadProjectAssemblyCallback = &LoadProjectAssembly,
+ LoadToolsAssemblyCallback = &LoadToolsAssembly,
+ UnloadProjectPluginCallback = &UnloadProjectPlugin,
+ };
+
+ *managedCallbacks = ManagedCallbacks.Create();
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct PluginsCallbacks
+ {
+ public unsafe delegate* unmanaged<char*, godot_string*, godot_bool> LoadProjectAssemblyCallback;
+ public unsafe delegate* unmanaged<char*, IntPtr, int, IntPtr> LoadToolsAssemblyCallback;
+ public unsafe delegate* unmanaged<godot_bool> UnloadProjectPluginCallback;
+ }
+
+ [UnmanagedCallersOnly]
+ private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath, godot_string* outLoadedAssemblyPath)
+ {
+ try
+ {
+ if (_projectLoadContext != null)
+ return godot_bool.True; // Already loaded
+
+ string assemblyPath = new(nAssemblyPath);
+
+ (var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath);
+
+ string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath;
+ *outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath);
+
+ ScriptManagerBridge.LookupScriptsInAssembly(projectAssembly);
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ string assemblyPath = new(nAssemblyPath);
+
+ if (_editorApiAssembly == null)
+ throw new InvalidOperationException("The Godot editor API assembly is not loaded");
+
+ var (assembly, _) = LoadPlugin(assemblyPath);
+
+ NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
+
+ var method = assembly.GetType("GodotTools.GodotSharpEditor")?
+ .GetMethod("InternalCreateInstance",
+ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (method == null)
+ {
+ throw new MissingMethodException("GodotTools.GodotSharpEditor",
+ "InternalCreateInstance");
+ }
+
+ return (IntPtr?)method
+ .Invoke(null, new object[] { unmanagedCallbacks, unmanagedCallbacksSize })
+ ?? IntPtr.Zero;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return IntPtr.Zero;
+ }
+ }
+
+ private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath)
+ {
+ string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
+
+ var sharedAssemblies = new List<string>();
+
+ foreach (var sharedAssembly in SharedAssemblies)
+ {
+ string? sharedAssemblyName = sharedAssembly.Name;
+ if (sharedAssemblyName != null)
+ sharedAssemblies.Add(sharedAssemblyName);
+ }
+
+ return PluginLoadContextWrapper.CreateAndLoadFromAssemblyName(
+ new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext);
+ }
+
+ [UnmanagedCallersOnly]
+ private static godot_bool UnloadProjectPlugin()
+ {
+ try
+ {
+ return UnloadPlugin(ref _projectLoadContext).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ private static bool UnloadPlugin(ref PluginLoadContextWrapper? pluginLoadContext)
+ {
+ try
+ {
+ if (pluginLoadContext == null)
+ return true;
+
+ Console.WriteLine("Unloading assembly load context...");
+
+ var alcWeakReference = pluginLoadContext.CreateWeakReference();
+
+ pluginLoadContext.Unload();
+ pluginLoadContext = null;
+
+ int startTimeMs = Environment.TickCount;
+ bool takingTooLong = false;
+
+ while (alcWeakReference.IsAlive)
+ {
+ GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
+ GC.WaitForPendingFinalizers();
+
+ if (!alcWeakReference.IsAlive)
+ break;
+
+ int elapsedTimeMs = Environment.TickCount - startTimeMs;
+
+ if (!takingTooLong && elapsedTimeMs >= 2000)
+ {
+ takingTooLong = true;
+
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine("Assembly unloading is taking longer than expected...");
+ }
+ else if (elapsedTimeMs >= 5000)
+ {
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine(
+ "Failed to unload assemblies. Possible causes: Strong GC handles, running threads, etc.");
+
+ return false;
+ }
+ }
+
+ Console.WriteLine("Assembly load context unloaded successfully.");
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ // TODO: How to log exceptions from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine(e);
+ return false;
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
new file mode 100644
index 0000000000..dcd572c65e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace GodotPlugins
+{
+ public class PluginLoadContext : AssemblyLoadContext
+ {
+ private readonly AssemblyDependencyResolver _resolver;
+ private readonly ICollection<string> _sharedAssemblies;
+ private readonly AssemblyLoadContext _mainLoadContext;
+
+ public string? AssemblyLoadedPath { get; private set; }
+
+ public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
+ AssemblyLoadContext mainLoadContext)
+ : base(isCollectible: true)
+ {
+ _resolver = new AssemblyDependencyResolver(pluginPath);
+ _sharedAssemblies = sharedAssemblies;
+ _mainLoadContext = mainLoadContext;
+ }
+
+ protected override Assembly? Load(AssemblyName assemblyName)
+ {
+ if (assemblyName.Name == null)
+ return null;
+
+ if (_sharedAssemblies.Contains(assemblyName.Name))
+ return _mainLoadContext.LoadFromAssemblyName(assemblyName);
+
+ string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ AssemblyLoadedPath = assemblyPath;
+
+ // Load in memory to prevent locking the file
+ using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb");
+
+ if (File.Exists(pdbPath))
+ {
+ using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ return LoadFromStream(assemblyFile, pdbFile);
+ }
+
+ return LoadFromStream(assemblyFile);
+ }
+
+ return null;
+ }
+
+ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
+ if (libraryPath != null)
+ return LoadUnmanagedDllFromPath(libraryPath);
+
+ return IntPtr.Zero;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln
index 4896d0a07d..8db42c2d1a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp.sln
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln
@@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\Go
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotPlugins", "GodotPlugins\GodotPlugins.csproj", "{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{7749662B-E30C-419A-B745-13852573360A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +22,13 @@ Global
{8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
new file mode 100644
index 0000000000..ba65b61e95
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
@@ -0,0 +1,8 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GC/@EntryIndexedValue">GC</s:String>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=alcs/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=gdnative/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=godotsharp/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=icall/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=quat/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=vcall/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 850ae7fc3b..f87f37bc43 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index a412047196..81991c6626 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -1,47 +1,35 @@
using System;
using System.Collections.Generic;
using System.Collections;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot.Collections
{
- internal class ArraySafeHandle : SafeHandle
- {
- public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
- {
- this.handle = handle;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- protected override bool ReleaseHandle()
- {
- Array.godot_icall_Array_Dtor(handle);
- return true;
- }
- }
-
/// <summary>
/// Wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
/// </summary>
- public class Array : IList, IDisposable
+ public sealed class Array :
+ IList<Variant>,
+ IReadOnlyList<Variant>,
+ ICollection,
+ IDisposable
{
- private ArraySafeHandle _safeHandle;
- private bool _disposed = false;
+ internal godot_array.movable NativeValue;
+
+ private WeakReference<IDisposable> _weakReferenceToSelf;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
- _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
/// <summary>
@@ -49,12 +37,12 @@ namespace Godot.Collections
/// </summary>
/// <param name="collection">The collection of elements to construct from.</param>
/// <returns>A new Godot Array.</returns>
- public Array(IEnumerable collection) : this()
+ public Array(IEnumerable<Variant> collection) : this()
{
if (collection == null)
- throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+ throw new ArgumentNullException(nameof(collection));
- foreach (object element in collection)
+ foreach (Variant element in collection)
Add(element);
}
@@ -63,31 +51,126 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The objects to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
- public Array(params object[] array) : this()
+ public Array(Variant[] array) : this()
{
if (array == null)
- {
- throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
- }
- _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
}
- internal Array(ArraySafeHandle handle)
+ public Array(Span<StringName> array) : this()
{
- _safeHandle = handle;
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
}
- internal Array(IntPtr handle)
+ public Array(Span<NodePath> array) : this()
{
- _safeHandle = new ArraySafeHandle(handle);
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ public Array(Span<RID> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ // We must use ReadOnlySpan instead of Span here as this can accept implicit conversions
+ // from derived types (e.g.: Node[]). Implicit conversion from Derived[] to Base[] are
+ // fine as long as the array is not mutated. However, Span does this type checking at
+ // instantiation, so it's not possible to use it even when not mutating anything.
+ // ReSharper disable once RedundantNameQualifier
+ public Array(ReadOnlySpan<Godot.Object> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
}
- internal IntPtr GetPtr()
+ private Array(godot_array nativeValueToOwn)
{
- if (_disposed)
- throw new ObjectDisposedException(GetType().FullName);
+ NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ?
+ nativeValueToOwn :
+ NativeFuncs.godotsharp_array_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
+
+ // Explicit name to make it very clear
+ internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
+ => new Array(nativeValueToOwn);
- return _safeHandle.DangerousGetHandle();
+ ~Array()
+ {
+ Dispose(false);
+ }
+
+ /// <summary>
+ /// Disposes of this <see cref="Array"/>.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public void Dispose(bool disposing)
+ {
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
+ {
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+ }
}
/// <summary>
@@ -97,7 +180,10 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array Duplicate(bool deep = false)
{
- return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
+ godot_array newArray;
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_duplicate(ref self, deep.ToGodotBool(), out newArray);
+ return CreateTakingOwnershipOfDisposableValue(newArray);
}
/// <summary>
@@ -107,7 +193,8 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return godot_icall_Array_Resize(GetPtr(), newSize);
+ var self = (godot_array)NativeValue;
+ return NativeFuncs.godotsharp_array_resize(ref self, newSize);
}
/// <summary>
@@ -115,7 +202,8 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- godot_icall_Array_Shuffle(GetPtr());
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_shuffle(ref self);
}
/// <summary>
@@ -126,94 +214,136 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array operator +(Array left, Array right)
{
- return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
- }
-
- // IDisposable
-
- /// <summary>
- /// Disposes of this <see cref="Array"/>.
- /// </summary>
- public void Dispose()
- {
- if (_disposed)
- return;
-
- if (_safeHandle != null)
+ if (left == null)
{
- _safeHandle.Dispose();
- _safeHandle = null;
+ if (right == null)
+ return new Array();
+
+ return right.Duplicate(deep: false);
}
- _disposed = true;
- }
+ if (right == null)
+ return left.Duplicate(deep: false);
+
+ int leftCount = left.Count;
+ int rightCount = right.Count;
- // IList
+ Array newArray = left.Duplicate(deep: false);
+ newArray.Resize(leftCount + rightCount);
- bool IList.IsReadOnly => false;
+ for (int i = 0; i < rightCount; i++)
+ newArray[i + leftCount] = right[i];
- bool IList.IsFixedSize => false;
+ return newArray;
+ }
/// <summary>
- /// Returns the object at the given <paramref name="index"/>.
+ /// Returns the item at the given <paramref name="index"/>.
/// </summary>
- /// <value>The object at the given <paramref name="index"/>.</value>
- public object this[int index]
+ /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value>
+ public unsafe Variant this[int index]
{
- get => godot_icall_Array_At(GetPtr(), index);
- set => godot_icall_Array_SetAt(GetPtr(), index, value);
+ get
+ {
+ GetVariantBorrowElementAt(index, out godot_variant borrowElem);
+ return Variant.CreateCopyingBorrowed(borrowElem);
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ var self = (godot_array)NativeValue;
+ godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
+ godot_variant* itemPtr = &ptrw[index];
+ (*itemPtr).Dispose();
+ *itemPtr = value.CopyNativeVariant();
+ }
}
/// <summary>
- /// Adds an object to the end of this <see cref="Array"/>.
+ /// Adds an item to the end of this <see cref="Array"/>.
/// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
- /// <param name="value">The object to add.</param>
- /// <returns>The new size after adding the object.</returns>
- public int Add(object value) => godot_icall_Array_Add(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to add.</param>
+ public void Add(Variant item)
+ {
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
+ }
/// <summary>
- /// Checks if this <see cref="Array"/> contains the given object.
+ /// Checks if this <see cref="Array"/> contains the given item.
/// </summary>
- /// <param name="value">The item to look for.</param>
- /// <returns>Whether or not this array contains the given object.</returns>
- public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to look for.</param>
+ /// <returns>Whether or not this array contains the given item.</returns>
+ public bool Contains(Variant item) => IndexOf(item) != -1;
/// <summary>
/// Erases all items from this <see cref="Array"/>.
/// </summary>
- public void Clear() => godot_icall_Array_Clear(GetPtr());
+ public void Clear() => Resize(0);
/// <summary>
- /// Searches this <see cref="Array"/> for an object
+ /// Searches this <see cref="Array"/> for an item
/// and returns its index or -1 if not found.
/// </summary>
- /// <param name="value">The object to search for.</param>
- /// <returns>The index of the object, or -1 if not found.</returns>
- public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to search for.</param>
+ /// <returns>The index of the item, or -1 if not found.</returns>
+ public int IndexOf(Variant item)
+ {
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
+ }
/// <summary>
- /// Inserts a new object at a given position in the array.
+ /// Inserts a new item at a given position in the array.
/// The position must be a valid position of an existing item,
/// or the position at the end of the array.
/// Existing items will be moved to the right.
/// </summary>
/// <param name="index">The index to insert at.</param>
- /// <param name="value">The object to insert.</param>
- public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value);
+ /// <param name="item">The <see cref="Variant"/> item to insert.</param>
+ public void Insert(int index, Variant item)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
+ }
/// <summary>
- /// Removes the first occurrence of the specified <paramref name="value"/>
+ /// Removes the first occurrence of the specified <paramref name="item"/>
/// from this <see cref="Array"/>.
/// </summary>
- /// <param name="value">The value to remove.</param>
- public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value);
+ /// <param name="item">The value to remove.</param>
+ public bool Remove(Variant item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
+ }
/// <summary>
/// Removes an element from this <see cref="Array"/> by index.
/// </summary>
/// <param name="index">The index of the element to remove.</param>
- public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index);
+ public void RemoveAt(int index)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_remove_at(ref self, index);
+ }
// ICollection
@@ -222,28 +352,77 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count => godot_icall_Array_Count(GetPtr());
-
- object ICollection.SyncRoot => this;
+ public int Count => NativeValue.DangerousSelfRef.Size;
bool ICollection.IsSynchronized => false;
+ object ICollection.SyncRoot => false;
+
+ bool ICollection<Variant>.IsReadOnly => false;
+
/// <summary>
/// Copies the elements of this <see cref="Array"/> to the given
- /// untyped C# array, starting at the given index.
+ /// <see cref="Variant"/> C# array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
- /// <param name="index">The index to start at.</param>
- public void CopyTo(System.Array array, int index)
+ /// <param name="arrayIndex">The index to start at.</param>
+ public void CopyTo(Variant[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
+
+ int count = Count;
+
+ if (array.Length < (arrayIndex + count))
+ {
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ }
+
+ unsafe
+ {
+ for (int i = 0; i < count; i++)
+ {
+ array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+ arrayIndex++;
+ }
+ }
+ }
+
+ void ICollection.CopyTo(System.Array array, int index)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
+ {
+ throw new ArgumentOutOfRangeException(nameof(index),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
+
+ int count = Count;
- // Internal call may throw ArgumentException
- godot_icall_Array_CopyTo(GetPtr(), array, index);
+ if (array.Length < (index + count))
+ {
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ }
+
+ unsafe
+ {
+ for (int i = 0; i < count; i++)
+ {
+ object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
+ array.SetValue(obj, index);
+ index++;
+ }
+ }
}
// IEnumerable
@@ -252,7 +431,7 @@ namespace Godot.Collections
/// Gets an enumerator for this <see cref="Array"/>.
/// </summary>
/// <returns>An enumerator.</returns>
- public IEnumerator GetEnumerator()
+ public IEnumerator<Variant> GetEnumerator()
{
int count = Count;
@@ -262,77 +441,37 @@ namespace Godot.Collections
}
}
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
/// <summary>
/// Converts this <see cref="Array"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
public override string ToString()
{
- return godot_icall_Array_ToString(GetPtr());
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_to_string(ref self, out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Ctor();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Array_At(IntPtr ptr, int index);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_Count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_Add(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Clear(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
+ /// <summary>
+ /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
+ /// </summary>
+ internal void GetVariantBorrowElementAt(int index, out godot_variant elem)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ GetVariantBorrowElementAtUnchecked(index, out elem);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Array_ToString(IntPtr ptr);
+ /// <summary>
+ /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
+ /// </summary>
+ internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem)
+ {
+ elem = NativeValue.DangerousSelfRef.Elements[index];
+ }
}
/// <summary>
@@ -342,16 +481,45 @@ namespace Godot.Collections
/// such as arrays or <see cref="List{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the array.</typeparam>
- public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
+ [SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
+ [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
+ public sealed class Array<T> :
+ IList<T>,
+ IReadOnlyList<T>,
+ ICollection<T>,
+ IEnumerable<T>
{
- private Array _objectArray;
+ // ReSharper disable StaticMemberInGenericType
+ // Warning is about unique static fields being created for each generic type combination:
+ // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
+ // In our case this is exactly what we want.
- internal static int elemTypeEncoding;
- internal static IntPtr elemTypeClass;
+ private static unsafe delegate* managed<in T, godot_variant> _convertToVariantCallback;
+ private static unsafe delegate* managed<in godot_variant, T> _convertToManagedCallback;
- static Array()
+ // ReSharper restore StaticMemberInGenericType
+
+ static unsafe Array()
{
- Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass);
+ _convertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
+ _convertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
+ }
+
+ private static unsafe void ValidateVariantConversionCallbacks()
+ {
+ if (_convertToVariantCallback == null || _convertToManagedCallback == null)
+ {
+ throw new InvalidOperationException(
+ $"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'");
+ }
+ }
+
+ private readonly Array _underlyingArray;
+
+ internal ref godot_array.movable NativeValue
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _underlyingArray.NativeValue;
}
/// <summary>
@@ -359,7 +527,9 @@ namespace Godot.Collections
/// </summary>
public Array()
{
- _objectArray = new Array();
+ ValidateVariantConversionCallbacks();
+
+ _underlyingArray = new Array();
}
/// <summary>
@@ -369,10 +539,15 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array(IEnumerable<T> collection)
{
+ ValidateVariantConversionCallbacks();
+
if (collection == null)
- throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+ throw new ArgumentNullException(nameof(collection));
+
+ _underlyingArray = new Array();
- _objectArray = new Array(collection);
+ foreach (T element in collection)
+ Add(element);
}
/// <summary>
@@ -380,13 +555,17 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The items to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
- public Array(params T[] array) : this()
+ public Array(T[] array) : this()
{
+ ValidateVariantConversionCallbacks();
+
if (array == null)
- {
- throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
- }
- _objectArray = new Array(array);
+ throw new ArgumentNullException(nameof(array));
+
+ _underlyingArray = new Array();
+
+ foreach (T element in array)
+ Add(element);
}
/// <summary>
@@ -395,23 +574,14 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
- _objectArray = array;
- }
+ ValidateVariantConversionCallbacks();
- internal Array(IntPtr handle)
- {
- _objectArray = new Array(handle);
+ _underlyingArray = array;
}
- internal Array(ArraySafeHandle handle)
- {
- _objectArray = new Array(handle);
- }
-
- internal IntPtr GetPtr()
- {
- return _objectArray.GetPtr();
- }
+ // Explicit name to make it very clear
+ internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
+ => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
@@ -419,7 +589,7 @@ namespace Godot.Collections
/// <param name="from">The typed array to convert.</param>
public static explicit operator Array(Array<T> from)
{
- return from._objectArray;
+ return from?._underlyingArray;
}
/// <summary>
@@ -429,7 +599,7 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array<T> Duplicate(bool deep = false)
{
- return new Array<T>(_objectArray.Duplicate(deep));
+ return new Array<T>(_underlyingArray.Duplicate(deep));
}
/// <summary>
@@ -439,7 +609,7 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return _objectArray.Resize(newSize);
+ return _underlyingArray.Resize(newSize);
}
/// <summary>
@@ -447,7 +617,7 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- _objectArray.Shuffle();
+ _underlyingArray.Shuffle();
}
/// <summary>
@@ -458,7 +628,18 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array<T> operator +(Array<T> left, Array<T> right)
{
- return new Array<T>(left._objectArray + right._objectArray);
+ if (left == null)
+ {
+ if (right == null)
+ return new Array<T>();
+
+ return right.Duplicate(deep: false);
+ }
+
+ if (right == null)
+ return left.Duplicate(deep: false);
+
+ return new Array<T>(left._underlyingArray + right._underlyingArray);
}
// IList<T>
@@ -467,10 +648,23 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="index"/>.
/// </summary>
/// <value>The value at the given <paramref name="index"/>.</value>
- public T this[int index]
+ public unsafe T this[int index]
{
- get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
- set { _objectArray[index] = value; }
+ get
+ {
+ _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
+ return _convertToManagedCallback(borrowElem);
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ var self = (godot_array)_underlyingArray.NativeValue;
+ godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
+ godot_variant* itemPtr = &ptrw[index];
+ (*itemPtr).Dispose();
+ *itemPtr = _convertToVariantCallback(value);
+ }
}
/// <summary>
@@ -479,9 +673,11 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to search for.</param>
/// <returns>The index of the item, or -1 if not found.</returns>
- public int IndexOf(T item)
+ public unsafe int IndexOf(T item)
{
- return _objectArray.IndexOf(item);
+ using var variantValue = _convertToVariantCallback(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
}
/// <summary>
@@ -492,9 +688,14 @@ namespace Godot.Collections
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="item">The item to insert.</param>
- public void Insert(int index, T item)
+ public unsafe void Insert(int index, T item)
{
- _objectArray.Insert(index, item);
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ using var variantValue = _convertToVariantCallback(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
}
/// <summary>
@@ -503,7 +704,7 @@ namespace Godot.Collections
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
- _objectArray.RemoveAt(index);
+ _underlyingArray.RemoveAt(index);
}
// ICollection<T>
@@ -513,10 +714,7 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count
- {
- get { return _objectArray.Count; }
- }
+ public int Count => _underlyingArray.Count;
bool ICollection<T>.IsReadOnly => false;
@@ -526,9 +724,11 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>The new size after adding the item.</returns>
- public void Add(T item)
+ public unsafe void Add(T item)
{
- _objectArray.Add(item);
+ using var variantValue = _convertToVariantCallback(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
}
/// <summary>
@@ -536,7 +736,7 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
- _objectArray.Clear();
+ _underlyingArray.Clear();
}
/// <summary>
@@ -544,10 +744,7 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to look for.</param>
/// <returns>Whether or not this array contains the given item.</returns>
- public bool Contains(T item)
- {
- return _objectArray.Contains(item);
- }
+ public bool Contains(T item) => IndexOf(item) != -1;
/// <summary>
/// Copies the elements of this <see cref="Array{T}"/> to the given
@@ -561,19 +758,22 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
-
- // TODO This may be quite slow because every element access is an internal call.
- // It could be moved entirely to an internal call if we find out how to do the cast there.
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
- int count = _objectArray.Count;
+ int count = Count;
if (array.Length < (arrayIndex + count))
- throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ {
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ }
for (int i = 0; i < count; i++)
{
- array[arrayIndex] = (T)this[i];
+ array[arrayIndex] = this[i];
arrayIndex++;
}
}
@@ -586,7 +786,14 @@ namespace Godot.Collections
/// <returns>A <see langword="bool"/> indicating success or failure.</returns>
public bool Remove(T item)
{
- return Array.godot_icall_Array_Remove(GetPtr(), item);
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
}
// IEnumerable<T>
@@ -597,23 +804,26 @@ namespace Godot.Collections
/// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
- int count = _objectArray.Count;
+ int count = _underlyingArray.Count;
for (int i = 0; i < count; i++)
{
- yield return (T)this[i];
+ yield return this[i];
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Array{T}"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
- public override string ToString() => _objectArray.ToString();
+ public override string ToString() => _underlyingArray.ToString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Array<T> from) => Variant.CreateFrom(from);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index 2febf37f05..b7d633517a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -1,5 +1,7 @@
using System;
+#nullable enable
+
namespace Godot
{
/// <summary>
@@ -8,25 +10,28 @@ namespace Godot
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
- private readonly bool requiresLookup;
- private readonly System.Type[] scriptTypes;
+ public bool RequiresLookup { get; }
+ public Type[]? ScriptTypes { get; }
/// <summary>
/// Constructs a new AssemblyHasScriptsAttribute instance.
/// </summary>
public AssemblyHasScriptsAttribute()
{
- requiresLookup = true;
+ RequiresLookup = true;
+ ScriptTypes = null;
}
/// <summary>
/// Constructs a new AssemblyHasScriptsAttribute instance.
/// </summary>
/// <param name="scriptTypes">The specified type(s) of scripts.</param>
- public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
+ public AssemblyHasScriptsAttribute(Type[] scriptTypes)
{
- requiresLookup = false;
- this.scriptTypes = scriptTypes;
+ RequiresLookup = false;
+ ScriptTypes = scriptTypes;
}
}
}
+
+#nullable restore
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
deleted file mode 100644
index 0b00878e8c..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// An attribute that disables Godot Generators.
- /// </summary>
- [AttributeUsage(AttributeTargets.Class)]
- public class DisableGodotGeneratorsAttribute : Attribute { }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
index 46eb128d37..3d204bdf9f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
@@ -6,7 +6,7 @@ namespace Godot
/// An attribute used to export objects.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public class ExportAttribute : Attribute
+ public sealed class ExportAttribute : Attribute
{
private PropertyHint hint;
private string hintString;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
deleted file mode 100644
index 8d4ff0fdb7..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// An attribute for a method.
- /// </summary>
- [AttributeUsage(AttributeTargets.Method)]
- internal class GodotMethodAttribute : Attribute
- {
- private string methodName;
-
- public string MethodName { get { return methodName; } }
-
- /// <summary>
- /// Constructs a new GodotMethodAttribute instance.
- /// </summary>
- /// <param name="methodName">The name of the method.</param>
- public GodotMethodAttribute(string methodName)
- {
- this.methodName = methodName;
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index 3ebb6612de..2c8a53ae1c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -8,7 +8,7 @@ namespace Godot
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
- private string path;
+ public string Path { get; }
/// <summary>
/// Constructs a new ScriptPathAttribute instance.
@@ -16,7 +16,7 @@ namespace Godot
/// <param name="path">The file path to the script</param>
public ScriptPathAttribute(string path)
{
- this.path = path;
+ Path = path;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 437878818c..646681a9b1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -167,7 +162,7 @@ namespace Godot
case 2:
return Column2;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -184,7 +179,7 @@ namespace Godot
Column2 = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -386,7 +381,7 @@ namespace Godot
case 2:
return Row2;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
@@ -413,7 +408,7 @@ namespace Godot
Row2 = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs
new file mode 100644
index 0000000000..ac2e2fae3c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs
@@ -0,0 +1,18 @@
+namespace Godot.Bridge;
+
+public static class AlcReloadCfg
+{
+ private static bool _configured = false;
+
+ public static void Configure(bool alcReloadEnabled)
+ {
+ if (_configured)
+ return;
+
+ _configured = true;
+
+ IsAlcReloadingEnabled = alcReloadEnabled;
+ }
+
+ internal static bool IsAlcReloadingEnabled = false;
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
new file mode 100644
index 0000000000..ae44f8f4ba
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ internal static class CSharpInstanceBridge
+ {
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Call(IntPtr godotObjectGCHandle, godot_string_name* method,
+ godot_variant** args, int argCount, godot_variant_call_error* refCallError, godot_variant* ret)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ {
+ *ret = default;
+ (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
+ return godot_bool.False;
+ }
+
+ bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method),
+ new NativeVariantPtrArgs(args),
+ argCount, out godot_variant retValue);
+
+ if (!methodInvoked)
+ {
+ *ret = default;
+ // This is important, as it tells Object::call that no method was called.
+ // Otherwise, it would prevent Object::call from calling native methods.
+ (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD;
+ return godot_bool.False;
+ }
+
+ *ret = retValue;
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *ret = default;
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ throw new InvalidOperationException();
+
+ if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value)))
+ {
+ return godot_bool.True;
+ }
+
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
+ Variant valueManaged = Variant.CreateCopyingBorrowed(*value);
+
+ return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name,
+ godot_variant* outRet)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ throw new InvalidOperationException();
+
+ if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
+ {
+ *outRet = outRetValue;
+ return godot_bool.True;
+ }
+
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
+ Variant ret = godotObject._Get(nameManaged);
+
+ if (ret.VariantType == Variant.Type.Nil)
+ {
+ *outRet = default;
+ return godot_bool.False;
+ }
+
+ *outRet = Marshaling.ConvertManagedObjectToVariant(ret);
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRet = default;
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (okIfNull.ToBool())
+ godotObject?.Dispose();
+ else
+ godotObject!.Dispose();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid)
+ {
+ try
+ {
+ var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (self == null)
+ {
+ *outRes = default;
+ *outValid = godot_bool.False;
+ return;
+ }
+
+ var resultStr = self.ToString();
+
+ if (resultStr == null)
+ {
+ *outRes = default;
+ *outValid = godot_bool.False;
+ return;
+ }
+
+ *outRes = Marshaling.ConvertStringToNative(resultStr);
+ *outValid = godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRes = default;
+ *outValid = godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool HasMethodUnknownParams(IntPtr godotObjectGCHandle, godot_string_name* method)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return godot_bool.False;
+
+ return godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(method)).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void SerializeState(
+ IntPtr godotObjectGCHandle,
+ godot_dictionary* propertiesState,
+ godot_dictionary* signalEventsState
+ )
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return;
+
+ // Call OnBeforeSerialize
+
+ // ReSharper disable once SuspiciousTypeConversion.Global
+ if (godotObject is ISerializationListener serializationListener)
+ serializationListener.OnBeforeSerialize();
+
+ // Save instance state
+
+ using var info = GodotSerializationInfo.CreateCopyingBorrowed(
+ *propertiesState, *signalEventsState);
+
+ godotObject.SaveGodotObjectData(info);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void DeserializeState(
+ IntPtr godotObjectGCHandle,
+ godot_dictionary* propertiesState,
+ godot_dictionary* signalEventsState
+ )
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return;
+
+ // Restore instance state
+
+ using var info = GodotSerializationInfo.CreateCopyingBorrowed(
+ *propertiesState, *signalEventsState);
+
+ godotObject.RestoreGodotObjectData(info);
+
+ // Call OnAfterDeserialize
+
+ // ReSharper disable once SuspiciousTypeConversion.Global
+ if (godotObject is ISerializationListener serializationListener)
+ serializationListener.OnAfterDeserialize();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
new file mode 100644
index 0000000000..456a118b90
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ internal static class GCHandleBridge
+ {
+ [UnmanagedCallersOnly]
+ internal static void FreeGCHandle(IntPtr gcHandlePtr)
+ {
+ try
+ {
+ CustomGCHandle.Free(GCHandle.FromIntPtr(gcHandlePtr));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
new file mode 100644
index 0000000000..8f26967dcd
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge;
+
+public class GodotSerializationInfo : IDisposable
+{
+ private readonly Collections.Dictionary _properties;
+ private readonly Collections.Dictionary _signalEvents;
+
+ public void Dispose()
+ {
+ _properties?.Dispose();
+ _signalEvents?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ private GodotSerializationInfo(in godot_dictionary properties, in godot_dictionary signalEvents)
+ {
+ _properties = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(properties);
+ _signalEvents = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(signalEvents);
+ }
+
+ internal static GodotSerializationInfo CreateCopyingBorrowed(
+ in godot_dictionary properties, in godot_dictionary signalEvents)
+ {
+ return new(NativeFuncs.godotsharp_dictionary_new_copy(properties),
+ NativeFuncs.godotsharp_dictionary_new_copy(signalEvents));
+ }
+
+ public void AddProperty(StringName name, Variant value)
+ {
+ _properties[name] = value;
+ }
+
+ public bool TryGetProperty(StringName name, out Variant value)
+ {
+ return _properties.TryGetValue(name, out value);
+ }
+
+ public void AddSignalEventDelegate(StringName name, Delegate eventDelegate)
+ {
+ var serializedData = new Collections.Array();
+
+ if (DelegateUtils.TrySerializeDelegate(eventDelegate, serializedData))
+ {
+ _signalEvents[name] = serializedData;
+ }
+ else if (OS.IsStdoutVerbose())
+ {
+ Console.WriteLine($"Failed to serialize event signal delegate: {name}");
+ }
+ }
+
+ public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value)
+ where T : Delegate
+ {
+ if (_signalEvents.TryGetValue(name, out Variant serializedData))
+ {
+ if (DelegateUtils.TryDeserializeDelegate(serializedData.AsGodotArray(), out var eventDelegate))
+ {
+ value = eventDelegate as T;
+
+ if (value == null)
+ {
+ Console.WriteLine($"Cannot cast the deserialized event signal delegate: {name}. " +
+ $"Expected '{typeof(T).FullName}'; got '{eventDelegate.GetType().FullName}'.");
+ return false;
+ }
+
+ return true;
+ }
+ else if (OS.IsStdoutVerbose())
+ {
+ Console.WriteLine($"Failed to deserialize event signal delegate: {name}");
+ }
+
+ value = null;
+ return false;
+ }
+
+ value = null;
+ return false;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
new file mode 100644
index 0000000000..57240624bc
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct ManagedCallbacks
+ {
+ // @formatter:off
+ public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback;
+ public delegate* unmanaged<IntPtr, godot_variant**, uint, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
+ public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
+ public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
+ public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
+ public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
+ public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
+ public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
+ public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
+ public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
+ public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
+ public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
+ public delegate* unmanaged<IntPtr, godot_bool*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
+ public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
+ public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
+ public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> CSharpInstanceBridge_Call;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Set;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Get;
+ public delegate* unmanaged<IntPtr, godot_bool, void> CSharpInstanceBridge_CallDispose;
+ public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, void> CSharpInstanceBridge_CallToString;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_bool> CSharpInstanceBridge_HasMethodUnknownParams;
+ public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_SerializeState;
+ public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_DeserializeState;
+ public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
+ public delegate* unmanaged<void*, void> DebuggingUtils_GetCurrentStackInfo;
+ public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown;
+ public delegate* unmanaged<godot_bool, void> GD_OnCoreApiAssemblyLoaded;
+ // @formatter:on
+
+ public static ManagedCallbacks Create()
+ {
+ return new()
+ {
+ // @formatter:off
+ SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback,
+ DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
+ DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
+ DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
+ DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
+ ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
+ ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
+ ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
+ ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
+ ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
+ ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
+ ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
+ ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
+ ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath,
+ ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge,
+ ScriptManagerBridge_TryReloadRegisteredScriptWithClass = &ScriptManagerBridge.TryReloadRegisteredScriptWithClass,
+ ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo,
+ ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType,
+ ScriptManagerBridge_GetPropertyInfoList = &ScriptManagerBridge.GetPropertyInfoList,
+ ScriptManagerBridge_GetPropertyDefaultValues = &ScriptManagerBridge.GetPropertyDefaultValues,
+ CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call,
+ CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set,
+ CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get,
+ CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose,
+ CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString,
+ CSharpInstanceBridge_HasMethodUnknownParams = &CSharpInstanceBridge.HasMethodUnknownParams,
+ CSharpInstanceBridge_SerializeState = &CSharpInstanceBridge.SerializeState,
+ CSharpInstanceBridge_DeserializeState = &CSharpInstanceBridge.DeserializeState,
+ GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
+ DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo,
+ DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown,
+ GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded,
+ // @formatter:on
+ };
+ }
+
+ public static void Create(IntPtr outManagedCallbacks)
+ => *(ManagedCallbacks*)outManagedCallbacks = Create();
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
new file mode 100644
index 0000000000..647ae436ff
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public struct MethodInfo
+{
+ public StringName Name { get; init; }
+ public PropertyInfo ReturnVal { get; init; }
+ public MethodFlags Flags { get; init; }
+ public int Id { get; init; } = 0;
+ public List<PropertyInfo>? Arguments { get; init; }
+ public List<Variant>? DefaultArguments { get; init; }
+
+ public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags,
+ List<PropertyInfo>? arguments, List<Variant>? defaultArguments)
+ {
+ Name = name;
+ ReturnVal = returnVal;
+ Flags = flags;
+ Arguments = arguments;
+ DefaultArguments = defaultArguments;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
new file mode 100644
index 0000000000..80d6f7b4a5
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
@@ -0,0 +1,24 @@
+namespace Godot.Bridge;
+
+#nullable enable
+
+public struct PropertyInfo
+{
+ public Variant.Type Type { get; init; }
+ public StringName Name { get; init; }
+ public PropertyHint Hint { get; init; }
+ public string HintString { get; init; }
+ public PropertyUsageFlags Usage { get; init; }
+ public bool Exported { get; init; }
+
+ public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
+ PropertyUsageFlags usage, bool exported)
+ {
+ Type = type;
+ Name = name;
+ Hint = hint;
+ HintString = hintString;
+ Usage = usage;
+ Exported = exported;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
new file mode 100644
index 0000000000..0dc5ba7678
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -0,0 +1,1028 @@
+#nullable enable
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using System.Runtime.Serialization;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ // TODO: Make class internal once we replace LookupScriptsInAssembly (the only public member) with source generators
+ public static partial class ScriptManagerBridge
+ {
+ private static ConcurrentDictionary<AssemblyLoadContext, ConcurrentDictionary<Type, byte>>
+ _alcData = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OnAlcUnloading(AssemblyLoadContext alc)
+ {
+ if (_alcData.TryRemove(alc, out var typesInAlc))
+ {
+ foreach (var type in typesInAlc.Keys)
+ {
+ if (_scriptTypeBiMap.RemoveByScriptType(type, out IntPtr scriptPtr) &&
+ !_pathTypeBiMap.TryGetScriptPath(type, out _))
+ {
+ // For scripts without a path, we need to keep the class qualified name for reloading
+ _scriptDataForReload.TryAdd(scriptPtr,
+ (type.Assembly.GetName().Name, type.FullName ?? type.ToString()));
+ }
+
+ _pathTypeBiMap.RemoveByScriptType(type);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void AddTypeForAlcReloading(Type type)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(type.Assembly);
+ if (alc == null)
+ return;
+
+ var typesInAlc = _alcData.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ typesInAlc.TryAdd(type, 0);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void TrackAlcForUnloading(AssemblyLoadContext alc)
+ {
+ _ = _alcData.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ }
+
+ private static ScriptTypeBiMap _scriptTypeBiMap = new();
+ private static PathScriptTypeBiMap _pathTypeBiMap = new();
+
+ private static ConcurrentDictionary<IntPtr, (string? assemblyName, string classFullName)>
+ _scriptDataForReload = new();
+
+ [UnmanagedCallersOnly]
+ internal static void FrameCallback()
+ {
+ try
+ {
+ Dispatcher.DefaultGodotTaskScheduler?.Activate();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
+ IntPtr godotObject)
+ {
+ // TODO: Optimize with source generators and delegate pointers
+
+ try
+ {
+ using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(nativeTypeName)));
+ string nativeTypeNameStr = stringName.ToString();
+
+ Type nativeType = TypeGetProxyClass(nativeTypeNameStr) ?? throw new InvalidOperationException(
+ "Wrapper class not found for type: " + nativeTypeNameStr);
+ var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
+
+ var ctor = nativeType.GetConstructor(
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
+ null, Type.EmptyTypes, null);
+
+ obj.NativePtr = godotObject;
+
+ _ = ctor!.Invoke(obj, null);
+
+ return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(obj));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return IntPtr.Zero;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr,
+ IntPtr godotObject,
+ godot_variant** args, int argCount)
+ {
+ // TODO: Optimize with source generators and delegate pointers
+
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ var obj = (Object)FormatterServices.GetUninitializedObject(scriptType);
+
+ var ctor = scriptType
+ .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(c => c.GetParameters().Length == argCount)
+ .FirstOrDefault();
+
+ if (ctor == null)
+ {
+ if (argCount == 0)
+ {
+ throw new MissingMemberException(
+ $"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
+ }
+ else
+ {
+ throw new MissingMemberException(
+ $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
+ }
+ }
+
+ var parameters = ctor.GetParameters();
+ int paramCount = parameters.Length;
+
+ var invokeParams = new object?[paramCount];
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType(
+ *args[i], parameters[i].ParameterType);
+ }
+
+ obj.NativePtr = godotObject;
+
+ _ = ctor.Invoke(obj, invokeParams);
+
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes)
+ {
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType))
+ {
+ *outRes = default;
+ return;
+ }
+
+ var native = Object.InternalGetClassNativeBase(scriptType);
+
+ var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.Public | BindingFlags.NonPublic);
+
+ if (field == null)
+ {
+ *outRes = default;
+ return;
+ }
+
+ var nativeName = (StringName?)field.GetValue(null);
+
+ if (nativeName == null)
+ {
+ *outRes = default;
+ return;
+ }
+
+ *outRes = NativeFuncs.godotsharp_string_name_new_copy((godot_string_name)nativeName.NativeValue);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRes = default;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
+ {
+ try
+ {
+ var target = (Object?)GCHandle.FromIntPtr(gcHandlePtr).Target;
+ if (target != null)
+ target.NativePtr = newPtr;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static Type? TypeGetProxyClass(string nativeTypeNameStr)
+ {
+ // Performance is not critical here as this will be replaced with a generated dictionary.
+
+ if (nativeTypeNameStr[0] == '_')
+ nativeTypeNameStr = nativeTypeNameStr.Substring(1);
+
+ Type? wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr);
+
+ if (wrapperType == null)
+ {
+ wrapperType = AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor")?
+ .GetType("Godot." + nativeTypeNameStr);
+ }
+
+ static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed;
+
+ if (wrapperType != null && IsStatic(wrapperType))
+ {
+ // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
+ return typeof(Object);
+ }
+
+ return wrapperType;
+ }
+
+ // Called from GodotPlugins
+ // ReSharper disable once UnusedMember.Local
+ public static void LookupScriptsInAssembly(Assembly assembly)
+ {
+ static void LookupScriptForClass(Type type)
+ {
+ var scriptPathAttr = type.GetCustomAttributes(inherit: false)
+ .OfType<ScriptPathAttribute>()
+ .FirstOrDefault();
+
+ if (scriptPathAttr == null)
+ return;
+
+ _pathTypeBiMap.Add(scriptPathAttr.Path, type);
+
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ AddTypeForAlcReloading(type);
+ }
+ }
+
+ var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false)
+ .OfType<AssemblyHasScriptsAttribute>()
+ .FirstOrDefault();
+
+ if (assemblyHasScriptsAttr == null)
+ return;
+
+ if (assemblyHasScriptsAttr.RequiresLookup)
+ {
+ // This is supported for scenarios where specifying all types would be cumbersome,
+ // such as when disabling C# source generators (for whatever reason) or when using a
+ // language other than C# that has nothing similar to source generators to automate it.
+
+ var typeOfGodotObject = typeof(Object);
+
+ foreach (var type in assembly.GetTypes())
+ {
+ if (type.IsNested)
+ continue;
+
+ if (!typeOfGodotObject.IsAssignableFrom(type))
+ continue;
+
+ LookupScriptForClass(type);
+ }
+ }
+ else
+ {
+ // This is the most likely scenario as we use C# source generators
+
+ var scriptTypes = assemblyHasScriptsAttr.ScriptTypes;
+
+ if (scriptTypes != null)
+ {
+ for (int i = 0; i < scriptTypes.Length; i++)
+ {
+ LookupScriptForClass(scriptTypes[i]);
+ }
+ }
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr,
+ godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull)
+ {
+ try
+ {
+ var owner = (Object?)GCHandle.FromIntPtr(ownerGCHandlePtr).Target;
+
+ if (owner == null)
+ {
+ *outOwnerIsNull = godot_bool.True;
+ return;
+ }
+
+ *outOwnerIsNull = godot_bool.False;
+
+ owner.RaiseGodotClassSignalCallbacks(CustomUnsafe.AsRef(eventSignalName),
+ new NativeVariantPtrArgs(args), argCount);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outOwnerIsNull = godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
+ {
+ try
+ {
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType))
+ return godot_bool.False;
+
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtrMaybeBase, out Type? maybeBaseType))
+ return godot_bool.False;
+
+ return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (!_scriptTypeBiMap.IsScriptRegistered(scriptPtr))
+ {
+ string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
+
+ if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
+ return godot_bool.False;
+
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+ }
+ }
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetOrCreateScriptBridgeForPath(godot_string* scriptPath, godot_ref* outScript)
+ {
+ string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
+
+ if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
+ {
+ NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
+ return;
+ }
+
+ GetOrCreateScriptBridgeForType(scriptType, outScript);
+ }
+
+ private static unsafe void GetOrCreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr))
+ {
+ // Use existing
+ NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr);
+ return;
+ }
+
+ // This path is slower, but it's only executed for the first instantiation of the type
+ CreateScriptBridgeForType(scriptType, outScript);
+ }
+ }
+
+ internal static unsafe void GetOrLoadOrCreateScriptForType(Type scriptType, godot_ref* outScript)
+ {
+ static bool GetPathOtherwiseGetOrCreateScript(Type scriptType, godot_ref* outScript,
+ [MaybeNullWhen(false)] out string scriptPath)
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr))
+ {
+ // Use existing
+ NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr);
+ scriptPath = null;
+ return false;
+ }
+
+ // This path is slower, but it's only executed for the first instantiation of the type
+
+ if (_pathTypeBiMap.TryGetScriptPath(scriptType, out scriptPath))
+ return true;
+
+ CreateScriptBridgeForType(scriptType, outScript);
+ scriptPath = null;
+ return false;
+ }
+ }
+
+ if (GetPathOtherwiseGetOrCreateScript(scriptType, outScript, out string? scriptPath))
+ {
+ // This path is slower, but it's only executed for the first instantiation of the type
+
+ // This must be done outside the read-write lock, as the script resource loading can lock it
+ using godot_string scriptPathIn = Marshaling.ConvertStringToNative(scriptPath);
+ if (!NativeFuncs.godotsharp_internal_script_load(scriptPathIn, outScript).ToBool())
+ {
+ GD.PushError($"Cannot load script for type '{scriptType.FullName}'. Path: '{scriptPath}'.");
+
+ // If loading of the script fails, best we can do create a new script
+ // with no path, as we do for types without an associated script file.
+ GetOrCreateScriptBridgeForType(scriptType, outScript);
+ }
+ }
+ }
+
+ private static unsafe void CreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
+ {
+ NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
+ IntPtr scriptPtr = outScript->Reference;
+
+ // Caller takes care of locking
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void RemoveScriptBridge(IntPtr scriptPtr)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ _scriptTypeBiMap.Remove(scriptPtr);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static godot_bool TryReloadRegisteredScriptWithClass(IntPtr scriptPtr)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptType(scriptPtr, out _))
+ {
+ // NOTE:
+ // Currently, we reload all scripts, not only the ones from the unloaded ALC.
+ // As such, we need to handle this case instead of treating it as an error.
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+ return godot_bool.True;
+ }
+
+ if (!_scriptDataForReload.TryGetValue(scriptPtr, out var dataForReload))
+ {
+ GD.PushError("Missing class qualified name for reloading script");
+ return godot_bool.False;
+ }
+
+ _ = _scriptDataForReload.TryRemove(scriptPtr, out _);
+
+ if (dataForReload.assemblyName == null)
+ {
+ GD.PushError(
+ $"Missing assembly name of class '{dataForReload.classFullName}' for reloading script");
+ return godot_bool.False;
+ }
+
+ var scriptType = ReflectionUtils.FindTypeInLoadedAssemblies(dataForReload.assemblyName,
+ dataForReload.classFullName);
+
+ if (scriptType == null)
+ {
+ // The class was removed, can't reload
+ return godot_bool.False;
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ if (!typeof(Godot.Object).IsAssignableFrom(scriptType))
+ {
+ // The class no longer inherits Godot.Object, can't reload
+ return godot_bool.False;
+ }
+
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+
+ return godot_bool.True;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
+ godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
+ godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
+ {
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+
+ *outTool = scriptType.GetCustomAttributes(inherit: false)
+ .OfType<ToolAttribute>()
+ .Any().ToGodotBool();
+
+ if (!(*outTool).ToBool() && scriptType.IsNested)
+ {
+ *outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
+ .OfType<ToolAttribute>()
+ .Any() ?? false).ToGodotBool();
+ }
+
+ if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
+ *outTool = godot_bool.True;
+
+ // Methods
+
+ // Performance is not critical here as this will be replaced with source generators.
+ using var methods = new Collections.Array();
+
+ Type? top = scriptType;
+ Type native = Object.InternalGetClassNativeBase(top);
+
+ while (top != null && top != native)
+ {
+ var methodList = GetMethodListForType(top);
+
+ if (methodList != null)
+ {
+ foreach (var method in methodList)
+ {
+ var methodInfo = new Collections.Dictionary();
+
+ methodInfo.Add("name", method.Name);
+
+ var methodParams = new Collections.Array();
+
+ if (method.Arguments != null)
+ {
+ foreach (var param in method.Arguments)
+ {
+ methodParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", (int)param.Type },
+ { "usage", (int)param.Usage }
+ });
+ }
+ }
+
+ methodInfo.Add("params", methodParams);
+
+ methods.Add(methodInfo);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outMethodsDest = NativeFuncs.godotsharp_array_new_copy(
+ (godot_array)methods.NativeValue);
+
+ // RPC functions
+
+ Collections.Dictionary rpcFunctions = new();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
+ foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
+ BindingFlags.NonPublic | BindingFlags.Public))
+ {
+ if (method.IsStatic)
+ continue;
+
+ string methodName = method.Name;
+
+ if (rpcFunctions.ContainsKey(methodName))
+ continue;
+
+ var rpcAttr = method.GetCustomAttributes(inherit: false)
+ .OfType<RPCAttribute>().FirstOrDefault();
+
+ if (rpcAttr == null)
+ continue;
+
+ var rpcConfig = new Collections.Dictionary();
+
+ rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
+ rpcConfig["call_local"] = rpcAttr.CallLocal;
+ rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode;
+ rpcConfig["channel"] = rpcAttr.TransferChannel;
+
+ rpcFunctions.Add(methodName, rpcConfig);
+ }
+
+ top = top.BaseType;
+ }
+
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)(rpcFunctions).NativeValue);
+
+ // Event signals
+
+ // Performance is not critical here as this will be replaced with source generators.
+ using var signals = new Collections.Dictionary();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
+ var signalList = GetSignalListForType(top);
+
+ if (signalList != null)
+ {
+ foreach (var signal in signalList)
+ {
+ string signalName = signal.Name;
+
+ if (signals.ContainsKey(signalName))
+ continue;
+
+ var signalParams = new Collections.Array();
+
+ if (signal.Arguments != null)
+ {
+ foreach (var param in signal.Arguments)
+ {
+ signalParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", (int)param.Type },
+ { "usage", (int)param.Usage }
+ });
+ }
+ }
+
+ signals.Add(signalName, signalParams);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)signals.NativeValue);
+
+ // Base script
+
+ var baseType = scriptType.BaseType;
+ if (baseType != null && baseType != native)
+ {
+ GetOrLoadOrCreateScriptForType(baseType, outBaseScript);
+ }
+ else
+ {
+ *outBaseScript = default;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outTool = godot_bool.False;
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
+ *outBaseScript = default;
+ }
+ }
+
+ private static List<MethodInfo>? GetSignalListForType(Type type)
+ {
+ var getGodotSignalListMethod = type.GetMethod(
+ "GetGodotSignalList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotSignalListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotSignalListMethod.Invoke(null, null);
+ }
+
+ private static List<MethodInfo>? GetMethodListForType(Type type)
+ {
+ var getGodotMethodListMethod = type.GetMethod(
+ "GetGodotMethodList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotMethodListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotMethodListMethod.Invoke(null, null);
+ }
+
+ // ReSharper disable once InconsistentNaming
+ [SuppressMessage("ReSharper", "NotAccessedField.Local")]
+ [StructLayout(LayoutKind.Sequential)]
+ private ref struct godotsharp_property_info
+ {
+ // Careful with padding...
+ public godot_string_name Name; // Not owned
+ public godot_string HintString;
+ public int Type;
+ public int Hint;
+ public int Usage;
+ public godot_bool Exported;
+
+ public void Dispose()
+ {
+ HintString.Dispose();
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetPropertyInfoList(IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, godot_string*, void*, int, void> addPropInfoFunc)
+ {
+ try
+ {
+ Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ GetPropertyInfoListForType(scriptType, scriptPtr, addPropInfoFunc);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static unsafe void GetPropertyInfoListForType(Type type, IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, godot_string*, void*, int, void> addPropInfoFunc)
+ {
+ try
+ {
+ var getGodotPropertyListMethod = type.GetMethod(
+ "GetGodotPropertyList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotPropertyListMethod == null)
+ return;
+
+ var properties = (List<PropertyInfo>?)
+ getGodotPropertyListMethod.Invoke(null, null);
+
+ if (properties == null || properties.Count <= 0)
+ return;
+
+ int length = properties.Count;
+
+ // There's no recursion here, so it's ok to go with a big enough number for most cases
+ // stackMaxSize = stackMaxLength * sizeof(godotsharp_property_info)
+ const int stackMaxLength = 32;
+ bool useStack = length < stackMaxLength;
+
+ godotsharp_property_info* interopProperties;
+
+ if (useStack)
+ {
+ // Weird limitation, hence the need for aux:
+ // "In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable."
+ var aux = stackalloc godotsharp_property_info[length];
+ interopProperties = aux;
+ }
+ else
+ {
+ interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length))!;
+ }
+
+ try
+ {
+ for (int i = 0; i < length; i++)
+ {
+ var property = properties[i];
+
+ godotsharp_property_info interopProperty = new()
+ {
+ Type = (int)property.Type,
+ Name = (godot_string_name)property.Name.NativeValue, // Not owned
+ Hint = (int)property.Hint,
+ HintString = Marshaling.ConvertStringToNative(property.HintString),
+ Usage = (int)property.Usage,
+ Exported = property.Exported.ToGodotBool()
+ };
+
+ interopProperties[i] = interopProperty;
+ }
+
+ using godot_string currentClassName = Marshaling.ConvertStringToNative(type.Name);
+
+ addPropInfoFunc(scriptPtr, &currentClassName, 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.
+ GC.KeepAlive(properties);
+ }
+ finally
+ {
+ for (int i = 0; i < length; i++)
+ interopProperties[i].Dispose();
+
+ if (!useStack)
+ NativeMemory.Free(interopProperties);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ // ReSharper disable once InconsistentNaming
+ [SuppressMessage("ReSharper", "NotAccessedField.Local")]
+ [StructLayout(LayoutKind.Sequential)]
+ private ref struct godotsharp_property_def_val_pair
+ {
+ // Careful with padding...
+ public godot_string_name Name; // Not owned
+ public godot_variant Value;
+
+ public void Dispose()
+ {
+ Value.Dispose();
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetPropertyDefaultValues(IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, void*, int, void> addDefValFunc)
+ {
+ try
+ {
+ Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ Type native = Object.InternalGetClassNativeBase(top);
+
+ while (top != null && top != native)
+ {
+ GetPropertyDefaultValuesForType(top, scriptPtr, addDefValFunc);
+
+ top = top.BaseType;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [SkipLocalsInit]
+ private static unsafe void GetPropertyDefaultValuesForType(Type type, IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, void*, int, void> addDefValFunc)
+ {
+ try
+ {
+ var getGodotPropertyDefaultValuesMethod = type.GetMethod(
+ "GetGodotPropertyDefaultValues",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotPropertyDefaultValuesMethod == null)
+ return;
+
+ var defaultValues = (Dictionary<StringName, object>?)
+ getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+
+ if (defaultValues == null || defaultValues.Count <= 0)
+ return;
+
+ int length = defaultValues.Count;
+
+ // There's no recursion here, so it's ok to go with a big enough number for most cases
+ // stackMaxSize = stackMaxLength * sizeof(godotsharp_property_def_val_pair)
+ const int stackMaxLength = 32;
+ bool useStack = length < stackMaxLength;
+
+ godotsharp_property_def_val_pair* interopDefaultValues;
+
+ if (useStack)
+ {
+ // Weird limitation, hence the need for aux:
+ // "In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable."
+ var aux = stackalloc godotsharp_property_def_val_pair[length];
+ interopDefaultValues = aux;
+ }
+ else
+ {
+ interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length))!;
+ }
+
+ try
+ {
+ int i = 0;
+ foreach (var defaultValuePair in defaultValues)
+ {
+ godotsharp_property_def_val_pair interopProperty = new()
+ {
+ Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned
+ Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value)
+ };
+
+ interopDefaultValues[i] = interopProperty;
+
+ i++;
+ }
+
+ 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.
+ GC.KeepAlive(defaultValues);
+ }
+ finally
+ {
+ for (int i = 0; i < length; i++)
+ interopDefaultValues[i].Dispose();
+
+ if (!useStack)
+ NativeMemory.Free(interopDefaultValues);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
+ godot_bool createWeak)
+ {
+ try
+ {
+ var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
+
+ object? target = oldGCHandle.Target;
+
+ if (target == null)
+ {
+ CustomGCHandle.Free(oldGCHandle);
+ *outNewGCHandlePtr = IntPtr.Zero;
+ return godot_bool.False; // Called after the managed side was collected, so nothing to do here
+ }
+
+ // Release the current weak handle and replace it with a strong handle.
+ var newGCHandle = createWeak.ToBool() ?
+ CustomGCHandle.AllocWeak(target) :
+ CustomGCHandle.AllocStrong(target);
+
+ CustomGCHandle.Free(oldGCHandle);
+ *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outNewGCHandlePtr = IntPtr.Zero;
+ return godot_bool.False;
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
new file mode 100644
index 0000000000..a58f6849ad
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public static partial class ScriptManagerBridge
+{
+ private class ScriptTypeBiMap
+ {
+ public readonly object ReadWriteLock = new();
+ private System.Collections.Generic.Dictionary<IntPtr, Type> _scriptTypeMap = new();
+ private System.Collections.Generic.Dictionary<Type, IntPtr> _typeScriptMap = new();
+
+ public void Add(IntPtr scriptPtr, Type scriptType)
+ {
+ // TODO: What if this is called while unloading a load context, but after we already did cleanup in preparation for unloading?
+
+ _scriptTypeMap.Add(scriptPtr, scriptType);
+ _typeScriptMap.Add(scriptType, scriptPtr);
+
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ AddTypeForAlcReloading(scriptType);
+ }
+ }
+
+ public void Remove(IntPtr scriptPtr)
+ {
+ if (_scriptTypeMap.Remove(scriptPtr, out Type? scriptType))
+ _ = _typeScriptMap.Remove(scriptType);
+ }
+
+ public bool RemoveByScriptType(Type scriptType, out IntPtr scriptPtr)
+ {
+ if (_typeScriptMap.Remove(scriptType, out scriptPtr))
+ return _scriptTypeMap.Remove(scriptPtr);
+ return false;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Type GetScriptType(IntPtr scriptPtr) => _scriptTypeMap[scriptPtr];
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptType(IntPtr scriptPtr, [MaybeNullWhen(false)] out Type scriptType) =>
+ _scriptTypeMap.TryGetValue(scriptPtr, out scriptType);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptPtr(Type scriptType, out IntPtr scriptPtr) =>
+ _typeScriptMap.TryGetValue(scriptType, out scriptPtr);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsScriptRegistered(IntPtr scriptPtr) => _scriptTypeMap.ContainsKey(scriptPtr);
+ }
+
+ private class PathScriptTypeBiMap
+ {
+ private System.Collections.Generic.Dictionary<string, Type> _pathTypeMap = new();
+ private System.Collections.Generic.Dictionary<Type, string> _typePathMap = new();
+
+ public void Add(string scriptPath, Type scriptType)
+ {
+ _pathTypeMap.Add(scriptPath, scriptType);
+
+ // Due to partial classes, more than one file can point to the same type, so
+ // there could be duplicate keys in this case. We only add a type as key once.
+ _typePathMap.TryAdd(scriptType, scriptPath);
+ }
+
+ public void RemoveByScriptType(Type scriptType)
+ {
+ foreach (var pair in _pathTypeMap
+ .Where(p => p.Value == scriptType).ToArray())
+ {
+ _pathTypeMap.Remove(pair.Key);
+ }
+
+ _typePathMap.Remove(scriptType);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptType(string scriptPath, [MaybeNullWhen(false)] out Type scriptType) =>
+ _pathTypeMap.TryGetValue(scriptPath, out scriptType);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptPath(Type scriptType, [MaybeNullWhen(false)] out string scriptPath) =>
+ _typePathMap.TryGetValue(scriptType, out scriptPath);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index 2722b64e6d..8d0e77d171 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -24,7 +26,7 @@ namespace Godot
/// }
/// </code>
/// </example>
- public struct Callable
+ public readonly struct Callable
{
private readonly Object _target;
private readonly StringName _method;
@@ -34,10 +36,12 @@ namespace Godot
/// Object that contains the method.
/// </summary>
public Object Target => _target;
+
/// <summary>
/// Name of the method that will be called.
/// </summary>
public StringName Method => _method;
+
/// <summary>
/// Delegate of the method that will be called.
/// </summary>
@@ -73,15 +77,43 @@ namespace Godot
_delegate = @delegate;
}
+ private const int VarArgsSpanThreshold = 5;
+
/// <summary>
/// Calls the method represented by this <see cref="Callable"/>.
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
/// <returns>The value returned by the method.</returns>
- public object Call(params object[] args)
+ public unsafe Variant Call(params Variant[] args)
{
- return godot_icall_Callable_Call(ref this, args);
+ using godot_callable callable = Marshaling.ConvertCallableToNative(this);
+
+ int argc = args.Length;
+
+ Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() :
+ new godot_variant.movable[argc];
+
+ Span<IntPtr> argsSpan = argc <= 10 ?
+ stackalloc IntPtr[argc] :
+ new IntPtr[argc];
+
+ using var variantSpanDisposer = new VariantSpanDisposer(argsStoreSpan);
+
+ fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
+ fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
+ {
+ for (int i = 0; i < argc; i++)
+ {
+ varargs[i] = (godot_variant)args[i].NativeVar;
+ argsPtr[i] = new IntPtr(&varargs[i]);
+ }
+
+ godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
+ (godot_variant**)argsPtr, argc, out _);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
+ }
}
/// <summary>
@@ -89,9 +121,33 @@ namespace Godot
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
- public void CallDeferred(params object[] args)
+ public unsafe void CallDeferred(params Variant[] args)
{
- godot_icall_Callable_CallDeferred(ref this, args);
+ using godot_callable callable = Marshaling.ConvertCallableToNative(this);
+
+ int argc = args.Length;
+
+ Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() :
+ new godot_variant.movable[argc];
+
+ Span<IntPtr> argsSpan = argc <= 10 ?
+ stackalloc IntPtr[argc] :
+ new IntPtr[argc];
+
+ using var variantSpanDisposer = new VariantSpanDisposer(argsStoreSpan);
+
+ fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
+ fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
+ {
+ for (int i = 0; i < argc; i++)
+ {
+ varargs[i] = (godot_variant)args[i].NativeVar;
+ argsPtr[i] = new IntPtr(&varargs[i]);
+ }
+
+ NativeFuncs.godotsharp_callable_call_deferred(callable, (godot_variant**)argsPtr, argc);
+ }
}
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index a6324504fc..ed0e1efd35 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -210,7 +210,7 @@ namespace Godot
case 3:
return a;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -230,7 +230,7 @@ namespace Godot
a = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -841,7 +841,7 @@ namespace Godot
return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
}
- private string ToHex32(float val)
+ private static string ToHex32(float val)
{
byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
return b.HexEncode();
@@ -849,7 +849,7 @@ namespace Godot
internal static bool HtmlIsValid(string color)
{
- if (color.Length == 0)
+ if (string.IsNullOrEmpty(color))
{
return false;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs
new file mode 100644
index 0000000000..42f19ace1a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs
@@ -0,0 +1,98 @@
+#nullable enable
+
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.Bridge;
+
+namespace Godot;
+
+/// <summary>
+/// Provides a GCHandle that becomes weak when unloading the assembly load context, without having
+/// to manually replace the GCHandle. This hides all the complexity of releasing strong GC handles
+/// to allow the assembly load context to unload properly.
+///
+/// Internally, a strong CustomGCHandle actually contains a weak GCHandle, while the actual strong
+/// reference is stored in a static table.
+/// </summary>
+public static class CustomGCHandle
+{
+ // ConditionalWeakTable uses DependentHandle, so it stores weak references.
+ // Having the assembly load context as key won't prevent it from unloading.
+ private static ConditionalWeakTable<AssemblyLoadContext, object?> _alcsBeingUnloaded = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static bool IsAlcBeingUnloaded(AssemblyLoadContext alc) => _alcsBeingUnloaded.TryGetValue(alc, out _);
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<
+ AssemblyLoadContext,
+ ConcurrentDictionary<GCHandle, object>
+ > _strongReferencesByAlc = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OnAlcUnloading(AssemblyLoadContext alc)
+ {
+ _alcsBeingUnloaded.Add(alc, null);
+
+ if (_strongReferencesByAlc.TryRemove(alc, out var strongReferences))
+ {
+ strongReferences.Clear();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static GCHandle AllocStrong(object value)
+ => AllocStrong(value, value.GetType());
+
+ public static GCHandle AllocStrong(object value, Type valueType)
+ {
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(valueType.Assembly);
+
+ if (alc != null)
+ {
+ var weakHandle = GCHandle.Alloc(value, GCHandleType.Weak);
+
+ if (!IsAlcBeingUnloaded(alc))
+ {
+ var strongReferences = _strongReferencesByAlc.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ strongReferences.TryAdd(weakHandle, value);
+ }
+
+ return weakHandle;
+ }
+ }
+
+ return GCHandle.Alloc(value, GCHandleType.Normal);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static GCHandle AllocWeak(object value) => GCHandle.Alloc(value, GCHandleType.Weak);
+
+ public static void Free(GCHandle handle)
+ {
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ var target = handle.Target;
+
+ if (target != null)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(target.GetType().Assembly);
+
+ if (alc != null && _strongReferencesByAlc.TryGetValue(alc, out var strongReferences))
+ _ = strongReferences.TryRemove(handle, out _);
+ }
+ }
+
+ handle.Free();
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
index edfe3464ec..6fbc04702a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
@@ -1,13 +1,18 @@
using System;
using System.Diagnostics;
using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
+using Godot.NativeInterop;
+
+#nullable enable
namespace Godot
{
internal static class DebuggingUtils
{
- internal static void AppendTypeName(this StringBuilder sb, Type type)
+ private static void AppendTypeName(this StringBuilder sb, Type type)
{
if (type.IsPrimitive)
sb.Append(type.Name);
@@ -16,21 +21,97 @@ namespace Godot
else
sb.Append(type);
- sb.Append(" ");
+ sb.Append(' ');
}
- public static void InstallTraceListener()
+ internal static void InstallTraceListener()
{
Trace.Listeners.Clear();
Trace.Listeners.Add(new GodotTraceListener());
}
- public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal ref struct godot_stack_info
+ {
+ public godot_string File;
+ public godot_string Func;
+ public int Line;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal ref struct godot_stack_info_vector
{
- fileName = frame.GetFileName();
- fileLineNumber = frame.GetFileLineNumber();
+ private IntPtr _writeProxy;
+ private unsafe godot_stack_info* _ptr;
+
+ public readonly unsafe godot_stack_info* Elements
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public void Resize(int size)
+ {
+ if (size < 0)
+ throw new ArgumentOutOfRangeException(nameof(size));
+ var err = NativeFuncs.godotsharp_stack_info_vector_resize(ref this, size);
+ if (err != Error.Ok)
+ throw new InvalidOperationException("Failed to resize vector. Error code is: " + err.ToString());
+ }
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_stack_info_vector_destroy(ref this);
+ _ptr = null;
+ }
+ }
- MethodBase methodBase = frame.GetMethod();
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetCurrentStackInfo(void* destVector)
+ {
+ try
+ {
+ var vector = (godot_stack_info_vector*)destVector;
+ var stackTrace = new StackTrace(skipFrames: 1, fNeedFileInfo: true);
+ int frameCount = stackTrace.FrameCount;
+
+ if (frameCount == 0)
+ return;
+
+ vector->Resize(frameCount);
+
+ int i = 0;
+ foreach (StackFrame frame in stackTrace.GetFrames())
+ {
+ string? fileName = frame.GetFileName();
+ int fileLineNumber = frame.GetFileLineNumber();
+
+ GetStackFrameMethodDecl(frame, out string methodDecl);
+
+ godot_stack_info* stackInfo = &vector->Elements[i];
+
+ // Assign directly to element in Vector. This way we don't need to worry
+ // about disposal if an exception is thrown. The Vector takes care of it.
+ stackInfo->File = Marshaling.ConvertStringToNative(fileName);
+ stackInfo->Func = Marshaling.ConvertStringToNative(methodDecl);
+ stackInfo->Line = fileLineNumber;
+
+ i++;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ internal static void GetStackFrameMethodDecl(StackFrame frame, out string methodDecl)
+ {
+ MethodBase? methodBase = frame.GetMethod();
if (methodBase == null)
{
@@ -43,15 +124,15 @@ namespace Godot
if (methodBase is MethodInfo)
sb.AppendTypeName(((MethodInfo)methodBase).ReturnType);
- sb.Append(methodBase.DeclaringType.FullName);
- sb.Append(".");
+ sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>");
+ sb.Append('.');
sb.Append(methodBase.Name);
if (methodBase.IsGenericMethod)
{
Type[] genericParams = methodBase.GetGenericArguments();
- sb.Append("<");
+ sb.Append('<');
for (int j = 0; j < genericParams.Length; j++)
{
@@ -61,10 +142,10 @@ namespace Godot
sb.AppendTypeName(genericParams[j]);
}
- sb.Append(">");
+ sb.Append('>');
}
- sb.Append("(");
+ sb.Append('(');
bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0;
@@ -81,7 +162,7 @@ namespace Godot
sb.AppendTypeName(parameter[i].ParameterType);
}
- sb.Append(")");
+ sb.Append(')');
methodDecl = sb.ToString();
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 1dca9e6ea7..266038a0af 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -1,14 +1,72 @@
+#nullable enable
+
using System;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
internal static class DelegateUtils
{
+ [UnmanagedCallersOnly]
+ internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
+ {
+ try
+ {
+ var @delegateA = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleA).Target;
+ var @delegateB = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleB).Target;
+ return (@delegateA! == @delegateB!).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
+ godot_variant* outRet)
+ {
+ try
+ {
+ // TODO: Optimize
+ var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
+ var managedArgs = new object?[argc];
+
+ var parameterInfos = @delegate.Method.GetParameters();
+ var paramsLength = parameterInfos.Length;
+
+ if (argc != paramsLength)
+ {
+ throw new InvalidOperationException(
+ $"The delegate expects {paramsLength} arguments, but received {argc}.");
+ }
+
+ for (uint i = 0; i < argc; i++)
+ {
+ managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType(
+ *args[i], parameterInfos[i].ParameterType);
+ }
+
+ object? invokeRet = @delegate.DynamicInvoke(managedArgs);
+
+ *outRet = Marshaling.ConvertManagedObjectToVariant(invokeRet);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRet = default;
+ }
+ }
+
+ // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).
+
private enum TargetKind : uint
{
Static,
@@ -39,20 +97,20 @@ namespace Godot
}
}
- if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
+ if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer))
{
- serializedData.Add(buffer);
+ serializedData.Add((Span<byte>)buffer);
return true;
}
return false;
}
- private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
+ private static bool TrySerializeSingleDelegate(Delegate @delegate, [MaybeNullWhen(false)] out byte[] buffer)
{
buffer = null;
- object target = @delegate.Target;
+ object? target = @delegate.Target;
switch (target)
{
@@ -72,12 +130,14 @@ namespace Godot
return true;
}
}
+ // ReSharper disable once RedundantNameQualifier
case Godot.Object godotObject:
{
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write((ulong)TargetKind.GodotObject);
+ // ReSharper disable once RedundantCast
writer.Write((ulong)godotObject.GetInstanceId());
SerializeType(writer, @delegate.GetType());
@@ -93,7 +153,7 @@ namespace Godot
{
Type targetType = target.GetType();
- if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
+ if (targetType.IsDefined(typeof(CompilerGeneratedAttribute), true))
{
// Compiler generated. Probably a closure. Try to serialize it.
@@ -121,8 +181,18 @@ namespace Godot
if (variantType == Variant.Type.Nil)
return false;
+ static byte[] Var2Bytes(in godot_variant var)
+ {
+ NativeFuncs.godotsharp_var2bytes(var, false.ToGodotBool(), out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
+ }
+
writer.Write(field.Name);
- byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
+
+ var fieldValue = field.GetValue(target);
+ using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue);
+ byte[] valueBuffer = Var2Bytes(fieldValueVariant);
writer.Write(valueBuffer.Length);
writer.Write(valueBuffer);
}
@@ -139,9 +209,6 @@ namespace Godot
private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
{
- if (methodInfo == null)
- return false;
-
SerializeType(writer, methodInfo.DeclaringType);
writer.Write(methodInfo.Name);
@@ -180,7 +247,7 @@ namespace Godot
return true;
}
- private static void SerializeType(BinaryWriter writer, Type type)
+ private static void SerializeType(BinaryWriter writer, Type? type)
{
if (type == null)
{
@@ -195,9 +262,8 @@ namespace Godot
int genericArgumentsCount = genericArgs.Length;
writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
+ writer.Write(genericTypeDef.Assembly.GetName().Name ?? "");
+ writer.Write(genericTypeDef.FullName ?? genericTypeDef.ToString());
for (int i = 0; i < genericArgs.Length; i++)
SerializeType(writer, genericArgs[i]);
@@ -207,17 +273,71 @@ namespace Godot
int genericArgumentsCount = 0;
writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = type.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
+ writer.Write(type.Assembly.GetName().Name ?? "");
+ writer.Write(type.FullName ?? type.ToString());
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle,
+ godot_array* nSerializedData)
+ {
+ try
+ {
+ var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
+
+ var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
+
+ return TrySerializeDelegate(@delegate, serializedData)
+ .ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
}
}
- private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool TryDeserializeDelegateWithGCHandle(godot_array* nSerializedData,
+ IntPtr* delegateGCHandle)
{
+ try
+ {
+ var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
+
+ if (TryDeserializeDelegate(serializedData, out Delegate? @delegate))
+ {
+ *delegateGCHandle = GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(@delegate));
+ return godot_bool.True;
+ }
+ else
+ {
+ *delegateGCHandle = IntPtr.Zero;
+ return godot_bool.False;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *delegateGCHandle = default;
+ return godot_bool.False;
+ }
+ }
+
+ internal static bool TryDeserializeDelegate(Collections.Array serializedData,
+ [MaybeNullWhen(false)] out Delegate @delegate)
+ {
+ @delegate = null;
+
if (serializedData.Count == 1)
{
- object elem = serializedData[0];
+ var elem = serializedData[0].Obj;
+
+ if (elem == null)
+ return false;
if (elem is Collections.Array multiCastData)
return TryDeserializeDelegate(multiCastData, out @delegate);
@@ -225,20 +345,23 @@ namespace Godot
return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
}
- @delegate = null;
-
var delegates = new List<Delegate>(serializedData.Count);
- foreach (object elem in serializedData)
+ foreach (Variant variantElem in serializedData)
{
+ var elem = variantElem.Obj;
+
+ if (elem == null)
+ continue;
+
if (elem is Collections.Array multiCastData)
{
- if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
+ if (TryDeserializeDelegate(multiCastData, out Delegate? oneDelegate))
delegates.Add(oneDelegate);
}
else
{
- if (TryDeserializeSingleDelegate((byte[])elem, out Delegate oneDelegate))
+ if (TryDeserializeSingleDelegate((byte[])elem, out Delegate? oneDelegate))
delegates.Add(oneDelegate);
}
}
@@ -246,11 +369,11 @@ namespace Godot
if (delegates.Count <= 0)
return false;
- @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
+ @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray())!;
return true;
}
- private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
+ private static bool TryDeserializeSingleDelegate(byte[] buffer, [MaybeNullWhen(false)] out Delegate @delegate)
{
@delegate = null;
@@ -263,49 +386,59 @@ namespace Godot
{
case TargetKind.Static:
{
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo, throwOnBindFailure: false);
+
+ if (@delegate == null)
return false;
- @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
return true;
}
case TargetKind.GodotObject:
{
ulong objectId = reader.ReadUInt64();
+ // ReSharper disable once RedundantNameQualifier
Godot.Object godotObject = GD.InstanceFromId(objectId);
if (godotObject == null)
return false;
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo,
+ throwOnBindFailure: false);
+
+ if (@delegate == null)
return false;
- @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
return true;
}
case TargetKind.CompilerGenerated:
{
- Type targetType = DeserializeType(reader);
+ Type? targetType = DeserializeType(reader);
if (targetType == null)
return false;
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
return false;
int fieldCount = reader.ReadInt32();
- object recreatedTarget = Activator.CreateInstance(targetType);
+ object recreatedTarget = Activator.CreateInstance(targetType)!;
for (int i = 0; i < fieldCount; i++)
{
@@ -313,11 +446,17 @@ namespace Godot
int valueBufferLength = reader.ReadInt32();
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
- FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
+ FieldInfo? fieldInfo = targetType.GetField(name,
+ BindingFlags.Instance | BindingFlags.Public);
fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
}
- @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
+ @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
+ throwOnBindFailure: false);
+
+ if (@delegate == null)
+ return false;
+
return true;
}
default:
@@ -326,18 +465,22 @@ namespace Godot
}
}
- private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
+ private static bool TryDeserializeMethodInfo(BinaryReader reader,
+ [MaybeNullWhen(false)] out MethodInfo methodInfo)
{
methodInfo = null;
- Type declaringType = DeserializeType(reader);
+ Type? declaringType = DeserializeType(reader);
+
+ if (declaringType == null)
+ return false;
string methodName = reader.ReadString();
int flags = reader.ReadInt32();
bool hasReturn = reader.ReadBoolean();
- Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
+ Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void);
int parametersCount = reader.ReadInt32();
@@ -347,7 +490,7 @@ namespace Godot
for (int i = 0; i < parametersCount; i++)
{
- Type parameterType = DeserializeType(reader);
+ Type? parameterType = DeserializeType(reader);
if (parameterType == null)
return false;
parameterTypes[i] = parameterType;
@@ -361,15 +504,23 @@ namespace Godot
return methodInfo != null && methodInfo.ReturnType == returnType;
}
- private static Type DeserializeType(BinaryReader reader)
+ private static Type? DeserializeType(BinaryReader reader)
{
int genericArgumentsCount = reader.ReadInt32();
if (genericArgumentsCount == -1)
return null;
- string assemblyQualifiedName = reader.ReadString();
- var type = Type.GetType(assemblyQualifiedName);
+ string assemblyName = reader.ReadString();
+
+ if (assemblyName.Length == 0)
+ {
+ GD.PushError($"Missing assembly name of type when attempting to deserialize delegate");
+ return null;
+ }
+
+ string typeFullName = reader.ReadString();
+ var type = ReflectionUtils.FindTypeInLoadedAssemblies(assemblyName, typeFullName);
if (type == null)
return null; // Type not found
@@ -380,7 +531,7 @@ namespace Godot
for (int i = 0; i < genericArgumentsCount; i++)
{
- Type genericArgumentType = DeserializeType(reader);
+ Type? genericArgumentType = DeserializeType(reader);
if (genericArgumentType == null)
return null;
genericArgumentTypes[i] = genericArgumentType;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index e80b6af68f..fa8c94ed18 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -1,79 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot.Collections
{
- internal class DictionarySafeHandle : SafeHandle
- {
- public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
- {
- this.handle = handle;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- protected override bool ReleaseHandle()
- {
- Dictionary.godot_icall_Dictionary_Dtor(handle);
- return true;
- }
- }
-
/// <summary>
/// Wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine.
/// </summary>
- public class Dictionary : IDictionary, IDisposable
+ public sealed class Dictionary :
+ IDictionary<Variant, Variant>,
+ IReadOnlyDictionary<Variant, Variant>,
+ IDisposable
{
- private DictionarySafeHandle _safeHandle;
- private bool _disposed = false;
+ internal godot_dictionary.movable NativeValue;
+
+ private WeakReference<IDisposable> _weakReferenceToSelf;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
- _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- /// <summary>
- /// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements.
- /// </summary>
- /// <param name="dictionary">The dictionary to construct from.</param>
- /// <returns>A new Godot Dictionary.</returns>
- public Dictionary(IDictionary dictionary) : this()
- {
- if (dictionary == null)
- throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
-
- foreach (DictionaryEntry entry in dictionary)
- Add(entry.Key, entry.Value);
- }
-
- internal Dictionary(DictionarySafeHandle handle)
+ private Dictionary(godot_dictionary nativeValueToOwn)
{
- _safeHandle = handle;
+ NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ?
+ nativeValueToOwn :
+ NativeFuncs.godotsharp_dictionary_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- internal Dictionary(IntPtr handle)
- {
- _safeHandle = new DictionarySafeHandle(handle);
- }
+ // Explicit name to make it very clear
+ internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn)
+ => new Dictionary(nativeValueToOwn);
- internal IntPtr GetPtr()
+ ~Dictionary()
{
- if (_disposed)
- throw new ObjectDisposedException(GetType().FullName);
-
- return _safeHandle.DangerousGetHandle();
+ Dispose(false);
}
/// <summary>
@@ -81,16 +52,19 @@ namespace Godot.Collections
/// </summary>
public void Dispose()
{
- if (_disposed)
- return;
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
- if (_safeHandle != null)
+ public void Dispose(bool disposing)
+ {
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
{
- _safeHandle.Dispose();
- _safeHandle = null;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
-
- _disposed = true;
}
/// <summary>
@@ -100,7 +74,10 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary Duplicate(bool deep = false)
{
- return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
+ godot_dictionary newDictionary;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_duplicate(ref self, deep.ToGodotBool(), out newDictionary);
+ return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
// IDictionary
@@ -108,176 +85,250 @@ namespace Godot.Collections
/// <summary>
/// Gets the collection of keys in this <see cref="Dictionary"/>.
/// </summary>
- public ICollection Keys
+ public ICollection<Variant> Keys
{
get
{
- IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
- return new Array(new ArraySafeHandle(handle));
+ godot_array keysArray;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
+ return Array.CreateTakingOwnershipOfDisposableValue(keysArray);
}
}
/// <summary>
/// Gets the collection of elements in this <see cref="Dictionary"/>.
/// </summary>
- public ICollection Values
+ public ICollection<Variant> Values
{
get
{
- IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
- return new Array(new ArraySafeHandle(handle));
+ godot_array valuesArray;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
+ return Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
+ IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Keys => Keys;
+
+ IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Values => Values;
+
private (Array keys, Array values, int count) GetKeyValuePairs()
{
- int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle);
- Array keys = new Array(new ArraySafeHandle(keysHandle));
- Array values = new Array(new ArraySafeHandle(valuesHandle));
- return (keys, values, count);
- }
+ var self = (godot_dictionary)NativeValue;
- bool IDictionary.IsFixedSize => false;
+ godot_array keysArray;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
+ var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray);
- bool IDictionary.IsReadOnly => false;
+ godot_array valuesArray;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out valuesArray);
+ var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
+
+ int count = NativeFuncs.godotsharp_dictionary_count(ref self);
+
+ return (keys, values, count);
+ }
/// <summary>
- /// Returns the object at the given <paramref name="key"/>.
+ /// Returns the value at the given <paramref name="key"/>.
/// </summary>
- /// <value>The object at the given <paramref name="key"/>.</value>
- public object this[object key]
+ /// <value>The value at the given <paramref name="key"/>.</value>
+ public Variant this[Variant key]
{
- get => godot_icall_Dictionary_GetValue(GetPtr(), key);
- set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
+ get
+ {
+ var self = (godot_dictionary)NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ (godot_variant)key.NativeVar, out godot_variant value).ToBool())
+ {
+ return Variant.CreateTakingOwnershipOfDisposableValue(value);
+ }
+ else
+ {
+ throw new KeyNotFoundException();
+ }
+ }
+ set
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_set_value(ref self,
+ (godot_variant)key.NativeVar, (godot_variant)value.NativeVar);
+ }
}
/// <summary>
- /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// Adds an value <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary"/>.
/// </summary>
- /// <param name="key">The key at which to add the object.</param>
- /// <param name="value">The object to add.</param>
- public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value);
+ /// <param name="key">The key at which to add the value.</param>
+ /// <param name="value">The value to add.</param>
+ public void Add(Variant key, Variant value)
+ {
+ var variantKey = (godot_variant)key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
+ throw new ArgumentException("An element with the same key already exists", nameof(key));
+
+ godot_variant variantValue = (godot_variant)value.NativeVar;
+ NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
+ }
+
+ void ICollection<KeyValuePair<Variant, Variant>>.Add(KeyValuePair<Variant, Variant> item)
+ => Add(item.Key, item.Value);
/// <summary>
/// Erases all items from this <see cref="Dictionary"/>.
/// </summary>
- public void Clear() => godot_icall_Dictionary_Clear(GetPtr());
+ public void Clear()
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_clear(ref self);
+ }
/// <summary>
/// Checks if this <see cref="Dictionary"/> contains the given key.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
- public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ public bool ContainsKey(Variant key)
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool();
+ }
- /// <summary>
- /// Gets an enumerator for this <see cref="Dictionary"/>.
- /// </summary>
- /// <returns>An enumerator.</returns>
- public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this);
+ public bool Contains(KeyValuePair<Variant, Variant> item)
+ {
+ godot_variant variantKey = (godot_variant)item.Key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ godot_variant variantValue = (godot_variant)item.Value.NativeVar;
+ return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
+ }
+ }
/// <summary>
/// Removes an element from this <see cref="Dictionary"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
- public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ public bool Remove(Variant key)
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool();
+ }
- // ICollection
+ public bool Remove(KeyValuePair<Variant, Variant> item)
+ {
+ godot_variant variantKey = (godot_variant)item.Key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
- object ICollection.SyncRoot => this;
+ godot_variant variantValue = (godot_variant)item.Value.NativeVar;
+ if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
+ {
+ return NativeFuncs.godotsharp_dictionary_remove_key(
+ ref self, variantKey).ToBool();
+ }
- bool ICollection.IsSynchronized => false;
+ return false;
+ }
+ }
/// <summary>
/// Returns the number of elements in this <see cref="Dictionary"/>.
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count => godot_icall_Dictionary_Count(GetPtr());
+ public int Count
+ {
+ get
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_count(ref self);
+ }
+ }
+
+ bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false;
+
+ public bool TryGetValue(Variant key, out Variant value)
+ {
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ (godot_variant)key.NativeVar, out godot_variant retValue).ToBool();
+
+ value = found ? Variant.CreateTakingOwnershipOfDisposableValue(retValue) : default;
+
+ return found;
+ }
/// <summary>
- /// Copies the elements of this <see cref="Dictionary"/> to the given
- /// untyped C# array, starting at the given index.
+ /// Copies the elements of this <see cref="Dictionary"/> to the given untyped
+ /// <see cref="KeyValuePair{TKey, TValue}"/> array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
- /// <param name="index">The index to start at.</param>
- public void CopyTo(System.Array array, int index)
+ /// <param name="arrayIndex">The index to start at.</param>
+ public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
var (keys, values, count) = GetKeyValuePairs();
- if (array.Length < (index + count))
- throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ if (array.Length < (arrayIndex + count))
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
- array.SetValue(new DictionaryEntry(keys[i], values[i]), index);
- index++;
+ array[arrayIndex] = new(keys[i], values[i]);
+ arrayIndex++;
}
}
// IEnumerable
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- private class DictionaryEnumerator : IDictionaryEnumerator
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
+ public IEnumerator<KeyValuePair<Variant, Variant>> GetEnumerator()
{
- private readonly Dictionary _dictionary;
- private readonly int _count;
- private int _index = -1;
- private bool _dirty = true;
-
- private DictionaryEntry _entry;
-
- public DictionaryEnumerator(Dictionary dictionary)
- {
- _dictionary = dictionary;
- _count = dictionary.Count;
- }
-
- public object Current => Entry;
-
- public DictionaryEntry Entry
- {
- get
- {
- if (_dirty)
- {
- UpdateEntry();
- }
- return _entry;
- }
- }
-
- private void UpdateEntry()
+ for (int i = 0; i < Count; i++)
{
- _dirty = false;
- godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value);
- _entry = new DictionaryEntry(key, value);
+ yield return GetKeyValuePair(i);
}
+ }
- public object Key => Entry.Key;
-
- public object Value => Entry.Value;
-
- public bool MoveNext()
- {
- _index++;
- _dirty = true;
- return _index < _count;
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- public void Reset()
- {
- _index = -1;
- _dirty = true;
- }
+ private KeyValuePair<Variant, Variant> GetKeyValuePair(int index)
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
+ out godot_variant key,
+ out godot_variant value);
+ return new KeyValuePair<Variant, Variant>(Variant.CreateTakingOwnershipOfDisposableValue(key),
+ Variant.CreateTakingOwnershipOfDisposableValue(value));
}
/// <summary>
@@ -286,74 +337,11 @@ namespace Godot.Collections
/// <returns>A string representation of this dictionary.</returns>
public override string ToString()
{
- return godot_icall_Dictionary_ToString(GetPtr());
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_to_string(ref self, out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Ctor();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Dictionary_Count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_KeyValuePairAt_Generic(IntPtr ptr, int index, out object key, out object value, int valueTypeEncoding, IntPtr valueTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr);
}
/// <summary>
@@ -364,16 +352,51 @@ namespace Godot.Collections
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
- public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>
+ public class Dictionary<TKey, TValue> :
+ IDictionary<TKey, TValue>,
+ IReadOnlyDictionary<TKey, TValue>
{
- private readonly Dictionary _objectDict;
+ // ReSharper disable StaticMemberInGenericType
+ // Warning is about unique static fields being created for each generic type combination:
+ // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
+ // In our case this is exactly what we want.
+
+ private static unsafe delegate* managed<in TKey, godot_variant> _convertKeyToVariantCallback;
+ private static unsafe delegate* managed<in godot_variant, TKey> _convertKeyToManagedCallback;
+ private static unsafe delegate* managed<in TValue, godot_variant> _convertValueToVariantCallback;
+ private static unsafe delegate* managed<in godot_variant, TValue> _convertValueToManagedCallback;
+
+ // ReSharper restore StaticMemberInGenericType
+
+ static unsafe Dictionary()
+ {
+ _convertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
+ _convertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
+ _convertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
+ _convertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
+ }
+
+ private static unsafe void ValidateVariantConversionCallbacks()
+ {
+ if (_convertKeyToVariantCallback == null || _convertKeyToManagedCallback == null)
+ {
+ throw new InvalidOperationException(
+ $"The dictionary key type is not supported for conversion to Variant: '{typeof(TKey).FullName}'");
+ }
+
+ if (_convertValueToVariantCallback == null || _convertValueToManagedCallback == null)
+ {
+ throw new InvalidOperationException(
+ $"The dictionary value type is not supported for conversion to Variant: '{typeof(TValue).FullName}'");
+ }
+ }
- internal static int valTypeEncoding;
- internal static IntPtr valTypeClass;
+ private readonly Dictionary _underlyingDict;
- static Dictionary()
+ internal ref godot_dictionary.movable NativeValue
{
- Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _underlyingDict.NativeValue;
}
/// <summary>
@@ -381,7 +404,9 @@ namespace Godot.Collections
/// </summary>
public Dictionary()
{
- _objectDict = new Dictionary();
+ ValidateVariantConversionCallbacks();
+
+ _underlyingDict = new Dictionary();
}
/// <summary>
@@ -391,19 +416,15 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
- _objectDict = new Dictionary();
+ ValidateVariantConversionCallbacks();
if (dictionary == null)
- throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
-
- // TODO: Can be optimized
+ throw new ArgumentNullException(nameof(dictionary));
- IntPtr godotDictionaryPtr = GetPtr();
+ _underlyingDict = new Dictionary();
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
- {
- Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
- }
+ Add(entry.Key, entry.Value);
}
/// <summary>
@@ -413,18 +434,15 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
- _objectDict = dictionary;
- }
+ ValidateVariantConversionCallbacks();
- internal Dictionary(IntPtr handle)
- {
- _objectDict = new Dictionary(handle);
+ _underlyingDict = dictionary;
}
- internal Dictionary(DictionarySafeHandle handle)
- {
- _objectDict = new Dictionary(handle);
- }
+ // Explicit name to make it very clear
+ internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
+ godot_dictionary nativeValueToOwn)
+ => new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
@@ -432,12 +450,7 @@ namespace Godot.Collections
/// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
- return from._objectDict;
- }
-
- internal IntPtr GetPtr()
- {
- return _objectDict.GetPtr();
+ return from?._underlyingDict;
}
/// <summary>
@@ -447,7 +460,7 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
- return new Dictionary<TKey, TValue>(_objectDict.Duplicate(deep));
+ return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
}
// IDictionary<TKey, TValue>
@@ -456,10 +469,32 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
/// <value>The value at the given <paramref name="key"/>.</value>
- public TValue this[TKey key]
+ public unsafe TValue this[TKey key]
{
- get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
- set { _objectDict[key] = value; }
+ get
+ {
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant value).ToBool())
+ {
+ using (value)
+ return _convertValueToManagedCallback(value);
+ }
+ else
+ {
+ throw new KeyNotFoundException();
+ }
+ }
+ set
+ {
+ using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantValue = _convertValueToVariantCallback(value);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_set_value(ref self,
+ variantKey, variantValue);
+ }
}
/// <summary>
@@ -469,8 +504,10 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr());
- return new Array<TKey>(new ArraySafeHandle(handle));
+ godot_array keyArray;
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray);
+ return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
}
}
@@ -481,15 +518,30 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr());
- return new Array<TValue>(new ArraySafeHandle(handle));
+ godot_array valuesArray;
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
+ return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
- private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
+
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
+
+ private unsafe KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
- Dictionary.godot_icall_Dictionary_KeyValuePairAt_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass);
- return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
+ out godot_variant key,
+ out godot_variant value);
+ using (key)
+ using (value)
+ {
+ return new KeyValuePair<TKey, TValue>(
+ _convertKeyToManagedCallback(key),
+ _convertValueToManagedCallback(value));
+ }
}
/// <summary>
@@ -498,9 +550,16 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
- public void Add(TKey key, TValue value)
+ public unsafe void Add(TKey key, TValue value)
{
- _objectDict.Add(key, value);
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
+ throw new ArgumentException("An element with the same key already exists", nameof(key));
+
+ using var variantValue = _convertValueToVariantCallback(value);
+ NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
}
/// <summary>
@@ -508,18 +567,22 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
- public bool ContainsKey(TKey key)
+ public unsafe bool ContainsKey(TKey key)
{
- return _objectDict.Contains(key);
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
}
/// <summary>
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
- public bool Remove(TKey key)
+ public unsafe bool Remove(TKey key)
{
- return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
}
/// <summary>
@@ -528,10 +591,16 @@ namespace Godot.Collections
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
- public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
+ public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
- bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass);
- value = found ? (TValue)retValue : default;
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ value = found ? _convertValueToManagedCallback(retValue) : default;
+
return found;
}
@@ -542,29 +611,33 @@ namespace Godot.Collections
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count
- {
- get { return _objectDict.Count; }
- }
+ public int Count => _underlyingDict.Count;
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
- {
- _objectDict.Add(item.Key, item.Value);
- }
+ => Add(item.Key, item.Value);
/// <summary>
/// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
- public void Clear()
- {
- _objectDict.Clear();
- }
+ public void Clear() => _underlyingDict.Clear();
- bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
+ unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
- return _objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ using var variantKey = _convertKeyToVariantCallback(item.Key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ using var variantValue = _convertValueToVariantCallback(item.Value);
+ return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
+ }
}
/// <summary>
@@ -579,12 +652,14 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
int count = Count;
if (array.Length < (arrayIndex + count))
- throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
@@ -593,10 +668,27 @@ namespace Godot.Collections
}
}
- bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
+ unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
- return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
- ;
+ using var variantKey = _convertKeyToVariantCallback(item.Key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ using var variantValue = _convertValueToVariantCallback(item.Value);
+ if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
+ {
+ return NativeFuncs.godotsharp_dictionary_remove_key(
+ ref self, variantKey).ToBool();
+ }
+
+ return false;
+ }
}
// IEnumerable<KeyValuePair<TKey, TValue>>
@@ -613,15 +705,18 @@ namespace Godot.Collections
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
- public override string ToString() => _objectDict.ToString();
+ public override string ToString() => _underlyingDict.ToString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.CreateFrom(from);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotDictionary<TKey, TValue>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index 6475237002..e6cb4171a7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -1,20 +1,16 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
public static class Dispatcher
{
- /// <summary>
- /// Implements an external instance of GodotTaskScheduler.
- /// </summary>
- /// <returns>A GodotTaskScheduler instance.</returns>
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler();
+ internal static GodotTaskScheduler DefaultGodotTaskScheduler;
- /// <summary>
- /// Initializes the synchronization context as the context of the GodotTaskScheduler.
- /// </summary>
- public static GodotSynchronizationContext SynchronizationContext =>
- godot_icall_DefaultGodotTaskScheduler().Context;
+ internal static void InitializeDefaultGodotTaskScheduler()
+ => DefaultGodotTaskScheduler = new GodotTaskScheduler();
+
+ public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
new file mode 100644
index 0000000000..75793ea446
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+#nullable enable
+
+namespace Godot
+{
+ internal static class DisposablesTracker
+ {
+ [UnmanagedCallersOnly]
+ internal static void OnGodotShuttingDown()
+ {
+ try
+ {
+ OnGodotShuttingDownImpl();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static void OnGodotShuttingDownImpl()
+ {
+ bool isStdoutVerbose;
+
+ try
+ {
+ isStdoutVerbose = OS.IsStdoutVerbose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // OS singleton already disposed. Maybe OnUnloading was called twice.
+ isStdoutVerbose = false;
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Disposing tracked instances...");
+
+ // Dispose Godot Objects first, and only then dispose other disposables
+ // like StringName, NodePath, Godot.Collections.Array/Dictionary, etc.
+ // The Godot Object Dispose() method may need any of the later instances.
+
+ foreach (WeakReference<Object> item in GodotObjectInstances.Keys)
+ {
+ if (item.TryGetTarget(out Object? self))
+ self.Dispose();
+ }
+
+ foreach (WeakReference<IDisposable> item in OtherInstances.Keys)
+ {
+ if (item.TryGetTarget(out IDisposable? self))
+ self.Dispose();
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Finished disposing tracked instances.");
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<WeakReference<Godot.Object>, byte> GodotObjectInstances { get; } =
+ new();
+
+ private static ConcurrentDictionary<WeakReference<IDisposable>, byte> OtherInstances { get; } =
+ new();
+
+ public static WeakReference<Object> RegisterGodotObject(Object godotObject)
+ {
+ var weakReferenceToSelf = new WeakReference<Object>(godotObject);
+ GodotObjectInstances.TryAdd(weakReferenceToSelf, 0);
+ return weakReferenceToSelf;
+ }
+
+ public static WeakReference<IDisposable> RegisterDisposable(IDisposable disposable)
+ {
+ var weakReferenceToSelf = new WeakReference<IDisposable>(disposable);
+ OtherInstances.TryAdd(weakReferenceToSelf, 0);
+ return weakReferenceToSelf;
+ }
+
+ public static void UnregisterGodotObject(Object godotObject, WeakReference<Object> weakReferenceToSelf)
+ {
+ if (!GodotObjectInstances.TryRemove(weakReferenceToSelf, out _))
+ throw new ArgumentException("Godot Object not registered", nameof(weakReferenceToSelf));
+ }
+
+ public static void UnregisterDisposable(WeakReference<IDisposable> weakReference)
+ {
+ if (!OtherInstances.TryRemove(weakReference, out _))
+ throw new ArgumentException("Disposable not registered", nameof(weakReference));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
deleted file mode 100644
index 26d5f9c796..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Dynamic;
-using System.Linq.Expressions;
-using System.Runtime.CompilerServices;
-
-namespace Godot
-{
- /// <summary>
- /// Represents an <see cref="Object"/> whose members can be dynamically accessed at runtime through the Variant API.
- /// </summary>
- /// <remarks>
- /// <para>
- /// The <see cref="DynamicGodotObject"/> class enables access to the Variant
- /// members of a <see cref="Object"/> instance at runtime.
- /// </para>
- /// <para>
- /// This allows accessing the class members using their original names in the engine as well as the members from the
- /// script attached to the <see cref="Object"/>, regardless of the scripting language it was written in.
- /// </para>
- /// </remarks>
- /// <example>
- /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Object"/>.
- /// <code>
- /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject;
- /// sprite.add_child(this);
- ///
- /// if ((sprite.hframes * sprite.vframes) &gt; 0)
- /// sprite.frame = 0;
- /// </code>
- /// </example>
- /// <example>
- /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Object"/>.
- /// <code>
- /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject;
- ///
- /// if (childNode.print_allowed)
- /// {
- /// childNode.message = "Hello from C#";
- /// childNode.print_message(3);
- /// }
- /// </code>
- /// The <c>ChildNode</c> node has the following GDScript script attached:
- /// <code>
- /// // # ChildNode.gd
- /// // var print_allowed = true
- /// // var message = ""
- /// //
- /// // func print_message(times):
- /// // for i in times:
- /// // print(message)
- /// </code>
- /// </example>
- public class DynamicGodotObject : DynamicObject
- {
- /// <summary>
- /// Gets the <see cref="Object"/> associated with this <see cref="DynamicGodotObject"/>.
- /// </summary>
- public Object Value { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DynamicGodotObject"/> class.
- /// </summary>
- /// <param name="godotObject">
- /// The <see cref="Object"/> that will be associated with this <see cref="DynamicGodotObject"/>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// Thrown when the <paramref name="godotObject"/> parameter is <see langword="null"/>.
- /// </exception>
- public DynamicGodotObject(Object godotObject)
- {
- if (godotObject == null)
- throw new ArgumentNullException(nameof(godotObject));
-
- Value = godotObject;
- }
-
- /// <inheritdoc/>
- public override IEnumerable<string> GetDynamicMemberNames()
- {
- return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value));
- }
-
- /// <inheritdoc/>
- public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
- {
- switch (binder.Operation)
- {
- case ExpressionType.Equal:
- case ExpressionType.NotEqual:
- if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool)))
- {
- if (arg == null)
- {
- bool boolResult = Object.IsInstanceValid(Value);
-
- if (binder.Operation == ExpressionType.Equal)
- boolResult = !boolResult;
-
- result = boolResult;
- return true;
- }
-
- if (arg is Object other)
- {
- bool boolResult = (Value == other);
-
- if (binder.Operation == ExpressionType.NotEqual)
- boolResult = !boolResult;
-
- result = boolResult;
- return true;
- }
- }
-
- break;
- default:
- // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual).
- // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly.
- break;
- }
-
- return base.TryBinaryOperation(binder, arg, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryConvert(ConvertBinder binder, out object result)
- {
- if (binder.Type == typeof(Object))
- {
- result = Value;
- return true;
- }
-
- if (typeof(Object).IsAssignableFrom(binder.Type))
- {
- // Throws InvalidCastException when the cast fails
- result = Convert.ChangeType(Value, binder.Type);
- return true;
- }
-
- return base.TryConvert(binder, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
- {
- if (indexes.Length == 1)
- {
- if (indexes[0] is string name)
- {
- return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result);
- }
- }
-
- return base.TryGetIndex(binder, indexes, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryGetMember(GetMemberBinder binder, out object result)
- {
- return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
- {
- return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result);
- }
-
- /// <inheritdoc/>
- public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
- {
- if (indexes.Length == 1)
- {
- if (indexes[0] is string name)
- {
- return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value);
- }
- }
-
- return base.TrySetIndex(binder, indexes, value);
- }
-
- /// <inheritdoc/>
- public override bool TrySetMember(SetMemberBinder binder, object value)
- {
- return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value);
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value);
-
- #region We don't override these methods
-
- // Looks like this is not usable from C#
- //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);
-
- // Object members cannot be deleted
- //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);
- //public override bool TryDeleteMember(DeleteMemberBinder binder);
-
- // Invocation on the object itself, e.g.: obj(param)
- //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result);
-
- // No unnary operations to handle
- //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
-
- #endregion
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
index 691fd85964..4094ceeb22 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -32,10 +32,17 @@ namespace Godot
/// </returns>
public static WeakRef WeakRef(Object obj)
{
- return godot_icall_Object_weakref(GetPtr(obj));
- }
+ if (!IsInstanceValid(obj))
+ return null;
+
+ NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef);
+ using (weakRef)
+ {
+ if (weakRef.IsNull)
+ return null;
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj);
+ return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef.Reference);
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
deleted file mode 100644
index df130a5c77..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using Godot.Collections;
-
-namespace Godot
-{
- public partial class SceneTree
- {
- /// <summary>
- /// Returns a list of all nodes assigned to the given <paramref name="group"/>.
- /// </summary>
- /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
- public Array<T> GetNodesInGroup<T>(StringName group) where T : class
- {
- return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T)));
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 236d0666bc..9348cc1d00 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -1,11 +1,6 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Collections.Generic;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -26,9 +21,11 @@ namespace Godot
/// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param>
/// <param name="allowObjects">If objects should be decoded.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
- public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
+ public static Variant Bytes2Var(Span<byte> bytes, bool allowObjects = false)
{
- return godot_icall_GD_bytes2var(bytes, allowObjects);
+ using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
+ NativeFuncs.godotsharp_bytes2var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@@ -46,9 +43,10 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
- public static object Convert(object what, Variant.Type type)
+ public static Variant Convert(Variant what, Variant.Type type)
{
- return godot_icall_GD_convert(what, type);
+ NativeFuncs.godotsharp_convert((godot_variant)what.NativeVar, (int)type, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@@ -62,7 +60,7 @@ namespace Godot
return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
- private static object[] GetPrintParams(object[] parameters)
+ private static string[] GetPrintParams(object[] parameters)
{
if (parameters == null)
{
@@ -82,9 +80,9 @@ namespace Godot
/// </example>
/// <param name="var">Variable that will be hashed.</param>
/// <returns>Hash of the variable passed.</returns>
- public static int Hash(object var)
+ public static int Hash(Variant var)
{
- return godot_icall_GD_hash(var);
+ return NativeFuncs.godotsharp_hash((godot_variant)var.NativeVar);
}
/// <summary>
@@ -110,7 +108,7 @@ namespace Godot
/// <returns>The <see cref="Object"/> instance.</returns>
public static Object InstanceFromId(ulong instanceId)
{
- return godot_icall_GD_instance_from_id(instanceId);
+ return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
}
/// <summary>
@@ -202,7 +200,8 @@ namespace Godot
/// <param name="message">Error message.</param>
public static void PushError(string message)
{
- godot_icall_GD_pusherror(message);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pusherror(godotStr);
}
/// <summary>
@@ -214,7 +213,8 @@ namespace Godot
/// <param name="message">Warning message.</param>
public static void PushWarning(string message)
{
- godot_icall_GD_pushwarning(message);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pushwarning(godotStr);
}
/// <summary>
@@ -235,7 +235,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void Print(params object[] what)
{
- godot_icall_GD_print(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_print(godotStr);
}
/// <summary>
@@ -264,7 +266,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRich(params object[] what)
{
- godot_icall_GD_print_rich(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_print_rich(godotStr);
}
/// <summary>
@@ -286,7 +290,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printerr(godotStr);
}
/// <summary>
@@ -306,7 +312,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printraw(godotStr);
}
/// <summary>
@@ -320,7 +328,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(GetPrintParams(what));
+ string str = string.Join(' ', GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_prints(godotStr);
}
/// <summary>
@@ -334,7 +344,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(GetPrintParams(what));
+ string str = string.Join('\t', GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printt(godotStr);
}
/// <summary>
@@ -348,7 +360,7 @@ namespace Godot
/// <returns>A random <see langword="float"/> number.</returns>
public static float Randf()
{
- return godot_icall_GD_randf();
+ return NativeFuncs.godotsharp_randf();
}
/// <summary>
@@ -358,7 +370,7 @@ namespace Godot
/// <returns>A random normally-distributed <see langword="float"/> number.</returns>
public static double Randfn(double mean, double deviation)
{
- return godot_icall_GD_randfn(mean, deviation);
+ return NativeFuncs.godotsharp_randfn(mean, deviation);
}
/// <summary>
@@ -376,7 +388,7 @@ namespace Godot
/// <returns>A random <see langword="uint"/> number.</returns>
public static uint Randi()
{
- return godot_icall_GD_randi();
+ return NativeFuncs.godotsharp_randi();
}
/// <summary>
@@ -389,7 +401,7 @@ namespace Godot
/// </summary>
public static void Randomize()
{
- godot_icall_GD_randomize();
+ NativeFuncs.godotsharp_randomize();
}
/// <summary>
@@ -404,7 +416,7 @@ namespace Godot
/// <returns>A random <see langword="double"/> number inside the given range.</returns>
public static double RandRange(double from, double to)
{
- return godot_icall_GD_randf_range(from, to);
+ return NativeFuncs.godotsharp_randf_range(from, to);
}
/// <summary>
@@ -421,7 +433,7 @@ namespace Godot
/// <returns>A random <see langword="int"/> number inside the given range.</returns>
public static int RandRange(int from, int to)
{
- return godot_icall_GD_randi_range(from, to);
+ return NativeFuncs.godotsharp_randi_range(from, to);
}
/// <summary>
@@ -434,7 +446,7 @@ namespace Godot
/// <returns>A random <see langword="uint"/> number.</returns>
public static uint RandFromSeed(ref ulong seed)
{
- return godot_icall_GD_rand_seed(seed, out seed);
+ return NativeFuncs.godotsharp_rand_from_seed(seed, out seed);
}
/// <summary>
@@ -491,7 +503,7 @@ namespace Godot
/// <param name="seed">Seed that will be used.</param>
public static void Seed(ulong seed)
{
- godot_icall_GD_seed(seed);
+ NativeFuncs.godotsharp_seed(seed);
}
/// <summary>
@@ -499,13 +511,16 @@ namespace Godot
/// </summary>
/// <param name="what">Arguments that will converted to string.</param>
/// <returns>The string formed by the given arguments.</returns>
- public static string Str(params object[] what)
+ public static string Str(params Variant[] what)
{
- return godot_icall_GD_str(what);
+ using var whatGodot = new Godot.Collections.Array(what);
+ NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret);
+ using (ret)
+ return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
- /// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value.
+ /// Converts a formatted string that was returned by <see cref="Var2Str(Variant)"/> to the original value.
/// </summary>
/// <example>
/// <code>
@@ -516,32 +531,27 @@ namespace Godot
/// </example>
/// <param name="str">String that will be converted to Variant.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
- public static object Str2Var(string str)
+ public static Variant Str2Var(string str)
{
- return godot_icall_GD_str2var(str);
- }
-
- /// <summary>
- /// Returns whether the given class exists in <see cref="ClassDB"/>.
- /// </summary>
- /// <returns>If the class exists in <see cref="ClassDB"/>.</returns>
- public static bool TypeExists(StringName type)
- {
- return godot_icall_GD_type_exists(StringName.GetPtr(type));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_str2var(godotStr, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
/// Encodes a <c>Variant</c> value to a byte array.
/// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed
/// (and can potentially include code).
- /// Deserialization can be done with <see cref="Bytes2Var(byte[], bool)"/>.
+ /// Deserialization can be done with <see cref="Bytes2Var(Span{byte}, bool)"/>.
/// </summary>
/// <param name="var">Variant that will be encoded.</param>
/// <param name="fullObjects">If objects should be serialized.</param>
/// <returns>The <c>Variant</c> encoded as an array of bytes.</returns>
- public static byte[] Var2Bytes(object var, bool fullObjects = false)
+ public static byte[] Var2Bytes(Variant var, bool fullObjects = false)
{
- return godot_icall_GD_var2bytes(var, fullObjects);
+ NativeFuncs.godotsharp_var2bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
}
/// <summary>
@@ -561,9 +571,11 @@ namespace Godot
/// </example>
/// <param name="var">Variant that will be converted to string.</param>
/// <returns>The <c>Variant</c> encoded as a string.</returns>
- public static string Var2Str(object var)
+ public static string Var2Str(Variant var)
{
- return godot_icall_GD_var2str(var);
+ NativeFuncs.godotsharp_var2str((godot_variant)var.NativeVar, out godot_string ret);
+ using (ret)
+ return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
@@ -572,85 +584,7 @@ namespace Godot
/// <returns>The <see cref="Variant.Type"/> for the given <paramref name="type"/>.</returns>
public static Variant.Type TypeToVariantType(Type type)
{
- return godot_icall_TypeToVariantType(type);
+ return Marshaling.ConvertManagedTypeToVariantType(type, out bool _);
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_convert(object what, Variant.Type type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_GD_hash(object var);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_print(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_print_rich(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_printerr(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_printraw(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_prints(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_printt(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_randomize();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern uint godot_icall_GD_randi();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern float godot_icall_GD_randf();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_GD_randi_range(int from, int to);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern double godot_icall_GD_randf_range(double from, double to);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern double godot_icall_GD_randfn(double mean, double deviation);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_seed(ulong seed);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_GD_str(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_str2var(string str);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_GD_type_exists(IntPtr type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_GD_var2str(object var);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_pusherror(string type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_pushwarning(string type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
index 9ccac1faaf..78a9d0fe9d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
@@ -17,10 +17,7 @@ namespace Godot
public override void Fail(string message, string detailMessage)
{
GD.PrintErr("Assertion failed: ", message);
- if (detailMessage != null)
- {
- GD.PrintErr(" Details: ", detailMessage);
- }
+ GD.PrintErr(" Details: ", detailMessage);
try
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
index 729529d093..a17741ddeb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
@@ -1,17 +1,33 @@
using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
public static partial class GD
{
- /// <summary>
- /// Fires when an unhandled exception occurs, regardless of project settings.
- /// </summary>
- public static event EventHandler<UnhandledExceptionArgs> UnhandledException;
-
- private static void OnUnhandledException(Exception e)
+ [UnmanagedCallersOnly]
+ internal static void OnCoreApiAssemblyLoaded(godot_bool isDebug)
{
- UnhandledException?.Invoke(null, new UnhandledExceptionArgs(e));
+ try
+ {
+ Dispatcher.InitializeDefaultGodotTaskScheduler();
+
+ if (isDebug.ToBool())
+ {
+ DebuggingUtils.InstallTraceListener();
+
+ AppDomain.CurrentDomain.UnhandledException += (_, e) =>
+ {
+ // Exception.ToString() includes the inner exception
+ ExceptionUtils.LogUnhandledException((Exception)e.ExceptionObject);
+ };
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
deleted file mode 100644
index 50ae2eb112..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Godot
-{
- internal static class MarshalUtils
- {
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericArray(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Collections.Array<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="List{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsSystemGenericList(Type type) =>
- type.GetGenericTypeDefinition() == typeof(List<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsSystemGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Dictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="IEnumerable{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="ICollection{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="IDictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type.
- /// </summary>
- private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false);
-
- /// <summary>
- /// Returns the generic type definition of <paramref name="type"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition)
- {
- genericTypeDefinition = type.GetGenericTypeDefinition();
- }
-
- /// <summary>
- /// Gets the element type for the given <paramref name="arrayType"/>.
- /// </summary>
- /// <param name="arrayType">Type for the generic array.</param>
- /// <param name="elementType">Element type for the generic array.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="arrayType"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void ArrayGetElementType(Type arrayType, out Type elementType)
- {
- elementType = arrayType.GetGenericArguments()[0];
- }
-
- /// <summary>
- /// Gets the key type and the value type for the given <paramref name="dictionaryType"/>.
- /// </summary>
- /// <param name="dictionaryType">The type for the generic dictionary.</param>
- /// <param name="keyType">Key type for the generic dictionary.</param>
- /// <param name="valueType">Value type for the generic dictionary.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="dictionaryType"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
- {
- var genericArgs = dictionaryType.GetGenericArguments();
- keyType = genericArgs[0];
- valueType = genericArgs[1];
- }
-
- /// <summary>
- /// Constructs a new <see cref="Type"/> from <see cref="Collections.Array{T}"/>
- /// where the generic type for the elements is <paramref name="elemType"/>.
- /// </summary>
- /// <param name="elemType">Element type for the array.</param>
- /// <returns>The generic array type with the specified element type.</returns>
- private static Type MakeGenericArrayType(Type elemType)
- {
- return typeof(Collections.Array<>).MakeGenericType(elemType);
- }
-
- /// <summary>
- /// Constructs a new <see cref="Type"/> from <see cref="Collections.Dictionary{TKey, TValue}"/>
- /// where the generic type for the keys is <paramref name="keyType"/> and
- /// for the values is <paramref name="valueType"/>.
- /// </summary>
- /// <param name="keyType">Key type for the dictionary.</param>
- /// <param name="valueType">Key type for the dictionary.</param>
- /// <returns>The generic dictionary type with the specified key and value types.</returns>
- private static Type MakeGenericDictionaryType(Type keyType, Type valueType)
- {
- return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 36b7d0f80f..08beff8104 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
namespace Godot
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index f15d01b34b..ea05c1547c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
namespace Godot
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
new file mode 100644
index 0000000000..afef20a7f2
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
@@ -0,0 +1,313 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop;
+
+// Ref structs are not allowed as generic type parameters, so we can't use Unsafe.AsPointer<T>/AsRef<T>.
+// As a workaround we create our own overloads for our structs with some tricks under the hood.
+
+public static class CustomUnsafe
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_ref* AsPointer(ref godot_ref value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_ref* ReadOnlyRefAsPointer(in godot_ref value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_ref AsRef(godot_ref* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_ref AsRef(in godot_ref source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant_call_error* AsPointer(ref godot_variant_call_error value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant_call_error* ReadOnlyRefAsPointer(in godot_variant_call_error value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant_call_error AsRef(godot_variant_call_error* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant_call_error AsRef(in godot_variant_call_error source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant* AsPointer(ref godot_variant value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant* ReadOnlyRefAsPointer(in godot_variant value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant AsRef(godot_variant* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant AsRef(in godot_variant source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string* AsPointer(ref godot_string value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string* ReadOnlyRefAsPointer(in godot_string value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string AsRef(godot_string* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string AsRef(in godot_string source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string_name* AsPointer(ref godot_string_name value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string_name* ReadOnlyRefAsPointer(in godot_string_name value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string_name AsRef(godot_string_name* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string_name AsRef(in godot_string_name source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_node_path* AsPointer(ref godot_node_path value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_node_path* ReadOnlyRefAsPointer(in godot_node_path value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_node_path AsRef(godot_node_path* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_node_path AsRef(in godot_node_path source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_signal* AsPointer(ref godot_signal value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_signal* ReadOnlyRefAsPointer(in godot_signal value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_signal AsRef(godot_signal* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_signal AsRef(in godot_signal source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_callable* AsPointer(ref godot_callable value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_callable* ReadOnlyRefAsPointer(in godot_callable value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_callable AsRef(godot_callable* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_callable AsRef(in godot_callable source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_array* AsPointer(ref godot_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_array* ReadOnlyRefAsPointer(in godot_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_array AsRef(godot_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_array AsRef(in godot_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_dictionary* AsPointer(ref godot_dictionary value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_dictionary* ReadOnlyRefAsPointer(in godot_dictionary value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_dictionary AsRef(godot_dictionary* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_dictionary AsRef(in godot_dictionary source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_byte_array* AsPointer(ref godot_packed_byte_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_byte_array* ReadOnlyRefAsPointer(in godot_packed_byte_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_byte_array AsRef(godot_packed_byte_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_byte_array AsRef(in godot_packed_byte_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int32_array* AsPointer(ref godot_packed_int32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int32_array* ReadOnlyRefAsPointer(in godot_packed_int32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int32_array AsRef(godot_packed_int32_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int32_array AsRef(in godot_packed_int32_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int64_array* AsPointer(ref godot_packed_int64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int64_array* ReadOnlyRefAsPointer(in godot_packed_int64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int64_array AsRef(godot_packed_int64_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int64_array AsRef(in godot_packed_int64_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float32_array* AsPointer(ref godot_packed_float32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float32_array* ReadOnlyRefAsPointer(in godot_packed_float32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float32_array AsRef(godot_packed_float32_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float32_array AsRef(in godot_packed_float32_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float64_array* AsPointer(ref godot_packed_float64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float64_array* ReadOnlyRefAsPointer(in godot_packed_float64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float64_array AsRef(godot_packed_float64_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float64_array AsRef(in godot_packed_float64_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_string_array* AsPointer(ref godot_packed_string_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_string_array* ReadOnlyRefAsPointer(in godot_packed_string_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_string_array AsRef(godot_packed_string_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_string_array AsRef(in godot_packed_string_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector2_array* AsPointer(ref godot_packed_vector2_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector2_array* ReadOnlyRefAsPointer(in godot_packed_vector2_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector2_array AsRef(godot_packed_vector2_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector2_array AsRef(in godot_packed_vector2_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector3_array* AsPointer(ref godot_packed_vector3_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector3_array* ReadOnlyRefAsPointer(in godot_packed_vector3_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector3_array AsRef(godot_packed_vector3_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector3_array AsRef(in godot_packed_vector3_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_color_array* AsPointer(ref godot_packed_color_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_color_array* ReadOnlyRefAsPointer(in godot_packed_color_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_color_array AsRef(godot_packed_color_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_color_array AsRef(in godot_packed_color_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
new file mode 100644
index 0000000000..5a0ea2ba13
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ internal static class ExceptionUtils
+ {
+ public static void PushError(string message)
+ {
+ GD.PushError(message);
+ }
+
+ private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog)
+ {
+ try
+ {
+ // This better not throw
+ PushError(string.Concat("Exception thrown while trying to log another exception...",
+ "\n### Exception ###\n", exceptionToLog.ToString(),
+ "\n### Logger exception ###\n", loggerException.ToString()));
+ }
+ catch (Exception)
+ {
+ // Well, too bad...
+ }
+ }
+
+ private record struct StackInfoTuple(string? File, string Func, int Line);
+
+ private static void CollectExceptionInfo(Exception exception, List<StackInfoTuple> globalFrames,
+ StringBuilder excMsg)
+ {
+ if (excMsg.Length > 0)
+ excMsg.Append(" ---> ");
+ excMsg.Append(exception.GetType().FullName);
+ excMsg.Append(": ");
+ excMsg.Append(exception.Message);
+
+ var innerExc = exception.InnerException;
+
+ if (innerExc != null)
+ {
+ CollectExceptionInfo(innerExc, globalFrames, excMsg);
+ globalFrames.Add(new("", "--- End of inner exception stack trace ---", 0));
+ }
+
+ var stackTrace = new StackTrace(exception, fNeedFileInfo: true);
+
+ foreach (StackFrame frame in stackTrace.GetFrames())
+ {
+ DebuggingUtils.GetStackFrameMethodDecl(frame, out string methodDecl);
+ globalFrames.Add(new(frame.GetFileName(), methodDecl, frame.GetFileLineNumber()));
+ }
+ }
+
+ private static void SendToScriptDebugger(Exception e)
+ {
+ var globalFrames = new List<StackInfoTuple>();
+
+ var excMsg = new StringBuilder();
+
+ CollectExceptionInfo(e, globalFrames, excMsg);
+
+ string file = globalFrames.Count > 0 ? globalFrames[0].File ?? "" : "";
+ string func = globalFrames.Count > 0 ? globalFrames[0].Func : "";
+ int line = globalFrames.Count > 0 ? globalFrames[0].Line : 0;
+ string errorMsg = "Exception";
+
+ using godot_string nFile = Marshaling.ConvertStringToNative(file);
+ using godot_string nFunc = Marshaling.ConvertStringToNative(func);
+ using godot_string nErrorMsg = Marshaling.ConvertStringToNative(errorMsg);
+ using godot_string nExcMsg = Marshaling.ConvertStringToNative(excMsg.ToString());
+
+ using DebuggingUtils.godot_stack_info_vector stackInfoVector = default;
+
+ stackInfoVector.Resize(globalFrames.Count);
+
+ unsafe
+ {
+ for (int i = 0; i < globalFrames.Count; i++)
+ {
+ DebuggingUtils.godot_stack_info* stackInfo = &stackInfoVector.Elements[i];
+
+ var globalFrame = globalFrames[i];
+
+ // Assign directly to element in Vector. This way we don't need to worry
+ // about disposal if an exception is thrown. The Vector takes care of it.
+ stackInfo->File = Marshaling.ConvertStringToNative(globalFrame.File);
+ stackInfo->Func = Marshaling.ConvertStringToNative(globalFrame.Func);
+ stackInfo->Line = globalFrame.Line;
+ }
+
+ NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line,
+ nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector);
+ }
+ }
+
+ public static void LogException(Exception e)
+ {
+ try
+ {
+ if (NativeFuncs.godotsharp_internal_script_debugger_is_active())
+ {
+ SendToScriptDebugger(e);
+ }
+ else
+ {
+ GD.PushError(e.ToString());
+ }
+ }
+ catch (Exception unexpected)
+ {
+ OnExceptionLoggerException(unexpected, e);
+ }
+ }
+
+ public static void LogUnhandledException(Exception e)
+ {
+ try
+ {
+ if (NativeFuncs.godotsharp_internal_script_debugger_is_active())
+ {
+ SendToScriptDebugger(e);
+ }
+
+ // In this case, print it as well in addition to sending it to the script debugger
+ GD.PushError("Unhandled exception\n" + e);
+ }
+ catch (Exception unexpected)
+ {
+ OnExceptionLoggerException(unexpected, e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs
new file mode 100644
index 0000000000..5579992d2b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public class GodotDllImportResolver
+ {
+ private IntPtr _internalHandle;
+
+ public GodotDllImportResolver(IntPtr internalHandle)
+ {
+ _internalHandle = internalHandle;
+ }
+
+ public IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ if (libraryName == "__Internal")
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Win32.GetModuleHandle(IntPtr.Zero);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return _internalHandle;
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return MacOS.dlopen(IntPtr.Zero, MacOS.RTLD_LAZY);
+ }
+ }
+
+ return IntPtr.Zero;
+ }
+
+ // ReSharper disable InconsistentNaming
+ private static class MacOS
+ {
+ private const string SystemLibrary = "/usr/lib/libSystem.dylib";
+
+ public const int RTLD_LAZY = 1;
+
+ [DllImport(SystemLibrary)]
+ public static extern IntPtr dlopen(IntPtr path, int mode);
+ }
+
+ private static class Win32
+ {
+ private const string SystemLibrary = "Kernel32.dll";
+
+ [DllImport(SystemLibrary)]
+ public static extern IntPtr GetModuleHandle(IntPtr lpModuleName);
+ }
+ // ReSharper restore InconsistentNaming
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
new file mode 100644
index 0000000000..44806e8ecf
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -0,0 +1,1091 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot.NativeInterop
+{
+ // NOTES:
+ // ref structs cannot implement interfaces, but they still work in `using` directives if they declare Dispose()
+
+ public static class GodotBoolExtensions
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_bool ToGodotBool(this bool @bool)
+ {
+ return *(godot_bool*)&@bool;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe bool ToBool(this godot_bool godotBool)
+ {
+ return *(bool*)&godotBool;
+ }
+ }
+
+ // Apparently a struct with a byte is not blittable? It crashes when calling a UnmanagedCallersOnly function ptr.
+ // ReSharper disable once InconsistentNaming
+ public enum godot_bool : byte
+ {
+ True = 1,
+ False = 0
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_ref
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_ref* GetUnsafeAddress()
+ => (godot_ref*)Unsafe.AsPointer(ref Unsafe.AsRef(in _reference));
+
+ private IntPtr _reference;
+
+ public void Dispose()
+ {
+ if (_reference == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_ref_destroy(ref this);
+ _reference = IntPtr.Zero;
+ }
+
+ public readonly IntPtr Reference
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _reference;
+ }
+
+ public readonly bool IsNull
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _reference == IntPtr.Zero;
+ }
+ }
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum godot_variant_call_error_error
+ {
+ GODOT_CALL_ERROR_CALL_OK = 0,
+ GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD,
+ GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT,
+ GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS,
+ GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS,
+ GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_variant_call_error
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_variant_call_error* GetUnsafeAddress()
+ => (godot_variant_call_error*)Unsafe.AsPointer(ref Unsafe.AsRef(in error));
+
+ private godot_variant_call_error_error error;
+ private int argument;
+ private int expected;
+
+ public godot_variant_call_error_error Error
+ {
+ readonly get => error;
+ set => error = value;
+ }
+
+ public int Argument
+ {
+ readonly get => argument;
+ set => argument = value;
+ }
+
+ public Godot.Variant.Type Expected
+ {
+ readonly get => (Godot.Variant.Type)expected;
+ set => expected = (int)value;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_variant
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_variant* GetUnsafeAddress()
+ => (godot_variant*)Unsafe.AsPointer(ref Unsafe.AsRef(in _typeField));
+
+ // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits.
+ [FieldOffset(0)] private int _typeField;
+
+ // There's padding here
+
+ [FieldOffset(8)] private godot_variant_data _data;
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ private unsafe ref struct godot_variant_data
+ {
+ [FieldOffset(0)] public godot_bool _bool;
+ [FieldOffset(0)] public long _int;
+ [FieldOffset(0)] public double _float;
+ [FieldOffset(0)] public Transform2D* _transform2D;
+ [FieldOffset(0)] public AABB* _aabb;
+ [FieldOffset(0)] public Basis* _basis;
+ [FieldOffset(0)] public Transform3D* _transform3D;
+ [FieldOffset(0)] public Vector4* _vector4;
+ [FieldOffset(0)] public Vector4i* _vector4i;
+ [FieldOffset(0)] public Projection* _projection;
+ [FieldOffset(0)] private godot_variant_data_mem _mem;
+
+ // The following fields are not in the C++ union, but this is how they're stored in _mem.
+ [FieldOffset(0)] public godot_string_name _m_string_name;
+ [FieldOffset(0)] public godot_string _m_string;
+ [FieldOffset(0)] public Vector3 _m_vector3;
+ [FieldOffset(0)] public Vector3i _m_vector3i;
+ [FieldOffset(0)] public Vector2 _m_vector2;
+ [FieldOffset(0)] public Vector2i _m_vector2i;
+ [FieldOffset(0)] public Rect2 _m_rect2;
+ [FieldOffset(0)] public Rect2i _m_rect2i;
+ [FieldOffset(0)] public Plane _m_plane;
+ [FieldOffset(0)] public Quaternion _m_quaternion;
+ [FieldOffset(0)] public Color _m_color;
+ [FieldOffset(0)] public godot_node_path _m_node_path;
+ [FieldOffset(0)] public RID _m_rid;
+ [FieldOffset(0)] public godot_variant_obj_data _m_obj_data;
+ [FieldOffset(0)] public godot_callable _m_callable;
+ [FieldOffset(0)] public godot_signal _m_signal;
+ [FieldOffset(0)] public godot_dictionary _m_dictionary;
+ [FieldOffset(0)] public godot_array _m_array;
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public struct godot_variant_obj_data
+ {
+ public ulong id;
+ public IntPtr obj;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public struct godot_variant_data_mem
+ {
+#pragma warning disable 169
+ private real_t _mem0;
+ private real_t _mem1;
+ private real_t _mem2;
+ private real_t _mem3;
+#pragma warning restore 169
+ }
+ }
+
+ public Variant.Type Type
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => (Variant.Type)_typeField;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _typeField = (int)value;
+ }
+
+ public godot_bool Bool
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._bool;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._bool = value;
+ }
+
+ public long Int
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._int;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._int = value;
+ }
+
+ public double Float
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._float;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._float = value;
+ }
+
+ public readonly unsafe Transform2D* Transform2D
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._transform2D;
+ }
+
+ public readonly unsafe AABB* AABB
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._aabb;
+ }
+
+ public readonly unsafe Basis* Basis
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._basis;
+ }
+
+ public readonly unsafe Transform3D* Transform3D
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._transform3D;
+ }
+
+ public readonly unsafe Vector4* Vector4
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._vector4;
+ }
+
+ public readonly unsafe Vector4i* Vector4i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._vector4i;
+ }
+
+ public readonly unsafe Projection* Projection
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._projection;
+ }
+
+ public godot_string_name StringName
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_string_name;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_string_name = value;
+ }
+
+ public godot_string String
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_string;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_string = value;
+ }
+
+ public Vector3 Vector3
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector3;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector3 = value;
+ }
+
+ public Vector3i Vector3i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector3i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector3i = value;
+ }
+
+ public Vector2 Vector2
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector2 = value;
+ }
+
+ public Vector2i Vector2i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector2i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector2i = value;
+ }
+
+ public Rect2 Rect2
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rect2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rect2 = value;
+ }
+
+ public Rect2i Rect2i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rect2i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rect2i = value;
+ }
+
+ public Plane Plane
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_plane;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_plane = value;
+ }
+
+ public Quaternion Quaternion
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_quaternion;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_quaternion = value;
+ }
+
+ public Color Color
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_color;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_color = value;
+ }
+
+ public godot_node_path NodePath
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_node_path;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_node_path = value;
+ }
+
+ public RID RID
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rid;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rid = value;
+ }
+
+ public godot_callable Callable
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_callable;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_callable = value;
+ }
+
+ public godot_signal Signal
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_signal;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_signal = value;
+ }
+
+ public godot_dictionary Dictionary
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_dictionary;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_dictionary = value;
+ }
+
+ public godot_array Array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_array;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_array = value;
+ }
+
+ public readonly IntPtr Object
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._m_obj_data.obj;
+ }
+
+ public void Dispose()
+ {
+ switch (Type)
+ {
+ case Variant.Type.Nil:
+ case Variant.Type.Bool:
+ case Variant.Type.Int:
+ case Variant.Type.Float:
+ case Variant.Type.Vector2:
+ case Variant.Type.Vector2i:
+ case Variant.Type.Rect2:
+ case Variant.Type.Rect2i:
+ case Variant.Type.Vector3:
+ case Variant.Type.Vector3i:
+ case Variant.Type.Plane:
+ case Variant.Type.Quaternion:
+ case Variant.Type.Color:
+ case Variant.Type.Rid:
+ return;
+ }
+
+ NativeFuncs.godotsharp_variant_destroy(ref this);
+ Type = Variant.Type.Nil;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits.
+ [FieldOffset(0)] private int _typeField;
+
+ // There's padding here
+
+ [FieldOffset(8)] private godot_variant_data.godot_variant_data_mem _data;
+
+ public static unsafe explicit operator movable(in godot_variant value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_variant(movable value)
+ => *(godot_variant*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_variant DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_variant*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_string
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_string* GetUnsafeAddress()
+ => (godot_string*)Unsafe.AsPointer(ref Unsafe.AsRef(in _ptr));
+
+ private IntPtr _ptr;
+
+ public void Dispose()
+ {
+ if (_ptr == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_string_destroy(ref this);
+ _ptr = IntPtr.Zero;
+ }
+
+ public readonly IntPtr Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ // Size including the null termination character
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_string_name
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_string_name* GetUnsafeAddress()
+ => (godot_string_name*)Unsafe.AsPointer(ref Unsafe.AsRef(in _data));
+
+ private IntPtr _data;
+
+ public void Dispose()
+ {
+ if (_data == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_string_name_destroy(ref this);
+ _data = IntPtr.Zero;
+ }
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data != IntPtr.Zero;
+ }
+
+ public readonly bool IsEmpty
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++.
+ get => _data == IntPtr.Zero;
+ }
+
+ public static bool operator ==(godot_string_name left, godot_string_name right)
+ {
+ return left._data == right._data;
+ }
+
+ public static bool operator !=(godot_string_name left, godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public bool Equals(godot_string_name other)
+ {
+ return _data == other._data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is StringName s && s.Equals(this);
+ }
+
+ public override int GetHashCode()
+ {
+ return _data.GetHashCode();
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _data;
+
+ public static unsafe explicit operator movable(in godot_string_name value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_string_name(movable value)
+ => *(godot_string_name*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_string_name DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_string_name*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_node_path
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_node_path* GetUnsafeAddress()
+ => (godot_node_path*)Unsafe.AsPointer(ref Unsafe.AsRef(in _data));
+
+ private IntPtr _data;
+
+ public void Dispose()
+ {
+ if (_data == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_node_path_destroy(ref this);
+ _data = IntPtr.Zero;
+ }
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data != IntPtr.Zero;
+ }
+
+ public readonly bool IsEmpty
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does.
+ get => _data == IntPtr.Zero;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _data;
+
+ public static unsafe explicit operator movable(in godot_node_path value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_node_path(movable value)
+ => *(godot_node_path*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_node_path DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_node_path*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_signal
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_signal* GetUnsafeAddress()
+ => (godot_signal*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private godot_string_name _name;
+
+ // There's padding here on 32-bit
+
+ [FieldOffset(8)] private ulong _objectId;
+
+ public godot_signal(godot_string_name name, ulong objectId) : this()
+ {
+ _name = name;
+ _objectId = objectId;
+ }
+
+ public godot_string_name Name
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _name;
+ }
+
+ public ulong ObjectId
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _objectId;
+ }
+
+ public void Dispose()
+ {
+ if (!_name.IsAllocated)
+ return;
+ NativeFuncs.godotsharp_signal_destroy(ref this);
+ _name = default;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_callable
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_callable* GetUnsafeAddress()
+ => (godot_callable*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private godot_string_name _method;
+
+ // There's padding here on 32-bit
+
+ // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
+ [FieldOffset(8)] private ulong _objectId;
+ [FieldOffset(8)] private IntPtr _custom;
+
+ public godot_callable(godot_string_name method, ulong objectId) : this()
+ {
+ _method = method;
+ _objectId = objectId;
+ }
+
+ public void Dispose()
+ {
+ // _custom needs freeing as well
+ if (!_method.IsAllocated && _custom == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_callable_destroy(ref this);
+ _method = default;
+ _custom = IntPtr.Zero;
+ }
+ }
+
+ // A correctly constructed value needs to call the native default constructor to allocate `_p`.
+ // Don't pass a C# default constructed `godot_array` to native code, unless it's going to
+ // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_array* GetUnsafeAddress()
+ => (godot_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private unsafe ArrayPrivate* _p;
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct ArrayPrivate
+ {
+ private uint _safeRefCount;
+
+ public VariantVector _arrayVector;
+ // There are more fields here, but we don't care as we never store this in C#
+
+ public readonly int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _arrayVector.Size;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct VariantVector
+ {
+ private IntPtr _writeProxy;
+ public unsafe godot_variant* _ptr;
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ public readonly unsafe godot_variant* Elements
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p->_arrayVector._ptr;
+ }
+
+ public readonly unsafe bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != null;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != null ? _p->Size : 0;
+ }
+
+ public unsafe void Dispose()
+ {
+ if (_p == null)
+ return;
+ NativeFuncs.godotsharp_array_destroy(ref this);
+ _p = null;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private unsafe ArrayPrivate* _p;
+
+ public static unsafe explicit operator movable(in godot_array value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_array(movable value)
+ => *(godot_array*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_array DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_array*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ // IMPORTANT:
+ // A correctly constructed value needs to call the native default constructor to allocate `_p`.
+ // Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to
+ // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_dictionary
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_dictionary* GetUnsafeAddress()
+ => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _p));
+
+ private IntPtr _p;
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != IntPtr.Zero;
+ }
+
+ public void Dispose()
+ {
+ if (_p == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_dictionary_destroy(ref this);
+ _p = IntPtr.Zero;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _p;
+
+ public static unsafe explicit operator movable(in godot_dictionary value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_dictionary(movable value)
+ => *(godot_dictionary*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_dictionary DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_dictionary*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_byte_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_byte_array* GetUnsafeAddress()
+ => (godot_packed_byte_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe byte* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_byte_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe byte* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_int32_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_int32_array* GetUnsafeAddress()
+ => (godot_packed_int32_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe int* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_int32_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe int* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *(_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_int64_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_int64_array* GetUnsafeAddress()
+ => (godot_packed_int64_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe long* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_int64_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe long* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_float32_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_float32_array* GetUnsafeAddress()
+ => (godot_packed_float32_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe float* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_float32_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe float* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_float64_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_float64_array* GetUnsafeAddress()
+ => (godot_packed_float64_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe double* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_float64_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe double* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_string_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_string_array* GetUnsafeAddress()
+ => (godot_packed_string_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe godot_string* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_string_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe godot_string* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector2_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector2_array* GetUnsafeAddress()
+ => (godot_packed_vector2_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector2* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector2* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector3_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector3_array* GetUnsafeAddress()
+ => (godot_packed_vector3_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector3* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector3* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_color_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_color_array* GetUnsafeAddress()
+ => (godot_packed_color_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Color* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_color_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Color* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs
new file mode 100644
index 0000000000..82f1c04d40
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ internal static class InteropUtils
+ {
+ public static Object UnmanagedGetManaged(IntPtr unmanaged)
+ {
+ // The native pointer may be null
+ if (unmanaged == IntPtr.Zero)
+ return null;
+
+ IntPtr gcHandlePtr;
+ godot_bool hasCsScriptInstance;
+
+ // First try to get the tied managed instance from a CSharpInstance script instance
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed(
+ unmanaged, out hasCsScriptInstance);
+
+ if (gcHandlePtr != IntPtr.Zero)
+ return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
+
+ // Otherwise, if the object has a CSharpInstance script instance, return null
+
+ if (hasCsScriptInstance.ToBool())
+ return null;
+
+ // If it doesn't have a CSharpInstance script instance, try with native instance bindings
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged);
+
+ object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
+
+ if (target != null)
+ return (Object)target;
+
+ // If the native instance binding GC handle target was collected, create a new one
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed(
+ unmanaged, gcHandlePtr);
+
+ return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
+ }
+
+ public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
+ StringName nativeName, bool refCounted, Type type, Type nativeType)
+ {
+ var gcHandle = refCounted ?
+ CustomGCHandle.AllocWeak(managed) :
+ CustomGCHandle.AllocStrong(managed, type);
+
+ if (type == nativeType)
+ {
+ var nativeNameSelf = (godot_string_name)nativeName.NativeValue;
+ NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged(
+ GCHandle.ToIntPtr(gcHandle), unmanaged, nativeNameSelf, refCounted.ToGodotBool());
+ }
+ else
+ {
+ unsafe
+ {
+ // We don't dispose `script` ourselves here.
+ // `tie_user_managed_to_unmanaged` does it for us to avoid another P/Invoke call.
+ godot_ref script;
+ ScriptManagerBridge.GetOrLoadOrCreateScriptForType(type, &script);
+
+ // IMPORTANT: This must be called after GetOrCreateScriptBridgeForType
+ NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged(
+ GCHandle.ToIntPtr(gcHandle), unmanaged, &script, refCounted.ToGodotBool());
+ }
+ }
+ }
+
+ public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged,
+ Type type, Type nativeType)
+ {
+ if (type == nativeType)
+ return;
+
+ var strongGCHandle = CustomGCHandle.AllocStrong(managed);
+ NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
+ GCHandle.ToIntPtr(strongGCHandle), unmanaged);
+ }
+
+ public static Object EngineGetSingleton(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(src));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
new file mode 100644
index 0000000000..eee19aea46
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -0,0 +1,1092 @@
+using System;
+using System.Runtime.InteropServices;
+
+// ReSharper disable InconsistentNaming
+
+// We want to use full name qualifiers here even if redundant for clarity
+// ReSharper disable RedundantNameQualifier
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public static class Marshaling
+ {
+ internal static Variant.Type ConvertManagedTypeToVariantType(Type type, out bool r_nil_is_variant)
+ {
+ r_nil_is_variant = false;
+
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.Boolean:
+ return Variant.Type.Bool;
+ case TypeCode.Char:
+ return Variant.Type.Int;
+ case TypeCode.SByte:
+ return Variant.Type.Int;
+ case TypeCode.Int16:
+ return Variant.Type.Int;
+ case TypeCode.Int32:
+ return Variant.Type.Int;
+ case TypeCode.Int64:
+ return Variant.Type.Int;
+ case TypeCode.Byte:
+ return Variant.Type.Int;
+ case TypeCode.UInt16:
+ return Variant.Type.Int;
+ case TypeCode.UInt32:
+ return Variant.Type.Int;
+ case TypeCode.UInt64:
+ return Variant.Type.Int;
+ case TypeCode.Single:
+ return Variant.Type.Float;
+ case TypeCode.Double:
+ return Variant.Type.Float;
+ case TypeCode.String:
+ return Variant.Type.String;
+ default:
+ {
+ if (type == typeof(Vector2))
+ return Variant.Type.Vector2;
+
+ if (type == typeof(Vector2i))
+ return Variant.Type.Vector2i;
+
+ if (type == typeof(Rect2))
+ return Variant.Type.Rect2;
+
+ if (type == typeof(Rect2i))
+ return Variant.Type.Rect2i;
+
+ if (type == typeof(Transform2D))
+ return Variant.Type.Transform2d;
+
+ if (type == typeof(Vector3))
+ return Variant.Type.Vector3;
+
+ if (type == typeof(Vector3i))
+ return Variant.Type.Vector3i;
+
+ if (type == typeof(Vector4))
+ return Variant.Type.Vector4;
+
+ if (type == typeof(Vector4i))
+ return Variant.Type.Vector4i;
+
+ if (type == typeof(Basis))
+ return Variant.Type.Basis;
+
+ if (type == typeof(Quaternion))
+ return Variant.Type.Quaternion;
+
+ if (type == typeof(Transform3D))
+ return Variant.Type.Transform3d;
+
+ if (type == typeof(Projection))
+ return Variant.Type.Projection;
+
+ if (type == typeof(AABB))
+ return Variant.Type.Aabb;
+
+ if (type == typeof(Color))
+ return Variant.Type.Color;
+
+ if (type == typeof(Plane))
+ return Variant.Type.Plane;
+
+ if (type == typeof(Callable))
+ return Variant.Type.Callable;
+
+ if (type == typeof(SignalInfo))
+ return Variant.Type.Signal;
+
+ if (type.IsEnum)
+ return Variant.Type.Int;
+
+ if (type.IsArray || type.IsSZArray)
+ {
+ if (type == typeof(byte[]))
+ return Variant.Type.PackedByteArray;
+
+ if (type == typeof(int[]))
+ return Variant.Type.PackedInt32Array;
+
+ if (type == typeof(long[]))
+ return Variant.Type.PackedInt64Array;
+
+ if (type == typeof(float[]))
+ return Variant.Type.PackedFloat32Array;
+
+ if (type == typeof(double[]))
+ return Variant.Type.PackedFloat64Array;
+
+ if (type == typeof(string[]))
+ return Variant.Type.PackedStringArray;
+
+ if (type == typeof(Vector2[]))
+ return Variant.Type.PackedVector2Array;
+
+ if (type == typeof(Vector3[]))
+ return Variant.Type.PackedVector3Array;
+
+ if (type == typeof(Color[]))
+ return Variant.Type.PackedColorArray;
+
+ if (type == typeof(StringName[]))
+ return Variant.Type.Array;
+
+ if (type == typeof(NodePath[]))
+ return Variant.Type.Array;
+
+ if (type == typeof(RID[]))
+ return Variant.Type.Array;
+
+ if (typeof(Godot.Object[]).IsAssignableFrom(type))
+ return Variant.Type.Array;
+ }
+ else if (type.IsGenericType)
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Variant.Type.Object;
+ }
+ else if (type == typeof(Variant))
+ {
+ r_nil_is_variant = true;
+ return Variant.Type.Nil;
+ }
+ else
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Variant.Type.Object;
+
+ if (typeof(StringName) == type)
+ return Variant.Type.StringName;
+
+ if (typeof(NodePath) == type)
+ return Variant.Type.NodePath;
+
+ if (typeof(RID) == type)
+ return Variant.Type.Rid;
+
+ if (typeof(Collections.Dictionary) == type)
+ return Variant.Type.Dictionary;
+
+ if (typeof(Collections.Array) == type)
+ return Variant.Type.Array;
+ }
+
+ break;
+ }
+ }
+
+ // Unknown
+ 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 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;
+ }
+
+ 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)
+ {
+ if (p_mono_string == null)
+ return new godot_string();
+
+ fixed (char* methodChars = p_mono_string)
+ {
+ NativeFuncs.godotsharp_string_new_with_utf16_chars(out godot_string dest, methodChars);
+ return dest;
+ }
+ }
+
+ public static unsafe string ConvertStringToManaged(in godot_string p_string)
+ {
+ if (p_string.Buffer == IntPtr.Zero)
+ return string.Empty;
+
+ const int sizeOfChar32 = 4;
+ byte* bytes = (byte*)p_string.Buffer;
+ int size = p_string.Size;
+ if (size == 0)
+ return string.Empty;
+ size -= 1; // zero at the end
+ int sizeInBytes = size * sizeOfChar32;
+ return System.Text.Encoding.UTF32.GetString(bytes, sizeInBytes);
+ }
+
+ // Callable
+
+ public static godot_callable ConvertCallableToNative(in Callable p_managed_callable)
+ {
+ if (p_managed_callable.Delegate != null)
+ {
+ var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
+ NativeFuncs.godotsharp_callable_new_with_delegate(
+ GCHandle.ToIntPtr(gcHandle), out godot_callable callable);
+ return callable;
+ }
+ else
+ {
+ godot_string_name method;
+
+ if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty)
+ {
+ var src = (godot_string_name)p_managed_callable.Method.NativeValue;
+ method = NativeFuncs.godotsharp_string_name_new_copy(src);
+ }
+ else
+ {
+ method = default;
+ }
+
+ return new godot_callable(method /* Takes ownership of disposable */,
+ p_managed_callable.Target.GetInstanceId());
+ }
+ }
+
+ public static Callable ConvertCallableToManaged(in godot_callable p_callable)
+ {
+ if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(p_callable,
+ out IntPtr delegateGCHandle, out IntPtr godotObject,
+ out godot_string_name name).ToBool())
+ {
+ if (delegateGCHandle != IntPtr.Zero)
+ {
+ return new Callable((Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target);
+ }
+ else
+ {
+ return new Callable(
+ InteropUtils.UnmanagedGetManaged(godotObject),
+ StringName.CreateTakingOwnershipOfDisposableValue(name));
+ }
+ }
+
+ // Some other unsupported callable
+ return new Callable();
+ }
+
+ // SignalInfo
+
+ public static godot_signal ConvertSignalToNative(in SignalInfo p_managed_signal)
+ {
+ ulong ownerId = p_managed_signal.Owner.GetInstanceId();
+ godot_string_name name;
+
+ if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty)
+ {
+ var src = (godot_string_name)p_managed_signal.Name.NativeValue;
+ name = NativeFuncs.godotsharp_string_name_new_copy(src);
+ }
+ else
+ {
+ name = default;
+ }
+
+ return new godot_signal(name, ownerId);
+ }
+
+ public static SignalInfo ConvertSignalToManaged(in godot_signal p_signal)
+ {
+ var owner = GD.InstanceFromId(p_signal.ObjectId);
+ var name = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name));
+ return new SignalInfo(owner, name);
+ }
+
+ // Array
+
+ internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array)
+ where T : Godot.Object
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new T[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = (T)array[i].AsGodotObject();
+
+ return ret;
+ }
+
+ // TODO: This needs reflection. Look for an alternative.
+ internal static Godot.Object[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(in godot_array p_array,
+ Type type)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = (Godot.Object[])Activator.CreateInstance(type, length)!;
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsGodotObject();
+
+ return ret;
+ }
+
+ internal static StringName[] ConvertNativeGodotArrayToSystemArrayOfStringName(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new StringName[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsStringName();
+
+ return ret;
+ }
+
+ internal static NodePath[] ConvertNativeGodotArrayToSystemArrayOfNodePath(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new NodePath[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsNodePath();
+
+ return ret;
+ }
+
+ internal static RID[] ConvertNativeGodotArrayToSystemArrayOfRID(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new RID[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsRID();
+
+ return ret;
+ }
+
+ // PackedByteArray
+
+ public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array)
+ {
+ byte* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<byte>();
+ var array = new byte[size];
+ fixed (byte* dest = array)
+ Buffer.MemoryCopy(buffer, dest, size, size);
+ return array;
+ }
+
+ public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(Span<byte> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_byte_array();
+ fixed (byte* src = p_array)
+ return NativeFuncs.godotsharp_packed_byte_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedInt32Array
+
+ public static unsafe int[] ConvertNativePackedInt32ArrayToSystemArray(godot_packed_int32_array p_array)
+ {
+ int* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<int>();
+ int sizeInBytes = size * sizeof(int);
+ var array = new int[size];
+ fixed (int* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(Span<int> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_int32_array();
+ fixed (int* src = p_array)
+ return NativeFuncs.godotsharp_packed_int32_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedInt64Array
+
+ public static unsafe long[] ConvertNativePackedInt64ArrayToSystemArray(godot_packed_int64_array p_array)
+ {
+ long* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<long>();
+ int sizeInBytes = size * sizeof(long);
+ var array = new long[size];
+ fixed (long* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(Span<long> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_int64_array();
+ fixed (long* src = p_array)
+ return NativeFuncs.godotsharp_packed_int64_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedFloat32Array
+
+ public static unsafe float[] ConvertNativePackedFloat32ArrayToSystemArray(godot_packed_float32_array p_array)
+ {
+ float* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<float>();
+ int sizeInBytes = size * sizeof(float);
+ var array = new float[size];
+ fixed (float* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array(
+ Span<float> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_float32_array();
+ fixed (float* src = p_array)
+ return NativeFuncs.godotsharp_packed_float32_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedFloat64Array
+
+ public static unsafe double[] ConvertNativePackedFloat64ArrayToSystemArray(godot_packed_float64_array p_array)
+ {
+ double* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<double>();
+ int sizeInBytes = size * sizeof(double);
+ var array = new double[size];
+ fixed (double* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array(
+ Span<double> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_float64_array();
+ fixed (double* src = p_array)
+ return NativeFuncs.godotsharp_packed_float64_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedStringArray
+
+ public static unsafe string[] ConvertNativePackedStringArrayToSystemArray(godot_packed_string_array p_array)
+ {
+ godot_string* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<string>();
+ var array = new string[size];
+ for (int i = 0; i < size; i++)
+ array[i] = ConvertStringToManaged(buffer[i]);
+ return array;
+ }
+
+ public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(Span<string> p_array)
+ {
+ godot_packed_string_array dest = new godot_packed_string_array();
+
+ if (p_array.IsEmpty)
+ return dest;
+
+ /* TODO: Replace godotsharp_packed_string_array_add with a single internal call to
+ get the write address. We can't use `dest._ptr` directly for writing due to COW. */
+
+ for (int i = 0; i < p_array.Length; i++)
+ {
+ using godot_string godotStrElem = ConvertStringToNative(p_array[i]);
+ NativeFuncs.godotsharp_packed_string_array_add(ref dest, godotStrElem);
+ }
+
+ return dest;
+ }
+
+ // PackedVector2Array
+
+ public static unsafe Vector2[] ConvertNativePackedVector2ArrayToSystemArray(godot_packed_vector2_array p_array)
+ {
+ Vector2* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector2>();
+ int sizeInBytes = size * sizeof(Vector2);
+ var array = new Vector2[size];
+ fixed (Vector2* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array(
+ Span<Vector2> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector2_array();
+ fixed (Vector2* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector2_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedVector3Array
+
+ public static unsafe Vector3[] ConvertNativePackedVector3ArrayToSystemArray(godot_packed_vector3_array p_array)
+ {
+ Vector3* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector3>();
+ int sizeInBytes = size * sizeof(Vector3);
+ var array = new Vector3[size];
+ fixed (Vector3* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array(
+ Span<Vector3> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector3_array();
+ fixed (Vector3* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedColorArray
+
+ public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array)
+ {
+ Color* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Color>();
+ int sizeInBytes = size * sizeof(Color);
+ var array = new Color[size];
+ fixed (Color* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(Span<Color> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_color_array();
+ fixed (Color* src = p_array)
+ return NativeFuncs.godotsharp_packed_color_array_new_mem_copy(src, p_array.Length);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
new file mode 100644
index 0000000000..6d2534e6f7
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -0,0 +1,522 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.SourceGenerators.Internal;
+
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ /*
+ * IMPORTANT:
+ * The order of the methods defined in NativeFuncs must match the order
+ * in the array defined at the bottom of 'glue/runtime_interop.cpp'.
+ */
+
+ [GenerateUnmanagedCallbacks(typeof(UnmanagedCallbacks))]
+ public static unsafe partial class NativeFuncs
+ {
+ private static bool initialized = false;
+
+ // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
+ public static void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ if (initialized)
+ throw new InvalidOperationException("Already initialized");
+ initialized = true;
+
+ if (unmanagedCallbacksSize != sizeof(UnmanagedCallbacks))
+ throw new ArgumentException("Unmanaged callbacks size mismatch");
+
+ _unmanagedCallbacks = Unsafe.AsRef<UnmanagedCallbacks>((void*)unmanagedCallbacks);
+ }
+
+ private partial struct UnmanagedCallbacks
+ {
+ }
+
+ // Custom functions
+
+ public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
+ in godot_string_name p_methodname);
+
+ public static partial delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(
+ in godot_string_name p_classname);
+
+ public static partial IntPtr godotsharp_engine_get_singleton(in godot_string p_name);
+
+
+ internal static partial Error godotsharp_stack_info_vector_resize(
+ ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector, int p_size);
+
+ internal static partial void godotsharp_stack_info_vector_destroy(
+ ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+
+ internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
+ in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
+ godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+
+ internal static partial bool godotsharp_internal_script_debugger_is_active();
+
+ internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr);
+
+ internal static partial void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree);
+
+ internal static partial void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree,
+ godot_bool isFinalizer);
+
+ internal static partial Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
+ in godot_string_name signal,
+ IntPtr target, IntPtr awaiterHandlePtr);
+
+ internal static partial void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
+ IntPtr unmanaged, in godot_string_name nativeName, godot_bool refCounted);
+
+ internal static partial void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
+ IntPtr unmanaged, godot_ref* scriptPtr, godot_bool refCounted);
+
+ internal static partial void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
+ IntPtr gcHandleIntPtr, IntPtr unmanaged);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
+ out godot_bool r_has_cs_script_instance);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
+ IntPtr oldGCHandlePtr);
+
+ internal static partial void godotsharp_internal_new_csharp_script(godot_ref* r_dest);
+
+ internal static partial godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest);
+
+ internal static partial void godotsharp_internal_reload_registered_script(IntPtr scriptPtr);
+
+ internal static partial void godotsharp_array_filter_godot_objects_by_native(in godot_string_name p_native_name,
+ in godot_array p_input, out godot_array r_output);
+
+ internal static partial void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input,
+ out godot_array r_output);
+
+ public static partial void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest,
+ IntPtr p_ref_counted_ptr);
+
+ public static partial void godotsharp_ref_destroy(ref godot_ref p_instance);
+
+ public static partial void godotsharp_string_name_new_from_string(out godot_string_name r_dest,
+ in godot_string p_name);
+
+ public static partial void godotsharp_node_path_new_from_string(out godot_node_path r_dest,
+ in godot_string p_name);
+
+ public static partial void
+ godotsharp_string_name_as_string(out godot_string r_dest, in godot_string_name p_name);
+
+ public static partial void godotsharp_node_path_as_string(out godot_string r_dest, in godot_node_path p_np);
+
+ public static partial godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src,
+ int p_length);
+
+ public static partial godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src,
+ int p_length);
+
+ public static partial godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src,
+ int p_length);
+
+ public static partial godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src,
+ int p_length);
+
+ public static partial godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src,
+ int p_length);
+
+ public static partial godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src,
+ int p_length);
+
+ public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
+ int p_length);
+
+ public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
+ int p_length);
+
+ public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
+ in godot_string p_element);
+
+ public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
+ out godot_callable r_callable);
+
+ internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,
+ out IntPtr r_delegate_handle, out IntPtr r_object, out godot_string_name r_name);
+
+ internal static partial godot_variant godotsharp_callable_call(in godot_callable p_callable,
+ godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
+
+ internal static partial void godotsharp_callable_call_deferred(in godot_callable p_callable,
+ godot_variant** p_args, int p_arg_count);
+
+ // GDNative functions
+
+ // gdnative.h
+
+ public static partial void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args,
+ void* p_ret);
+
+ public static partial godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance,
+ godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
+
+ // variant.h
+
+ public static partial void
+ godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s);
+
+ public static partial void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src);
+
+ public static partial void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np);
+
+ public static partial void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj);
+
+ public static partial void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d);
+
+ public static partial void godotsharp_variant_new_vector4(out godot_variant r_dest, in Vector4 p_vec4);
+
+ public static partial void godotsharp_variant_new_vector4i(out godot_variant r_dest, in Vector4i p_vec4i);
+
+ public static partial void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis);
+
+ public static partial void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans);
+
+ public static partial void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj);
+
+ public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb);
+
+ public static partial void godotsharp_variant_new_dictionary(out godot_variant r_dest,
+ in godot_dictionary p_dict);
+
+ public static partial void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr);
+
+ public static partial void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest,
+ in godot_packed_byte_array p_pba);
+
+ public static partial void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest,
+ in godot_packed_int32_array p_pia);
+
+ public static partial void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest,
+ in godot_packed_int64_array p_pia);
+
+ public static partial void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest,
+ in godot_packed_float32_array p_pra);
+
+ public static partial void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest,
+ in godot_packed_float64_array p_pra);
+
+ public static partial void godotsharp_variant_new_packed_string_array(out godot_variant r_dest,
+ in godot_packed_string_array p_psa);
+
+ public static partial void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest,
+ in godot_packed_vector2_array p_pv2a);
+
+ public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest,
+ in godot_packed_vector3_array p_pv3a);
+
+ public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest,
+ in godot_packed_color_array p_pca);
+
+ public static partial godot_bool godotsharp_variant_as_bool(in godot_variant p_self);
+
+ public static partial Int64 godotsharp_variant_as_int(in godot_variant p_self);
+
+ public static partial double godotsharp_variant_as_float(in godot_variant p_self);
+
+ public static partial godot_string godotsharp_variant_as_string(in godot_variant p_self);
+
+ public static partial Vector2 godotsharp_variant_as_vector2(in godot_variant p_self);
+
+ public static partial Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self);
+
+ public static partial Rect2 godotsharp_variant_as_rect2(in godot_variant p_self);
+
+ public static partial Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self);
+
+ public static partial Vector3 godotsharp_variant_as_vector3(in godot_variant p_self);
+
+ public static partial Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self);
+
+ public static partial Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self);
+
+ public static partial Vector4 godotsharp_variant_as_vector4(in godot_variant p_self);
+
+ public static partial Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self);
+
+ public static partial Plane godotsharp_variant_as_plane(in godot_variant p_self);
+
+ public static partial Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self);
+
+ public static partial AABB godotsharp_variant_as_aabb(in godot_variant p_self);
+
+ public static partial Basis godotsharp_variant_as_basis(in godot_variant p_self);
+
+ public static partial Transform3D godotsharp_variant_as_transform3d(in godot_variant p_self);
+
+ public static partial Projection godotsharp_variant_as_projection(in godot_variant p_self);
+
+ public static partial Color godotsharp_variant_as_color(in godot_variant p_self);
+
+ public static partial godot_string_name godotsharp_variant_as_string_name(in godot_variant p_self);
+
+ public static partial godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self);
+
+ public static partial RID godotsharp_variant_as_rid(in godot_variant p_self);
+
+ public static partial godot_callable godotsharp_variant_as_callable(in godot_variant p_self);
+
+ public static partial godot_signal godotsharp_variant_as_signal(in godot_variant p_self);
+
+ public static partial godot_dictionary godotsharp_variant_as_dictionary(in godot_variant p_self);
+
+ public static partial godot_array godotsharp_variant_as_array(in godot_variant p_self);
+
+ public static partial godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self);
+
+ public static partial godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self);
+
+ public static partial godot_packed_int64_array godotsharp_variant_as_packed_int64_array(in godot_variant p_self);
+
+ public static partial godot_packed_float32_array godotsharp_variant_as_packed_float32_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_float64_array godotsharp_variant_as_packed_float64_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_string_array godotsharp_variant_as_packed_string_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self);
+
+ public static partial godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b);
+
+ // string.h
+
+ public static partial void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents);
+
+ // string_name.h
+
+ public static partial void godotsharp_string_name_new_copy(out godot_string_name r_dest,
+ in godot_string_name p_src);
+
+ // node_path.h
+
+ public static partial void godotsharp_node_path_new_copy(out godot_node_path r_dest, in godot_node_path p_src);
+
+ // array.h
+
+ public static partial void godotsharp_array_new(out godot_array r_dest);
+
+ public static partial void godotsharp_array_new_copy(out godot_array r_dest, in godot_array p_src);
+
+ public static partial godot_variant* godotsharp_array_ptrw(ref godot_array p_self);
+
+ // dictionary.h
+
+ public static partial void godotsharp_dictionary_new(out godot_dictionary r_dest);
+
+ public static partial void godotsharp_dictionary_new_copy(out godot_dictionary r_dest,
+ in godot_dictionary p_src);
+
+ // destroy functions
+
+ public static partial void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self);
+
+ public static partial void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self);
+
+ public static partial void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self);
+
+ public static partial void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self);
+
+ public static partial void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self);
+
+ public static partial void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self);
+
+ public static partial void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self);
+
+ public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
+
+ public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
+
+ public static partial void godotsharp_variant_destroy(ref godot_variant p_self);
+
+ public static partial void godotsharp_string_destroy(ref godot_string p_self);
+
+ public static partial void godotsharp_string_name_destroy(ref godot_string_name p_self);
+
+ public static partial void godotsharp_node_path_destroy(ref godot_node_path p_self);
+
+ public static partial void godotsharp_signal_destroy(ref godot_signal p_self);
+
+ public static partial void godotsharp_callable_destroy(ref godot_callable p_self);
+
+ public static partial void godotsharp_array_destroy(ref godot_array p_self);
+
+ public static partial void godotsharp_dictionary_destroy(ref godot_dictionary p_self);
+
+ // Array
+
+ public static partial int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item);
+
+ public static partial void
+ godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest);
+
+ public static partial int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item);
+
+ public static partial void godotsharp_array_insert(ref godot_array p_self, int p_index, in godot_variant p_item);
+
+ public static partial void godotsharp_array_remove_at(ref godot_array p_self, int p_index);
+
+ public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size);
+
+ public static partial Error godotsharp_array_shuffle(ref godot_array p_self);
+
+ public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str);
+
+ // Dictionary
+
+ public static partial godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self,
+ in godot_variant p_key,
+ out godot_variant r_value);
+
+ public static partial void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key,
+ in godot_variant p_value);
+
+ public static partial void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest);
+
+ public static partial void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest);
+
+ public static partial int godotsharp_dictionary_count(ref godot_dictionary p_self);
+
+ public static partial void godotsharp_dictionary_key_value_pair_at(ref godot_dictionary p_self, int p_index,
+ out godot_variant r_key, out godot_variant r_value);
+
+ public static partial void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key,
+ in godot_variant p_value);
+
+ public static partial void godotsharp_dictionary_clear(ref godot_dictionary p_self);
+
+ public static partial godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self,
+ in godot_variant p_key);
+
+ public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep,
+ out godot_dictionary r_dest);
+
+ public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self,
+ in godot_variant p_key);
+
+ public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str);
+
+ // StringExtensions
+
+ public static partial void godotsharp_string_md5_buffer(in godot_string p_self,
+ out godot_packed_byte_array r_md5_buffer);
+
+ public static partial void godotsharp_string_md5_text(in godot_string p_self, out godot_string r_md5_text);
+
+ public static partial int godotsharp_string_rfind(in godot_string p_self, in godot_string p_what, int p_from);
+
+ public static partial int godotsharp_string_rfindn(in godot_string p_self, in godot_string p_what, int p_from);
+
+ public static partial void godotsharp_string_sha256_buffer(in godot_string p_self,
+ out godot_packed_byte_array r_sha256_buffer);
+
+ public static partial void godotsharp_string_sha256_text(in godot_string p_self,
+ out godot_string r_sha256_text);
+
+ public static partial void godotsharp_string_simplify_path(in godot_string p_self,
+ out godot_string r_simplified_path);
+
+ // NodePath
+
+ public static partial void godotsharp_node_path_get_as_property_path(in godot_node_path p_self,
+ ref godot_node_path r_dest);
+
+ public static partial void godotsharp_node_path_get_concatenated_names(in godot_node_path p_self,
+ out godot_string r_names);
+
+ public static partial void godotsharp_node_path_get_concatenated_subnames(in godot_node_path p_self,
+ out godot_string r_subnames);
+
+ public static partial void godotsharp_node_path_get_name(in godot_node_path p_self, int p_idx,
+ out godot_string r_name);
+
+ public static partial int godotsharp_node_path_get_name_count(in godot_node_path p_self);
+
+ public static partial void godotsharp_node_path_get_subname(in godot_node_path p_self, int p_idx,
+ out godot_string r_subname);
+
+ public static partial int godotsharp_node_path_get_subname_count(in godot_node_path p_self);
+
+ public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self);
+
+ // GD, etc
+
+ internal static partial void godotsharp_bytes2var(in godot_packed_byte_array p_bytes,
+ godot_bool p_allow_objects,
+ out godot_variant r_ret);
+
+ internal static partial void godotsharp_convert(in godot_variant p_what, int p_type,
+ out godot_variant r_ret);
+
+ internal static partial int godotsharp_hash(in godot_variant p_var);
+
+ internal static partial IntPtr godotsharp_instance_from_id(ulong p_instance_id);
+
+ internal static partial void godotsharp_print(in godot_string p_what);
+
+ public static partial void godotsharp_print_rich(in godot_string p_what);
+
+ internal static partial void godotsharp_printerr(in godot_string p_what);
+
+ internal static partial void godotsharp_printraw(in godot_string p_what);
+
+ internal static partial void godotsharp_prints(in godot_string p_what);
+
+ internal static partial void godotsharp_printt(in godot_string p_what);
+
+ internal static partial float godotsharp_randf();
+
+ internal static partial uint godotsharp_randi();
+
+ internal static partial void godotsharp_randomize();
+
+ internal static partial double godotsharp_randf_range(double from, double to);
+
+ internal static partial double godotsharp_randfn(double mean, double deviation);
+
+ internal static partial int godotsharp_randi_range(int from, int to);
+
+ internal static partial uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed);
+
+ internal static partial void godotsharp_seed(ulong seed);
+
+ internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref);
+
+ internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret);
+
+ internal static partial void godotsharp_str2var(in godot_string p_str, out godot_variant r_ret);
+
+ internal static partial void godotsharp_var2bytes(in godot_variant p_what, godot_bool p_full_objects,
+ out godot_packed_byte_array r_bytes);
+
+ internal static partial void godotsharp_var2str(in godot_variant p_var, out godot_string r_ret);
+
+ internal static partial void godotsharp_pusherror(in godot_string p_str);
+
+ internal static partial void godotsharp_pushwarning(in godot_string p_str);
+
+ // Object
+
+ public static partial void godotsharp_object_to_string(IntPtr ptr, out godot_string r_str);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
new file mode 100644
index 0000000000..2ea3c18d26
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
@@ -0,0 +1,99 @@
+// ReSharper disable InconsistentNaming
+
+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)
+ return default;
+ godotsharp_string_name_new_copy(out godot_string_name ret, src);
+ return ret;
+ }
+
+ public static godot_node_path godotsharp_node_path_new_copy(in godot_node_path src)
+ {
+ if (src.IsEmpty)
+ return default;
+ godotsharp_node_path_new_copy(out godot_node_path ret, src);
+ return ret;
+ }
+
+ public static godot_array godotsharp_array_new()
+ {
+ godotsharp_array_new(out godot_array ret);
+ return ret;
+ }
+
+ public static godot_array godotsharp_array_new_copy(in godot_array src)
+ {
+ godotsharp_array_new_copy(out godot_array ret, src);
+ return ret;
+ }
+
+ public static godot_dictionary godotsharp_dictionary_new()
+ {
+ godotsharp_dictionary_new(out godot_dictionary ret);
+ return ret;
+ }
+
+ public static godot_dictionary godotsharp_dictionary_new_copy(in godot_dictionary src)
+ {
+ godotsharp_dictionary_new_copy(out godot_dictionary ret, src);
+ return ret;
+ }
+
+ public static godot_string_name godotsharp_string_name_new_from_string(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ godotsharp_string_name_new_from_string(out godot_string_name ret, src);
+ return ret;
+ }
+
+ public static godot_node_path godotsharp_node_path_new_from_string(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ godotsharp_node_path_new_from_string(out godot_node_path ret, src);
+ return ret;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
new file mode 100644
index 0000000000..422df74c23
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
@@ -0,0 +1,20 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop
+{
+ // Our source generators will add trampolines methods that access variant arguments.
+ // This struct makes that possible without having to enable `AllowUnsafeBlocks` in game projects.
+
+ public unsafe ref struct NativeVariantPtrArgs
+ {
+ private godot_variant** _args;
+
+ internal NativeVariantPtrArgs(godot_variant** args) => _args = args;
+
+ public ref godot_variant this[int index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref *_args[index];
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
new file mode 100644
index 0000000000..2b5bf2e142
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
@@ -0,0 +1,976 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.NativeInterop;
+
+internal static unsafe class VariantConversionCallbacks
+{
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ internal static delegate* <in T, godot_variant> GetToVariantCallback<T>()
+ {
+ static godot_variant FromBool(in bool @bool) =>
+ VariantUtils.CreateFromBool(@bool);
+
+ static godot_variant FromChar(in char @char) =>
+ VariantUtils.CreateFromInt(@char);
+
+ static godot_variant FromInt8(in sbyte @int8) =>
+ VariantUtils.CreateFromInt(@int8);
+
+ static godot_variant FromInt16(in short @int16) =>
+ VariantUtils.CreateFromInt(@int16);
+
+ static godot_variant FromInt32(in int @int32) =>
+ VariantUtils.CreateFromInt(@int32);
+
+ static godot_variant FromInt64(in long @int64) =>
+ VariantUtils.CreateFromInt(@int64);
+
+ static godot_variant FromUInt8(in byte @uint8) =>
+ VariantUtils.CreateFromInt(@uint8);
+
+ static godot_variant FromUInt16(in ushort @uint16) =>
+ VariantUtils.CreateFromInt(@uint16);
+
+ static godot_variant FromUInt32(in uint @uint32) =>
+ VariantUtils.CreateFromInt(@uint32);
+
+ static godot_variant FromUInt64(in ulong @uint64) =>
+ VariantUtils.CreateFromInt(@uint64);
+
+ static godot_variant FromFloat(in float @float) =>
+ VariantUtils.CreateFromFloat(@float);
+
+ static godot_variant FromDouble(in double @double) =>
+ VariantUtils.CreateFromFloat(@double);
+
+ static godot_variant FromVector2(in Vector2 @vector2) =>
+ VariantUtils.CreateFromVector2(@vector2);
+
+ static godot_variant FromVector2I(in Vector2i vector2I) =>
+ VariantUtils.CreateFromVector2i(vector2I);
+
+ static godot_variant FromRect2(in Rect2 @rect2) =>
+ VariantUtils.CreateFromRect2(@rect2);
+
+ static godot_variant FromRect2I(in Rect2i rect2I) =>
+ VariantUtils.CreateFromRect2i(rect2I);
+
+ static godot_variant FromTransform2D(in Transform2D @transform2D) =>
+ VariantUtils.CreateFromTransform2D(@transform2D);
+
+ static godot_variant FromVector3(in Vector3 @vector3) =>
+ VariantUtils.CreateFromVector3(@vector3);
+
+ static godot_variant FromVector3I(in Vector3i vector3I) =>
+ VariantUtils.CreateFromVector3i(vector3I);
+
+ static godot_variant FromBasis(in Basis @basis) =>
+ VariantUtils.CreateFromBasis(@basis);
+
+ static godot_variant FromQuaternion(in Quaternion @quaternion) =>
+ VariantUtils.CreateFromQuaternion(@quaternion);
+
+ static godot_variant FromTransform3D(in Transform3D @transform3d) =>
+ VariantUtils.CreateFromTransform3D(@transform3d);
+
+ static godot_variant FromAabb(in AABB @aabb) =>
+ VariantUtils.CreateFromAABB(@aabb);
+
+ static godot_variant FromColor(in Color @color) =>
+ VariantUtils.CreateFromColor(@color);
+
+ static godot_variant FromPlane(in Plane @plane) =>
+ VariantUtils.CreateFromPlane(@plane);
+
+ static godot_variant FromCallable(in Callable @callable) =>
+ VariantUtils.CreateFromCallable(@callable);
+
+ static godot_variant FromSignalInfo(in SignalInfo @signalInfo) =>
+ VariantUtils.CreateFromSignalInfo(@signalInfo);
+
+ static godot_variant FromString(in string @string) =>
+ VariantUtils.CreateFromString(@string);
+
+ static godot_variant FromByteArray(in byte[] byteArray) =>
+ VariantUtils.CreateFromPackedByteArray(byteArray);
+
+ static godot_variant FromInt32Array(in int[] int32Array) =>
+ VariantUtils.CreateFromPackedInt32Array(int32Array);
+
+ static godot_variant FromInt64Array(in long[] int64Array) =>
+ VariantUtils.CreateFromPackedInt64Array(int64Array);
+
+ static godot_variant FromFloatArray(in float[] floatArray) =>
+ VariantUtils.CreateFromPackedFloat32Array(floatArray);
+
+ static godot_variant FromDoubleArray(in double[] doubleArray) =>
+ VariantUtils.CreateFromPackedFloat64Array(doubleArray);
+
+ static godot_variant FromStringArray(in string[] stringArray) =>
+ VariantUtils.CreateFromPackedStringArray(stringArray);
+
+ static godot_variant FromVector2Array(in Vector2[] vector2Array) =>
+ VariantUtils.CreateFromPackedVector2Array(vector2Array);
+
+ static godot_variant FromVector3Array(in Vector3[] vector3Array) =>
+ VariantUtils.CreateFromPackedVector3Array(vector3Array);
+
+ static godot_variant FromColorArray(in Color[] colorArray) =>
+ VariantUtils.CreateFromPackedColorArray(colorArray);
+
+ static godot_variant FromStringNameArray(in StringName[] stringNameArray) =>
+ VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
+
+ static godot_variant FromNodePathArray(in NodePath[] nodePathArray) =>
+ VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
+
+ static godot_variant FromRidArray(in RID[] ridArray) =>
+ VariantUtils.CreateFromSystemArrayOfRID(ridArray);
+
+ static godot_variant FromGodotObject(in Godot.Object godotObject) =>
+ VariantUtils.CreateFromGodotObject(godotObject);
+
+ static godot_variant FromStringName(in StringName stringName) =>
+ VariantUtils.CreateFromStringName(stringName);
+
+ static godot_variant FromNodePath(in NodePath nodePath) =>
+ VariantUtils.CreateFromNodePath(nodePath);
+
+ static godot_variant FromRid(in RID rid) =>
+ VariantUtils.CreateFromRID(rid);
+
+ static godot_variant FromGodotDictionary(in Collections.Dictionary godotDictionary) =>
+ VariantUtils.CreateFromDictionary(godotDictionary);
+
+ static godot_variant FromGodotArray(in Collections.Array godotArray) =>
+ VariantUtils.CreateFromArray(godotArray);
+
+ static godot_variant FromVariant(in Variant variant) =>
+ NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
+
+ var typeOfT = typeof(T);
+
+ if (typeOfT == typeof(bool))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in bool, godot_variant>)
+ &FromBool;
+ }
+
+ if (typeOfT == typeof(char))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in char, godot_variant>)
+ &FromChar;
+ }
+
+ if (typeOfT == typeof(sbyte))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in sbyte, godot_variant>)
+ &FromInt8;
+ }
+
+ if (typeOfT == typeof(short))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in short, godot_variant>)
+ &FromInt16;
+ }
+
+ if (typeOfT == typeof(int))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in int, godot_variant>)
+ &FromInt32;
+ }
+
+ if (typeOfT == typeof(long))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in long, godot_variant>)
+ &FromInt64;
+ }
+
+ if (typeOfT == typeof(byte))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in byte, godot_variant>)
+ &FromUInt8;
+ }
+
+ if (typeOfT == typeof(ushort))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ushort, godot_variant>)
+ &FromUInt16;
+ }
+
+ if (typeOfT == typeof(uint))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in uint, godot_variant>)
+ &FromUInt32;
+ }
+
+ if (typeOfT == typeof(ulong))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ulong, godot_variant>)
+ &FromUInt64;
+ }
+
+ if (typeOfT == typeof(float))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in float, godot_variant>)
+ &FromFloat;
+ }
+
+ if (typeOfT == typeof(double))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in double, godot_variant>)
+ &FromDouble;
+ }
+
+ if (typeOfT == typeof(Vector2))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector2, godot_variant>)
+ &FromVector2;
+ }
+
+ if (typeOfT == typeof(Vector2i))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector2i, godot_variant>)
+ &FromVector2I;
+ }
+
+ if (typeOfT == typeof(Rect2))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Rect2, godot_variant>)
+ &FromRect2;
+ }
+
+ if (typeOfT == typeof(Rect2i))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Rect2i, godot_variant>)
+ &FromRect2I;
+ }
+
+ if (typeOfT == typeof(Transform2D))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Transform2D, godot_variant>)
+ &FromTransform2D;
+ }
+
+ if (typeOfT == typeof(Vector3))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector3, godot_variant>)
+ &FromVector3;
+ }
+
+ if (typeOfT == typeof(Vector3i))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector3i, godot_variant>)
+ &FromVector3I;
+ }
+
+ if (typeOfT == typeof(Basis))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Basis, godot_variant>)
+ &FromBasis;
+ }
+
+ if (typeOfT == typeof(Quaternion))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Quaternion, godot_variant>)
+ &FromQuaternion;
+ }
+
+ if (typeOfT == typeof(Transform3D))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Transform3D, godot_variant>)
+ &FromTransform3D;
+ }
+
+ if (typeOfT == typeof(AABB))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in AABB, godot_variant>)
+ &FromAabb;
+ }
+
+ if (typeOfT == typeof(Color))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Color, godot_variant>)
+ &FromColor;
+ }
+
+ if (typeOfT == typeof(Plane))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Plane, godot_variant>)
+ &FromPlane;
+ }
+
+ if (typeOfT == typeof(Callable))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Callable, godot_variant>)
+ &FromCallable;
+ }
+
+ if (typeOfT == typeof(SignalInfo))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in SignalInfo, godot_variant>)
+ &FromSignalInfo;
+ }
+
+ if (typeOfT.IsEnum)
+ {
+ var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in sbyte, godot_variant>)
+ &FromInt8;
+ }
+ case TypeCode.Int16:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in short, godot_variant>)
+ &FromInt16;
+ }
+ case TypeCode.Int32:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in int, godot_variant>)
+ &FromInt32;
+ }
+ case TypeCode.Int64:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in long, godot_variant>)
+ &FromInt64;
+ }
+ case TypeCode.Byte:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in byte, godot_variant>)
+ &FromUInt8;
+ }
+ case TypeCode.UInt16:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ushort, godot_variant>)
+ &FromUInt16;
+ }
+ case TypeCode.UInt32:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in uint, godot_variant>)
+ &FromUInt32;
+ }
+ case TypeCode.UInt64:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ulong, godot_variant>)
+ &FromUInt64;
+ }
+ default:
+ return null;
+ }
+ }
+
+ if (typeOfT == typeof(string))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in string, godot_variant>)
+ &FromString;
+ }
+
+ if (typeOfT == typeof(byte[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in byte[], godot_variant>)
+ &FromByteArray;
+ }
+
+ if (typeOfT == typeof(int[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in int[], godot_variant>)
+ &FromInt32Array;
+ }
+
+ if (typeOfT == typeof(long[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in long[], godot_variant>)
+ &FromInt64Array;
+ }
+
+ if (typeOfT == typeof(float[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in float[], godot_variant>)
+ &FromFloatArray;
+ }
+
+ if (typeOfT == typeof(double[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in double[], godot_variant>)
+ &FromDoubleArray;
+ }
+
+ if (typeOfT == typeof(string[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in string[], godot_variant>)
+ &FromStringArray;
+ }
+
+ if (typeOfT == typeof(Vector2[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector2[], godot_variant>)
+ &FromVector2Array;
+ }
+
+ if (typeOfT == typeof(Vector3[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector3[], godot_variant>)
+ &FromVector3Array;
+ }
+
+ if (typeOfT == typeof(Color[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Color[], godot_variant>)
+ &FromColorArray;
+ }
+
+ if (typeOfT == typeof(StringName[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in StringName[], godot_variant>)
+ &FromStringNameArray;
+ }
+
+ if (typeOfT == typeof(NodePath[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in NodePath[], godot_variant>)
+ &FromNodePathArray;
+ }
+
+ if (typeOfT == typeof(RID[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in RID[], godot_variant>)
+ &FromRidArray;
+ }
+
+ if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Godot.Object, godot_variant>)
+ &FromGodotObject;
+ }
+
+ if (typeOfT == typeof(StringName))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in StringName, godot_variant>)
+ &FromStringName;
+ }
+
+ if (typeOfT == typeof(NodePath))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in NodePath, godot_variant>)
+ &FromNodePath;
+ }
+
+ if (typeOfT == typeof(RID))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in RID, godot_variant>)
+ &FromRid;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Dictionary))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Godot.Collections.Dictionary, godot_variant>)
+ &FromGodotDictionary;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Array))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Godot.Collections.Array, godot_variant>)
+ &FromGodotArray;
+ }
+
+ if (typeOfT == typeof(Variant))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Variant, godot_variant>)
+ &FromVariant;
+ }
+
+ return null;
+ }
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ internal static delegate* <in godot_variant, T> GetToManagedCallback<T>()
+ {
+ static bool ToBool(in godot_variant variant) =>
+ VariantUtils.ConvertToBool(variant);
+
+ static char ToChar(in godot_variant variant) =>
+ VariantUtils.ConvertToChar(variant);
+
+ static sbyte ToInt8(in godot_variant variant) =>
+ VariantUtils.ConvertToInt8(variant);
+
+ static short ToInt16(in godot_variant variant) =>
+ VariantUtils.ConvertToInt16(variant);
+
+ static int ToInt32(in godot_variant variant) =>
+ VariantUtils.ConvertToInt32(variant);
+
+ static long ToInt64(in godot_variant variant) =>
+ VariantUtils.ConvertToInt64(variant);
+
+ static byte ToUInt8(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt8(variant);
+
+ static ushort ToUInt16(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt16(variant);
+
+ static uint ToUInt32(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt32(variant);
+
+ static ulong ToUInt64(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt64(variant);
+
+ static float ToFloat(in godot_variant variant) =>
+ VariantUtils.ConvertToFloat32(variant);
+
+ static double ToDouble(in godot_variant variant) =>
+ VariantUtils.ConvertToFloat64(variant);
+
+ static Vector2 ToVector2(in godot_variant variant) =>
+ VariantUtils.ConvertToVector2(variant);
+
+ static Vector2i ToVector2I(in godot_variant variant) =>
+ VariantUtils.ConvertToVector2i(variant);
+
+ static Rect2 ToRect2(in godot_variant variant) =>
+ VariantUtils.ConvertToRect2(variant);
+
+ static Rect2i ToRect2I(in godot_variant variant) =>
+ VariantUtils.ConvertToRect2i(variant);
+
+ static Transform2D ToTransform2D(in godot_variant variant) =>
+ VariantUtils.ConvertToTransform2D(variant);
+
+ static Vector3 ToVector3(in godot_variant variant) =>
+ VariantUtils.ConvertToVector3(variant);
+
+ static Vector3i ToVector3I(in godot_variant variant) =>
+ VariantUtils.ConvertToVector3i(variant);
+
+ static Basis ToBasis(in godot_variant variant) =>
+ VariantUtils.ConvertToBasis(variant);
+
+ static Quaternion ToQuaternion(in godot_variant variant) =>
+ VariantUtils.ConvertToQuaternion(variant);
+
+ static Transform3D ToTransform3D(in godot_variant variant) =>
+ VariantUtils.ConvertToTransform3D(variant);
+
+ static AABB ToAabb(in godot_variant variant) =>
+ VariantUtils.ConvertToAABB(variant);
+
+ static Color ToColor(in godot_variant variant) =>
+ VariantUtils.ConvertToColor(variant);
+
+ static Plane ToPlane(in godot_variant variant) =>
+ VariantUtils.ConvertToPlane(variant);
+
+ static Callable ToCallable(in godot_variant variant) =>
+ VariantUtils.ConvertToCallableManaged(variant);
+
+ static SignalInfo ToSignalInfo(in godot_variant variant) =>
+ VariantUtils.ConvertToSignalInfo(variant);
+
+ static string ToString(in godot_variant variant) =>
+ VariantUtils.ConvertToStringObject(variant);
+
+ static byte[] ToByteArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedByteArrayToSystemArray(variant);
+
+ static int[] ToInt32Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(variant);
+
+ static long[] ToInt64Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(variant);
+
+ static float[] ToFloatArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(variant);
+
+ static double[] ToDoubleArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(variant);
+
+ static string[] ToStringArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedStringArrayToSystemArray(variant);
+
+ static Vector2[] ToVector2Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(variant);
+
+ static Vector3[] ToVector3Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(variant);
+
+ static Color[] ToColorArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedColorArrayToSystemArray(variant);
+
+ static StringName[] ToStringNameArray(in godot_variant variant) =>
+ VariantUtils.ConvertToSystemArrayOfStringName(variant);
+
+ static NodePath[] ToNodePathArray(in godot_variant variant) =>
+ VariantUtils.ConvertToSystemArrayOfNodePath(variant);
+
+ static RID[] ToRidArray(in godot_variant variant) =>
+ VariantUtils.ConvertToSystemArrayOfRID(variant);
+
+ static Godot.Object ToGodotObject(in godot_variant variant) =>
+ VariantUtils.ConvertToGodotObject(variant);
+
+ static StringName ToStringName(in godot_variant variant) =>
+ VariantUtils.ConvertToStringNameObject(variant);
+
+ static NodePath ToNodePath(in godot_variant variant) =>
+ VariantUtils.ConvertToNodePathObject(variant);
+
+ static RID ToRid(in godot_variant variant) =>
+ VariantUtils.ConvertToRID(variant);
+
+ static Collections.Dictionary ToGodotDictionary(in godot_variant variant) =>
+ VariantUtils.ConvertToDictionaryObject(variant);
+
+ static Collections.Array ToGodotArray(in godot_variant variant) =>
+ VariantUtils.ConvertToArrayObject(variant);
+
+ static Variant ToVariant(in godot_variant variant) =>
+ Variant.CreateCopyingBorrowed(variant);
+
+ var typeOfT = typeof(T);
+
+ // ReSharper disable RedundantCast
+ // Rider is being stupid here. These casts are definitely needed. We get build errors without them.
+
+ if (typeOfT == typeof(bool))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, bool>)
+ &ToBool;
+ }
+
+ if (typeOfT == typeof(char))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, char>)
+ &ToChar;
+ }
+
+ if (typeOfT == typeof(sbyte))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, sbyte>)
+ &ToInt8;
+ }
+
+ if (typeOfT == typeof(short))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, short>)
+ &ToInt16;
+ }
+
+ if (typeOfT == typeof(int))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, int>)
+ &ToInt32;
+ }
+
+ if (typeOfT == typeof(long))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, long>)
+ &ToInt64;
+ }
+
+ if (typeOfT == typeof(byte))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, byte>)
+ &ToUInt8;
+ }
+
+ if (typeOfT == typeof(ushort))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ushort>)
+ &ToUInt16;
+ }
+
+ if (typeOfT == typeof(uint))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, uint>)
+ &ToUInt32;
+ }
+
+ if (typeOfT == typeof(ulong))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ulong>)
+ &ToUInt64;
+ }
+
+ if (typeOfT == typeof(float))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, float>)
+ &ToFloat;
+ }
+
+ if (typeOfT == typeof(double))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, double>)
+ &ToDouble;
+ }
+
+ if (typeOfT == typeof(Vector2))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector2>)
+ &ToVector2;
+ }
+
+ if (typeOfT == typeof(Vector2i))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector2i>)
+ &ToVector2I;
+ }
+
+ if (typeOfT == typeof(Rect2))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Rect2>)
+ &ToRect2;
+ }
+
+ if (typeOfT == typeof(Rect2i))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Rect2i>)
+ &ToRect2I;
+ }
+
+ if (typeOfT == typeof(Transform2D))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Transform2D>)
+ &ToTransform2D;
+ }
+
+ if (typeOfT == typeof(Vector3))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector3>)
+ &ToVector3;
+ }
+
+ if (typeOfT == typeof(Vector3i))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector3i>)
+ &ToVector3I;
+ }
+
+ if (typeOfT == typeof(Basis))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Basis>)
+ &ToBasis;
+ }
+
+ if (typeOfT == typeof(Quaternion))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Quaternion>)
+ &ToQuaternion;
+ }
+
+ if (typeOfT == typeof(Transform3D))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Transform3D>)
+ &ToTransform3D;
+ }
+
+ if (typeOfT == typeof(AABB))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, AABB>)
+ &ToAabb;
+ }
+
+ if (typeOfT == typeof(Color))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Color>)
+ &ToColor;
+ }
+
+ if (typeOfT == typeof(Plane))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Plane>)
+ &ToPlane;
+ }
+
+ if (typeOfT == typeof(Callable))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Callable>)
+ &ToCallable;
+ }
+
+ if (typeOfT == typeof(SignalInfo))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, SignalInfo>)
+ &ToSignalInfo;
+ }
+
+ if (typeOfT.IsEnum)
+ {
+ var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, sbyte>)
+ &ToInt8;
+ }
+ case TypeCode.Int16:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, short>)
+ &ToInt16;
+ }
+ case TypeCode.Int32:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, int>)
+ &ToInt32;
+ }
+ case TypeCode.Int64:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, long>)
+ &ToInt64;
+ }
+ case TypeCode.Byte:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, byte>)
+ &ToUInt8;
+ }
+ case TypeCode.UInt16:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ushort>)
+ &ToUInt16;
+ }
+ case TypeCode.UInt32:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, uint>)
+ &ToUInt32;
+ }
+ case TypeCode.UInt64:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ulong>)
+ &ToUInt64;
+ }
+ default:
+ return null;
+ }
+ }
+
+ if (typeOfT == typeof(string))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, string>)
+ &ToString;
+ }
+
+ if (typeOfT == typeof(byte[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, byte[]>)
+ &ToByteArray;
+ }
+
+ if (typeOfT == typeof(int[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, int[]>)
+ &ToInt32Array;
+ }
+
+ if (typeOfT == typeof(long[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, long[]>)
+ &ToInt64Array;
+ }
+
+ if (typeOfT == typeof(float[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, float[]>)
+ &ToFloatArray;
+ }
+
+ if (typeOfT == typeof(double[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, double[]>)
+ &ToDoubleArray;
+ }
+
+ if (typeOfT == typeof(string[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, string[]>)
+ &ToStringArray;
+ }
+
+ if (typeOfT == typeof(Vector2[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector2[]>)
+ &ToVector2Array;
+ }
+
+ if (typeOfT == typeof(Vector3[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector3[]>)
+ &ToVector3Array;
+ }
+
+ if (typeOfT == typeof(Color[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Color[]>)
+ &ToColorArray;
+ }
+
+ if (typeOfT == typeof(StringName[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, StringName[]>)
+ &ToStringNameArray;
+ }
+
+ if (typeOfT == typeof(NodePath[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, NodePath[]>)
+ &ToNodePathArray;
+ }
+
+ if (typeOfT == typeof(RID[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, RID[]>)
+ &ToRidArray;
+ }
+
+ if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Godot.Object>)
+ &ToGodotObject;
+ }
+
+ if (typeOfT == typeof(StringName))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, StringName>)
+ &ToStringName;
+ }
+
+ if (typeOfT == typeof(NodePath))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, NodePath>)
+ &ToNodePath;
+ }
+
+ if (typeOfT == typeof(RID))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, RID>)
+ &ToRid;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Dictionary))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Godot.Collections.Dictionary>)
+ &ToGodotDictionary;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Array))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Godot.Collections.Array>)
+ &ToGodotArray;
+ }
+
+ if (typeOfT == typeof(Variant))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Variant>)
+ &ToVariant;
+ }
+
+ // ReSharper restore RedundantCast
+
+ return null;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs
new file mode 100644
index 0000000000..46f31bbf4e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Godot.NativeInterop
+{
+ internal readonly ref struct VariantSpanDisposer
+ {
+ private readonly Span<godot_variant.movable> _variantSpan;
+
+ // IMPORTANT: The span element must be default initialized.
+ // Make sure call Clear() on the span if it was created with stackalloc.
+ public VariantSpanDisposer(Span<godot_variant.movable> variantSpan)
+ {
+ _variantSpan = variantSpan;
+ }
+
+ public void Dispose()
+ {
+ for (int i = 0; i < _variantSpan.Length; i++)
+ _variantSpan[i].DangerousSelfRef.Dispose();
+ }
+ }
+
+ internal static class VariantSpanExtensions
+ {
+ // Used to make sure we always initialize the span values to the default,
+ // as we need that in order to safely dispose all elements after.
+ public static Span<godot_variant.movable> Cleared(this Span<godot_variant.movable> span)
+ {
+ span.Clear();
+ return span;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
new file mode 100644
index 0000000000..491ccf904e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -0,0 +1,611 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.Collections;
+
+// ReSharper disable InconsistentNaming
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public static class VariantUtils
+ {
+ public static godot_variant CreateFromRID(RID from)
+ => new() { Type = Variant.Type.Rid, RID = from };
+
+ public static godot_variant CreateFromBool(bool from)
+ => new() { Type = Variant.Type.Bool, Bool = from.ToGodotBool() };
+
+ public static godot_variant CreateFromInt(long from)
+ => new() { Type = Variant.Type.Int, Int = from };
+
+ public static godot_variant CreateFromInt(ulong from)
+ => new() { Type = Variant.Type.Int, Int = (long)from };
+
+ public static godot_variant CreateFromFloat(double from)
+ => new() { Type = Variant.Type.Float, Float = from };
+
+ public static godot_variant CreateFromVector2(Vector2 from)
+ => new() { Type = Variant.Type.Vector2, Vector2 = from };
+
+ public static godot_variant CreateFromVector2i(Vector2i from)
+ => new() { Type = Variant.Type.Vector2i, Vector2i = from };
+
+ public static godot_variant CreateFromVector3(Vector3 from)
+ => new() { Type = Variant.Type.Vector3, Vector3 = from };
+
+ public static godot_variant CreateFromVector3i(Vector3i from)
+ => new() { Type = Variant.Type.Vector3i, Vector3i = from };
+
+ public static godot_variant CreateFromRect2(Rect2 from)
+ => new() { Type = Variant.Type.Rect2, Rect2 = from };
+
+ public static godot_variant CreateFromRect2i(Rect2i from)
+ => new() { Type = Variant.Type.Rect2i, Rect2i = from };
+
+ public static godot_variant CreateFromQuaternion(Quaternion from)
+ => new() { Type = Variant.Type.Quaternion, Quaternion = from };
+
+ public static godot_variant CreateFromColor(Color from)
+ => new() { Type = Variant.Type.Color, Color = from };
+
+ public static godot_variant CreateFromPlane(Plane from)
+ => new() { Type = Variant.Type.Plane, Plane = from };
+
+ public static godot_variant CreateFromTransform2D(Transform2D from)
+ {
+ NativeFuncs.godotsharp_variant_new_transform2d(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromVector4(Vector4 from)
+ {
+ NativeFuncs.godotsharp_variant_new_vector4(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromVector4i(Vector4i from)
+ {
+ NativeFuncs.godotsharp_variant_new_vector4i(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromBasis(Basis from)
+ {
+ NativeFuncs.godotsharp_variant_new_basis(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromTransform3D(Transform3D from)
+ {
+ NativeFuncs.godotsharp_variant_new_transform3d(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromProjection(Projection from)
+ {
+ NativeFuncs.godotsharp_variant_new_projection(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromAABB(AABB from)
+ {
+ NativeFuncs.godotsharp_variant_new_aabb(out godot_variant ret, from);
+ return ret;
+ }
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
+ => new() { Type = Variant.Type.Callable, Callable = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromCallable(Callable from)
+ => CreateFromCallableTakingOwnershipOfDisposableValue(
+ Marshaling.ConvertCallableToNative(from));
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
+ => new() { Type = Variant.Type.Signal, Signal = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromSignalInfo(SignalInfo from)
+ => CreateFromSignalTakingOwnershipOfDisposableValue(
+ Marshaling.ConvertSignalToNative(from));
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
+ => new() { Type = Variant.Type.String, String = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromString(string? from)
+ => CreateFromStringTakingOwnershipOfDisposableValue(Marshaling.ConvertStringToNative(from));
+
+ public static godot_variant CreateFromPackedByteArray(in godot_packed_byte_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_byte_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedInt32Array(in godot_packed_int32_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_int32_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedInt64Array(in godot_packed_int64_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_int64_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedFloat32Array(in godot_packed_float32_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_float32_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedFloat64Array(in godot_packed_float64_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_float64_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedStringArray(in godot_packed_string_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_string_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedVector2Array(in godot_packed_vector2_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector2_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedVector3Array(in godot_packed_vector3_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector3_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedColorArray(in godot_packed_color_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedByteArray(Span<byte> from)
+ => CreateFromPackedByteArray(Marshaling.ConvertSystemArrayToNativePackedByteArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedInt32Array(Span<int> from)
+ => CreateFromPackedInt32Array(Marshaling.ConvertSystemArrayToNativePackedInt32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedInt64Array(Span<long> from)
+ => CreateFromPackedInt64Array(Marshaling.ConvertSystemArrayToNativePackedInt64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedFloat32Array(Span<float> from)
+ => CreateFromPackedFloat32Array(Marshaling.ConvertSystemArrayToNativePackedFloat32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedFloat64Array(Span<double> from)
+ => CreateFromPackedFloat64Array(Marshaling.ConvertSystemArrayToNativePackedFloat64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedStringArray(Span<string> from)
+ => CreateFromPackedStringArray(Marshaling.ConvertSystemArrayToNativePackedStringArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector2Array(Span<Vector2> from)
+ => CreateFromPackedVector2Array(Marshaling.ConvertSystemArrayToNativePackedVector2Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector3Array(Span<Vector3> from)
+ => CreateFromPackedVector3Array(Marshaling.ConvertSystemArrayToNativePackedVector3Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedColorArray(Span<Color> from)
+ => CreateFromPackedColorArray(Marshaling.ConvertSystemArrayToNativePackedColorArray(from));
+
+ public static godot_variant CreateFromSystemArrayOfStringName(Span<StringName> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ public static godot_variant CreateFromSystemArrayOfNodePath(Span<NodePath> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ public static godot_variant CreateFromSystemArrayOfRID(Span<RID> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ // ReSharper disable once RedundantNameQualifier
+ public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from)
+ {
+ if (from == null)
+ return default; // Nil
+ using var fromGodot = new Collections.Array(from);
+ return CreateFromArray((godot_array)fromGodot.NativeValue);
+ }
+
+ public static godot_variant CreateFromArray(godot_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromArray(Collections.Array? from)
+ => from != null ? CreateFromArray((godot_array)from.NativeValue) : default;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromArray<T>(Array<T>? from)
+ => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;
+
+ public static godot_variant CreateFromDictionary(godot_dictionary from)
+ {
+ NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromDictionary(Dictionary? from)
+ => from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from)
+ => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default;
+
+ public static godot_variant CreateFromStringName(godot_string_name from)
+ {
+ NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromStringName(StringName? from)
+ => from != null ? CreateFromStringName((godot_string_name)from.NativeValue) : default;
+
+ public static godot_variant CreateFromNodePath(godot_node_path from)
+ {
+ NativeFuncs.godotsharp_variant_new_node_path(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromNodePath(NodePath? from)
+ => from != null ? CreateFromNodePath((godot_node_path)from.NativeValue) : default;
+
+ public static godot_variant CreateFromGodotObjectPtr(IntPtr from)
+ {
+ if (from == IntPtr.Zero)
+ return new godot_variant();
+ NativeFuncs.godotsharp_variant_new_object(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // ReSharper disable once RedundantNameQualifier
+ public static godot_variant CreateFromGodotObject(Godot.Object? from)
+ => from != null ? CreateFromGodotObjectPtr(Object.GetPtr(from)) : default;
+
+ // We avoid the internal call if the stored type is the same we want.
+
+ public static bool ConvertToBool(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Bool ?
+ p_var.Bool.ToBool() :
+ NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool();
+
+ public static char ConvertToChar(in godot_variant p_var)
+ => (char)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static sbyte ConvertToInt8(in godot_variant p_var)
+ => (sbyte)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static short ConvertToInt16(in godot_variant p_var)
+ => (short)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static int ConvertToInt32(in godot_variant p_var)
+ => (int)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static long ConvertToInt64(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var);
+
+ public static byte ConvertToUInt8(in godot_variant p_var)
+ => (byte)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static ushort ConvertToUInt16(in godot_variant p_var)
+ => (ushort)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static uint ConvertToUInt32(in godot_variant p_var)
+ => (uint)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static ulong ConvertToUInt64(in godot_variant p_var)
+ => (ulong)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static float ConvertToFloat32(in godot_variant p_var)
+ => (float)(p_var.Type == Variant.Type.Float ?
+ p_var.Float :
+ NativeFuncs.godotsharp_variant_as_float(p_var));
+
+ public static double ConvertToFloat64(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Float ?
+ p_var.Float :
+ NativeFuncs.godotsharp_variant_as_float(p_var);
+
+ public static Vector2 ConvertToVector2(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector2 ?
+ p_var.Vector2 :
+ NativeFuncs.godotsharp_variant_as_vector2(p_var);
+
+ public static Vector2i ConvertToVector2i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector2i ?
+ p_var.Vector2i :
+ NativeFuncs.godotsharp_variant_as_vector2i(p_var);
+
+ public static Rect2 ConvertToRect2(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rect2 ?
+ p_var.Rect2 :
+ NativeFuncs.godotsharp_variant_as_rect2(p_var);
+
+ public static Rect2i ConvertToRect2i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rect2i ?
+ p_var.Rect2i :
+ NativeFuncs.godotsharp_variant_as_rect2i(p_var);
+
+ public static unsafe Transform2D ConvertToTransform2D(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Transform2d ?
+ *p_var.Transform2D :
+ NativeFuncs.godotsharp_variant_as_transform2d(p_var);
+
+ public static Vector3 ConvertToVector3(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector3 ?
+ p_var.Vector3 :
+ NativeFuncs.godotsharp_variant_as_vector3(p_var);
+
+ public static Vector3i ConvertToVector3i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector3i ?
+ p_var.Vector3i :
+ NativeFuncs.godotsharp_variant_as_vector3i(p_var);
+
+ public static unsafe Vector4 ConvertToVector4(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector4 ?
+ *p_var.Vector4 :
+ NativeFuncs.godotsharp_variant_as_vector4(p_var);
+
+ public static unsafe Vector4i ConvertToVector4i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector4i ?
+ *p_var.Vector4i :
+ NativeFuncs.godotsharp_variant_as_vector4i(p_var);
+
+ public static unsafe Basis ConvertToBasis(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Basis ?
+ *p_var.Basis :
+ NativeFuncs.godotsharp_variant_as_basis(p_var);
+
+ public static Quaternion ConvertToQuaternion(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Quaternion ?
+ p_var.Quaternion :
+ NativeFuncs.godotsharp_variant_as_quaternion(p_var);
+
+ public static unsafe Transform3D ConvertToTransform3D(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Transform3d ?
+ *p_var.Transform3D :
+ NativeFuncs.godotsharp_variant_as_transform3d(p_var);
+
+ public static unsafe Projection ConvertToProjection(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Projection ?
+ *p_var.Projection :
+ NativeFuncs.godotsharp_variant_as_projection(p_var);
+
+ public static unsafe AABB ConvertToAABB(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Aabb ?
+ *p_var.AABB :
+ NativeFuncs.godotsharp_variant_as_aabb(p_var);
+
+ public static Color ConvertToColor(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Color ?
+ p_var.Color :
+ NativeFuncs.godotsharp_variant_as_color(p_var);
+
+ public static Plane ConvertToPlane(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Plane ?
+ p_var.Plane :
+ NativeFuncs.godotsharp_variant_as_plane(p_var);
+
+ public static RID ConvertToRID(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rid ?
+ p_var.RID :
+ NativeFuncs.godotsharp_variant_as_rid(p_var);
+
+ public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // ReSharper disable once RedundantNameQualifier
+ public static Godot.Object ConvertToGodotObject(in godot_variant p_var)
+ => InteropUtils.UnmanagedGetManaged(ConvertToGodotObjectPtr(p_var));
+
+ public static string ConvertToStringObject(in godot_variant p_var)
+ {
+ switch (p_var.Type)
+ {
+ case Variant.Type.Nil:
+ return ""; // Otherwise, Variant -> String would return the string "Null"
+ case Variant.Type.String:
+ {
+ // We avoid the internal call if the stored type is the same we want.
+ return Marshaling.ConvertStringToManaged(p_var.String);
+ }
+ default:
+ {
+ using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
+ return Marshaling.ConvertStringToManaged(godotString);
+ }
+ }
+ }
+
+ public static godot_string_name ConvertToStringName(in godot_variant p_var)
+ => p_var.Type == Variant.Type.StringName ?
+ NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName) :
+ NativeFuncs.godotsharp_variant_as_string_name(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static StringName ConvertToStringNameObject(in godot_variant p_var)
+ => StringName.CreateTakingOwnershipOfDisposableValue(ConvertToStringName(p_var));
+
+ public static godot_node_path ConvertToNodePath(in godot_variant p_var)
+ => p_var.Type == Variant.Type.NodePath ?
+ NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath) :
+ NativeFuncs.godotsharp_variant_as_node_path(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static NodePath ConvertToNodePathObject(in godot_variant p_var)
+ => NodePath.CreateTakingOwnershipOfDisposableValue(ConvertToNodePath(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_callable ConvertToCallable(in godot_variant p_var)
+ => NativeFuncs.godotsharp_variant_as_callable(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Callable ConvertToCallableManaged(in godot_variant p_var)
+ => Marshaling.ConvertCallableToManaged(ConvertToCallable(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_signal ConvertToSignal(in godot_variant p_var)
+ => NativeFuncs.godotsharp_variant_as_signal(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static SignalInfo ConvertToSignalInfo(in godot_variant p_var)
+ => Marshaling.ConvertSignalToManaged(ConvertToSignal(p_var));
+
+ public static godot_array ConvertToArray(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Array ?
+ NativeFuncs.godotsharp_array_new_copy(p_var.Array) :
+ NativeFuncs.godotsharp_variant_as_array(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Collections.Array ConvertToArrayObject(in godot_variant p_var)
+ => Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Array<T> ConvertToArrayObject<T>(in godot_variant p_var)
+ => Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var));
+
+ public static godot_dictionary ConvertToDictionary(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Dictionary ?
+ NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) :
+ NativeFuncs.godotsharp_variant_as_dictionary(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary ConvertToDictionaryObject(in godot_variant p_var)
+ => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary<TKey, TValue> ConvertToDictionaryObject<TKey, TValue>(in godot_variant p_var)
+ => Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var));
+
+ public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(packedArray);
+ }
+
+ public static int[] ConvertAsPackedInt32ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
+ return Marshaling.ConvertNativePackedInt32ArrayToSystemArray(packedArray);
+ }
+
+ public static long[] ConvertAsPackedInt64ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
+ return Marshaling.ConvertNativePackedInt64ArrayToSystemArray(packedArray);
+ }
+
+ public static float[] ConvertAsPackedFloat32ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
+ return Marshaling.ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
+ }
+
+ public static double[] ConvertAsPackedFloat64ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
+ return Marshaling.ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
+ }
+
+ public static string[] ConvertAsPackedStringArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
+ return Marshaling.ConvertNativePackedStringArrayToSystemArray(packedArray);
+ }
+
+ public static Vector2[] ConvertAsPackedVector2ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
+ return Marshaling.ConvertNativePackedVector2ArrayToSystemArray(packedArray);
+ }
+
+ public static Vector3[] ConvertAsPackedVector3ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
+ return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
+ }
+
+ public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
+ return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray);
+ }
+
+ public static StringName[] ConvertToSystemArrayOfStringName(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfStringName(godotArray);
+ }
+
+ public static NodePath[] ConvertToSystemArrayOfNodePath(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray);
+ }
+
+ public static RID[] ConvertToSystemArrayOfRID(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRID(godotArray);
+ }
+
+ public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var)
+ // ReSharper disable once RedundantNameQualifier
+ where T : Godot.Object
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(godotArray);
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ public static Godot.Object[] ConvertToSystemArrayOfGodotObject(in godot_variant p_var, Type type)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 9ae01016cb..b02bd167a1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -39,22 +39,11 @@ namespace Godot
/// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
/// </code>
/// </example>
- public sealed partial class NodePath : IDisposable
+ public sealed class NodePath : IDisposable
{
- private bool _disposed = false;
+ internal godot_node_path.movable NativeValue;
- private IntPtr ptr;
-
- internal static IntPtr GetPtr(NodePath instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null.");
-
- if (instance._disposed)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
+ private WeakReference<IDisposable> _weakReferenceToSelf;
~NodePath()
{
@@ -70,29 +59,33 @@ namespace Godot
GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ public void Dispose(bool disposing)
{
- if (_disposed)
- return;
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
- if (ptr != IntPtr.Zero)
+ if (_weakReferenceToSelf != null)
{
- godot_icall_NodePath_Dtor(ptr);
- ptr = IntPtr.Zero;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
-
- _disposed = true;
}
- internal NodePath(IntPtr ptr)
+ private NodePath(godot_node_path nativeValueToOwn)
{
- this.ptr = ptr;
+ NativeValue = (godot_node_path.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
+ // Explicit name to make it very clear
+ internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn)
+ => new NodePath(nativeValueToOwn);
+
/// <summary>
/// Constructs an empty <see cref="NodePath"/>.
/// </summary>
- public NodePath() : this(string.Empty) { }
+ public NodePath()
+ {
+ }
/// <summary>
/// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>,
@@ -125,7 +118,11 @@ namespace Godot
/// <param name="path">A string that represents a path in a scene tree.</param>
public NodePath(string path)
{
- ptr = godot_icall_NodePath_Ctor(path);
+ if (!string.IsNullOrEmpty(path))
+ {
+ NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -138,7 +135,7 @@ namespace Godot
/// Converts this <see cref="NodePath"/> to a string.
/// </summary>
/// <param name="from">The <see cref="NodePath"/> to convert.</param>
- public static implicit operator string(NodePath from) => from.ToString();
+ public static implicit operator string(NodePath from) => from?.ToString();
/// <summary>
/// Converts this <see cref="NodePath"/> to a string.
@@ -146,7 +143,13 @@ namespace Godot
/// <returns>A string representation of this <see cref="NodePath"/>.</returns>
public override string ToString()
{
- return godot_icall_NodePath_operator_String(GetPtr(this));
+ if (IsEmpty)
+ return string.Empty;
+
+ var src = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_as_string(out godot_string dest, src);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
}
/// <summary>
@@ -166,7 +169,10 @@ namespace Godot
/// <returns>The <see cref="NodePath"/> as a pure property path.</returns>
public NodePath GetAsPropertyPath()
{
- return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
+ godot_node_path propertyPath = default;
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_as_property_path(self, ref propertyPath);
+ return CreateTakingOwnershipOfDisposableValue(propertyPath);
}
/// <summary>
@@ -181,7 +187,10 @@ namespace Godot
/// <returns>The names concatenated with <c>/</c>.</returns>
public string GetConcatenatedNames()
{
- return godot_icall_NodePath_get_concatenated_names(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_concatenated_names(self, out godot_string names);
+ using (names)
+ return Marshaling.ConvertStringToManaged(names);
}
/// <summary>
@@ -195,9 +204,12 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The subnames concatenated with <c>:</c>.</returns>
- public string GetConcatenatedSubnames()
+ public string GetConcatenatedSubNames()
{
- return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_concatenated_subnames(self, out godot_string subNames);
+ using (subNames)
+ return Marshaling.ConvertStringToManaged(subNames);
}
/// <summary>
@@ -215,28 +227,35 @@ namespace Godot
/// <returns>The name at the given index <paramref name="idx"/>.</returns>
public string GetName(int idx)
{
- return godot_icall_NodePath_get_name(GetPtr(this), idx);
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_name(self, idx, out godot_string name);
+ using (name)
+ return Marshaling.ConvertStringToManaged(name);
}
/// <summary>
/// Gets the number of node names which make up the path.
- /// Subnames (see <see cref="GetSubnameCount"/>) are not included.
+ /// Subnames (see <see cref="GetSubNameCount"/>) are not included.
/// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names.
/// </summary>
/// <returns>The number of node names which make up the path.</returns>
public int GetNameCount()
{
- return godot_icall_NodePath_get_name_count(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_get_name_count(self);
}
/// <summary>
- /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubnameCount"/>).
+ /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubNameCount"/>).
/// </summary>
/// <param name="idx">The subname index.</param>
/// <returns>The subname at the given index <paramref name="idx"/>.</returns>
- public string GetSubname(int idx)
+ public string GetSubName(int idx)
{
- return godot_icall_NodePath_get_subname(GetPtr(this), idx);
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_subname(self, idx, out godot_string subName);
+ using (subName)
+ return Marshaling.ConvertStringToManaged(subName);
}
/// <summary>
@@ -245,9 +264,10 @@ namespace Godot
/// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames.
/// </summary>
/// <returns>The number of subnames in the path.</returns>
- public int GetSubnameCount()
+ public int GetSubNameCount()
{
- return godot_icall_NodePath_get_subname_count(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_get_subname_count(self);
}
/// <summary>
@@ -259,52 +279,14 @@ namespace Godot
/// <returns>If the <see cref="NodePath"/> is an absolute path.</returns>
public bool IsAbsolute()
{
- return godot_icall_NodePath_is_absolute(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_is_absolute(self).ToBool();
}
/// <summary>
/// Returns <see langword="true"/> if the node path is empty.
/// </summary>
/// <returns>If the <see cref="NodePath"/> is empty.</returns>
- public bool IsEmpty()
- {
- return godot_icall_NodePath_is_empty(GetPtr(this));
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_NodePath_Ctor(string path);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr);
+ public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 746612477d..5cb678c280 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -1,54 +1,78 @@
using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
namespace Godot
{
public partial class Object : IDisposable
{
private bool _disposed = false;
+ private static readonly Type CachedType = typeof(Object);
- private static StringName nativeName = "Object";
+ internal IntPtr NativePtr;
+ private bool _memoryOwn;
- internal IntPtr ptr;
- internal bool memoryOwn;
+ private WeakReference<Object> _weakReferenceToSelf;
/// <summary>
/// Constructs a new <see cref="Object"/>.
/// </summary>
public Object() : this(false)
{
- if (ptr == IntPtr.Zero)
- ptr = godot_icall_Object_Ctor(this);
- _InitializeGodotScriptInstanceInternals();
+ unsafe
+ {
+ _ConstructAndInitialize(NativeCtor, NativeName, CachedType, refCounted: false);
+ }
}
- internal void _InitializeGodotScriptInstanceInternals()
+ internal unsafe void _ConstructAndInitialize(
+ delegate* unmanaged<IntPtr> nativeCtor,
+ StringName nativeName,
+ Type cachedType,
+ bool refCounted
+ )
{
- godot_icall_Object_ConnectEventSignals(ptr);
+ if (NativePtr == IntPtr.Zero)
+ {
+ NativePtr = nativeCtor();
+
+ InteropUtils.TieManagedToUnmanaged(this, NativePtr,
+ nativeName, refCounted, GetType(), cachedType);
+ }
+ else
+ {
+ InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
+ GetType(), cachedType);
+ }
+
+ _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
}
internal Object(bool memoryOwn)
{
- this.memoryOwn = memoryOwn;
+ _memoryOwn = memoryOwn;
}
/// <summary>
/// The pointer to the native instance of this <see cref="Object"/>.
/// </summary>
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
+ public IntPtr NativeInstance => NativePtr;
internal static IntPtr GetPtr(Object instance)
{
if (instance == null)
return IntPtr.Zero;
- if (instance._disposed)
+ // We check if NativePtr is null because this may be called by the debugger.
+ // If the debugger puts a breakpoint in one of the base constructors, before
+ // NativePtr is assigned, that would result in UB or crashes when calling
+ // native functions that receive the pointer, which can happen because the
+ // debugger calls ToString() and tries to get the value of properties.
+ if (instance._disposed || instance.NativePtr == IntPtr.Zero)
throw new ObjectDisposedException(instance.GetType().FullName);
- return instance.ptr;
+ return instance.NativePtr;
}
~Object()
@@ -73,22 +97,35 @@ namespace Godot
if (_disposed)
return;
- if (ptr != IntPtr.Zero)
+ _disposed = true;
+
+ if (NativePtr != IntPtr.Zero)
{
- if (memoryOwn)
+ IntPtr gcHandleToFree = NativeFuncs.godotsharp_internal_object_get_associated_gchandle(NativePtr);
+
+ if (gcHandleToFree != IntPtr.Zero)
+ {
+ object target = GCHandle.FromIntPtr(gcHandleToFree).Target;
+ // The GC handle may have been replaced in another thread. Release it only if
+ // it's associated to this managed instance, or if the target is no longer alive.
+ if (target != this && target != null)
+ gcHandleToFree = IntPtr.Zero;
+ }
+
+ if (_memoryOwn)
{
- memoryOwn = false;
- godot_icall_RefCounted_Disposed(this, ptr, !disposing);
+ NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree,
+ (!disposing).ToGodotBool());
}
else
{
- godot_icall_Object_Disposed(this, ptr);
+ NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree);
}
- ptr = IntPtr.Zero;
+ NativePtr = IntPtr.Zero;
}
- _disposed = true;
+ DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
}
/// <summary>
@@ -97,7 +134,9 @@ namespace Godot
/// <returns>A string representation of this object.</returns>
public override string ToString()
{
- return godot_icall_Object_ToString(GetPtr(this));
+ NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
/// <summary>
@@ -132,33 +171,72 @@ namespace Godot
return new SignalAwaiter(source, signal, this);
}
- /// <summary>
- /// Gets a new <see cref="DynamicGodotObject"/> associated with this instance.
- /// </summary>
- public dynamic DynamicObject => new DynamicGodotObject(this);
+ internal static Type InternalGetClassNativeBase(Type t)
+ {
+ do
+ {
+ var assemblyName = t.Assembly.GetName();
- internal static IntPtr __ClassDB_get_method(StringName type, string method)
+ if (assemblyName.Name == "GodotSharp")
+ return t;
+
+ if (assemblyName.Name == "GodotSharpEditor")
+ return t;
+ } while ((t = t.BaseType) != null);
+
+ return null;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
+ {
+ return false;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
+ {
+ value = default;
+ return false;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
+ NativeVariantPtrArgs args, int argCount)
{
- return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
+ internal static IntPtr ClassDB_get_method(StringName type, StringName method)
+ {
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var methodSelf = (godot_string_name)method.NativeValue;
+ IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
+
+ if (methodBind == IntPtr.Zero)
+ throw new NativeMethodBindNotFoundException(type + "." + method);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
+ return methodBind;
+ }
+
+ internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
+ {
+ // for some reason the '??' operator doesn't support 'delegate*'
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+ if (nativeConstructor == null)
+ throw new NativeConstructorNotFoundException(type);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
+ return nativeConstructor;
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Object_ToString(IntPtr ptr);
+ protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
+ {
+ }
- // Used by the generated API
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
+ // TODO: Should this be a constructor overload?
+ protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
+ {
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs
new file mode 100644
index 0000000000..eb2811c73d
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs
@@ -0,0 +1,135 @@
+using System;
+
+#nullable enable
+
+namespace Godot
+{
+ public partial class Object
+ {
+ public class NativeMemberNotFoundException : Exception
+ {
+ public NativeMemberNotFoundException()
+ {
+ }
+
+ public NativeMemberNotFoundException(string? message) : base(message)
+ {
+ }
+
+ public NativeMemberNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ public class NativeConstructorNotFoundException : NativeMemberNotFoundException
+ {
+ private readonly string? _nativeClassName;
+
+ // ReSharper disable once InconsistentNaming
+ private const string Arg_NativeConstructorNotFoundException = "Unable to find the native constructor.";
+
+ public NativeConstructorNotFoundException()
+ : base(Arg_NativeConstructorNotFoundException)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? nativeClassName)
+ : this(Arg_NativeConstructorNotFoundException, nativeClassName)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? message, string? nativeClassName)
+ : base(message)
+ {
+ _nativeClassName = nativeClassName;
+ }
+
+ public NativeConstructorNotFoundException(string? message, string? nativeClassName, Exception? innerException)
+ : base(message, innerException)
+ {
+ _nativeClassName = nativeClassName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ string s = base.Message;
+
+ if (string.IsNullOrEmpty(s))
+ {
+ s = Arg_NativeConstructorNotFoundException;
+ }
+
+ if (!string.IsNullOrEmpty(_nativeClassName))
+ {
+ s += " " + string.Format("(Class '{0}')", _nativeClassName);
+ }
+
+ return s;
+ }
+ }
+ }
+
+ public class NativeMethodBindNotFoundException : NativeMemberNotFoundException
+ {
+ private readonly string? _nativeMethodName;
+
+ // ReSharper disable once InconsistentNaming
+ private const string Arg_NativeMethodBindNotFoundException = "Unable to find the native method bind.";
+
+ public NativeMethodBindNotFoundException()
+ : base(Arg_NativeMethodBindNotFoundException)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? nativeMethodName)
+ : this(Arg_NativeMethodBindNotFoundException, nativeMethodName)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? message, string? nativeMethodName)
+ : base(message)
+ {
+ _nativeMethodName = nativeMethodName;
+ }
+
+ public NativeMethodBindNotFoundException(string? message, string? nativeMethodName, Exception? innerException)
+ : base(message, innerException)
+ {
+ _nativeMethodName = nativeMethodName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ string s = base.Message;
+
+ if (string.IsNullOrEmpty(s))
+ {
+ s = Arg_NativeMethodBindNotFoundException;
+ }
+
+ if (!string.IsNullOrEmpty(_nativeMethodName))
+ {
+ s += " " + string.Format("(Method '{0}')", _nativeMethodName);
+ }
+
+ return s;
+ }
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index fd97a71e47..50832d7679 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index d774021131..df16fe5718 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index e38dca414f..90e4e3b41e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -73,7 +68,7 @@ namespace Godot
case 3:
return w;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -93,7 +88,7 @@ namespace Godot
w = value;
break;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
index 1588869ec0..a31fef8360 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -9,99 +11,32 @@ namespace Godot
/// resource by themselves. They are used by and with the low-level Server
/// classes such as <see cref="RenderingServer"/>.
/// </summary>
- public sealed partial class RID : IDisposable
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RID
{
- private bool _disposed = false;
+ private ulong _id; // Default is 0
- internal IntPtr ptr;
-
- internal static IntPtr GetPtr(RID instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(RID)} is null.");
-
- if (instance._disposed)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
-
- ~RID()
- {
- Dispose(false);
- }
-
- /// <summary>
- /// Disposes of this <see cref="RID"/>.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
- {
- if (_disposed)
- return;
-
- if (ptr != IntPtr.Zero)
- {
- godot_icall_RID_Dtor(ptr);
- ptr = IntPtr.Zero;
- }
-
- _disposed = true;
- }
-
- internal RID(IntPtr ptr)
+ internal RID(ulong id)
{
- this.ptr = ptr;
- }
-
- /// <summary>
- /// The pointer to the native instance of this <see cref="RID"/>.
- /// </summary>
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
-
- internal RID()
- {
- this.ptr = IntPtr.Zero;
+ _id = id;
}
/// <summary>
/// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>.
/// </summary>
public RID(Object from)
- {
- this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from));
- }
+ => _id = from is Resource res ? res.GetRid()._id : default;
/// <summary>
/// Returns the ID of the referenced resource.
/// </summary>
/// <returns>The ID of the referenced resource.</returns>
- public int GetId()
- {
- return godot_icall_RID_get_id(GetPtr(this));
- }
+ public ulong Id => _id;
/// <summary>
/// Converts this <see cref="RID"/> to a string.
/// </summary>
/// <returns>A string representation of this RID.</returns>
- public override string ToString() => "[RID]";
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_RID_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_RID_get_id(IntPtr ptr);
+ public override string ToString() => $"RID({Id})";
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index ec16920fed..d2c9b0ca8b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
new file mode 100644
index 0000000000..ee605f8d8f
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Linq;
+
+#nullable enable
+
+namespace Godot;
+
+internal class ReflectionUtils
+{
+ public static Type? FindTypeInLoadedAssemblies(string assemblyName, string typeFullName)
+ {
+ return AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == assemblyName)?
+ .GetType(typeFullName);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
index 2ba0493002..96fb891086 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
@@ -1,50 +1,67 @@
using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
- public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
+ public class SignalAwaiter : IAwaiter<Variant[]>, IAwaitable<Variant[]>
{
private bool _completed;
- private object[] _result;
- private Action _action;
+ private Variant[] _result;
+ private Action _continuation;
public SignalAwaiter(Object source, StringName signal, Object target)
{
- godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this);
+ var awaiterGcHandle = CustomGCHandle.AllocStrong(this);
+ using godot_string_name signalSrc = NativeFuncs.godotsharp_string_name_new_copy(
+ (godot_string_name)(signal?.NativeValue ?? default));
+ NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), in signalSrc,
+ Object.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle));
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
+ public bool IsCompleted => _completed;
- public bool IsCompleted
+ public void OnCompleted(Action continuation)
{
- get
- {
- return _completed;
- }
+ _continuation = continuation;
}
- public void OnCompleted(Action action)
- {
- this._action = action;
- }
+ public Variant[] GetResult() => _result;
- public object[] GetResult()
- {
- return _result;
- }
+ public IAwaiter<Variant[]> GetAwaiter() => this;
- public IAwaiter<object[]> GetAwaiter()
+ [UnmanagedCallersOnly]
+ internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
+ godot_bool* outAwaiterIsNull)
{
- return this;
- }
+ try
+ {
+ var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
- internal void SignalCallback(object[] args)
- {
- _completed = true;
- _result = args;
- _action?.Invoke();
+ if (awaiter == null)
+ {
+ *outAwaiterIsNull = godot_bool.True;
+ return;
+ }
+
+ *outAwaiterIsNull = godot_bool.False;
+
+ awaiter._completed = true;
+
+ Variant[] signalArgs = new Variant[argCount];
+
+ for (int i = 0; i < argCount; i++)
+ signalArgs[i] = Variant.CreateCopyingBorrowed(*args[i]);
+
+ awaiter._result = signalArgs;
+
+ awaiter._continuation?.Invoke();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outAwaiterIsNull = godot_bool.False;
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
index da01300586..3f50df0a0d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
@@ -3,7 +3,7 @@ namespace Godot
/// <summary>
/// Represents a signal defined in an object.
/// </summary>
- public struct SignalInfo
+ public readonly struct SignalInfo
{
private readonly Object _owner;
private readonly StringName _signalName;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index a1f058ffe5..f0bc5949df 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
+using Godot.NativeInterop;
+
+#nullable enable
namespace Godot
{
@@ -177,6 +179,7 @@ namespace Godot
{
return 0;
}
+
if (from == 0 && to == len)
{
str = instance;
@@ -214,7 +217,7 @@ namespace Godot
/// <returns>The escaped string.</returns>
public static string CEscape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
@@ -239,7 +242,7 @@ namespace Godot
/// <returns>The unescaped string.</returns>
public static string CUnescape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
@@ -471,7 +474,8 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true)
{
- return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ return instance.IndexOf(what, from,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -490,7 +494,8 @@ namespace Godot
{
// TODO: Could be more efficient if we get a char version of `IndexOf`.
// See https://github.com/dotnet/runtime/issues/44116
- return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ return instance.IndexOf(what.ToString(), from,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>Find the last occurrence of a substring.</summary>
@@ -519,7 +524,8 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int FindLast(this string instance, string what, int from, bool caseSensitive = true)
{
- return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ return instance.LastIndexOf(what, from,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -804,6 +810,7 @@ namespace Godot
{
match = instance[source] == text[target];
}
+
if (match)
{
source++;
@@ -926,7 +933,7 @@ namespace Godot
/// <returns>The escaped string.</returns>
public static string JSONEscape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
@@ -1015,15 +1022,18 @@ namespace Godot
switch (expr[0])
{
case '*':
- return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive));
+ return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 &&
+ ExprMatch(instance.Substring(1), expr, caseSensitive));
case '?':
- return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return instance.Length > 0 && instance[0] != '.' &&
+ ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
default:
if (instance.Length == 0)
return false;
if (caseSensitive)
return instance[0] == expr[0];
- return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) &&
+ ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
}
}
@@ -1070,12 +1080,12 @@ namespace Godot
/// <returns>The MD5 hash of the string.</returns>
public static byte[] MD5Buffer(this string instance)
{
- return godot_icall_String_md5_buffer(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_md5_buffer(instanceStr, out var md5Buffer);
+ using (md5Buffer)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(md5Buffer);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_String_md5_buffer(string str);
-
/// <summary>
/// Returns the MD5 hash of the string as a string.
/// </summary>
@@ -1084,12 +1094,12 @@ namespace Godot
/// <returns>The MD5 hash of the string.</returns>
public static string MD5Text(this string instance)
{
- return godot_icall_String_md5_text(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_md5_text(instanceStr, out var md5Text);
+ using (md5Text)
+ return Marshaling.ConvertStringToManaged(md5Text);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_md5_text(string str);
-
/// <summary>
/// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
@@ -1244,12 +1254,11 @@ namespace Godot
/// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFind(this string instance, string what, int from = -1)
{
- return godot_icall_String_rfind(instance, what, from);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ using godot_string whatStr = Marshaling.ConvertStringToNative(instance);
+ return NativeFuncs.godotsharp_string_rfind(instanceStr, whatStr, from);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_String_rfind(string str, string what, int from);
-
/// <summary>
/// Perform a search for a substring, but start from the end of the string instead of the beginning.
/// Also search case-insensitive.
@@ -1261,12 +1270,11 @@ namespace Godot
/// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFindN(this string instance, string what, int from = -1)
{
- return godot_icall_String_rfindn(instance, what, from);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ using godot_string whatStr = Marshaling.ConvertStringToNative(instance);
+ return NativeFuncs.godotsharp_string_rfindn(instanceStr, whatStr, from);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_String_rfindn(string str, string what, int from);
-
/// <summary>
/// Returns the right side of the string from a given position.
/// </summary>
@@ -1321,12 +1329,12 @@ namespace Godot
/// <returns>The SHA-256 hash of the string.</returns>
public static byte[] SHA256Buffer(this string instance)
{
- return godot_icall_String_sha256_buffer(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_sha256_buffer(instanceStr, out var sha256Buffer);
+ using (sha256Buffer)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(sha256Buffer);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_String_sha256_buffer(string str);
-
/// <summary>
/// Returns the SHA-256 hash of the string as a string.
/// </summary>
@@ -1335,12 +1343,12 @@ namespace Godot
/// <returns>The SHA-256 hash of the string.</returns>
public static string SHA256Text(this string instance)
{
- return godot_icall_String_sha256_text(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_sha256_text(instanceStr, out var sha256Text);
+ using (sha256Text)
+ return Marshaling.ConvertStringToManaged(sha256Text);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_sha256_text(string str);
-
/// <summary>
/// Returns the similarity index of the text compared to this string.
/// 1 means totally similar and 0 means totally dissimilar.
@@ -1355,6 +1363,7 @@ namespace Godot
// Equal strings are totally similar
return 1.0f;
}
+
if (instance.Length < 2 || text.Length < 2)
{
// No way to calculate similarity without a single bigram
@@ -1390,12 +1399,12 @@ namespace Godot
/// </summary>
public static string SimplifyPath(this string instance)
{
- return godot_icall_String_simplify_path(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_simplify_path(instanceStr, out godot_string simplifiedPath);
+ using (simplifiedPath)
+ return Marshaling.ConvertStringToManaged(simplifiedPath);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_simplify_path(string str);
-
/// <summary>
/// Split the string by a divisor string, return an array of the substrings.
/// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
@@ -1409,7 +1418,8 @@ namespace Godot
/// <returns>The array of strings split from the string.</returns>
public static string[] Split(this string instance, string divisor, bool allowEmpty = true)
{
- return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
+ return instance.Split(new[] { divisor },
+ allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
@@ -1605,9 +1615,9 @@ namespace Godot
/// <seealso cref="XMLEscape(string)"/>
/// <param name="instance">The string to unescape.</param>
/// <returns>The unescaped string.</returns>
- public static string XMLUnescape(this string instance)
+ public static string? XMLUnescape(this string instance)
{
- return SecurityElement.FromString(instance).Text;
+ return SecurityElement.FromString(instance)?.Text;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index b1d504410b..10739c02a7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -10,20 +10,11 @@ namespace Godot
/// Comparing them is much faster than with regular strings, because only the pointers are compared,
/// not the whole strings.
/// </summary>
- public sealed partial class StringName : IDisposable
+ public sealed class StringName : IDisposable
{
- private IntPtr ptr;
+ internal godot_string_name.movable NativeValue;
- internal static IntPtr GetPtr(StringName instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(StringName)} is null.");
-
- if (instance.ptr == IntPtr.Zero)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
+ private WeakReference<IDisposable> _weakReferenceToSelf;
~StringName()
{
@@ -39,35 +30,45 @@ namespace Godot
GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ public void Dispose(bool disposing)
{
- if (ptr != IntPtr.Zero)
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
{
- godot_icall_StringName_Dtor(ptr);
- ptr = IntPtr.Zero;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
}
- internal StringName(IntPtr ptr)
+ private StringName(godot_string_name nativeValueToOwn)
{
- this.ptr = ptr;
+ NativeValue = (godot_string_name.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
+ // Explicit name to make it very clear
+ internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn)
+ => new StringName(nativeValueToOwn);
+
/// <summary>
/// Constructs an empty <see cref="StringName"/>.
/// </summary>
public StringName()
{
- ptr = IntPtr.Zero;
}
/// <summary>
/// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string.
/// </summary>
/// <param name="path">String to construct the <see cref="StringName"/> from.</param>
- public StringName(string path)
+ public StringName(string name)
{
- ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
+ if (!string.IsNullOrEmpty(name))
+ {
+ NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -80,7 +81,7 @@ namespace Godot
/// Converts a <see cref="StringName"/> to a string.
/// </summary>
/// <param name="from">The <see cref="StringName"/> to convert.</param>
- public static implicit operator string(StringName from) => from.ToString();
+ public static implicit operator string(StringName from) => from?.ToString();
/// <summary>
/// Converts this <see cref="StringName"/> to a string.
@@ -88,28 +89,75 @@ namespace Godot
/// <returns>A string representation of this <see cref="StringName"/>.</returns>
public override string ToString()
{
- return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
+ if (IsEmpty)
+ return string.Empty;
+
+ var src = (godot_string_name)NativeValue;
+ NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, src);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
}
/// <summary>
/// Check whether this <see cref="StringName"/> is empty.
/// </summary>
/// <returns>If the <see cref="StringName"/> is empty.</returns>
- public bool IsEmpty()
+ public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
+
+ public static bool operator ==(StringName left, StringName right)
{
- return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
+ if (left is null)
+ return right is null;
+ return left.Equals(right);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_StringName_Ctor(string path);
+ public static bool operator !=(StringName left, StringName right)
+ {
+ return !(left == right);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
+ public bool Equals(StringName other)
+ {
+ if (other is null)
+ return false;
+ return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef;
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
+ public static bool operator ==(StringName left, in godot_string_name right)
+ {
+ if (left is null)
+ return right.IsEmpty;
+ return left.Equals(right);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
+ public static bool operator !=(StringName left, in godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator ==(in godot_string_name left, StringName right)
+ {
+ return right == left;
+ }
+
+ public static bool operator !=(in godot_string_name left, StringName right)
+ {
+ return !(right == left);
+ }
+
+ public bool Equals(in godot_string_name other)
+ {
+ return NativeValue.DangerousSelfRef == other;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other));
+ }
+
+ public override int GetHashCode()
+ {
+ return NativeValue.GetHashCode();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 68d097eb4e..ab2c0cd785 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -93,7 +88,7 @@ namespace Godot
case 2:
return origin;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -110,7 +105,7 @@ namespace Godot
origin = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 9eaf4f3252..810f55e150 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -52,7 +47,7 @@ namespace Godot
case 3:
return origin;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -72,7 +67,7 @@ namespace Godot
origin = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -168,7 +163,7 @@ namespace Godot
/// <param name="target">The object to look at.</param>
/// <param name="up">The relative up direction.</param>
/// <returns>The resulting transform.</returns>
- public Transform3D LookingAt(Vector3 target, Vector3 up)
+ public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
{
Transform3D t = this;
t.SetLookAt(origin, target, up);
@@ -194,7 +189,7 @@ namespace Godot
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
/// <param name="angle">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
- public Transform3D Rotated(Vector3 axis, real_t angle)
+ public readonly Transform3D Rotated(Vector3 axis, real_t angle)
{
return new Transform3D(new Basis(axis, angle), new Vector3()) * this;
}
@@ -443,7 +438,7 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the transform and the object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
if (obj is Transform3D)
{
@@ -460,7 +455,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are exactly equal.</returns>
- public bool Equals(Transform3D other)
+ public readonly bool Equals(Transform3D other)
{
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
deleted file mode 100644
index eae8927ceb..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// Event arguments for when unhandled exceptions occur.
- /// </summary>
- public class UnhandledExceptionArgs
- {
- /// <summary>
- /// Exception object.
- /// </summary>
- public Exception Exception { get; private set; }
-
- internal UnhandledExceptionArgs(Exception exception)
- {
- Exception = exception;
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 67f70390dd..e47efacf69 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -62,7 +57,7 @@ namespace Godot
case 1:
return y;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -76,7 +71,7 @@ namespace Godot
y = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index b61954a84c..84790404d7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -62,7 +57,7 @@ namespace Godot
case 1:
return y;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -76,7 +71,7 @@ namespace Godot
y = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 67a98efc2d..e796d2f20f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -74,7 +69,7 @@ namespace Godot
case 2:
return z;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -91,7 +86,7 @@ namespace Godot
z = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index 0d4894f206..897e14ae88 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -74,7 +69,7 @@ namespace Godot
case 2:
return z;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -91,7 +86,7 @@ namespace Godot
z = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
index 4af817455c..f60033078c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
index 365dcef486..2802c1bb06 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs b/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs
new file mode 100644
index 0000000000..263a934fae
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs
@@ -0,0 +1,5 @@
+#if REAL_T_IS_DOUBLE
+global using real_t = System.Double;
+#else
+global using real_t = System.Single;
+#endif
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 4f55ce47e8..111920ecf6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -4,25 +4,67 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>10</LangVersion>
+
+ <AnalysisMode>Recommended</AnalysisMode>
+
+ <!-- Disabled temporarily as it pollutes the warnings, but we need to document public APIs. -->
+ <NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
+ <Description>Godot C# Core API.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>GodotSharp</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ </PropertyGroup>
+ <ItemGroup>
+ <!-- SdkPackageVersions.props for easy access -->
+ <None Include="$(GodotSdkPackageVersionsFilePath)">
+ <Link>SdkPackageVersions.props</Link>
+ </None>
+ </ItemGroup>
+ <PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="ReflectionAnalyzers" Version="0.1.22-dev" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" />
+ <!--PackageReference Include="IDisposableAnalyzers" Version="3.4.13" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" /-->
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+ <!-- Sources -->
+ <ItemGroup>
<Compile Include="Core\AABB.cs" />
+ <Compile Include="Core\Bridge\GodotSerializationInfo.cs" />
+ <Compile Include="Core\Bridge\MethodInfo.cs" />
+ <Compile Include="Core\CustomGCHandle.cs" />
<Compile Include="Core\Array.cs" />
<Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
- <Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
- <Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
<Compile Include="Core\Attributes\RPCAttribute.cs" />
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
<Compile Include="Core\Basis.cs" />
+ <Compile Include="Core\Bridge\CSharpInstanceBridge.cs" />
+ <Compile Include="Core\Bridge\GCHandleBridge.cs" />
+ <Compile Include="Core\Bridge\AlcReloadCfg.cs" />
+ <Compile Include="Core\Bridge\ManagedCallbacks.cs" />
+ <Compile Include="Core\Bridge\PropertyInfo.cs" />
+ <Compile Include="Core\Bridge\ScriptManagerBridge.cs" />
+ <Compile Include="Core\Bridge\ScriptManagerBridge.types.cs" />
<Compile Include="Core\Callable.cs" />
<Compile Include="Core\Color.cs" />
<Compile Include="Core\Colors.cs" />
@@ -30,45 +72,58 @@
<Compile Include="Core\DelegateUtils.cs" />
<Compile Include="Core\Dictionary.cs" />
<Compile Include="Core\Dispatcher.cs" />
- <Compile Include="Core\DynamicObject.cs" />
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
- <Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />
<Compile Include="Core\GodotTraceListener.cs" />
<Compile Include="Core\GodotUnhandledExceptionEvent.cs" />
+ <Compile Include="Core\DisposablesTracker.cs" />
<Compile Include="Core\Interfaces\IAwaitable.cs" />
<Compile Include="Core\Interfaces\IAwaiter.cs" />
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
- <Compile Include="Core\MarshalUtils.cs" />
<Compile Include="Core\Mathf.cs" />
<Compile Include="Core\MathfEx.cs" />
+ <Compile Include="Core\NativeInterop\CustomUnsafe.cs" />
+ <Compile Include="Core\NativeInterop\ExceptionUtils.cs" />
+ <Compile Include="Core\NativeInterop\GodotDllImportResolver.cs" />
+ <Compile Include="Core\NativeInterop\InteropUtils.cs" />
+ <Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
+ <Compile Include="Core\NativeInterop\NativeVariantPtrArgs.cs" />
+ <Compile Include="Core\NativeInterop\VariantConversionCallbacks.cs" />
+ <Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" />
+ <Compile Include="Core\NativeInterop\VariantUtils.cs" />
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
+ <Compile Include="Core\Object.exceptions.cs" />
<Compile Include="Core\Plane.cs" />
<Compile Include="Core\Projection.cs" />
<Compile Include="Core\Quaternion.cs" />
<Compile Include="Core\Rect2.cs" />
<Compile Include="Core\Rect2i.cs" />
+ <Compile Include="Core\ReflectionUtils.cs" />
<Compile Include="Core\RID.cs" />
+ <Compile Include="Core\NativeInterop\NativeFuncs.cs" />
+ <Compile Include="Core\NativeInterop\InteropStructs.cs" />
+ <Compile Include="Core\NativeInterop\Marshaling.cs" />
<Compile Include="Core\SignalInfo.cs" />
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />
<Compile Include="Core\StringName.cs" />
<Compile Include="Core\Transform2D.cs" />
<Compile Include="Core\Transform3D.cs" />
- <Compile Include="Core\UnhandledExceptionArgs.cs" />
<Compile Include="Core\Vector2.cs" />
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
<Compile Include="Core\Vector3i.cs" />
<Compile Include="Core\Vector4.cs" />
<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.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings
new file mode 100644
index 0000000000..1add6cc77e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings
@@ -0,0 +1,5 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=core/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
+</wpf:ResourceDictionary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs
new file mode 100644
index 0000000000..eb8b061120
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs
@@ -0,0 +1,841 @@
+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 override string ToString() => AsString();
+
+ public object? Obj
+ {
+ get
+ {
+ if (_obj == null)
+ _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar);
+
+ return _obj;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool AsBool() =>
+ VariantUtils.ConvertToBool((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public char AsChar() =>
+ (char)VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public sbyte AsSByte() =>
+ VariantUtils.ConvertToInt8((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public short AsInt16() =>
+ VariantUtils.ConvertToInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int AsInt32() =>
+ VariantUtils.ConvertToInt32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long AsInt64() =>
+ VariantUtils.ConvertToInt64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte AsByte() =>
+ VariantUtils.ConvertToUInt8((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ushort AsUInt16() =>
+ VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint AsUInt32() =>
+ VariantUtils.ConvertToUInt32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ulong AsUInt64() =>
+ VariantUtils.ConvertToUInt64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float AsSingle() =>
+ VariantUtils.ConvertToFloat32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double AsDouble() =>
+ VariantUtils.ConvertToFloat64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string AsString() =>
+ VariantUtils.ConvertToStringObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2 AsVector2() =>
+ VariantUtils.ConvertToVector2((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2i AsVector2i() =>
+ VariantUtils.ConvertToVector2i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rect2 AsRect2() =>
+ VariantUtils.ConvertToRect2((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rect2i AsRect2i() =>
+ VariantUtils.ConvertToRect2i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Transform2D AsTransform2D() =>
+ VariantUtils.ConvertToTransform2D((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3 AsVector3() =>
+ VariantUtils.ConvertToVector3((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3i AsVector3i() =>
+ VariantUtils.ConvertToVector3i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Basis AsBasis() =>
+ VariantUtils.ConvertToBasis((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Quaternion AsQuaternion() =>
+ VariantUtils.ConvertToQuaternion((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Transform3D AsTransform3D() =>
+ VariantUtils.ConvertToTransform3D((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 AsVector4() =>
+ VariantUtils.ConvertToVector4((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4i AsVector4i() =>
+ VariantUtils.ConvertToVector4i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Projection AsProjection() =>
+ VariantUtils.ConvertToProjection((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AABB AsAABB() =>
+ VariantUtils.ConvertToAABB((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Color AsColor() =>
+ VariantUtils.ConvertToColor((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Plane AsPlane() =>
+ VariantUtils.ConvertToPlane((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Callable AsCallable() =>
+ VariantUtils.ConvertToCallableManaged((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public SignalInfo AsSignalInfo() =>
+ VariantUtils.ConvertToSignalInfo((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte[] AsByteArray() =>
+ VariantUtils.ConvertAsPackedByteArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int[] AsInt32Array() =>
+ VariantUtils.ConvertAsPackedInt32ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long[] AsInt64Array() =>
+ VariantUtils.ConvertAsPackedInt64ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float[] AsFloat32Array() =>
+ VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double[] AsFloat64Array() =>
+ VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string[] AsStringArray() =>
+ VariantUtils.ConvertAsPackedStringArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2[] AsVector2Array() =>
+ VariantUtils.ConvertAsPackedVector2ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3[] AsVector3Array() =>
+ VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Color[] AsColorArray() =>
+ VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T[] AsGodotObjectArray<T>()
+ where T : Godot.Object =>
+ VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Dictionary<TKey, TValue> AsGodotDictionary<TKey, TValue>() =>
+ VariantUtils.ConvertToDictionaryObject<TKey, TValue>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Array<T> AsGodotArray<T>() =>
+ VariantUtils.ConvertToArrayObject<T>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public StringName[] AsSystemArrayOfStringName() =>
+ VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public NodePath[] AsSystemArrayOfNodePath() =>
+ VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RID[] AsSystemArrayOfRID() =>
+ VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Godot.Object AsGodotObject() =>
+ VariantUtils.ConvertToGodotObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public StringName AsStringName() =>
+ VariantUtils.ConvertToStringNameObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public NodePath AsNodePath() =>
+ VariantUtils.ConvertToNodePathObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RID AsRID() =>
+ VariantUtils.ConvertToRID((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Dictionary AsGodotDictionary() =>
+ VariantUtils.ConvertToDictionaryObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Array AsGodotArray() =>
+ VariantUtils.ConvertToArrayObject((godot_variant)NativeVar);
+
+ // Explicit conversion operators to supported types
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator bool(Variant from) => from.AsBool();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator char(Variant from) => from.AsChar();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator sbyte(Variant from) => from.AsSByte();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator short(Variant from) => from.AsInt16();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator int(Variant from) => from.AsInt32();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator long(Variant from) => from.AsInt64();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator byte(Variant from) => from.AsByte();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator ushort(Variant from) => from.AsUInt16();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator uint(Variant from) => from.AsUInt32();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator ulong(Variant from) => from.AsUInt64();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator float(Variant from) => from.AsSingle();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator double(Variant from) => from.AsDouble();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator string(Variant from) => from.AsString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2(Variant from) => from.AsVector2();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2i(Variant from) => from.AsVector2i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Rect2(Variant from) => from.AsRect2();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Rect2i(Variant from) => from.AsRect2i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Transform2D(Variant from) => from.AsTransform2D();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3(Variant from) => from.AsVector3();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3i(Variant from) => from.AsVector3i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Basis(Variant from) => from.AsBasis();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Quaternion(Variant from) => from.AsQuaternion();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Transform3D(Variant from) => from.AsTransform3D();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4(Variant from) => from.AsVector4();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4i(Variant from) => from.AsVector4i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Projection(Variant from) => from.AsProjection();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator AABB(Variant from) => from.AsAABB();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Color(Variant from) => from.AsColor();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Plane(Variant from) => from.AsPlane();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Callable(Variant from) => from.AsCallable();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator SignalInfo(Variant from) => from.AsSignalInfo();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator byte[](Variant from) => from.AsByteArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator int[](Variant from) => from.AsInt32Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator long[](Variant from) => from.AsInt64Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator float[](Variant from) => from.AsFloat32Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator double[](Variant from) => from.AsFloat64Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator string[](Variant from) => from.AsStringArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2[](Variant from) => from.AsVector2Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3[](Variant from) => from.AsVector3Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Color[](Variant from) => from.AsColorArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Godot.Object(Variant from) => from.AsGodotObject();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator StringName(Variant from) => from.AsStringName();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator NodePath(Variant from) => from.AsNodePath();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator RID(Variant from) => from.AsRID();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Collections.Dictionary(Variant from) => from.AsGodotDictionary();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Collections.Array(Variant from) => from.AsGodotArray();
+
+ // While we provide implicit conversion operators, normal methods are still needed for
+ // casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc).
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(bool from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(char from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(sbyte from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(short from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(int from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(long from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(byte from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(ushort from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(uint from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(ulong from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(float from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(double from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(string from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector2 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector2i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Rect2 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Rect2i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Transform2D from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector3 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector3i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Basis from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Quaternion from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Transform3D from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector4 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector4i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Projection from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(AABB from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Color from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Plane from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Callable from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(SignalInfo from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<byte> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<int> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<long> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<float> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<double> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<string> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector2> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector3> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Color> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Godot.Object[] from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom<T>(Collections.Array<T> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<StringName> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<NodePath> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<RID> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Godot.Object from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(StringName from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(NodePath from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(RID from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Collections.Dictionary from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Collections.Array from) => from;
+
+ // Implicit conversion operators
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(bool from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(char from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(sbyte from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(short from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(int from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(long from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(byte from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(ushort from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(uint from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(ulong from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(float from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(double from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(string from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromString(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Rect2 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Rect2i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Transform2D from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform2D(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Basis from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBasis(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Quaternion from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromQuaternion(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Transform3D from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Projection from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(AABB from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Color from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromColor(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Plane from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPlane(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Callable from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromCallable(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(SignalInfo from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSignalInfo(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(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 implicit operator Variant(Span<StringName> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<NodePath> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<RID> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Godot.Object from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromGodotObject(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(StringName from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromStringName(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(NodePath from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromNodePath(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(RID from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRID(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Collections.Dictionary from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Collections.Array from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index a8c4ba96b5..ebf09aab7b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -4,9 +4,24 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>10</LangVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <Description>Godot C# Editor API.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>GodotSharpEditor</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings
new file mode 100644
index 0000000000..c7ff6fd3ee
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings
@@ -0,0 +1,4 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
+</wpf:ResourceDictionary>