From 124fbf95f8ef065215e9fcc937a370dbef3196e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Mon, 3 May 2021 15:21:06 +0200 Subject: C#: Move marshaling logic and generated glue to C# We will be progressively moving most code to C#. The plan is to only use Mono's embedding APIs to set things at launch. This will make it much easier to later support CoreCLR too which doesn't have rich embedding APIs. Additionally the code in C# is more maintainable and makes it easier to implement new features, e.g.: runtime codegen which we could use to avoid using reflection for marshaling everytime a field, property or method is accessed. SOME NOTES ON INTEROP We make the same assumptions as GDNative about the size of the Godot structures we use. We take it a bit further by also assuming the layout of fields in some cases, which is riskier but let's us squeeze out some performance by avoiding unnecessary managed to native calls. Code that deals with native structs is less safe than before as there's no RAII and copy constructors in C#. It's like using the GDNative C API directly. One has to take special care to free values they own. Perhaps we could use roslyn analyzers to check this, but I don't know any that uses attributes to determine what's owned or borrowed. As to why we maily use pointers for native structs instead of ref/out: - AFAIK (and confirmed with a benchmark) ref/out are pinned during P/Invoke calls and that has a cost. - Native struct fields can't be ref/out in the first place. - A `using` local can't be passed as ref/out, only `in`. Calling a method or property on an `in` value makes a silent copy, so we want to avoid `in`. REGARDING THE BUILD SYSTEM There's no longer a `mono_glue=yes/no` SCons options. We no longer need to build with `mono_glue=no`, generate the glue and then build again with `mono_glue=yes`. We build only once and generate the glue (which is in C# now). However, SCons no longer builds the C# projects for us. Instead one must run `build_assemblies.py`, e.g.: ```sh %godot_src_root%/modules/mono/build_scripts/build_assemblies.py \ --godot-output-dir=%godot_src_root%/bin \ --godot-target=release_debug` ``` We could turn this into a custom build target, but I don't know how to do that with SCons (it's possible with Meson). OTHER NOTES Most of the moved code doesn't follow the C# naming convention and still has the word Mono in the names despite no longer dealing with Mono's embedding APIs. This is just temporary while transitioning, to make it easier to understand what was moved where. --- .../glue/GodotSharp/GodotSharp.sln.DotSettings | 7 + .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 279 ++-- .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 59 +- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 314 ++--- .../Core/Extensions/SceneTreeExtensions.cs | 7 +- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 23 +- .../GodotSharp/GodotSharp/Core/MarshalUtils.cs | 141 -- .../Core/NativeInterop/InteropStructs.cs | 438 +++++++ .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 32 + .../GodotSharp/Core/NativeInterop/Marshaling.cs | 1370 ++++++++++++++++++++ .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 346 +++++ .../Core/NativeInterop/NativeFuncs.extended.cs | 70 + .../Core/NativeInterop/VariantSpanHelpers.cs | 33 + .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 335 +++++ .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 119 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 92 +- .../GodotSharp/Core/Object.exceptions.cs | 135 ++ .../mono/glue/GodotSharp/GodotSharp/Core/RID.cs | 85 +- .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 23 +- .../GodotSharp/GodotSharp/Core/StringExtensions.cs | 6 +- .../glue/GodotSharp/GodotSharp/Core/StringName.cs | 67 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 17 + .../GodotSharp/GodotSharp.csproj.DotSettings | 5 + .../GodotSharpEditor/GodotSharpEditor.csproj | 2 + .../GodotSharpEditor.csproj.DotSettings | 4 + 25 files changed, 3319 insertions(+), 690 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings create mode 100644 modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings new file mode 100644 index 0000000000..3103fa78c7 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings @@ -0,0 +1,7 @@ + + GC + True + True + True + True + True diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a412047196..c32895baab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -1,47 +1,28 @@ 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; - } - } - /// /// 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 or . /// - public class Array : IList, IDisposable + public sealed class Array : IList, IDisposable { - private ArraySafeHandle _safeHandle; - private bool _disposed = false; + internal godot_array NativeValue; /// /// Constructs a new empty . /// public Array() { - _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + godot_icall_Array_Ctor(out NativeValue); } /// @@ -58,6 +39,7 @@ namespace Godot.Collections Add(element); } + // TODO: This must be removed. Lots of silent mistakes as it takes pretty much anything. /// /// Constructs a new from the given objects. /// @@ -69,25 +51,37 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array)); + + godot_icall_Array_Ctor_MonoArray(array, out NativeValue); } - internal Array(ArraySafeHandle handle) + private Array(godot_array nativeValueToOwn) { - _safeHandle = handle; + NativeValue = nativeValueToOwn; } - internal Array(IntPtr handle) + // Explicit name to make it very clear + internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) + => new Array(nativeValueToOwn); + + ~Array() { - _safeHandle = new ArraySafeHandle(handle); + Dispose(false); } - internal IntPtr GetPtr() + /// + /// Disposes of this . + /// + public void Dispose() { - if (_disposed) - throw new ObjectDisposedException(GetType().FullName); + Dispose(true); + GC.SuppressFinalize(this); + } - return _safeHandle.DangerousGetHandle(); + public void Dispose(bool disposing) + { + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } /// @@ -97,7 +91,9 @@ namespace Godot.Collections /// A new Godot Array. public Array Duplicate(bool deep = false) { - return new Array(godot_icall_Array_Duplicate(GetPtr(), deep)); + godot_array newArray; + godot_icall_Array_Duplicate(ref NativeValue, deep, out newArray); + return CreateTakingOwnershipOfDisposableValue(newArray); } /// @@ -107,7 +103,7 @@ namespace Godot.Collections /// if successful, or an error code. public Error Resize(int newSize) { - return godot_icall_Array_Resize(GetPtr(), newSize); + return godot_icall_Array_Resize(ref NativeValue, newSize); } /// @@ -115,7 +111,7 @@ namespace Godot.Collections /// public void Shuffle() { - godot_icall_Array_Shuffle(GetPtr()); + godot_icall_Array_Shuffle(ref NativeValue); } /// @@ -126,26 +122,9 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr())); - } - - // IDisposable - - /// - /// Disposes of this . - /// - public void Dispose() - { - if (_disposed) - return; - - if (_safeHandle != null) - { - _safeHandle.Dispose(); - _safeHandle = null; - } - - _disposed = true; + godot_array newArray; + godot_icall_Array_Concatenate(ref left.NativeValue, ref right.NativeValue, out newArray); + return CreateTakingOwnershipOfDisposableValue(newArray); } // IList @@ -160,8 +139,16 @@ namespace Godot.Collections /// The object at the given . public object this[int index] { - get => godot_icall_Array_At(GetPtr(), index); - set => godot_icall_Array_SetAt(GetPtr(), index, value); + get + { + godot_icall_Array_At(ref NativeValue, index, out godot_variant elem); + unsafe + { + using (elem) + return Marshaling.variant_to_mono_object(&elem); + } + } + set => godot_icall_Array_SetAt(ref NativeValue, index, value); } /// @@ -170,19 +157,19 @@ namespace Godot.Collections /// /// The object to add. /// The new size after adding the object. - public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); + public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value); /// /// Checks if this contains the given object. /// /// The item to look for. /// Whether or not this array contains the given object. - public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); + public bool Contains(object value) => godot_icall_Array_Contains(ref NativeValue, value); /// /// Erases all items from this . /// - public void Clear() => godot_icall_Array_Clear(GetPtr()); + public void Clear() => godot_icall_Array_Clear(ref NativeValue); /// /// Searches this for an object @@ -190,7 +177,7 @@ namespace Godot.Collections /// /// The object to search for. /// The index of the object, or -1 if not found. - public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); + public int IndexOf(object value) => godot_icall_Array_IndexOf(ref NativeValue, value); /// /// Inserts a new object at a given position in the array. @@ -200,20 +187,20 @@ namespace Godot.Collections /// /// The index to insert at. /// The object to insert. - public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value); + public void Insert(int index, object value) => godot_icall_Array_Insert(ref NativeValue, index, value); /// /// Removes the first occurrence of the specified /// from this . /// /// The value to remove. - public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value); + public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value); /// /// Removes an element from this by index. /// /// The index of the element to remove. - public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index); + public void RemoveAt(int index) => godot_icall_Array_RemoveAt(ref NativeValue, index); // ICollection @@ -222,7 +209,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// /// The number of elements. - public int Count => godot_icall_Array_Count(GetPtr()); + public int Count => godot_icall_Array_Count(ref NativeValue); object ICollection.SyncRoot => this; @@ -243,7 +230,7 @@ namespace Godot.Collections throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(GetPtr(), array, index); + godot_icall_Array_CopyTo(ref NativeValue, array, index); } // IEnumerable @@ -268,73 +255,71 @@ namespace Godot.Collections /// A string representation of this array. public override string ToString() { - return godot_icall_Array_ToString(GetPtr()); + return godot_icall_Array_ToString(ref NativeValue); } [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); + internal static extern void godot_icall_Array_Ctor(out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Array_At(IntPtr ptr, int index); + internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Count(IntPtr ptr); + internal static extern int godot_icall_Array_Count(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Add(IntPtr ptr, object item); + internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Clear(IntPtr ptr); + internal static extern void godot_icall_Array_Clear(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); + internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); + internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep); + internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item); + internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize); + internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr); + internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); + internal static extern string godot_icall_Array_ToString(ref godot_array ptr); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Array_ToString(IntPtr ptr); + internal interface IGenericGodotArray + { + Array UnderlyingArray { get; } + Type TypeOfElements { get; } } + // TODO: Now we should be able to avoid boxing /// /// Typed wrapper around Godot's Array class, an array of Variant /// typed elements allocated in the engine in C++. Useful when @@ -342,24 +327,29 @@ namespace Godot.Collections /// such as arrays or . /// /// The type of the array. - public class Array : IList, ICollection, IEnumerable + [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] + public sealed class Array : IList, ICollection, IEnumerable, IGenericGodotArray { - private Array _objectArray; + private readonly Array _underlyingArray; - internal static int elemTypeEncoding; - internal static IntPtr elemTypeClass; + internal ref godot_array NativeValue => ref _underlyingArray.NativeValue; - static Array() - { - Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); - } + // ReSharper disable StaticMemberInGenericType + // Warning is about unique static fields being created for each generic type combination: + // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html + // In our case this is exactly what we want. + private static readonly Type TypeOfElements = typeof(T); + // ReSharper restore StaticMemberInGenericType + + Array IGenericGodotArray.UnderlyingArray => _underlyingArray; + Type IGenericGodotArray.TypeOfElements => TypeOfElements; /// /// Constructs a new empty . /// public Array() { - _objectArray = new Array(); + _underlyingArray = new Array(); } /// @@ -372,7 +362,7 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - _objectArray = new Array(collection); + _underlyingArray = new Array(collection); } /// @@ -386,7 +376,8 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - _objectArray = new Array(array); + + _underlyingArray = new Array(array); } /// @@ -395,23 +386,12 @@ namespace Godot.Collections /// The untyped array to construct from. public Array(Array array) { - _objectArray = array; - } - - internal Array(IntPtr handle) - { - _objectArray = new Array(handle); - } - - internal Array(ArraySafeHandle handle) - { - _objectArray = new Array(handle); + _underlyingArray = array; } - internal IntPtr GetPtr() - { - return _objectArray.GetPtr(); - } + // Explicit name to make it very clear + internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) + => new Array(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// /// Converts this typed to an untyped . @@ -419,7 +399,7 @@ namespace Godot.Collections /// The typed array to convert. public static explicit operator Array(Array from) { - return from._objectArray; + return from._underlyingArray; } /// @@ -429,7 +409,7 @@ namespace Godot.Collections /// A new Godot Array. public Array Duplicate(bool deep = false) { - return new Array(_objectArray.Duplicate(deep)); + return new Array(_underlyingArray.Duplicate(deep)); } /// @@ -439,7 +419,7 @@ namespace Godot.Collections /// if successful, or an error code. public Error Resize(int newSize) { - return _objectArray.Resize(newSize); + return _underlyingArray.Resize(newSize); } /// @@ -447,7 +427,7 @@ namespace Godot.Collections /// public void Shuffle() { - _objectArray.Shuffle(); + _underlyingArray.Shuffle(); } /// @@ -458,7 +438,7 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - return new Array(left._objectArray + right._objectArray); + return new Array(left._underlyingArray + right._underlyingArray); } // IList @@ -469,8 +449,16 @@ namespace Godot.Collections /// The value at the given . public T this[int index] { - get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } - set { _objectArray[index] = value; } + get + { + Array.godot_icall_Array_At(ref _underlyingArray.NativeValue, index, out godot_variant elem); + unsafe + { + using (elem) + return (T)Marshaling.variant_to_mono_object_of_type(&elem, TypeOfElements); + } + } + set => _underlyingArray[index] = value; } /// @@ -481,7 +469,7 @@ namespace Godot.Collections /// The index of the item, or -1 if not found. public int IndexOf(T item) { - return _objectArray.IndexOf(item); + return _underlyingArray.IndexOf(item); } /// @@ -494,7 +482,7 @@ namespace Godot.Collections /// The item to insert. public void Insert(int index, T item) { - _objectArray.Insert(index, item); + _underlyingArray.Insert(index, item); } /// @@ -503,7 +491,7 @@ namespace Godot.Collections /// The index of the element to remove. public void RemoveAt(int index) { - _objectArray.RemoveAt(index); + _underlyingArray.RemoveAt(index); } // ICollection @@ -513,10 +501,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// /// The number of elements. - public int Count - { - get { return _objectArray.Count; } - } + public int Count => _underlyingArray.Count; bool ICollection.IsReadOnly => false; @@ -528,7 +513,7 @@ namespace Godot.Collections /// The new size after adding the item. public void Add(T item) { - _objectArray.Add(item); + _underlyingArray.Add(item); } /// @@ -536,7 +521,7 @@ namespace Godot.Collections /// public void Clear() { - _objectArray.Clear(); + _underlyingArray.Clear(); } /// @@ -546,7 +531,7 @@ namespace Godot.Collections /// Whether or not this array contains the given item. public bool Contains(T item) { - return _objectArray.Contains(item); + return _underlyingArray.Contains(item); } /// @@ -563,17 +548,14 @@ namespace Godot.Collections 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. - - int count = _objectArray.Count; + int count = _underlyingArray.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."); for (int i = 0; i < count; i++) { - array[arrayIndex] = (T)this[i]; + array[arrayIndex] = this[i]; arrayIndex++; } } @@ -586,7 +568,7 @@ namespace Godot.Collections /// A indicating success or failure. public bool Remove(T item) { - return Array.godot_icall_Array_Remove(GetPtr(), item); + return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item); } // IEnumerable @@ -597,23 +579,20 @@ namespace Godot.Collections /// An enumerator. public IEnumerator 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(); /// /// Converts this to a string. /// /// A string representation of this array. - public override string ToString() => _objectArray.ToString(); + public override string ToString() => _underlyingArray.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 1dca9e6ea7..187d910f9f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -4,11 +4,53 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot { internal static class DelegateUtils { + // TODO: Move somewhere else once we need to for things other than delegates + internal static void FreeGCHandle(IntPtr delegateGCHandle) + => GCHandle.FromIntPtr(delegateGCHandle).Free(); + + internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) + { + var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; + var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; + return @delegateA == @delegateB; + } + + internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, godot_variant* ret) + { + // 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.variant_to_mono_object_of_type( + args[i], parameterInfos[i].ParameterType); + } + + object invokeRet = @delegate.DynamicInvoke(managedArgs); + + *ret = Marshaling.mono_object_to_variant(invokeRet); + } + + // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance). + private enum TargetKind : uint { Static, @@ -16,7 +58,10 @@ namespace Godot CompilerGenerated } - internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) + internal static bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle, Collections.Array serializedData) + => TrySerializeDelegate((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target, serializedData); + + private static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) { if (@delegate is MulticastDelegate multicastDelegate) { @@ -72,12 +117,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 +140,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. @@ -213,6 +260,13 @@ namespace Godot } } + private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, out IntPtr delegateGCHandle) + { + bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate); + delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate)); + return res; + } + private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate) { if (serializedData.Count == 1) @@ -276,6 +330,7 @@ namespace Godot case TargetKind.GodotObject: { ulong objectId = reader.ReadUInt64(); + // ReSharper disable once RedundantNameQualifier Godot.Object godotObject = GD.InstanceFromId(objectId); if (godotObject == null) return false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index e80b6af68f..d0c7e4523b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -2,46 +2,28 @@ using System; using System.Collections.Generic; using System.Collections; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; 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; - } - } - /// /// Wrapper around Godot's Dictionary class, a dictionary of Variant /// typed elements allocated in the engine in C++. Useful when /// interfacing with the engine. /// - public class Dictionary : IDictionary, IDisposable + public sealed class Dictionary : + IDictionary, + IDisposable { - private DictionarySafeHandle _safeHandle; - private bool _disposed = false; + internal godot_dictionary NativeValue; /// /// Constructs a new empty . /// public Dictionary() { - _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + godot_icall_Dictionary_Ctor(out NativeValue); } /// @@ -58,22 +40,18 @@ namespace Godot.Collections Add(entry.Key, entry.Value); } - internal Dictionary(DictionarySafeHandle handle) + private Dictionary(godot_dictionary nativeValueToOwn) { - _safeHandle = handle; + NativeValue = nativeValueToOwn; } - 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); } /// @@ -81,16 +59,14 @@ namespace Godot.Collections /// public void Dispose() { - if (_disposed) - return; - - if (_safeHandle != null) - { - _safeHandle.Dispose(); - _safeHandle = null; - } + Dispose(true); + GC.SuppressFinalize(this); + } - _disposed = true; + public void Dispose(bool disposing) + { + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } /// @@ -100,7 +76,9 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary Duplicate(bool deep = false) { - return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep)); + godot_dictionary newDictionary; + godot_icall_Dictionary_Duplicate(ref NativeValue, deep, out newDictionary); + return CreateTakingOwnershipOfDisposableValue(newDictionary); } // IDictionary @@ -112,8 +90,9 @@ namespace Godot.Collections { get { - IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array keysArray; + godot_icall_Dictionary_Keys(ref NativeValue, out keysArray); + return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -124,16 +103,19 @@ namespace Godot.Collections { get { - IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array valuesArray; + godot_icall_Dictionary_Values(ref NativeValue, out valuesArray); + return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } 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)); + godot_array keysArray; + godot_array valuesArray; + int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray); + var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); + var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); return (keys, values, count); } @@ -147,8 +129,16 @@ namespace Godot.Collections /// The object at the given . public object this[object key] { - get => godot_icall_Dictionary_GetValue(GetPtr(), key); - set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); + get + { + godot_icall_Dictionary_GetValue(ref NativeValue, key, out godot_variant value); + unsafe + { + using (value) + return Marshaling.variant_to_mono_object(&value); + } + } + set => godot_icall_Dictionary_SetValue(ref NativeValue, key, value); } /// @@ -157,19 +147,19 @@ namespace Godot.Collections /// /// The key at which to add the object. /// The object to add. - public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); + public void Add(object key, object value) => godot_icall_Dictionary_Add(ref NativeValue, key, value); /// /// Erases all items from this . /// - public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); + public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue); /// /// Checks if this contains the given key. /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(ref NativeValue, key); /// /// Gets an enumerator for this . @@ -181,7 +171,7 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(ref NativeValue, key); // ICollection @@ -194,7 +184,7 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count => godot_icall_Dictionary_Count(GetPtr()); + public int Count => godot_icall_Dictionary_Count(ref NativeValue); /// /// Copies the elements of this to the given @@ -258,8 +248,17 @@ namespace Godot.Collections private void UpdateEntry() { _dirty = false; - godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value); - _entry = new DictionaryEntry(key, value); + godot_icall_Dictionary_KeyValuePairAt(ref _dictionary.NativeValue, _index, out var vKey, out var vValue); + unsafe + { + using (vKey) + using (vValue) + { + var key = Marshaling.variant_to_mono_object(&vKey); + var value = Marshaling.variant_to_mono_object(&vValue); + _entry = new DictionaryEntry(key, value); + } + } } public object Key => Entry.Key; @@ -286,76 +285,70 @@ namespace Godot.Collections /// A string representation of this dictionary. public override string ToString() { - return godot_icall_Dictionary_ToString(GetPtr()); + return godot_icall_Dictionary_ToString(ref NativeValue); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Ctor(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + internal static extern int godot_icall_Dictionary_KeyValuePairs(ref godot_dictionary ptr, out godot_array keys, out godot_array values); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_Count(IntPtr ptr); + internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out godot_variant key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values); + internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value); + internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr); [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); + internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr); + internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep); + internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value); [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); + internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr); + internal interface IGenericGodotDictionary + { + Dictionary UnderlyingDictionary { get; } + Type TypeOfKeys { get; } + Type TypeOfValues { get; } } + // TODO: Now we should be able to avoid boxing + /// /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant /// typed elements allocated in the engine in C++. Useful when @@ -364,24 +357,32 @@ namespace Godot.Collections /// /// The type of the dictionary's keys. /// The type of the dictionary's values. - public class Dictionary : IDictionary + public sealed class Dictionary : + IDictionary, IGenericGodotDictionary { - private readonly Dictionary _objectDict; + private readonly Dictionary _underlyingDict; - internal static int valTypeEncoding; - internal static IntPtr valTypeClass; + internal ref godot_dictionary NativeValue => ref _underlyingDict.NativeValue; - static Dictionary() - { - Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); - } + // ReSharper disable StaticMemberInGenericType + // Warning is about unique static fields being created for each generic type combination: + // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html + // In our case this is exactly what we want. + private static readonly Type TypeOfKeys = typeof(TKey); + + private static readonly Type TypeOfValues = typeof(TValue); + // ReSharper restore StaticMemberInGenericType + + Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict; + Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys; + Type IGenericGodotDictionary.TypeOfValues => TypeOfValues; /// /// Constructs a new empty . /// public Dictionary() { - _objectDict = new Dictionary(); + _underlyingDict = new Dictionary(); } /// @@ -391,19 +392,13 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary(IDictionary dictionary) { - _objectDict = new Dictionary(); + _underlyingDict = new Dictionary(); if (dictionary == null) throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); - // TODO: Can be optimized - - IntPtr godotDictionaryPtr = GetPtr(); - foreach (KeyValuePair entry in dictionary) - { - Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); - } + Add(entry.Key, entry.Value); } /// @@ -413,18 +408,12 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary(Dictionary dictionary) { - _objectDict = dictionary; + _underlyingDict = dictionary; } - internal Dictionary(IntPtr handle) - { - _objectDict = new Dictionary(handle); - } - - internal Dictionary(DictionarySafeHandle handle) - { - _objectDict = new Dictionary(handle); - } + // Explicit name to make it very clear + internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + => new Dictionary(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// /// Converts this typed to an untyped . @@ -432,12 +421,7 @@ namespace Godot.Collections /// The typed dictionary to convert. public static explicit operator Dictionary(Dictionary from) { - return from._objectDict; - } - - internal IntPtr GetPtr() - { - return _objectDict.GetPtr(); + return from._underlyingDict; } /// @@ -447,7 +431,7 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary Duplicate(bool deep = false) { - return new Dictionary(_objectDict.Duplicate(deep)); + return new Dictionary(_underlyingDict.Duplicate(deep)); } // IDictionary @@ -458,8 +442,16 @@ namespace Godot.Collections /// The value at the given . public TValue this[TKey key] { - get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } - set { _objectDict[key] = value; } + get + { + Dictionary.godot_icall_Dictionary_GetValue(ref _underlyingDict.NativeValue, key, out godot_variant value); + unsafe + { + using (value) + return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + } + } + set => _underlyingDict[key] = value; } /// @@ -469,8 +461,9 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array keyArray; + Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray); + return Array.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -481,15 +474,25 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array valuesArray; + Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray); + return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } private KeyValuePair GetKeyValuePair(int index) { - Dictionary.godot_icall_Dictionary_KeyValuePairAt_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass); - return new KeyValuePair((TKey)key, (TValue)value); + Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out var vKey, out var vValue); + unsafe + { + using (vKey) + using (vValue) + { + var key = Marshaling.variant_to_mono_object_of_type(&vKey, TypeOfKeys); + var value = Marshaling.variant_to_mono_object_of_type(&vValue, TypeOfValues); + return new KeyValuePair((TKey)key, (TValue)value); + } + } } /// @@ -500,7 +503,7 @@ namespace Godot.Collections /// The object to add. public void Add(TKey key, TValue value) { - _objectDict.Add(key, value); + _underlyingDict.Add(key, value); } /// @@ -510,7 +513,7 @@ namespace Godot.Collections /// Whether or not this dictionary contains the given key. public bool ContainsKey(TKey key) { - return _objectDict.Contains(key); + return _underlyingDict.Contains(key); } /// @@ -519,7 +522,7 @@ namespace Godot.Collections /// The key of the element to remove. public bool Remove(TKey key) { - return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); + return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key); } /// @@ -530,8 +533,18 @@ namespace Godot.Collections /// If an object was found for the given . public 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; + bool found = Dictionary.godot_icall_Dictionary_TryGetValue(ref _underlyingDict.NativeValue, key, out godot_variant retValue); + + unsafe + { + using (retValue) + { + value = found ? + (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : + default; + } + } + return found; } @@ -542,16 +555,13 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count - { - get { return _objectDict.Count; } - } + public int Count => _underlyingDict.Count; bool ICollection>.IsReadOnly => false; void ICollection>.Add(KeyValuePair item) { - _objectDict.Add(item.Key, item.Value); + _underlyingDict.Add(item.Key, item.Value); } /// @@ -559,12 +569,12 @@ namespace Godot.Collections /// public void Clear() { - _objectDict.Clear(); + _underlyingDict.Clear(); } bool ICollection>.Contains(KeyValuePair item) { - return _objectDict.Contains(new KeyValuePair(item.Key, item.Value)); + return _underlyingDict.Contains(new KeyValuePair(item.Key, item.Value)); } /// @@ -595,8 +605,7 @@ namespace Godot.Collections bool ICollection>.Remove(KeyValuePair item) { - return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); - ; + return Dictionary.godot_icall_Dictionary_Remove(ref _underlyingDict.NativeValue, item.Key, item.Value); } // IEnumerable> @@ -613,15 +622,12 @@ namespace Godot.Collections } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Converts this to a string. /// /// A string representation of this dictionary. - public override string ToString() => _objectDict.ToString(); + public override string ToString() => _underlyingDict.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index df130a5c77..7922f38ac5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using Godot.Collections; +using Godot.NativeInterop; namespace Godot { @@ -12,10 +13,12 @@ namespace Godot /// The type to cast to. Should be a descendant of . public Array GetNodesInGroup(StringName group) where T : class { - return new Array(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T))); + godot_array array; + godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array); + return Array.CreateTakingOwnershipOfDisposableValue(array); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType); + internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 236d0666bc..e8ea8f379b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -6,6 +6,7 @@ using real_t = System.Single; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -26,9 +27,10 @@ namespace Godot /// Byte array that will be decoded to a Variant. /// If objects should be decoded. /// The decoded Variant. - public static object Bytes2Var(byte[] bytes, bool allowObjects = false) + public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false) { - return godot_icall_GD_bytes2var(bytes, allowObjects); + using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); + return godot_icall_GD_bytes2var(&varBytes, allowObjects); } /// @@ -527,7 +529,7 @@ namespace Godot /// If the class exists in . public static bool TypeExists(StringName type) { - return godot_icall_GD_type_exists(StringName.GetPtr(type)); + return godot_icall_GD_type_exists(ref type.NativeValue); } /// @@ -539,9 +541,14 @@ namespace Godot /// Variant that will be encoded. /// If objects should be serialized. /// The Variant encoded as an array of bytes. - public static byte[] Var2Bytes(object var, bool fullObjects = false) + public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false) { - return godot_icall_GD_var2bytes(var, fullObjects); + godot_packed_byte_array varBytes; + godot_icall_GD_var2bytes(var, fullObjects, &varBytes); + using (varBytes) + { + return Marshaling.PackedByteArray_to_mono_array(&varBytes); + } } /// @@ -576,7 +583,7 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects); + internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* bytes, bool allowObjects); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object godot_icall_GD_convert(object what, Variant.Type type); @@ -636,10 +643,10 @@ namespace Godot internal static extern object godot_icall_GD_str2var(string str); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_GD_type_exists(IntPtr type); + internal static extern bool godot_icall_GD_type_exists(ref godot_string_name type); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects); + internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string godot_icall_GD_var2str(object var); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index 50ae2eb112..733a8ac114 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -5,150 +5,9 @@ namespace Godot { internal static class MarshalUtils { - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericArray(Type type) => - type.GetGenericTypeDefinition() == typeof(Collections.Array<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsSystemGenericList(Type type) => - type.GetGenericTypeDefinition() == typeof(List<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsSystemGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Dictionary<,>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); - /// /// Returns if the is applied to the given type. /// private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false); - - /// - /// Returns the generic type definition of . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition) - { - genericTypeDefinition = type.GetGenericTypeDefinition(); - } - - /// - /// Gets the element type for the given . - /// - /// Type for the generic array. - /// Element type for the generic array. - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static void ArrayGetElementType(Type arrayType, out Type elementType) - { - elementType = arrayType.GetGenericArguments()[0]; - } - - /// - /// Gets the key type and the value type for the given . - /// - /// The type for the generic dictionary. - /// Key type for the generic dictionary. - /// Value type for the generic dictionary. - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) - { - var genericArgs = dictionaryType.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - } - - /// - /// Constructs a new from - /// where the generic type for the elements is . - /// - /// Element type for the array. - /// The generic array type with the specified element type. - private static Type MakeGenericArrayType(Type elemType) - { - return typeof(Collections.Array<>).MakeGenericType(elemType); - } - - /// - /// Constructs a new from - /// where the generic type for the keys is and - /// for the values is . - /// - /// Key type for the dictionary. - /// Key type for the dictionary. - /// The generic dictionary type with the specified key and value types. - private static Type MakeGenericDictionaryType(Type keyType, Type valueType) - { - return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType); - } } } 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..6ee8cbc0bc --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -0,0 +1,438 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; + +#endif + +namespace Godot.NativeInterop +{ + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_bool + { + public byte _value; + + public unsafe godot_bool(bool value) => _value = *(byte*)&value; + + public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value; + public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool); + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_ref : IDisposable + { + internal IntPtr _reference; + + public void Dispose() + { + if (_reference == IntPtr.Zero) + return; + NativeFuncs.godotsharp_ref_destroy(ref this); + _reference = IntPtr.Zero; + } + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal 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 + internal struct godot_variant_call_error + { + godot_variant_call_error_error error; + int argument; + int expected; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant : IDisposable + { + // 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)] internal godot_variant_data _data; + + public Variant.Type _type + { + get => (Variant.Type)_typeField; + set => _typeField = (int)value; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal unsafe 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 + internal struct godot_variant_obj_data + { + public UInt64 id; + public IntPtr obj; + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + private 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 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.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_string : IDisposable + { + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_string_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_string_name : IDisposable + { + internal IntPtr _data; + + public void Dispose() + { + if (_data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_string_name_destroy(ref this); + _data = IntPtr.Zero; + } + + // An static method because an instance method could result in a hidden copy if called on an `in` parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(in godot_string_name name) => + // This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++. + name._data == IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_node_path : IDisposable + { + internal IntPtr _data; + + public void Dispose() + { + if (_data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_node_path_destroy(ref this); + _data = IntPtr.Zero; + } + + // An static method because an instance method could result in a hidden copy if called on an `in` parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(in godot_node_path nodePath) => + // This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does. + nodePath._data == IntPtr.Zero; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_signal : IDisposable + { + [FieldOffset(0)] public godot_string_name _name; + + // There's padding here on 32-bit + + [FieldOffset(8)] public UInt64 _objectId; + + public void Dispose() + { + if (_name._data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_signal_destroy(ref this); + _name._data = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_callable : IDisposable + { + [FieldOffset(0)] public godot_string_name _method; + + // There's padding here on 32-bit + + [FieldOffset(8)] public UInt64 _objectId; + [FieldOffset(8)] public IntPtr _custom; + + public void Dispose() + { + if (_method._data == IntPtr.Zero && _custom == IntPtr.Zero) + return; + NativeFuncs.godotsharp_callable_destroy(ref this); + _method._data = IntPtr.Zero; + _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.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_array : IDisposable + { + internal IntPtr _p; + + public void Dispose() + { + if (_p == IntPtr.Zero) + return; + NativeFuncs.godotsharp_array_destroy(ref this); + _p = IntPtr.Zero; + } + } + + // 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 + internal struct godot_dictionary : IDisposable + { + internal IntPtr _p; + + 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 godot_packed_byte_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_byte_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_int32_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_int32_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_int64_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_int64_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_float32_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_float32_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_float64_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_float64_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_string_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_string_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_vector2_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_vector3_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_color_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_color_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } +} 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..08d49bb937 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot.NativeInterop +{ + internal static class InteropUtils + { + public static Object UnmanagedGetManaged(IntPtr unmanaged) + { + // TODO: Move to C# + return internal_unmanaged_get_managed(unmanaged); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Object internal_unmanaged_get_managed(IntPtr unmanaged); + + public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged) + { + // TODO: Move to C# + internal_tie_managed_to_unmanaged(managed, unmanaged); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_tie_managed_to_unmanaged(Object managed, IntPtr unmanaged); + + public static unsafe Object EngineGetSingleton(string name) + { + using godot_string src = Marshaling.mono_string_to_godot(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..9b6f9633d1 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -0,0 +1,1370 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + // We want to use full name qualifiers here even if redundant for clarity + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + internal static class Marshaling + { + public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value) + { + var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType); + fieldInfo.SetValue(obj, valueObj); + } + + public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant) + { + 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(Int32[])) + return Variant.Type.PackedInt32Array; + + if (type == typeof(Int64[])) + 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 (typeof(Godot.Object[]).IsAssignableFrom(type)) + return Variant.Type.Array; + + if (type == typeof(object[])) + return Variant.Type.Array; + } + else if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(Collections.Array<>)) + return Variant.Type.Array; + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + return Variant.Type.Array; + + if (genericTypeDefinition == typeof(IDictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + return Variant.Type.Array; + } + else if (type == typeof(object)) + { + 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 || typeof(System.Collections.IDictionary) == type) + return Variant.Type.Dictionary; + + if (typeof(Collections.Array) == type || + typeof(System.Collections.ICollection) == type || + typeof(System.Collections.IEnumerable) == type) + { + return Variant.Type.Array; + } + } + + break; + } + } + + r_nil_is_variant = false; + + // Unknown + return Variant.Type.Nil; + } + + public static bool try_get_array_element_type(Type p_array_type, out Type r_elem_type) + { + if (p_array_type.IsArray || p_array_type.IsSZArray) + { + r_elem_type = p_array_type.GetElementType(); + return true; + } + else if (p_array_type.IsGenericType) + { + var genericTypeDefinition = p_array_type.GetGenericTypeDefinition(); + + if (typeof(Collections.Array) == genericTypeDefinition || + typeof(System.Collections.Generic.List<>) == genericTypeDefinition || + typeof(System.Collections.ICollection) == genericTypeDefinition || + typeof(System.Collections.IEnumerable) == genericTypeDefinition) + { + r_elem_type = p_array_type.GetGenericArguments()[0]; + return true; + } + } + + r_elem_type = null; + return false; + } + + /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ + + public static godot_variant mono_object_to_variant(object p_obj) + { + return mono_object_to_variant_impl(p_obj); + } + + public static godot_variant mono_object_to_variant_no_err(object p_obj) + { + return mono_object_to_variant_impl(p_obj); + } + + // TODO: Only called from C++. Remove once no longer needed. + private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret) + => *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err); + + private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) + { + 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 Int16 @int16: + return VariantUtils.CreateFromInt(@int16); + case Int32 @int32: + return VariantUtils.CreateFromInt(@int32); + case Int64 @int64: + return VariantUtils.CreateFromInt(@int64); + case Byte @uint8: + return VariantUtils.CreateFromInt(@uint8); + case UInt16 @uint16: + return VariantUtils.CreateFromInt(@uint16); + case UInt32 @uint32: + return VariantUtils.CreateFromInt(@uint32); + case UInt64 @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.CreateFromCallableTakingOwnershipOfDisposableValue( + ConvertCallableToNative(ref @callable)); + case SignalInfo @signalInfo: + return VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue( + ConvertSignalToNative(ref @signalInfo)); + case Enum @enum: + return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); + case string @string: + { + return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue( + mono_string_to_godot(@string)); + } + case Byte[] byteArray: + { + using godot_packed_byte_array array = mono_array_to_PackedByteArray(byteArray); + return VariantUtils.CreateFromPackedByteArray(&array); + } + case Int32[] int32Array: + { + using godot_packed_int32_array array = mono_array_to_PackedInt32Array(int32Array); + return VariantUtils.CreateFromPackedInt32Array(&array); + } + case Int64[] int64Array: + { + using godot_packed_int64_array array = mono_array_to_PackedInt64Array(int64Array); + return VariantUtils.CreateFromPackedInt64Array(&array); + } + case float[] floatArray: + { + using godot_packed_float32_array array = mono_array_to_PackedFloat32Array(floatArray); + return VariantUtils.CreateFromPackedFloat32Array(&array); + } + case double[] doubleArray: + { + using godot_packed_float64_array array = mono_array_to_PackedFloat64Array(doubleArray); + return VariantUtils.CreateFromPackedFloat64Array(&array); + } + case string[] stringArray: + { + using godot_packed_string_array array = mono_array_to_PackedStringArray(stringArray); + return VariantUtils.CreateFromPackedStringArray(&array); + } + case Vector2[] vector2Array: + { + using godot_packed_vector2_array array = mono_array_to_PackedVector2Array(vector2Array); + return VariantUtils.CreateFromPackedVector2Array(&array); + } + case Vector3[] vector3Array: + { + using godot_packed_vector3_array array = mono_array_to_PackedVector3Array(vector3Array); + return VariantUtils.CreateFromPackedVector3Array(&array); + } + case Color[] colorArray: + { + using godot_packed_color_array array = mono_array_to_PackedColorArray(colorArray); + return VariantUtils.CreateFromPackedColorArray(&array); + } + case Godot.Object[] godotObjectArray: + { + // ReSharper disable once CoVariantArrayConversion + using godot_array array = mono_array_to_Array(godotObjectArray); + return VariantUtils.CreateFromArray(&array); + } + case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] + { + // The pattern match for `object[]` catches arrays on any reference type, + // so we need to check the actual type to make sure it's truly `object[]`. + if (objectArray.GetType() == typeof(object[])) + { + using godot_array array = mono_array_to_Array(objectArray); + return VariantUtils.CreateFromArray(&array); + } + + if (p_fail_with_err) + { + GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); + return new godot_variant(); + } + else + { + return new godot_variant(); + } + } + case Godot.Object godotObject: + return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance); + case StringName stringName: + return VariantUtils.CreateFromStringName(ref stringName.NativeValue); + case NodePath nodePath: + return VariantUtils.CreateFromNodePath(ref nodePath.NativeValue); + case RID rid: + return VariantUtils.CreateFromRID(rid); + case Collections.Dictionary godotDictionary: + return VariantUtils.CreateFromDictionary(godotDictionary.NativeValue); + case Collections.Array godotArray: + return VariantUtils.CreateFromArray(godotArray.NativeValue); + case Collections.IGenericGodotDictionary genericGodotDictionary: + { + var godotDict = genericGodotDictionary.UnderlyingDictionary; + if (godotDict == null) + return new godot_variant(); + return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + } + case Collections.IGenericGodotArray genericGodotArray: + { + var godotArray = genericGodotArray.UnderlyingArray; + if (godotArray == null) + return new godot_variant(); + return VariantUtils.CreateFromArray(godotArray.NativeValue); + } + default: + { + var type = p_obj.GetType(); + + if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + { + // TODO: Validate key and value types are compatible with Variant +#if NET + Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj); +#else + var genericArguments = type.GetGenericArguments(); + + // With .NET Standard we need a package reference for Microsoft.CSharp in order to + // use dynamic, so we have this workaround for now until we switch to .NET 5/6. + var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary), + BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! + .MakeGenericMethod(genericArguments[0], genericArguments[1]); + + var genericGodotDictionary = (Collections.IGenericGodotDictionary)method + .Invoke(null, new[] {p_obj}); +#endif + + var godotDict = genericGodotDictionary.UnderlyingDictionary; + if (godotDict == null) + return new godot_variant(); + return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + } + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + { + // TODO: Validate element type is compatible with Variant +#if NET + var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); +#else + // With .NET Standard we need a package reference for Microsoft.CSharp in order to + // use dynamic, so we have this workaround for now until we switch to .NET 5/6. + // Also CollectionsMarshal.AsSpan is not available with .NET Standard. + + var collection = (System.Collections.ICollection)p_obj; + var array = new object[collection.Count]; + collection.CopyTo(array, 0); + var nativeGodotArray = mono_array_to_Array(array); +#endif + return VariantUtils.CreateFromArray(&nativeGodotArray); + } + } + + break; + } + } + + if (p_fail_with_err) + { + GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + + p_obj.GetType().FullName + "."); + return new godot_variant(); + } + else + { + return new godot_variant(); + } + } + + private static Collections.Dictionary IDictionaryToGenericGodotDictionary + (IDictionary dictionary) => new(dictionary); + + public static unsafe string variant_to_mono_string(godot_variant* p_var) + { + switch ((*p_var)._type) + { + case Variant.Type.Nil: + return null; // 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 mono_string_from_godot(&(*p_var)._data._m_string); + } + default: + { + using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); + return mono_string_from_godot(&godotString); + } + } + } + + public static unsafe object variant_to_mono_object_of_type(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 variant_to_mono_string(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)) + { + using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var); + return ConvertCallableToManaged(&callable); + } + + if (type == typeof(SignalInfo)) + { + using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var); + return ConvertSignalToManaged(&signal); + } + + 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 variant_to_mono_array_of_type(p_var, type); + else if (type.IsGenericType) + return variant_to_mono_object_of_genericinst(p_var, type); + else if (type == typeof(object)) + return variant_to_mono_object(p_var); + if (variant_to_mono_object_of_class(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 unsafe object variant_to_mono_array_of_type(godot_variant* p_var, Type type) + { + if (type == typeof(Byte[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + + if (type == typeof(Int32[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + + if (type == typeof(Int64[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + + if (type == typeof(float[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + + if (type == typeof(double[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + + if (type == typeof(string[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + + if (type == typeof(Vector2[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + + if (type == typeof(Vector3[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + + if (type == typeof(Color[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); + return PackedColorArray_to_mono_array(&packedArray); + } + + if (typeof(Godot.Object[]).IsAssignableFrom(type)) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Array_to_mono_array_of_type(&godotArray, type); + } + + if (type == typeof(object[])) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Array_to_mono_array(&godotArray); + } + + GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + + type.GetElementType()!.FullName + "."); + return null; + } + + private static unsafe bool variant_to_mono_object_of_class(godot_variant* p_var, Type type, out object res) + { + if (typeof(Godot.Object).IsAssignableFrom(type)) + { + res = InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); + return true; + } + + if (typeof(StringName) == type) + { + res = StringName.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToStringName(p_var)); + return true; + } + + if (typeof(NodePath) == type) + { + res = NodePath.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToNodePath(p_var)); + return true; + } + + if (typeof(RID) == type) + { + res = VariantUtils.ConvertToRID(p_var); + return true; + } + + if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + { + res = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + return true; + } + + if (typeof(Collections.Array) == type || + typeof(System.Collections.ICollection) == type || + typeof(System.Collections.IEnumerable) == type) + { + res = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + return true; + } + + res = null; + return false; + } + + private static unsafe object variant_to_mono_object_of_genericinst(godot_variant* p_var, Type type) + { + static object variant_to_generic_godot_collections_dictionary(godot_variant* p_var, Type fullType) + { + var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + return Activator.CreateInstance(fullType, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] {underlyingDict}, null); + } + + static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) + { + var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + return Activator.CreateInstance(fullType, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] {underlyingArray}, null); + } + + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) + return variant_to_generic_godot_collections_dictionary(p_var, type); + + if (genericTypeDefinition == typeof(Collections.Array<>)) + return variant_to_generic_godot_collections_array(p_var, type); + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + { + using var godotDictionary = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + + var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] + { + /* capacity: */ godotDictionary.Count + }, null); + + foreach (System.Collections.DictionaryEntry pair in godotDictionary) + dictionary.Add(pair.Key, pair.Value); + + return dictionary; + } + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + { + using var godotArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + + var list = (System.Collections.IList)Activator.CreateInstance(type, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] + { + /* capacity: */ godotArray.Count + }, null); + + foreach (object elem in godotArray) + list.Add(elem); + + return list; + } + + if (genericTypeDefinition == typeof(IDictionary<,>)) + { + var genericArgs = type.GetGenericArguments(); + var keyType = genericArgs[0]; + var valueType = genericArgs[1]; + var genericGodotDictionaryType = typeof(Collections.Dictionary<,>) + .MakeGenericType(keyType, valueType); + + return variant_to_generic_godot_collections_dictionary(p_var, genericGodotDictionaryType); + } + + if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + { + var elementType = type.GetGenericArguments()[0]; + var genericGodotArrayType = typeof(Collections.Array<>) + .MakeGenericType(elementType); + + return variant_to_generic_godot_collections_array(p_var, genericGodotArrayType); + } + + return null; + } + + public static unsafe object variant_to_mono_object(godot_variant* p_var) + { + switch ((*p_var)._type) + { + case Variant.Type.Bool: + return (bool)(*p_var)._data._bool; + case Variant.Type.Int: + return (*p_var)._data._int; + case Variant.Type.Float: + { +#if REAL_T_IS_DOUBLE + return (*p_var)._data._float; +#else + return (float)(*p_var)._data._float; +#endif + } + case Variant.Type.String: + return mono_string_from_godot(&(*p_var)._data._m_string); + case Variant.Type.Vector2: + return (*p_var)._data._m_vector2; + case Variant.Type.Vector2i: + return (*p_var)._data._m_vector2i; + case Variant.Type.Rect2: + return (*p_var)._data._m_rect2; + case Variant.Type.Rect2i: + return (*p_var)._data._m_rect2i; + case Variant.Type.Vector3: + return (*p_var)._data._m_vector3; + case Variant.Type.Vector3i: + return (*p_var)._data._m_vector3i; + case Variant.Type.Transform2d: + return *(*p_var)._data._transform2d; + case Variant.Type.Vector4: + return *(*p_var)._data._vector4; + case Variant.Type.Vector4i: + return *(*p_var)._data._vector4i; + case Variant.Type.Plane: + return (*p_var)._data._m_plane; + case Variant.Type.Quaternion: + return (*p_var)._data._m_quaternion; + case Variant.Type.Aabb: + return *(*p_var)._data._aabb; + case Variant.Type.Basis: + return *(*p_var)._data._basis; + case Variant.Type.Transform3d: + return *(*p_var)._data._transform3d; + case Variant.Type.Projection: + return *(*p_var)._data._projection; + case Variant.Type.Color: + return (*p_var)._data._m_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)._data._m_string_name)); + } + 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)._data._m_node_path)); + } + case Variant.Type.Rid: + return (*p_var)._data._m_rid; + case Variant.Type.Object: + return InteropUtils.UnmanagedGetManaged((*p_var)._data._m_obj_data.obj); + case Variant.Type.Callable: + return ConvertCallableToManaged(&(*p_var)._data._m_callable); + case Variant.Type.Signal: + return ConvertSignalToManaged(&(*p_var)._data._m_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)._data._m_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)._data._m_array)); + } + case Variant.Type.PackedByteArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + case Variant.Type.PackedInt32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedInt64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedFloat32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedFloat64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedStringArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + case Variant.Type.PackedVector2Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedVector3Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedColorArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); + return PackedColorArray_to_mono_array(&packedArray); + } + default: + return null; + } + } + + // String + + public static unsafe godot_string mono_string_to_godot(string p_mono_string) + { + if (p_mono_string == null) + return new godot_string(); + + fixed (char* methodChars = p_mono_string) + { + godot_string dest; + NativeFuncs.godotsharp_string_new_with_utf16_chars(&dest, methodChars); + return dest; + } + } + + public static unsafe string mono_string_from_godot(godot_string* p_string) + { + if ((*p_string)._ptr == IntPtr.Zero) + return string.Empty; + + const int sizeOfChar32 = 4; + byte* bytes = (byte*)(*p_string)._ptr; + int size = *((int*)(*p_string)._ptr - 1); + 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(ref Callable p_managed_callable) + { + if (p_managed_callable.Delegate != null) + { + unsafe + { + godot_callable callable; + NativeFuncs.godotsharp_callable_new_with_delegate( + GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), &callable); + return callable; + } + } + else + { + unsafe + { + godot_string_name method; + + if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty) + { + godot_string_name src = p_managed_callable.Method.NativeValue; + method = NativeFuncs.godotsharp_string_name_new_copy(&src); + } + else + { + method = default; + } + + return new godot_callable + { + _method = method, // Takes ownership of disposable + _objectId = p_managed_callable.Target.GetInstanceId() + }; + } + } + } + + public static unsafe Callable ConvertCallableToManaged(godot_callable* p_callable) + { + IntPtr delegateGCHandle; + IntPtr godotObject; + godot_string_name name; + + if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( + p_callable, &delegateGCHandle, &godotObject, &name)) + { + 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(ref SignalInfo p_managed_signal) + { + ulong ownerId = p_managed_signal.Owner.GetInstanceId(); + unsafe + { + godot_string_name name; + + if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty) + { + godot_string_name src = p_managed_signal.Name.NativeValue; + name = NativeFuncs.godotsharp_string_name_new_copy(&src); + } + else + { + name = default; + } + + return new godot_signal() + { + _name = name, + _objectId = ownerId + }; + } + } + + public static unsafe SignalInfo ConvertSignalToManaged(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 + + public static unsafe object[] Array_to_mono_array(godot_array* p_array) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + var ret = new object[length]; + + array.CopyTo(ret, 0); // variant_to_mono_object handled by Collections.Array + + return ret; + } + + public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + object ret = Activator.CreateInstance(type, length); + + array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array + + return ret; + } + + public static godot_array mono_array_to_Array(Span p_array) + { + if (p_array.IsEmpty) + { + godot_array ret; + Collections.Array.godot_icall_Array_Ctor(out ret); + return ret; + } + + using var array = new Collections.Array(); + array.Resize(p_array.Length); + + for (int i = 0; i < p_array.Length; i++) + array[i] = p_array[i]; + + godot_array src = array.NativeValue; + unsafe + { + return NativeFuncs.godotsharp_array_new_copy(&src); + } + } + + // PackedByteArray + + public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array) + { + byte* buffer = (byte*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + var array = new byte[size]; + fixed (byte* dest = array) + Buffer.MemoryCopy(buffer, dest, size, size); + return array; + } + + public static unsafe godot_packed_byte_array mono_array_to_PackedByteArray(Span 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[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array) + { + int* buffer = (int*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedInt32Array(Span 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[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array) + { + long* buffer = (long*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedInt64Array(Span 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[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array) + { + float* buffer = (float*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedFloat32Array(Span 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[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array) + { + double* buffer = (double*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedFloat64Array(Span 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[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) + { + godot_string* buffer = (godot_string*)(*p_array)._ptr; + if (buffer == null) + return new string[] { }; + int size = *((int*)(*p_array)._ptr - 1); + var array = new string[size]; + for (int i = 0; i < size; i++) + array[i] = mono_string_from_godot(&buffer[i]); + return array; + } + + public static unsafe godot_packed_string_array mono_array_to_PackedStringArray(Span 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 = mono_string_to_godot(p_array[i]); + NativeFuncs.godotsharp_packed_string_array_add(&dest, &godotStrElem); + } + + return dest; + } + + // PackedVector2Array + + public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array) + { + Vector2* buffer = (Vector2*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedVector2Array(Span 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[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array) + { + Vector3* buffer = (Vector3*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedVector3Array(Span 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[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array) + { + Color* buffer = (Color*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + 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 mono_array_to_PackedColorArray(Span 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..2f1056219d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -0,0 +1,346 @@ +using System; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ +#if !NET + // This improves P/Invoke performance. + // The attribute is not available with .NET Core and it's not needed there. + [System.Security.SuppressUnmanagedCodeSecurity] +#endif + internal static unsafe partial class NativeFuncs + { + private const string GodotDllName = "__Internal"; + + // Custom functions + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); + +#if NET + [DllImport(GodotDllName)] + public static extern delegate* unmanaged godotsharp_get_class_constructor(ref godot_string_name p_classname); +#else + // Workaround until we switch to .NET 5/6 + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func); +#endif + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_new_from_string(godot_string_name* dest, godot_string* name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_new_from_string(godot_node_path* dest, godot_string* name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_as_string(godot_string* r_dest, godot_string_name* p_name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np); + + [DllImport(GodotDllName)] + public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, godot_string* p_element); + + [DllImport(GodotDllName)] + public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, godot_callable* r_callable); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); + + // GDNative functions + + // gdnative.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret); + + [DllImport(GodotDllName)] + public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); + + // variant.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_string_name(godot_variant* r_dest, godot_string_name* p_s); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_node_path(godot_variant* r_dest, godot_node_path* p_np); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_object(godot_variant* r_dest, IntPtr p_obj); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_transform2d(godot_variant* r_dest, Transform2D* p_t2d); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_vector4(godot_variant* r_dest, Vector4* p_vec4); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_vector4i(godot_variant* r_dest, Vector4i* p_vec4i); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_basis(godot_variant* r_dest, Basis* p_basis); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_transform3d(godot_variant* r_dest, Transform3D* p_trans); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_projection(godot_variant* r_dest, Projection* p_proj); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_aabb(godot_variant* r_dest, AABB* p_aabb); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_dictionary(godot_variant* r_dest, godot_dictionary* p_dict); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, godot_packed_byte_array* p_pba); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, godot_packed_int32_array* p_pia); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, godot_packed_int64_array* p_pia); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, godot_packed_float32_array* p_pra); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, godot_packed_float64_array* p_pra); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, godot_packed_string_array* p_psa); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, godot_packed_vector2_array* p_pv2a); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, godot_packed_vector3_array* p_pv3a); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, godot_packed_color_array* p_pca); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_variant_as_bool(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern double godotsharp_variant_as_float(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_string godotsharp_variant_as_string(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector2 godotsharp_variant_as_vector2(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector2i godotsharp_variant_as_vector2i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Rect2 godotsharp_variant_as_rect2(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Rect2i godotsharp_variant_as_rect2i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector3 godotsharp_variant_as_vector3(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector3i godotsharp_variant_as_vector3i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Transform2D godotsharp_variant_as_transform2d(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector4 godotsharp_variant_as_vector4(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector4i godotsharp_variant_as_vector4i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Plane godotsharp_variant_as_plane(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Quaternion godotsharp_variant_as_quaternion(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern AABB godotsharp_variant_as_aabb(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Basis godotsharp_variant_as_basis(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Transform3D godotsharp_variant_as_transform3d(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Projection godotsharp_variant_as_projection(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Color godotsharp_variant_as_color(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_string_name godotsharp_variant_as_string_name(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_node_path godotsharp_variant_as_node_path(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern RID godotsharp_variant_as_rid(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_callable godotsharp_variant_as_callable(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_signal godotsharp_variant_as_signal(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_dictionary godotsharp_variant_as_dictionary(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_array godotsharp_variant_as_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self); + + // string.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_new_with_utf16_chars(godot_string* r_dest, char* p_contents); + + // string_name.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_new_copy(godot_string_name* r_dest, godot_string_name* p_src); + + // node_path.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_new_copy(godot_node_path* r_dest, godot_node_path* p_src); + + // array.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + + // dictionary.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); + + // destroy functions + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_destroy(ref godot_variant p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_destroy(ref godot_string p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_destroy(ref godot_string_name p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_destroy(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_signal_destroy(ref godot_signal p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_callable_destroy(ref godot_callable p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_destroy(ref godot_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + } +} 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..70df79c1de --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.CompilerServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + internal static unsafe partial class NativeFuncs + { + public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src) + { + godot_string_name ret; + godotsharp_string_name_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_string_name godotsharp_string_name_new_copy(godot_string_name src) + => godotsharp_string_name_new_copy(&src); + + public static godot_node_path godotsharp_node_path_new_copy(godot_node_path* src) + { + godot_node_path ret; + godotsharp_node_path_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_node_path godotsharp_node_path_new_copy(godot_node_path src) + => godotsharp_node_path_new_copy(&src); + + public static godot_array godotsharp_array_new_copy(godot_array* src) + { + godot_array ret; + godotsharp_array_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_array godotsharp_array_new_copy(godot_array src) + => godotsharp_array_new_copy(&src); + + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src) + { + godot_dictionary ret; + godotsharp_dictionary_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary src) + => godotsharp_dictionary_new_copy(&src); + + public static godot_string_name godotsharp_string_name_new_from_string(string name) + { + godot_string_name ret; + using godot_string src = Marshaling.mono_string_to_godot(name); + godotsharp_string_name_new_from_string(&ret, &src); + return ret; + } + + public static godot_node_path godotsharp_node_path_new_from_string(string name) + { + godot_node_path ret; + using godot_string src = Marshaling.mono_string_to_godot(name); + godotsharp_node_path_new_from_string(&ret, &src); + return ret; + } + } +} 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..2814f9d506 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs @@ -0,0 +1,33 @@ +using System; + +namespace Godot.NativeInterop +{ + internal ref struct VariantSpanDisposer + { + private readonly Span _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 variantSpan) + { + _variantSpan = variantSpan; + } + + public void Dispose() + { + for (int i = 0; i < _variantSpan.Length; i++) + _variantSpan[i].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 Cleared(this Span 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..67f9e23893 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -0,0 +1,335 @@ +using System; +using System.Runtime.CompilerServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + internal static class VariantUtils + { + public static godot_variant CreateFromRID(RID from) + => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; + + public static godot_variant CreateFromBool(bool from) + => new() {_type = Variant.Type.Bool, _data = {_bool = from}}; + + public static godot_variant CreateFromInt(long from) + => new() {_type = Variant.Type.Int, _data = {_int = from}}; + + public static godot_variant CreateFromInt(ulong from) + => new() {_type = Variant.Type.Int, _data = {_int = (long)from}}; + + public static godot_variant CreateFromFloat(double from) + => new() {_type = Variant.Type.Float, _data = {_float = from}}; + + public static godot_variant CreateFromVector2(Vector2 from) + => new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}}; + + public static godot_variant CreateFromVector2i(Vector2i from) + => new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}}; + + public static godot_variant CreateFromVector3(Vector3 from) + => new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}}; + + public static godot_variant CreateFromVector3i(Vector3i from) + => new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}}; + + public static godot_variant CreateFromRect2(Rect2 from) + => new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}}; + + public static godot_variant CreateFromRect2i(Rect2i from) + => new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}}; + + public static godot_variant CreateFromQuaternion(Quaternion from) + => new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}}; + + public static godot_variant CreateFromColor(Color from) + => new() {_type = Variant.Type.Color, _data = {_m_color = from}}; + + public static godot_variant CreateFromPlane(Plane from) + => new() {_type = Variant.Type.Plane, _data = {_m_plane = from}}; + + public static unsafe godot_variant CreateFromTransform2D(Transform2D from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_transform2d(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromVector4(Vector4 from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_vector4(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromVector4i(Vector4i from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_vector4i(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromBasis(Basis from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_basis(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromTransform3D(Transform3D from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_transform3d(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromProjection(Projection from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_projection(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromAABB(AABB from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_aabb(&ret, &from); + return ret; + } + + // Explicit name to make it very clear + public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from) + => new() {_type = Variant.Type.Callable, _data = {_m_callable = from}}; + + // Explicit name to make it very clear + public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from) + => new() {_type = Variant.Type.Signal, _data = {_m_signal = from}}; + + // Explicit name to make it very clear + public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from) + => new() {_type = Variant.Type.String, _data = {_m_string = from}}; + + public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_byte_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedInt32Array(godot_packed_int32_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_int32_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedInt64Array(godot_packed_int64_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_int64_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedFloat32Array(godot_packed_float32_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_float32_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedFloat64Array(godot_packed_float64_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_float64_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedStringArray(godot_packed_string_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_string_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedVector2Array(godot_packed_vector2_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_vector2_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedVector3Array(godot_packed_vector3_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_vector3_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedColorArray(godot_packed_color_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_color_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromArray(godot_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_array(&ret, from); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_variant CreateFromArray(godot_array from) + => CreateFromArray(&from); + + public static unsafe godot_variant CreateFromDictionary(godot_dictionary* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_dictionary(&ret, from); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_variant CreateFromDictionary(godot_dictionary from) + => CreateFromDictionary(&from); + + public static unsafe godot_variant CreateFromStringName(ref godot_string_name arg1) + { + godot_variant ret; + godot_string_name src = arg1; + NativeFuncs.godotsharp_variant_new_string_name(&ret, &src); + return ret; + } + + public static unsafe godot_variant CreateFromNodePath(ref godot_node_path arg1) + { + godot_variant ret; + godot_node_path src = arg1; + NativeFuncs.godotsharp_variant_new_node_path(&ret, &src); + return ret; + } + + public static unsafe godot_variant CreateFromGodotObject(IntPtr from) + { + if (from == IntPtr.Zero) + return new godot_variant(); + godot_variant ret; + NativeFuncs.godotsharp_variant_new_object(&ret, from); + return ret; + } + + // We avoid the internal call if the stored type is the same we want. + + public static unsafe bool ConvertToBool(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var); + + public static unsafe char ConvertToChar(godot_variant* p_var) + => (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe sbyte ConvertToInt8(godot_variant* p_var) + => (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int16 ConvertToInt16(godot_variant* p_var) + => (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int32 ConvertToInt32(godot_variant* p_var) + => (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int64 ConvertToInt64(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var); + + public static unsafe byte ConvertToUInt8(godot_variant* p_var) + => (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var) + => (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var) + => (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var) + => (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe float ConvertToFloat32(godot_variant* p_var) + => (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var)); + + public static unsafe double ConvertToFloat64(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var); + + public static unsafe Vector2 ConvertToVector2(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var); + + public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var); + + public static unsafe Rect2 ConvertToRect2(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var); + + public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var); + + public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var); + + public static unsafe Vector3 ConvertToVector3(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var); + + public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var); + + public static unsafe Vector4 ConvertToVector4(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector4 ? *(*p_var)._data._vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); + + public static unsafe Vector4i ConvertToVector4i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector4i ? *(*p_var)._data._vector4i : NativeFuncs.godotsharp_variant_as_vector4i(p_var); + + public static unsafe Basis ConvertToBasis(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var); + + public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var); + + public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var); + + public static unsafe Projection ConvertToProjection(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Projection ? *(*p_var)._data._projection : NativeFuncs.godotsharp_variant_as_projection(p_var); + + public static unsafe AABB ConvertToAABB(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var); + + public static unsafe Color ConvertToColor(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var); + + public static unsafe Plane ConvertToPlane(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var); + + public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero; + + public static unsafe RID ConvertToRID(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var); + + public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var) + => (*p_var)._type == Variant.Type.StringName ? + NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name) : + NativeFuncs.godotsharp_variant_as_string_name(p_var); + + public static unsafe godot_node_path ConvertToNodePath(godot_variant* p_var) + => (*p_var)._type == Variant.Type.NodePath ? + NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path) : + NativeFuncs.godotsharp_variant_as_node_path(p_var); + + public static unsafe godot_array ConvertToArray(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Array ? + NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array) : + NativeFuncs.godotsharp_variant_as_array(p_var); + + public static unsafe godot_dictionary ConvertToDictionary(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Dictionary ? + NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary) : + NativeFuncs.godotsharp_variant_as_dictionary(p_var); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 9ae01016cb..90e51e8a1a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -39,22 +40,9 @@ namespace Godot /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. /// /// - public sealed partial class NodePath : IDisposable + public sealed class NodePath : IDisposable { - private bool _disposed = false; - - 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; - } + internal godot_node_path NativeValue; ~NodePath() { @@ -70,29 +58,27 @@ namespace Godot GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + public void Dispose(bool disposing) { - if (_disposed) - return; - - if (ptr != IntPtr.Zero) - { - godot_icall_NodePath_Dtor(ptr); - ptr = IntPtr.Zero; - } - - _disposed = true; + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } - internal NodePath(IntPtr ptr) + private NodePath(godot_node_path nativeValueToOwn) { - this.ptr = ptr; + NativeValue = nativeValueToOwn; } + // Explicit name to make it very clear + internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn) + => new NodePath(nativeValueToOwn); + /// /// Constructs an empty . /// - public NodePath() : this(string.Empty) { } + public NodePath() + { + } /// /// Constructs a from a string , @@ -125,7 +111,8 @@ namespace Godot /// A string that represents a path in a scene tree. public NodePath(string path) { - ptr = godot_icall_NodePath_Ctor(path); + if (!string.IsNullOrEmpty(path)) + NativeValue = NativeFuncs.godotsharp_node_path_new_from_string(path); } /// @@ -144,9 +131,16 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this . - public override string ToString() + public override unsafe string ToString() { - return godot_icall_NodePath_operator_String(GetPtr(this)); + if (IsEmpty) + return string.Empty; + + godot_string dest; + godot_node_path src = NativeValue; + NativeFuncs.godotsharp_node_path_as_string(&dest, &src); + using (dest) + return Marshaling.mono_string_from_godot(&dest); } /// @@ -166,7 +160,9 @@ namespace Godot /// The as a pure property path. public NodePath GetAsPropertyPath() { - return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this))); + godot_node_path propertyPath = default; + godot_icall_NodePath_get_as_property_path(ref NativeValue, ref propertyPath); + return CreateTakingOwnershipOfDisposableValue(propertyPath); } /// @@ -181,7 +177,7 @@ namespace Godot /// The names concatenated with /. public string GetConcatenatedNames() { - return godot_icall_NodePath_get_concatenated_names(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_names(ref NativeValue); } /// @@ -195,9 +191,9 @@ namespace Godot /// /// /// The subnames concatenated with :. - public string GetConcatenatedSubnames() + public string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); } /// @@ -215,28 +211,28 @@ namespace Godot /// The name at the given index . public string GetName(int idx) { - return godot_icall_NodePath_get_name(GetPtr(this), idx); + return godot_icall_NodePath_get_name(ref NativeValue, idx); } /// /// Gets the number of node names which make up the path. - /// Subnames (see ) are not included. + /// Subnames (see ) are not included. /// For example, "Path2D/PathFollow2D/Sprite2D" has 3 names. /// /// The number of node names which make up the path. public int GetNameCount() { - return godot_icall_NodePath_get_name_count(GetPtr(this)); + return godot_icall_NodePath_get_name_count(ref NativeValue); } /// - /// Gets the resource or property name indicated by (0 to ). + /// Gets the resource or property name indicated by (0 to ). /// /// The subname index. /// The subname at the given index . - public string GetSubname(int idx) + public string GetSubName(int idx) { - return godot_icall_NodePath_get_subname(GetPtr(this), idx); + return godot_icall_NodePath_get_subname(ref NativeValue, idx); } /// @@ -245,9 +241,9 @@ namespace Godot /// For example, "Path2D/PathFollow2D/Sprite2D:texture:load_path" has 2 subnames. /// /// The number of subnames in the path. - public int GetSubnameCount() + public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(GetPtr(this)); + return godot_icall_NodePath_get_subname_count(ref NativeValue); } /// @@ -259,52 +255,37 @@ namespace Godot /// If the is an absolute path. public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(GetPtr(this)); + return godot_icall_NodePath_is_absolute(ref NativeValue); } /// /// Returns if the node path is empty. /// /// If the is empty. - 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); + public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr); + private static extern void godot_icall_NodePath_get_as_property_path(ref godot_node_path ptr, ref godot_node_path dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + private static extern string godot_icall_NodePath_get_concatenated_names(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + private static extern string godot_icall_NodePath_get_concatenated_subnames(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr); + private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); + private static extern int godot_icall_NodePath_get_name_count(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr); + private static extern string godot_icall_NodePath_get_subname(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr); + private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr); + private static extern bool godot_icall_NodePath_is_absolute(ref godot_node_path ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 746612477d..80d63581c1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -7,38 +7,44 @@ namespace Godot { private bool _disposed = false; - private static StringName nativeName = "Object"; - - internal IntPtr ptr; - internal bool memoryOwn; + internal IntPtr NativePtr; + internal bool MemoryOwn; /// /// Constructs a new . /// public Object() : this(false) { - if (ptr == IntPtr.Zero) - ptr = godot_icall_Object_Ctor(this); + if (NativePtr == IntPtr.Zero) + { +#if NET + unsafe + { + ptr = NativeCtor(); + } +#else + NativePtr = _gd__invoke_class_constructor(NativeCtor); +#endif + NativeInterop.InteropUtils.TieManagedToUnmanaged(this, NativePtr); + } + _InitializeGodotScriptInstanceInternals(); } internal void _InitializeGodotScriptInstanceInternals() { - godot_icall_Object_ConnectEventSignals(ptr); + godot_icall_Object_ConnectEventSignals(NativePtr); } internal Object(bool memoryOwn) { - this.memoryOwn = memoryOwn; + this.MemoryOwn = memoryOwn; } /// /// The pointer to the native instance of this . /// - public IntPtr NativeInstance - { - get { return ptr; } - } + public IntPtr NativeInstance => NativePtr; internal static IntPtr GetPtr(Object instance) { @@ -48,7 +54,7 @@ namespace Godot if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); - return instance.ptr; + return instance.NativePtr; } ~Object() @@ -73,19 +79,19 @@ namespace Godot if (_disposed) return; - if (ptr != IntPtr.Zero) + if (NativePtr != IntPtr.Zero) { - if (memoryOwn) + if (MemoryOwn) { - memoryOwn = false; - godot_icall_RefCounted_Disposed(this, ptr, !disposing); + MemoryOwn = false; + godot_icall_RefCounted_Disposed(this, NativePtr, !disposing); } else { - godot_icall_Object_Disposed(this, ptr); + godot_icall_Object_Disposed(this, NativePtr); } - ptr = IntPtr.Zero; + this.NativePtr = IntPtr.Zero; } _disposed = true; @@ -137,13 +143,49 @@ namespace Godot /// public dynamic DynamicObject => new DynamicGodotObject(this); - internal static IntPtr __ClassDB_get_method(StringName type, string method) + internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) + { + IntPtr methodBind; + fixed (char* methodChars = method) + { + methodBind = NativeInterop.NativeFuncs + .godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); + } + + if (methodBind == IntPtr.Zero) + throw new NativeMethodBindNotFoundException(type + "." + method); + + return methodBind; + } + +#if NET + internal static unsafe delegate* unmanaged _gd__ClassDB_get_constructor(StringName type) + { + // for some reason the '??' operator doesn't support 'delegate*' + var nativeConstructor = NativeInterop.NativeFuncs + .godotsharp_get_class_constructor(ref type.NativeValue); + + if (nativeConstructor == null) + throw new NativeConstructorNotFoundException(type); + + return nativeConstructor; + } +#else + internal static IntPtr ClassDB_get_constructor(StringName type) { - return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method); + // for some reason the '??' operator doesn't support 'delegate*' + var nativeConstructor = NativeInterop.NativeFuncs + .godotsharp_get_class_constructor(ref type.NativeValue); + + if (nativeConstructor == IntPtr.Zero) + throw new NativeConstructorNotFoundException(type); + + return nativeConstructor; } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Object_Ctor(Object obj); + internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr) + => NativeInterop.NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); +#endif [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr); @@ -156,9 +198,5 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string godot_icall_Object_ToString(IntPtr ptr); - - // Used by the generated API - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method); } } 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/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 . /// - 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); - } - - /// - /// Disposes of this . - /// - 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; - } - - /// - /// The pointer to the native instance of this . - /// - public IntPtr NativeInstance - { - get { return ptr; } - } - - internal RID() - { - this.ptr = IntPtr.Zero; + _id = id; } /// /// Constructs a new for the given . /// public RID(Object from) - { - this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); - } + => _id = from is Resource res ? res.GetRid()._id : default; /// /// Returns the ID of the referenced resource. /// /// The ID of the referenced resource. - public int GetId() - { - return godot_icall_RID_get_id(GetPtr(this)); - } + public ulong Id => _id; /// /// Converts this to a string. /// /// A string representation of this RID. - 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/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 2ba0493002..e485207fb4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -11,34 +12,22 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter); + internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter); - public bool IsCompleted - { - get - { - return _completed; - } - } + public bool IsCompleted => _completed; public void OnCompleted(Action action) { this._action = action; } - public object[] GetResult() - { - return _result; - } + public object[] GetResult() => _result; - public IAwaiter GetAwaiter() - { - return this; - } + public IAwaiter GetAwaiter() => this; internal void SignalCallback(object[] args) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index a1f058ffe5..058c5447e2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -214,7 +214,7 @@ namespace Godot /// The escaped string. 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 +239,7 @@ namespace Godot /// The unescaped string. 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"); @@ -926,7 +926,7 @@ namespace Godot /// The escaped string. 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"); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index b1d504410b..40d282eab4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -10,20 +11,9 @@ namespace Godot /// Comparing them is much faster than with regular strings, because only the pointers are compared, /// not the whole strings. /// - public sealed partial class StringName : IDisposable + public sealed class StringName : IDisposable { - private IntPtr ptr; - - 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; - } + internal godot_string_name NativeValue; ~StringName() { @@ -39,35 +29,36 @@ namespace Godot GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + public void Dispose(bool disposing) { - if (ptr != IntPtr.Zero) - { - godot_icall_StringName_Dtor(ptr); - ptr = IntPtr.Zero; - } + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } - internal StringName(IntPtr ptr) + private StringName(godot_string_name nativeValueToOwn) { - this.ptr = ptr; + NativeValue = nativeValueToOwn; } + // Explicit name to make it very clear + internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn) + => new StringName(nativeValueToOwn); + /// /// Constructs an empty . /// public StringName() { - ptr = IntPtr.Zero; } /// /// Constructs a from the given string. /// /// String to construct the from. - public StringName(string path) + public StringName(string name) { - ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path); + if (!string.IsNullOrEmpty(name)) + NativeValue = NativeFuncs.godotsharp_string_name_new_from_string(name); } /// @@ -86,30 +77,22 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this . - public override string ToString() + public override unsafe string ToString() { - return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this)); + if (IsEmpty) + return string.Empty; + + godot_string dest; + godot_string_name src = NativeValue; + NativeFuncs.godotsharp_string_name_as_string(&dest, &src); + using (dest) + return Marshaling.mono_string_from_godot(&dest); } /// /// Check whether this is empty. /// /// If the is empty. - public bool IsEmpty() - { - return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this)); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_StringName_Ctor(string path); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void godot_icall_StringName_Dtor(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_StringName_operator_String(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_StringName_is_empty(IntPtr ptr); + public bool IsEmpty => godot_string_name.IsEmpty(in NativeValue); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 4f55ce47e8..9683d8e812 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -7,10 +7,19 @@ netstandard2.1 $(OutputPath)/$(AssemblyName).xml false + true + 9 + + + CS1591 $(DefineConstants);GODOT + + + + @@ -47,14 +56,22 @@ + + + + + + + + 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 @@ + + True + True + True + diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index a8c4ba96b5..1082c74448 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -7,6 +7,8 @@ netstandard2.1 $(OutputPath)/$(AssemblyName).xml false + true + 9 $(DefineConstants);GODOT 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 @@ + + True + True + -- cgit v1.2.3 From c4ccabd3fb81702820ff0943532c3d8a1f1d1c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:49:23 +0200 Subject: C#: Remove DynamicGodotObject/Object.DynamicObject We are moving in the direction of no dynamic code generation, so this is no longer desired. The feature can still be easily implemented by any project that still want it. --- .../GodotSharp/GodotSharp/Core/DynamicObject.cs | 220 --------------------- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 5 - .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 - 3 files changed, 226 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs (limited to 'modules/mono/glue/GodotSharp') 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 -{ - /// - /// Represents an whose members can be dynamically accessed at runtime through the Variant API. - /// - /// - /// - /// The class enables access to the Variant - /// members of a instance at runtime. - /// - /// - /// This allows accessing the class members using their original names in the engine as well as the members from the - /// script attached to the , regardless of the scripting language it was written in. - /// - /// - /// - /// This sample shows how to use to dynamically access the engine members of a . - /// - /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject; - /// sprite.add_child(this); - /// - /// if ((sprite.hframes * sprite.vframes) > 0) - /// sprite.frame = 0; - /// - /// - /// - /// This sample shows how to use to dynamically access the members of the script attached to a . - /// - /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject; - /// - /// if (childNode.print_allowed) - /// { - /// childNode.message = "Hello from C#"; - /// childNode.print_message(3); - /// } - /// - /// The ChildNode node has the following GDScript script attached: - /// - /// // # ChildNode.gd - /// // var print_allowed = true - /// // var message = "" - /// // - /// // func print_message(times): - /// // for i in times: - /// // print(message) - /// - /// - public class DynamicGodotObject : DynamicObject - { - /// - /// Gets the associated with this . - /// - public Object Value { get; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The that will be associated with this . - /// - /// - /// Thrown when the parameter is . - /// - public DynamicGodotObject(Object godotObject) - { - if (godotObject == null) - throw new ArgumentNullException(nameof(godotObject)); - - Value = godotObject; - } - - /// - public override IEnumerable GetDynamicMemberNames() - { - return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value)); - } - - /// - 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); - } - - /// - 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); - } - - /// - 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); - } - - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result); - } - - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result); - } - - /// - 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); - } - - /// - 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/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 80d63581c1..9c4265517e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -138,11 +138,6 @@ namespace Godot return new SignalAwaiter(source, signal, this); } - /// - /// Gets a new associated with this instance. - /// - public dynamic DynamicObject => new DynamicGodotObject(this); - internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) { IntPtr methodBind; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 9683d8e812..0709723da0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -39,7 +39,6 @@ - -- cgit v1.2.3 From 9a51430441eecafbd07a7b9eb46967e2c3dd8b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:49:39 +0200 Subject: C#: Re-write Array, Dictionary, NodePath, String icalls as P/Invoke --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 222 ++++++++++--------- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 245 +++++++++++---------- .../Core/NativeInterop/InteropStructs.cs | 121 ++++++---- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 64 +++--- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 224 ++++++++++++++++--- .../Core/NativeInterop/NativeFuncs.extended.cs | 14 ++ .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 56 ++--- .../GodotSharp/GodotSharp/Core/StringExtensions.cs | 93 ++++---- 8 files changed, 648 insertions(+), 391 deletions(-) (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index c32895baab..893ab81e5d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Collections; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot.Collections @@ -22,7 +21,7 @@ namespace Godot.Collections /// public Array() { - godot_icall_Array_Ctor(out NativeValue); + NativeValue = NativeFuncs.godotsharp_array_new(); } /// @@ -48,11 +47,15 @@ namespace Godot.Collections public Array(params object[] array) : this() { if (array == null) - { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); - } - godot_icall_Array_Ctor_MonoArray(array, out NativeValue); + NativeValue = NativeFuncs.godotsharp_array_new(); + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; } private Array(godot_array nativeValueToOwn) @@ -92,7 +95,7 @@ namespace Godot.Collections public Array Duplicate(bool deep = false) { godot_array newArray; - godot_icall_Array_Duplicate(ref NativeValue, deep, out newArray); + NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep, out newArray); return CreateTakingOwnershipOfDisposableValue(newArray); } @@ -101,18 +104,12 @@ namespace Godot.Collections /// /// The new size of the array. /// if successful, or an error code. - public Error Resize(int newSize) - { - return godot_icall_Array_Resize(ref NativeValue, newSize); - } + public Error Resize(int newSize) => NativeFuncs.godotsharp_array_resize(ref NativeValue, newSize); /// /// Shuffles the contents of this into a random order. /// - public void Shuffle() - { - godot_icall_Array_Shuffle(ref NativeValue); - } + public void Shuffle() => NativeFuncs.godotsharp_array_shuffle(ref NativeValue); /// /// Concatenates these two s. @@ -122,9 +119,16 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - godot_array newArray; - godot_icall_Array_Concatenate(ref left.NativeValue, ref right.NativeValue, out newArray); - return CreateTakingOwnershipOfDisposableValue(newArray); + int leftCount = left.Count; + int rightCount = right.Count; + + Array newArray = left.Duplicate(deep: false); + newArray.Resize(leftCount + rightCount); + + for (int i = 0; i < rightCount; i++) + newArray[i + leftCount] = right[i]; + + return newArray; } // IList @@ -137,18 +141,20 @@ namespace Godot.Collections /// Returns the object at the given . /// /// The object at the given . - public object this[int index] + public unsafe object this[int index] { get { - godot_icall_Array_At(ref NativeValue, index, out godot_variant elem); - unsafe - { - using (elem) - return Marshaling.variant_to_mono_object(&elem); - } + GetVariantBorrowElementAt(index, out godot_variant borrowElem); + return Marshaling.variant_to_mono_object(&borrowElem); + } + set + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref NativeValue); + ptrw[index] = Marshaling.mono_object_to_variant(value); } - set => godot_icall_Array_SetAt(ref NativeValue, index, value); } /// @@ -157,19 +163,23 @@ namespace Godot.Collections /// /// The object to add. /// The new size after adding the object. - public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value); + public unsafe int Add(object value) + { + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + return NativeFuncs.godotsharp_array_add(ref NativeValue, &variantValue); + } /// /// Checks if this contains the given object. /// /// The item to look for. /// Whether or not this array contains the given object. - public bool Contains(object value) => godot_icall_Array_Contains(ref NativeValue, value); + public bool Contains(object value) => IndexOf(value) != -1; /// /// Erases all items from this . /// - public void Clear() => godot_icall_Array_Clear(ref NativeValue); + public void Clear() => Resize(0); /// /// Searches this for an object @@ -177,7 +187,11 @@ namespace Godot.Collections /// /// The object to search for. /// The index of the object, or -1 if not found. - public int IndexOf(object value) => godot_icall_Array_IndexOf(ref NativeValue, value); + public unsafe int IndexOf(object value) + { + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + return NativeFuncs.godotsharp_array_index_of(ref NativeValue, &variantValue); + } /// /// Inserts a new object at a given position in the array. @@ -187,20 +201,38 @@ namespace Godot.Collections /// /// The index to insert at. /// The object to insert. - public void Insert(int index, object value) => godot_icall_Array_Insert(ref NativeValue, index, value); + public unsafe void Insert(int index, object value) + { + if (index < 0 || index > Count) + throw new IndexOutOfRangeException(); + + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_array_insert(ref NativeValue, index, &variantValue); + } /// /// Removes the first occurrence of the specified /// from this . /// /// The value to remove. - public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value); + public void Remove(object value) + { + int index = IndexOf(value); + if (index >= 0) + RemoveAt(index); + } /// /// Removes an element from this by index. /// /// The index of the element to remove. - public void RemoveAt(int index) => godot_icall_Array_RemoveAt(ref NativeValue, index); + public void RemoveAt(int index) + { + if (index < 0 || index > Count) + throw new IndexOutOfRangeException(); + + NativeFuncs.godotsharp_array_remove_at(ref NativeValue, index); + } // ICollection @@ -209,7 +241,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// /// The number of elements. - public int Count => godot_icall_Array_Count(ref NativeValue); + public int Count => NativeValue.Size; object ICollection.SyncRoot => this; @@ -220,17 +252,35 @@ namespace Godot.Collections /// untyped C# array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(System.Array array, int destIndex) { 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 (destIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(destIndex), + "Number was less than the array's lower bound in the first dimension."); + } - // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(ref NativeValue, array, index); + int count = Count; + + if (array.Length < (destIndex + 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.variant_to_mono_object(&(*NativeValue._p)._arrayVector._ptr[i]); + array.SetValue(obj, destIndex); + destIndex++; + } + } } // IEnumerable @@ -253,64 +303,30 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this array. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Array_ToString(ref NativeValue); + using godot_string str = default; + NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str); + return Marshaling.mono_string_from_godot(&str); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Ctor(out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Count(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Clear(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr); + /// + /// The variant returned via the parameter is owned by the Array and must not be disposed. + /// + internal void GetVariantBorrowElementAt(int index, out godot_variant elem) + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + GetVariantBorrowElementAtUnchecked(index, out elem); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Array_ToString(ref godot_array ptr); + /// + /// The variant returned via the parameter is owned by the Array and must not be disposed. + /// + internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem) + { + elem = (*NativeValue._p)._arrayVector._ptr[index]; + } } internal interface IGenericGodotArray @@ -451,11 +467,10 @@ namespace Godot.Collections { get { - Array.godot_icall_Array_At(ref _underlyingArray.NativeValue, index, out godot_variant elem); + _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); unsafe { - using (elem) - return (T)Marshaling.variant_to_mono_object_of_type(&elem, TypeOfElements); + return (T)Marshaling.variant_to_mono_object_of_type(&borrowElem, TypeOfElements); } } set => _underlyingArray[index] = value; @@ -546,12 +561,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 = _underlyingArray.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++) { @@ -568,7 +585,14 @@ namespace Godot.Collections /// A indicating success or failure. public bool Remove(T item) { - return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item); + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; } // IEnumerable diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index d0c7e4523b..932ee33fe3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections; -using System.Runtime.CompilerServices; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; @@ -23,7 +22,7 @@ namespace Godot.Collections /// public Dictionary() { - godot_icall_Dictionary_Ctor(out NativeValue); + NativeValue = NativeFuncs.godotsharp_dictionary_new(); } /// @@ -77,7 +76,7 @@ namespace Godot.Collections public Dictionary Duplicate(bool deep = false) { godot_dictionary newDictionary; - godot_icall_Dictionary_Duplicate(ref NativeValue, deep, out newDictionary); + NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep, out newDictionary); return CreateTakingOwnershipOfDisposableValue(newDictionary); } @@ -91,7 +90,7 @@ namespace Godot.Collections get { godot_array keysArray; - godot_icall_Dictionary_Keys(ref NativeValue, out keysArray); + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -104,7 +103,7 @@ namespace Godot.Collections get { godot_array valuesArray; - godot_icall_Dictionary_Values(ref NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_values(ref NativeValue, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } @@ -112,10 +111,15 @@ namespace Godot.Collections private (Array keys, Array values, int count) GetKeyValuePairs() { godot_array keysArray; - godot_array valuesArray; - int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray); + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); + + godot_array valuesArray; + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out valuesArray); var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); + + int count = NativeFuncs.godotsharp_dictionary_count(ref NativeValue); + return (keys, values, count); } @@ -127,18 +131,28 @@ namespace Godot.Collections /// Returns the object at the given . /// /// The object at the given . - public object this[object key] + public unsafe object this[object key] { get { - godot_icall_Dictionary_GetValue(ref NativeValue, key, out godot_variant value); - unsafe + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey, + out godot_variant value)) { using (value) return Marshaling.variant_to_mono_object(&value); } + else + { + throw new KeyNotFoundException(); + } + } + set + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_dictionary_set_value(ref NativeValue, &variantKey, &variantValue); } - set => godot_icall_Dictionary_SetValue(ref NativeValue, key, value); } /// @@ -147,19 +161,32 @@ namespace Godot.Collections /// /// The key at which to add the object. /// The object to add. - public void Add(object key, object value) => godot_icall_Dictionary_Add(ref NativeValue, key, value); + public unsafe void Add(object key, object value) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + + if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey)) + throw new ArgumentException("An element with the same key already exists", nameof(key)); + + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_dictionary_add(ref NativeValue, &variantKey, &variantValue); + } /// /// Erases all items from this . /// - public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue); + public void Clear() => NativeFuncs.godotsharp_dictionary_clear(ref NativeValue); /// /// Checks if this contains the given key. /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(ref NativeValue, key); + public unsafe bool Contains(object key) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey); + } /// /// Gets an enumerator for this . @@ -171,7 +198,11 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public void Remove(object key) => godot_icall_Dictionary_RemoveKey(ref NativeValue, key); + public unsafe void Remove(object key) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + NativeFuncs.godotsharp_dictionary_remove_key(ref NativeValue, &variantKey); + } // ICollection @@ -184,7 +215,7 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count => godot_icall_Dictionary_Count(ref NativeValue); + public int Count => NativeFuncs.godotsharp_dictionary_count(ref NativeValue); /// /// Copies the elements of this to the given @@ -198,12 +229,14 @@ namespace Godot.Collections 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."); 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."); + 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++) { @@ -241,23 +274,22 @@ namespace Godot.Collections { UpdateEntry(); } + return _entry; } } - private void UpdateEntry() + private unsafe void UpdateEntry() { _dirty = false; - godot_icall_Dictionary_KeyValuePairAt(ref _dictionary.NativeValue, _index, out var vKey, out var vValue); - unsafe + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _dictionary.NativeValue, _index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) { - using (vKey) - using (vValue) - { - var key = Marshaling.variant_to_mono_object(&vKey); - var value = Marshaling.variant_to_mono_object(&vValue); - _entry = new DictionaryEntry(key, value); - } + _entry = new DictionaryEntry(Marshaling.variant_to_mono_object(&key), + Marshaling.variant_to_mono_object(&value)); } } @@ -283,61 +315,12 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this dictionary. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Dictionary_ToString(ref NativeValue); + using godot_string str = default; + NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str); + return Marshaling.mono_string_from_godot(&str); } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_KeyValuePairs(ref godot_dictionary ptr, out godot_array keys, out godot_array values); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out godot_variant key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr); } internal interface IGenericGodotDictionary @@ -412,7 +395,8 @@ namespace Godot.Collections } // Explicit name to make it very clear - internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + internal static Dictionary CreateTakingOwnershipOfDisposableValue( + godot_dictionary nativeValueToOwn) => new Dictionary(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// @@ -444,11 +428,19 @@ namespace Godot.Collections { get { - Dictionary.godot_icall_Dictionary_GetValue(ref _underlyingDict.NativeValue, key, out godot_variant value); unsafe { - using (value) - return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant value)) + { + using (value) + return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + } + else + { + throw new KeyNotFoundException(); + } } } set => _underlyingDict[key] = value; @@ -462,7 +454,7 @@ namespace Godot.Collections get { godot_array keyArray; - Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray); + NativeFuncs.godotsharp_dictionary_keys(ref _underlyingDict.NativeValue, out keyArray); return Array.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -475,23 +467,21 @@ namespace Godot.Collections get { godot_array valuesArray; - Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_values(ref _underlyingDict.NativeValue, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } - private KeyValuePair GetKeyValuePair(int index) + private unsafe KeyValuePair GetKeyValuePair(int index) { - Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out var vKey, out var vValue); - unsafe + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _underlyingDict.NativeValue, index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) { - using (vKey) - using (vValue) - { - var key = Marshaling.variant_to_mono_object_of_type(&vKey, TypeOfKeys); - var value = Marshaling.variant_to_mono_object_of_type(&vValue, TypeOfValues); - return new KeyValuePair((TKey)key, (TValue)value); - } + return new KeyValuePair((TKey)Marshaling.variant_to_mono_object(&key), + (TValue)Marshaling.variant_to_mono_object(&value)); } } @@ -520,9 +510,10 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public bool Remove(TKey key) + public unsafe bool Remove(TKey key) { - return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); } /// @@ -531,18 +522,17 @@ namespace Godot.Collections /// The key of the element to get. /// The value at the given . /// If an object was found for the given . - 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(ref _underlyingDict.NativeValue, key, out godot_variant retValue); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); - unsafe + using (retValue) { - using (retValue) - { - value = found ? - (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : - default; - } + value = found ? + (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : + default; } return found; @@ -572,9 +562,20 @@ namespace Godot.Collections _underlyingDict.Clear(); } - bool ICollection>.Contains(KeyValuePair item) + unsafe bool ICollection>.Contains(KeyValuePair item) { - return _underlyingDict.Contains(new KeyValuePair(item.Key, item.Value)); + using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); + + using (retValue) + { + if (!found) + return false; + + using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); + return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue); + } } /// @@ -589,12 +590,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++) { @@ -603,9 +606,23 @@ namespace Godot.Collections } } - bool ICollection>.Remove(KeyValuePair item) + unsafe bool ICollection>.Remove(KeyValuePair item) { - return Dictionary.godot_icall_Dictionary_Remove(ref _underlyingDict.NativeValue, item.Key, item.Value); + using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); + + using (retValue) + { + if (!found) + return false; + + using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); + if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue)) + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + + return false; + } } // IEnumerable> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 6ee8cbc0bc..ddcb6b9091 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -171,6 +171,9 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_string_destroy(ref this); _ptr = IntPtr.Zero; } + + // Size including the null termination character + public unsafe int Size => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -262,14 +265,34 @@ namespace Godot.NativeInterop // ReSharper disable once InconsistentNaming internal struct godot_array : IDisposable { - internal IntPtr _p; + internal unsafe ArrayPrivate* _p; - public void Dispose() + [StructLayout(LayoutKind.Sequential)] + internal struct ArrayPrivate { - if (_p == IntPtr.Zero) + private uint _safeRefCount; + + internal VariantVector _arrayVector; + // There's more here, but we don't care as we never store this in C# + } + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantVector + { + internal IntPtr _writeProxy; + internal unsafe godot_variant* _ptr; + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + } + + public unsafe int Size => _p != null ? _p->_arrayVector.Size : 0; + + public unsafe void Dispose() + { + if (_p == null) return; NativeFuncs.godotsharp_array_destroy(ref this); - _p = IntPtr.Zero; + _p = null; } } @@ -297,15 +320,17 @@ namespace Godot.NativeInterop internal struct godot_packed_byte_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe byte* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_byte_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -313,15 +338,17 @@ namespace Godot.NativeInterop internal struct godot_packed_int32_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe int* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_int32_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -329,15 +356,17 @@ namespace Godot.NativeInterop internal struct godot_packed_int64_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe long* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_int64_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -345,15 +374,17 @@ namespace Godot.NativeInterop internal struct godot_packed_float32_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe float* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_float32_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -361,15 +392,17 @@ namespace Godot.NativeInterop internal struct godot_packed_float64_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe double* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_float64_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -377,15 +410,17 @@ namespace Godot.NativeInterop internal struct godot_packed_string_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe godot_string* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_string_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -393,15 +428,17 @@ namespace Godot.NativeInterop internal struct godot_packed_vector2_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Vector2* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -409,15 +446,17 @@ namespace Godot.NativeInterop internal struct godot_packed_vector3_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Vector3* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -425,14 +464,16 @@ namespace Godot.NativeInterop internal struct godot_packed_color_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Color* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_color_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 9b6f9633d1..590fb082e8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -161,7 +161,8 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(IDictionary<,>)) return Variant.Type.Dictionary; - if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + if (genericTypeDefinition == typeof(ICollection<>) || + genericTypeDefinition == typeof(IEnumerable<>)) return Variant.Type.Array; } else if (type == typeof(object)) @@ -430,7 +431,8 @@ namespace Godot.NativeInterop { // TODO: Validate key and value types are compatible with Variant #if NET - Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj); + Collections.IGenericGodotDictionary genericGodotDictionary = + IDictionaryToGenericGodotDictionary((dynamic)p_obj); #else var genericArguments = type.GetGenericArguments(); @@ -441,7 +443,7 @@ namespace Godot.NativeInterop .MakeGenericMethod(genericArguments[0], genericArguments[1]); var genericGodotDictionary = (Collections.IGenericGodotDictionary)method - .Invoke(null, new[] {p_obj}); + .Invoke(null, new[] { p_obj }); #endif var godotDict = genericGodotDictionary.UnderlyingDictionary; @@ -454,7 +456,8 @@ namespace Godot.NativeInterop { // TODO: Validate element type is compatible with Variant #if NET - var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); + var nativeGodotArray = + mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); #else // With .NET Standard we need a package reference for Microsoft.CSharp in order to // use dynamic, so we have this workaround for now until we switch to .NET 5/6. @@ -623,8 +626,9 @@ namespace Godot.NativeInterop return VariantUtils.ConvertToUInt64(p_var); default: { - GD.PushError("Attempted to convert Variant to enum value of unsupported underlying type. Name: " + - type.FullName + " : " + enumUnderlyingType.FullName + "."); + GD.PushError( + "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); return null; } } @@ -777,7 +781,7 @@ namespace Godot.NativeInterop VariantUtils.ConvertToDictionary(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] {underlyingDict}, null); + args: new object[] { underlyingDict }, null); } static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) @@ -786,7 +790,7 @@ namespace Godot.NativeInterop VariantUtils.ConvertToArray(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] {underlyingArray}, null); + args: new object[] { underlyingArray }, null); } var genericTypeDefinition = type.GetGenericTypeDefinition(); @@ -1010,7 +1014,7 @@ namespace Godot.NativeInterop const int sizeOfChar32 = 4; byte* bytes = (byte*)(*p_string)._ptr; - int size = *((int*)(*p_string)._ptr - 1); + int size = (*p_string).Size; if (size == 0) return string.Empty; size -= 1; // zero at the end @@ -1148,11 +1152,7 @@ namespace Godot.NativeInterop public static godot_array mono_array_to_Array(Span p_array) { if (p_array.IsEmpty) - { - godot_array ret; - Collections.Array.godot_icall_Array_Ctor(out ret); - return ret; - } + return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); array.Resize(p_array.Length); @@ -1171,8 +1171,8 @@ namespace Godot.NativeInterop public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array) { - byte* buffer = (byte*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + byte* buffer = (*p_array)._ptr; + int size = (*p_array).Size; var array = new byte[size]; fixed (byte* dest = array) Buffer.MemoryCopy(buffer, dest, size, size); @@ -1191,8 +1191,8 @@ namespace Godot.NativeInterop public static unsafe int[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array) { - int* buffer = (int*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + int* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(int); var array = new int[size]; fixed (int* dest = array) @@ -1212,8 +1212,8 @@ namespace Godot.NativeInterop public static unsafe long[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array) { - long* buffer = (long*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + long* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(long); var array = new long[size]; fixed (long* dest = array) @@ -1233,8 +1233,8 @@ namespace Godot.NativeInterop public static unsafe float[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array) { - float* buffer = (float*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + float* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(float); var array = new float[size]; fixed (float* dest = array) @@ -1254,8 +1254,8 @@ namespace Godot.NativeInterop public static unsafe double[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array) { - double* buffer = (double*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + double* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(double); var array = new double[size]; fixed (double* dest = array) @@ -1275,10 +1275,10 @@ namespace Godot.NativeInterop public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) { - godot_string* buffer = (godot_string*)(*p_array)._ptr; + godot_string* buffer = (*p_array)._ptr; if (buffer == null) return new string[] { }; - int size = *((int*)(*p_array)._ptr - 1); + int size = (*p_array).Size; var array = new string[size]; for (int i = 0; i < size; i++) array[i] = mono_string_from_godot(&buffer[i]); @@ -1308,8 +1308,8 @@ namespace Godot.NativeInterop public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array) { - Vector2* buffer = (Vector2*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + Vector2* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector2); var array = new Vector2[size]; fixed (Vector2* dest = array) @@ -1329,8 +1329,8 @@ namespace Godot.NativeInterop public static unsafe Vector3[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array) { - Vector3* buffer = (Vector3*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + Vector3* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector3); var array = new Vector3[size]; fixed (Vector3* dest = array) @@ -1350,8 +1350,8 @@ namespace Godot.NativeInterop public static unsafe Color[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array) { - Color* buffer = (Color*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + Color* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Color); var array = new Color[size]; fixed (Color* dest = array) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 2f1056219d..0680ec7be5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -17,7 +17,8 @@ namespace Godot.NativeInterop // Custom functions [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); + public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, + char* p_methodname); #if NET [DllImport(GodotDllName)] @@ -50,47 +51,60 @@ namespace Godot.NativeInterop public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np); [DllImport(GodotDllName)] - public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length); + public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length); + public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length); + public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length); + public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length); + public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length); + public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length); + public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length); + public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, godot_string* p_element); + public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, + godot_string* p_element); [DllImport(GodotDllName)] - public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, godot_callable* r_callable); + public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, + godot_callable* r_callable); [DllImport(GodotDllName)] - public static extern bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); + public static extern godot_bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, + IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); // GDNative functions // gdnative.h [DllImport(GodotDllName)] - public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret); + public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, + void* p_ret); [DllImport(GodotDllName)] - public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); + public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, + godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); // variant.h @@ -131,34 +145,43 @@ namespace Godot.NativeInterop public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, godot_packed_byte_array* p_pba); + public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, + godot_packed_byte_array* p_pba); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, godot_packed_int32_array* p_pia); + public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, + godot_packed_int32_array* p_pia); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, godot_packed_int64_array* p_pia); + public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, + godot_packed_int64_array* p_pia); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, godot_packed_float32_array* p_pra); + public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, + godot_packed_float32_array* p_pra); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, godot_packed_float64_array* p_pra); + public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, + godot_packed_float64_array* p_pra); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, godot_packed_string_array* p_psa); + public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, + godot_packed_string_array* p_psa); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, godot_packed_vector2_array* p_pv2a); + public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, + godot_packed_vector2_array* p_pv2a); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, godot_packed_vector3_array* p_pv3a); + public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, + godot_packed_vector3_array* p_pv3a); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, godot_packed_color_array* p_pca); + public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, + godot_packed_color_array* p_pca); [DllImport(GodotDllName)] - public static extern bool godotsharp_variant_as_bool(godot_variant* p_self); + public static extern godot_bool godotsharp_variant_as_bool(godot_variant* p_self); [DllImport(GodotDllName)] public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); @@ -248,23 +271,30 @@ namespace Godot.NativeInterop public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(godot_variant* p_self); + public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array( + godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(godot_variant* p_self); + public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array( + godot_variant* p_self); [DllImport(GodotDllName)] public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(godot_variant* p_self); + public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array( + godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(godot_variant* p_self); + public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array( + godot_variant* p_self); [DllImport(GodotDllName)] public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self); + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_variant_equals(godot_variant* p_a, godot_variant* p_b); + // string.h [DllImport(GodotDllName)] @@ -282,11 +312,20 @@ namespace Godot.NativeInterop // array.h + [DllImport(GodotDllName)] + public static extern void godotsharp_array_new(godot_array* p_self); + [DllImport(GodotDllName)] public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + [DllImport(GodotDllName)] + public static extern godot_variant* godotsharp_array_ptrw(ref godot_array p_self); + // dictionary.h + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_new(godot_dictionary* p_self); + [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); @@ -342,5 +381,134 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + + // Array + + [DllImport(GodotDllName)] + public static extern int godotsharp_array_add(ref godot_array p_self, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void + godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern int godotsharp_array_index_of(ref godot_array p_self, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_remove_at(ref godot_array p_self, int p_index); + + [DllImport(GodotDllName)] + public static extern Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); + + [DllImport(GodotDllName)] + public static extern Error godotsharp_array_shuffle(ref godot_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_to_string(ref godot_array p_self, godot_string* r_str); + + // Dictionary + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self, + godot_variant* p_key, + out godot_variant r_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, godot_variant* p_key, + godot_variant* p_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern int godotsharp_dictionary_count(ref godot_dictionary p_self); + + [DllImport(GodotDllName)] + public static extern 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); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, godot_variant* p_key, + godot_variant* p_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_clear(ref godot_dictionary p_self); + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self, + godot_variant* p_key); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, + out godot_dictionary r_dest); + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, + godot_variant* p_key); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, godot_string* r_str); + + // StringExtensions + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_md5_buffer(godot_string* p_self, + godot_packed_byte_array* r_md5_buffer); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_md5_text(godot_string* p_self, godot_string* r_md5_text); + + [DllImport(GodotDllName)] + public static extern int godotsharp_string_rfind(godot_string* p_self, godot_string* p_what, int p_from); + + [DllImport(GodotDllName)] + public static extern int godotsharp_string_rfindn(godot_string* p_self, godot_string* p_what, int p_from); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_sha256_buffer(godot_string* p_self, + godot_packed_byte_array* r_sha256_buffer); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_sha256_text(godot_string* p_self, godot_string* r_sha256_text); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_simplify_path(godot_string* p_self, godot_string* r_simplified_path); + + // NodePath + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_as_property_path(ref godot_node_path p_self, + ref godot_node_path r_dest); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_concatenated_names(ref godot_node_path p_self, + godot_string* r_names); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_concatenated_subnames(ref godot_node_path p_self, + godot_string* r_subnames); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_name(ref godot_node_path p_self, int p_idx, + godot_string* r_name); + + [DllImport(GodotDllName)] + public static extern int godotsharp_node_path_get_name_count(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_subname(ref godot_node_path p_self, int p_idx, + godot_string* r_subname); + + [DllImport(GodotDllName)] + public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs index 70df79c1de..6001b3a0de 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -29,6 +29,13 @@ namespace Godot.NativeInterop public static godot_node_path godotsharp_node_path_new_copy(godot_node_path src) => godotsharp_node_path_new_copy(&src); + public static godot_array godotsharp_array_new() + { + godot_array ret; + godotsharp_array_new(&ret); + return ret; + } + public static godot_array godotsharp_array_new_copy(godot_array* src) { godot_array ret; @@ -40,6 +47,13 @@ namespace Godot.NativeInterop public static godot_array godotsharp_array_new_copy(godot_array src) => godotsharp_array_new_copy(&src); + public static godot_dictionary godotsharp_dictionary_new() + { + godot_dictionary ret; + godotsharp_dictionary_new(&ret); + return ret; + } + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src) { godot_dictionary ret; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 90e51e8a1a..541364b281 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -161,7 +161,7 @@ namespace Godot public NodePath GetAsPropertyPath() { godot_node_path propertyPath = default; - godot_icall_NodePath_get_as_property_path(ref NativeValue, ref propertyPath); + NativeFuncs.godotsharp_node_path_get_as_property_path(ref NativeValue, ref propertyPath); return CreateTakingOwnershipOfDisposableValue(propertyPath); } @@ -175,9 +175,11 @@ namespace Godot /// /// /// The names concatenated with /. - public string GetConcatenatedNames() + public unsafe string GetConcatenatedNames() { - return godot_icall_NodePath_get_concatenated_names(ref NativeValue); + using godot_string names = default; + NativeFuncs.godotsharp_node_path_get_concatenated_names(ref NativeValue, &names); + return Marshaling.mono_string_from_godot(&names); } /// @@ -191,9 +193,11 @@ namespace Godot /// /// /// The subnames concatenated with :. - public string GetConcatenatedSubNames() + public unsafe string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); + using godot_string subNames = default; + NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames); + return Marshaling.mono_string_from_godot(&subNames); } /// @@ -209,9 +213,11 @@ namespace Godot /// /// The name index. /// The name at the given index . - public string GetName(int idx) + public unsafe string GetName(int idx) { - return godot_icall_NodePath_get_name(ref NativeValue, idx); + using godot_string name = default; + NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name); + return Marshaling.mono_string_from_godot(&name); } /// @@ -222,7 +228,7 @@ namespace Godot /// The number of node names which make up the path. public int GetNameCount() { - return godot_icall_NodePath_get_name_count(ref NativeValue); + return NativeFuncs.godotsharp_node_path_get_name_count(ref NativeValue); } /// @@ -230,9 +236,11 @@ namespace Godot /// /// The subname index. /// The subname at the given index . - public string GetSubName(int idx) + public unsafe string GetSubName(int idx) { - return godot_icall_NodePath_get_subname(ref NativeValue, idx); + using godot_string subName = default; + NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName); + return Marshaling.mono_string_from_godot(&subName); } /// @@ -243,7 +251,7 @@ namespace Godot /// The number of subnames in the path. public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(ref NativeValue); + return NativeFuncs.godotsharp_node_path_get_subname_count(ref NativeValue); } /// @@ -255,7 +263,7 @@ namespace Godot /// If the is an absolute path. public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(ref NativeValue); + return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue); } /// @@ -263,29 +271,5 @@ namespace Godot /// /// If the is empty. public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void godot_icall_NodePath_get_as_property_path(ref godot_node_path ptr, ref godot_node_path dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_names(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_subnames(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_name_count(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_subname(ref godot_node_path ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_absolute(ref godot_node_path ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 058c5447e2..6c3d673fdc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Text.RegularExpressions; +using Godot.NativeInterop; namespace Godot { @@ -177,6 +178,7 @@ namespace Godot { return 0; } + if (from == 0 && to == len) { str = instance; @@ -471,7 +473,8 @@ namespace Godot /// The starting position of the substring, or -1 if not found. 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); } /// @@ -490,7 +493,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); } /// Find the last occurrence of a substring. @@ -519,7 +523,8 @@ namespace Godot /// The starting position of the substring, or -1 if not found. 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); } /// @@ -804,6 +809,7 @@ namespace Godot { match = instance[source] == text[target]; } + if (match) { source++; @@ -1015,15 +1021,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); } } @@ -1068,28 +1077,28 @@ namespace Godot /// /// The string to hash. /// The MD5 hash of the string. - public static byte[] MD5Buffer(this string instance) + public static unsafe byte[] MD5Buffer(this string instance) { - return godot_icall_String_md5_buffer(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_packed_byte_array md5Buffer = default; + NativeFuncs.godotsharp_string_md5_buffer(&instanceStr, &md5Buffer); + return Marshaling.PackedByteArray_to_mono_array(&md5Buffer); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_String_md5_buffer(string str); - /// /// Returns the MD5 hash of the string as a string. /// /// /// The string to hash. /// The MD5 hash of the string. - public static string MD5Text(this string instance) + public static unsafe string MD5Text(this string instance) { - return godot_icall_String_md5_text(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string md5Text = default; + NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text); + return Marshaling.mono_string_from_godot(&md5Text); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_String_md5_text(string str); - /// /// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. /// @@ -1242,14 +1251,13 @@ namespace Godot /// The substring to search in the string. /// The position at which to start searching. /// The position at which the substring was found, or -1 if not found. - public static int RFind(this string instance, string what, int from = -1) + public static unsafe int RFind(this string instance, string what, int from = -1) { - return godot_icall_String_rfind(instance, what, from); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string whatStr = Marshaling.mono_string_to_godot(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); - /// /// Perform a search for a substring, but start from the end of the string instead of the beginning. /// Also search case-insensitive. @@ -1259,14 +1267,13 @@ namespace Godot /// The substring to search in the string. /// The position at which to start searching. /// The position at which the substring was found, or -1 if not found. - public static int RFindN(this string instance, string what, int from = -1) + public static unsafe int RFindN(this string instance, string what, int from = -1) { - return godot_icall_String_rfindn(instance, what, from); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string whatStr = Marshaling.mono_string_to_godot(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); - /// /// Returns the right side of the string from a given position. /// @@ -1319,28 +1326,28 @@ namespace Godot /// /// The string to hash. /// The SHA-256 hash of the string. - public static byte[] SHA256Buffer(this string instance) + public static unsafe byte[] SHA256Buffer(this string instance) { - return godot_icall_String_sha256_buffer(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_packed_byte_array sha256Buffer = default; + NativeFuncs.godotsharp_string_sha256_buffer(&instanceStr, &sha256Buffer); + return Marshaling.PackedByteArray_to_mono_array(&sha256Buffer); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_String_sha256_buffer(string str); - /// /// Returns the SHA-256 hash of the string as a string. /// /// /// The string to hash. /// The SHA-256 hash of the string. - public static string SHA256Text(this string instance) + public static unsafe string SHA256Text(this string instance) { - return godot_icall_String_sha256_text(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string sha256Text = default; + NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text); + return Marshaling.mono_string_from_godot(&sha256Text); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_String_sha256_text(string str); - /// /// Returns the similarity index of the text compared to this string. /// 1 means totally similar and 0 means totally dissimilar. @@ -1355,6 +1362,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 @@ -1388,14 +1396,14 @@ namespace Godot /// /// Returns a simplified canonical path. /// - public static string SimplifyPath(this string instance) + public static unsafe string SimplifyPath(this string instance) { - return godot_icall_String_simplify_path(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string simplifiedPath = default; + NativeFuncs.godotsharp_string_simplify_path(&instanceStr, &simplifiedPath); + return Marshaling.mono_string_from_godot(&simplifiedPath); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_String_simplify_path(string str); - /// /// 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 +1417,8 @@ namespace Godot /// The array of strings split from the string. 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); } /// -- cgit v1.2.3 From 5e37d073bb86492e8c415964ffd554a2fa08920d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:50:13 +0200 Subject: C#: Re-write GD and some other icalls as P/Invoke --- .../glue/GodotSharp/GodotSharp/Core/Dispatcher.cs | 20 +-- .../GodotSharp/Core/Extensions/ObjectExtensions.cs | 20 ++- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 200 ++++++++------------- .../Core/NativeInterop/InteropStructs.cs | 2 + .../GodotSharp/Core/NativeInterop/Marshaling.cs | 14 +- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 84 +++++++++ .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 10 +- .../GodotSharp/GodotSharp/Core/ScriptManager.cs | 10 ++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 + 9 files changed, 202 insertions(+), 159 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index 6475237002..5f84bb530f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -1,20 +1,14 @@ -using System.Runtime.CompilerServices; - namespace Godot { public static class Dispatcher { - /// - /// Implements an external instance of GodotTaskScheduler. - /// - /// A GodotTaskScheduler instance. - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler(); + internal static GodotTaskScheduler DefaultGodotTaskScheduler; + + private static void InitializeDefaultGodotTaskScheduler() + { + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } - /// - /// Initializes the synchronization context as the context of the GodotTaskScheduler. - /// - public static GodotSynchronizationContext SynchronizationContext => - godot_icall_DefaultGodotTaskScheduler().Context; + public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs index 691fd85964..340780bb45 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,20 @@ namespace Godot /// public static WeakRef WeakRef(Object obj) { - return godot_icall_Object_weakref(GetPtr(obj)); - } + if (!IsInstanceValid(obj)) + return null; + + using godot_ref weakRef = default; + + unsafe + { + NativeFuncs.godotsharp_weakref(GetPtr(obj), &weakRef); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj); + if (weakRef.IsNull) + return null; + + return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef._reference); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index e8ea8f379b..9d237b8d93 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -5,7 +5,6 @@ using real_t = System.Single; #endif using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot @@ -30,7 +29,9 @@ namespace Godot public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false) { using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); - return godot_icall_GD_bytes2var(&varBytes, allowObjects); + using godot_variant ret = default; + NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects, &ret); + return Marshaling.variant_to_mono_object(&ret); } /// @@ -48,9 +49,12 @@ namespace Godot /// /// /// The Variant converted to the given . - public static object Convert(object what, Variant.Type type) + public static unsafe object Convert(object what, Variant.Type type) { - return godot_icall_GD_convert(what, type); + using var whatVariant = Marshaling.mono_object_to_variant(what); + using godot_variant ret = default; + NativeFuncs.godotsharp_convert(&whatVariant, (int)type, &ret); + return Marshaling.variant_to_mono_object(&ret); } /// @@ -64,7 +68,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) { @@ -84,9 +88,10 @@ namespace Godot /// /// Variable that will be hashed. /// Hash of the variable passed. - public static int Hash(object var) + public static unsafe int Hash(object var) { - return godot_icall_GD_hash(var); + using var variant = Marshaling.mono_object_to_variant(var); + return NativeFuncs.godotsharp_hash(&variant); } /// @@ -112,7 +117,7 @@ namespace Godot /// The instance. public static Object InstanceFromId(ulong instanceId) { - return godot_icall_GD_instance_from_id(instanceId); + return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); } /// @@ -202,9 +207,10 @@ namespace Godot /// /// /// Error message. - public static void PushError(string message) + public static unsafe void PushError(string message) { - godot_icall_GD_pusherror(message); + using var godotStr = Marshaling.mono_string_to_godot(message); + NativeFuncs.godotsharp_pusherror(&godotStr); } /// @@ -214,9 +220,10 @@ namespace Godot /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call /// /// Warning message. - public static void PushWarning(string message) + public static unsafe void PushWarning(string message) { - godot_icall_GD_pushwarning(message); + using var godotStr = Marshaling.mono_string_to_godot(message); + NativeFuncs.godotsharp_pushwarning(&godotStr); } /// @@ -235,9 +242,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void Print(params object[] what) + public static unsafe void Print(params object[] what) { - godot_icall_GD_print(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_print(&godotStr); } /// @@ -264,9 +273,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintRich(params object[] what) + public static unsafe void PrintRich(params object[] what) { - godot_icall_GD_print_rich(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_print_rich(&godotStr); } /// @@ -286,9 +297,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintErr(params object[] what) + public static unsafe void PrintErr(params object[] what) { - godot_icall_GD_printerr(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printerr(&godotStr); } /// @@ -306,9 +319,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintRaw(params object[] what) + public static unsafe void PrintRaw(params object[] what) { - godot_icall_GD_printraw(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printraw(&godotStr); } /// @@ -320,9 +335,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintS(params object[] what) + public static unsafe void PrintS(params object[] what) { - godot_icall_GD_prints(GetPrintParams(what)); + string str = string.Join(' ', GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_prints(&godotStr); } /// @@ -334,9 +351,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintT(params object[] what) + public static unsafe void PrintT(params object[] what) { - godot_icall_GD_printt(GetPrintParams(what)); + string str = string.Join('\t', GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printt(&godotStr); } /// @@ -350,7 +369,7 @@ namespace Godot /// A random number. public static float Randf() { - return godot_icall_GD_randf(); + return NativeFuncs.godotsharp_randf(); } /// @@ -360,7 +379,7 @@ namespace Godot /// A random normally-distributed number. public static double Randfn(double mean, double deviation) { - return godot_icall_GD_randfn(mean, deviation); + return NativeFuncs.godotsharp_randfn(mean, deviation); } /// @@ -378,7 +397,7 @@ namespace Godot /// A random number. public static uint Randi() { - return godot_icall_GD_randi(); + return NativeFuncs.godotsharp_randi(); } /// @@ -391,7 +410,7 @@ namespace Godot /// public static void Randomize() { - godot_icall_GD_randomize(); + NativeFuncs.godotsharp_randomize(); } /// @@ -406,7 +425,7 @@ namespace Godot /// A random number inside the given range. public static double RandRange(double from, double to) { - return godot_icall_GD_randf_range(from, to); + return NativeFuncs.godotsharp_randf_range(from, to); } /// @@ -423,7 +442,7 @@ namespace Godot /// A random number inside the given range. public static int RandRange(int from, int to) { - return godot_icall_GD_randi_range(from, to); + return NativeFuncs.godotsharp_randi_range(from, to); } /// @@ -436,7 +455,7 @@ namespace Godot /// A random number. 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); } /// @@ -493,7 +512,7 @@ namespace Godot /// Seed that will be used. public static void Seed(ulong seed) { - godot_icall_GD_seed(seed); + NativeFuncs.godotsharp_seed(seed); } /// @@ -501,9 +520,12 @@ namespace Godot /// /// Arguments that will converted to string. /// The string formed by the given arguments. - public static string Str(params object[] what) + public static unsafe string Str(params object[] what) { - return godot_icall_GD_str(what); + using var whatGodotArray = Marshaling.mono_array_to_Array(what); + using godot_string ret = default; + NativeFuncs.godotsharp_str(&whatGodotArray, &ret); + return Marshaling.mono_string_from_godot(&ret); } /// @@ -518,18 +540,12 @@ namespace Godot /// /// String that will be converted to Variant. /// The decoded Variant. - public static object Str2Var(string str) + public static unsafe object Str2Var(string str) { - return godot_icall_GD_str2var(str); - } - - /// - /// Returns whether the given class exists in . - /// - /// If the class exists in . - public static bool TypeExists(StringName type) - { - return godot_icall_GD_type_exists(ref type.NativeValue); + using var godotStr = Marshaling.mono_string_to_godot(str); + using godot_variant ret = default; + NativeFuncs.godotsharp_str2var(&godotStr, &ret); + return Marshaling.variant_to_mono_object(&ret); } /// @@ -543,12 +559,11 @@ namespace Godot /// The Variant encoded as an array of bytes. public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false) { - godot_packed_byte_array varBytes; - godot_icall_GD_var2bytes(var, fullObjects, &varBytes); + using var variant = Marshaling.mono_object_to_variant(var); + using godot_packed_byte_array varBytes = default; + NativeFuncs.godotsharp_var2bytes(&variant, fullObjects, &varBytes); using (varBytes) - { return Marshaling.PackedByteArray_to_mono_array(&varBytes); - } } /// @@ -568,9 +583,12 @@ namespace Godot /// /// Variant that will be converted to string. /// The Variant encoded as a string. - public static string Var2Str(object var) + public static unsafe string Var2Str(object var) { - return godot_icall_GD_var2str(var); + using var variant = Marshaling.mono_object_to_variant(var); + using godot_string ret = default; + NativeFuncs.godotsharp_var2str(&variant, &ret); + return Marshaling.mono_string_from_godot(&ret); } /// @@ -579,85 +597,7 @@ namespace Godot /// The for the given . public static Variant.Type TypeToVariantType(Type type) { - return godot_icall_TypeToVariantType(type); + return Marshaling.managed_to_variant_type(type, out bool _); } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* 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(ref godot_string_name type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes); - - [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/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index ddcb6b9091..865863cd3e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -36,6 +36,8 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_ref_destroy(ref this); _reference = IntPtr.Zero; } + + public bool IsNull => _reference == IntPtr.Zero; } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 590fb082e8..e0819b2857 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -18,8 +18,10 @@ namespace Godot.NativeInterop fieldInfo.SetValue(obj, valueObj); } - public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant) + public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) { + r_nil_is_variant = false; + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: @@ -199,8 +201,6 @@ namespace Godot.NativeInterop } } - r_nil_is_variant = false; - // Unknown return Variant.Type.Nil; } @@ -711,7 +711,7 @@ namespace Godot.NativeInterop if (typeof(Godot.Object[]).IsAssignableFrom(type)) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Array_to_mono_array_of_type(&godotArray, type); + return Array_to_mono_array_of_godot_object_type(&godotArray, type); } if (type == typeof(object[])) @@ -1136,7 +1136,7 @@ namespace Godot.NativeInterop return ret; } - public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type) + public static unsafe object Array_to_mono_array_of_godot_object_type(godot_array* p_array, Type type) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); @@ -1144,7 +1144,9 @@ namespace Godot.NativeInterop int length = array.Count; object ret = Activator.CreateInstance(type, length); - array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array + // variant_to_mono_object handled by Collections.Array + // variant_to_mono_object_of_type is not needed because target element types are Godot.Object (or derived) + array.CopyTo((object[])ret, 0); return ret; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 0680ec7be5..adbf5eb9b6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -510,5 +510,89 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); + + // GD, etc + + [DllImport(GodotDllName)] + public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, bool p_allow_objects, + godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_convert(godot_variant* p_what, int p_type, godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern int godotsharp_hash(godot_variant* var); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_instance_from_id(ulong instanceId); + + [DllImport(GodotDllName)] + public static extern void godotsharp_print(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_print_rich(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printerr(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printraw(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_prints(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printt(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern float godotsharp_randf(); + + [DllImport(GodotDllName)] + public static extern uint godotsharp_randi(); + + [DllImport(GodotDllName)] + public static extern void godotsharp_randomize(); + + [DllImport(GodotDllName)] + public static extern double godotsharp_randf_range(double from, double to); + + [DllImport(GodotDllName)] + public static extern double godotsharp_randfn(double mean, double deviation); + + [DllImport(GodotDllName)] + public static extern int godotsharp_randi_range(int from, int to); + + [DllImport(GodotDllName)] + public static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); + + [DllImport(GodotDllName)] + public static extern void godotsharp_seed(ulong seed); + + [DllImport(GodotDllName)] + public static extern void godotsharp_weakref(IntPtr obj, godot_ref* r_weak_ref); + + [DllImport(GodotDllName)] + public static extern string godotsharp_str(godot_array* p_what, godot_string* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_var2bytes(godot_variant* what, bool fullObjects, + godot_packed_byte_array* bytes); + + [DllImport(GodotDllName)] + public static extern void godotsharp_var2str(godot_variant* var, godot_string* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_pusherror(godot_string* type); + + [DllImport(GodotDllName)] + public static extern void godotsharp_pushwarning(godot_string* type); + + // Object + + [DllImport(GodotDllName)] + public static extern string godotsharp_object_to_string(IntPtr ptr, godot_string* r_str); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 9c4265517e..7bbaef62fa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -101,9 +102,11 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this object. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Object_ToString(GetPtr(this)); + using godot_string str = default; + NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str); + return Marshaling.mono_string_from_godot(&str); } /// @@ -190,8 +193,5 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Object_ToString(IntPtr ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs new file mode 100644 index 0000000000..e92688f5bb --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + internal class ScriptManager + { + internal static void FrameCallback() + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 0709723da0..36faf92144 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -71,6 +71,7 @@ + -- cgit v1.2.3 From 513ee857a938c466e0f7146f66db771b9c6e2024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 20:21:15 +0200 Subject: C#: Restructure code prior move to .NET Core The main focus here was to remove the majority of code that relied on Mono's embedding APIs, specially the reflection APIs. The embedding APIs we still use are the bare minimum we need for things to work. A lot of code was moved to C#. We no longer deal with any managed objects (`MonoObject*`, and such) in native code, and all marshaling is done in C#. The reason for restructuring the code and move away from embedding APIs is that once we move to .NET Core, we will be limited by the much more minimal .NET hosting. PERFORMANCE REGRESSIONS ----------------------- Some parts of the code were written with little to no concern about performance. This includes code that calls into script methods and accesses script fields, properties and events. The reason for this is that all of that will be moved to source generators, so any work prior to that would be a waste of time. DISABLED FEATURES ----------------- Some code was removed as it no longer makes sense (or won't make sense in the future). Other parts were commented out with `#if 0`s and TODO warnings because it doesn't make much sense to work on them yet as those parts will change heavily when we switch to .NET Core but also when we start introducing source generators. As such, the following features were disabled temporarily: - Assembly-reloading (will be done with ALCs in .NET Core). - Properties/fields exports and script method listing (will be handled by source generators in the future). - Exception logging in the editor and stack info for errors. - Exporting games. - Building of C# projects. We no longer copy the Godot API assemblies to the project directory, so MSBuild won't be able to find them. The idea is to turn them into NuGet packages in the future, which could also be obtained from local NuGet sources during development. --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 4 +- .../Core/Attributes/AssemblyHasScriptsAttribute.cs | 17 +- .../Core/Attributes/ScriptPathAttribute.cs | 4 +- .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 123 +++++ .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 11 + .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 501 +++++++++++++++++++++ .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 14 +- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 4 +- .../Core/Extensions/SceneTreeExtensions.cs | 62 ++- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 4 +- .../GodotSharp/Core/GodotTraceListener.cs | 5 +- .../Core/NativeInterop/InteropStructs.cs | 48 +- .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 102 ++++- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 30 +- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 2 +- .../Core/NativeInterop/NativeFuncs.extended.cs | 2 +- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 2 +- .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 12 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 270 ++++++++++- .../GodotSharp/GodotSharp/Core/ScriptManager.cs | 10 - .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 34 +- .../GodotSharp/GodotSharp/Core/StringExtensions.cs | 6 +- .../glue/GodotSharp/GodotSharp/Core/StringName.cs | 4 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 4 +- 24 files changed, 1143 insertions(+), 132 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 893ab81e5d..a2a97e0a3e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -14,7 +14,7 @@ namespace Godot.Collections /// public sealed class Array : IList, IDisposable { - internal godot_array NativeValue; + public godot_array NativeValue; /// /// Constructs a new empty . @@ -307,7 +307,7 @@ namespace Godot.Collections { using godot_string str = default; NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str); - return Marshaling.mono_string_from_godot(&str); + return Marshaling.mono_string_from_godot(str); } /// 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 { /// @@ -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; } /// /// Constructs a new AssemblyHasScriptsAttribute instance. /// public AssemblyHasScriptsAttribute() { - requiresLookup = true; + RequiresLookup = true; + ScriptTypes = null; } /// /// Constructs a new AssemblyHasScriptsAttribute instance. /// /// The specified type(s) of scripts. - 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/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; } /// /// Constructs a new ScriptPathAttribute instance. @@ -16,7 +16,7 @@ namespace Godot /// The file path to the script public ScriptPathAttribute(string path) { - this.path = path; + Path = path; } } } 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..16fde2a900 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -0,0 +1,123 @@ +using System; +using System.Runtime.InteropServices; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + internal static class CSharpInstanceBridge + { + private static unsafe void Call(IntPtr godotObjectGCHandle, godot_string_name* method, + godot_variant** args, int argCount, godot_variant_call_error* ref_callError, godot_variant* r_ret) + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + { + *r_ret = default; + (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; + return; + } + + using godot_string dest = default; + NativeFuncs.godotsharp_string_name_as_string(&dest, method); + string methodStr = Marshaling.mono_string_from_godot(dest); + + bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant outRet); + + if (!methodInvoked) + { + *r_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. + (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; + return; + } + + *r_ret = outRet; + } + + private static unsafe bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value) + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + throw new InvalidOperationException(); + + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); + + if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) + return true; + + object valueManaged = Marshaling.variant_to_mono_object(value); + + return godotObject._Set(nameManaged, valueManaged); + } + + private static unsafe bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* r_retValue) + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + throw new InvalidOperationException(); + + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); + + if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), + out godot_variant outRet)) + { + *r_retValue = outRet; + return true; + } + + object ret = godotObject._Get(nameManaged); + + if (ret == null) + { + *r_retValue = default; + return false; + } + + *r_retValue = Marshaling.mono_object_to_variant(ret); + return true; + } + + private static void CallDispose(IntPtr godotObjectGCHandle, bool okIfNull) + { + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (okIfNull) + godotObject?.Dispose(); + else + godotObject!.Dispose(); + } + + private static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* r_res, bool* r_valid) + { + var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (self == null) + { + *r_res = default; + *r_valid = false; + return; + } + + var resultStr = self.ToString(); + + if (resultStr == null) + { + *r_res = default; + *r_valid = false; + return; + } + + *r_res = Marshaling.mono_string_to_godot(resultStr); + *r_valid = true; + } + } +} 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..aa9e434b07 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot.Bridge +{ + internal static class GCHandleBridge + { + private static void FreeGCHandle(IntPtr gcHandlePtr) + => GCHandle.FromIntPtr(gcHandlePtr).Free(); + } +} 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..a39da68a02 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -0,0 +1,501 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using Godot.Collections; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + internal static class ScriptManagerBridge + { + private static System.Collections.Generic.Dictionary _scriptLookupMap = new(); + private static System.Collections.Generic.Dictionary _scriptBridgeMap = new(); + + private struct ScriptLookupInfo + { + public string ClassNamespace { get; private set; } + public string ClassName { get; private set; } + public Type ScriptType { get; private set; } + + public ScriptLookupInfo(string classNamespace, string className, Type scriptType) + { + ClassNamespace = classNamespace; + ClassName = className; + ScriptType = scriptType; + } + }; + + internal static void FrameCallback() + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + + internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName, + IntPtr godotObject) + { + Type nativeType = TypeGetProxyClass(nativeTypeName); + var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); + + obj.NativePtr = godotObject; + + var ctor = nativeType.GetConstructor( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, + null, Type.EmptyTypes, null); + _ = ctor!.Invoke(obj, null); + + return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + } + + internal static unsafe void CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, IntPtr godotObject, + godot_variant** args, int argCount) + { + // Performance is not critical here as this will be replaced with source generators. + Type scriptType = _scriptBridgeMap[scriptPtr]; + var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); + + obj.NativePtr = godotObject; + + 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; + + object[] invokeParams = new object[paramCount]; + + for (int i = 0; i < paramCount; i++) + { + invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameters[i].ParameterType); + } + + ctor.Invoke(obj, invokeParams); + } + + private static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* r_res) + { + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + { + *r_res = default; + return; + } + + var native = Object.InternalGetClassNativeBase(scriptType); + + var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); + + if (field == null) + { + *r_res = default; + return; + } + + var nativeName = (StringName)field.GetValue(null); + + *r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + } + + private static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr) + { + var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + if (target != null) + target.NativePtr = newPtr; + } + + private static unsafe Type TypeGetProxyClass(godot_string_name* nativeTypeName) + { + // Performance is not critical here as this will be replaced with a generated dictionary. + using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(nativeTypeName)); + string nativeTypeNameStr = stringName.ToString(); + + if (nativeTypeNameStr[0] == '_') + nativeTypeNameStr = nativeTypeNameStr.Substring(1); + + Type wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr); + + if (wrapperType == null) + { + wrapperType = AppDomain.CurrentDomain.GetAssemblies() + .First(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; + } + + internal static void LookupScriptsInAssembly(Assembly assembly) + { + static void LookupScriptForClass(Type type) + { + var scriptPathAttr = type.GetCustomAttributes(inherit: false) + .OfType() + .FirstOrDefault(); + + if (scriptPathAttr == null) + return; + + _scriptLookupMap[scriptPathAttr.Path] = new ScriptLookupInfo(type.Namespace, type.Name, type); + } + + var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false) + .OfType() + .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]); + } + } + } + } + + internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr, + godot_string_name* eventSignalName, godot_variant** args, int argCount, bool* r_ownerIsNull) + { + var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; + + if (owner == null) + { + *r_ownerIsNull = true; + return; + } + + *r_ownerIsNull = false; + + owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + } + + internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* r_retSignals) + { + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + // Legacy signals + + foreach (var signalDelegate in top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any())) + { + var invokeMethod = signalDelegate.GetMethod("Invoke"); + + if (invokeMethod == null) + throw new MissingMethodException(signalDelegate.FullName, "Invoke"); + + var signalParams = new Collections.Array(); + + foreach (var parameters in invokeMethod.GetParameters()) + { + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(signalDelegate.Name, signalParams); + } + + // Event signals + + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any()) + .Select(ev => ev.Name); + + var fields = top.GetFields( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + foreach (var eventSignalField in fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name))) + { + var delegateType = eventSignalField.FieldType; + var invokeMethod = delegateType.GetMethod("Invoke"); + + if (invokeMethod == null) + throw new MissingMethodException(delegateType.FullName, "Invoke"); + + var signalParams = new Collections.Array(); + + foreach (var parameters in invokeMethod.GetParameters()) + { + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(eventSignalField.Name, signalParams); + } + + top = top.BaseType; + } + + *r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); + } + + internal static unsafe bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) + { + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); + + string signalNameStr = Marshaling.mono_string_from_godot(*signalName); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + // Legacy signals + + if (top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any()) + .Any(signalDelegate => signalDelegate.Name == signalNameStr) + ) + { + return true; + } + + // Event signals + + if (top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any()) + .Any(eventSignal => eventSignal.Name == signalNameStr) + ) + { + return true; + } + + top = top.BaseType; + } + + return false; + } + + internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, bool deep) + { + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false; + + string methodStr = Marshaling.mono_string_from_godot(*method); + + if (deep) + { + Type top = scriptType; + Type native = Object.InternalGetClassNativeBase(scriptType); + + while (top != null && top != native) + { + var methodInfo = top.GetMethod(methodStr, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (methodInfo != null) + return true; + + top = top.BaseType; + } + + return false; + } + else + { + var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + return methodInfo != null; + } + } + + internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) + { + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false; + + if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) + return false; + + return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType); + } + + internal static unsafe bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) + { + string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); + + if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) + return false; + + _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); + + return true; + } + + internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType) + => _scriptBridgeMap.Add(scriptPtr, scriptType); + + internal static void RemoveScriptBridge(IntPtr scriptPtr) + => _scriptBridgeMap.Remove(scriptPtr); + + internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, bool* r_tool, + godot_dictionary* r_rpcFunctionsDest) + { + // Performance is not critical here as this will be replaced with source generators. + var scriptType = _scriptBridgeMap[scriptPtr]; + + *r_tool = scriptType.GetCustomAttributes(inherit: false) + .OfType() + .Any(); + + if (!*r_tool && scriptType.IsNested) + { + *r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false) + .OfType() + .Any() ?? false; + } + + if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools") + *r_tool = true; + + // RPC functions + + Dictionary rpcFunctions = new(); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + 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().FirstOrDefault(); + + if (rpcAttr == null) + continue; + + var rpcConfig = new 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; + } + + *r_rpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); + } + + internal static unsafe bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* r_newGCHandlePtr, + bool createWeak) + { + var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); + + object target = oldGCHandle.Target; + + if (target == null) + { + oldGCHandle.Free(); + *r_newGCHandlePtr = IntPtr.Zero; + return 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 = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal); + + oldGCHandle.Free(); + *r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); + return true; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 187d910f9f..2a562d4d48 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -11,10 +11,6 @@ namespace Godot { internal static class DelegateUtils { - // TODO: Move somewhere else once we need to for things other than delegates - internal static void FreeGCHandle(IntPtr delegateGCHandle) - => GCHandle.FromIntPtr(delegateGCHandle).Free(); - internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) { var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; @@ -22,14 +18,14 @@ namespace Godot return @delegateA == @delegateB; } - internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, godot_variant* ret) + internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, + godot_variant* ret) { // 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) @@ -260,7 +256,8 @@ namespace Godot } } - private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, out IntPtr delegateGCHandle) + private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, + out IntPtr delegateGCHandle) { bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate); delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate)); @@ -368,7 +365,8 @@ 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)); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 932ee33fe3..8bc33837e6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -15,7 +15,7 @@ namespace Godot.Collections IDictionary, IDisposable { - internal godot_dictionary NativeValue; + public godot_dictionary NativeValue; /// /// Constructs a new empty . @@ -319,7 +319,7 @@ namespace Godot.Collections { using godot_string str = default; NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str); - return Marshaling.mono_string_from_godot(&str); + return Marshaling.mono_string_from_godot(str); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index 7922f38ac5..b939da8778 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System.Reflection; using System.Runtime.CompilerServices; using Godot.Collections; using Godot.NativeInterop; @@ -11,14 +11,64 @@ namespace Godot /// Returns a list of all nodes assigned to the given . /// /// The type to cast to. Should be a descendant of . - public Array GetNodesInGroup(StringName group) where T : class + public unsafe Array GetNodesInGroup(StringName group) where T : class { - godot_array array; - godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array); - return Array.CreateTakingOwnershipOfDisposableValue(array); + var array = GetNodesInGroup(group); + + if (array.Count == 0) + return new Array(array); + + var typeOfT = typeof(T); + bool nativeBase = InternalIsClassNativeBase(typeOfT); + + if (nativeBase) + { + // Native type + var field = typeOfT.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); + + var nativeName = (StringName)field!.GetValue(null); + godot_string_name nativeNameAux = nativeName.NativeValue; + godot_array inputAux = array.NativeValue; + godot_array filteredArray; + godotsharp_array_filter_godot_objects_by_native(&nativeNameAux, &inputAux, &filteredArray); + return Array.CreateTakingOwnershipOfDisposableValue(filteredArray); + } + else + { + // Custom derived type + godot_array inputAux = array.NativeValue; + godot_array filteredArray; + godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); + + var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray); + + // Re-use first array as its size is the same or greater than the filtered one + var resWrapped = new Array(array); + + int j = 0; + for (int i = 0; i < filteredArrayWrapped.Count; i++) + { + if (filteredArrayWrapped[i] is T t) + { + resWrapped[j] = t; + j++; + } + } + + // Remove trailing elements, since this was re-used + resWrapped.Resize(j); + + return resWrapped; + } } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest); + internal extern unsafe void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, + godot_array* p_input, godot_array* r_output); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern unsafe void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, + godot_array* r_output); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 9d237b8d93..f428100ff7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -525,7 +525,7 @@ namespace Godot using var whatGodotArray = Marshaling.mono_array_to_Array(what); using godot_string ret = default; NativeFuncs.godotsharp_str(&whatGodotArray, &ret); - return Marshaling.mono_string_from_godot(&ret); + return Marshaling.mono_string_from_godot(ret); } /// @@ -588,7 +588,7 @@ namespace Godot using var variant = Marshaling.mono_object_to_variant(var); using godot_string ret = default; NativeFuncs.godotsharp_var2str(&variant, &ret); - return Marshaling.mono_string_from_godot(&ret); + return Marshaling.mono_string_from_godot(ret); } /// 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/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 865863cd3e..d8931f8348 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -13,7 +13,7 @@ namespace Godot.NativeInterop { [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_bool + public struct godot_bool { public byte _value; @@ -25,7 +25,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_ref : IDisposable + public struct godot_ref : IDisposable { internal IntPtr _reference; @@ -41,7 +41,7 @@ namespace Godot.NativeInterop } [SuppressMessage("ReSharper", "InconsistentNaming")] - internal enum godot_variant_call_error_error + public enum godot_variant_call_error_error { GODOT_CALL_ERROR_CALL_OK = 0, GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD, @@ -53,16 +53,16 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_variant_call_error + public struct godot_variant_call_error { - godot_variant_call_error_error error; - int argument; - int expected; + public godot_variant_call_error_error error; + public int argument; + public int expected; } [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal struct godot_variant : IDisposable + public struct godot_variant : IDisposable { // 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; @@ -162,7 +162,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_string : IDisposable + public struct godot_string : IDisposable { internal IntPtr _ptr; @@ -180,7 +180,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_string_name : IDisposable + public struct godot_string_name : IDisposable { internal IntPtr _data; @@ -201,7 +201,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_node_path : IDisposable + public struct godot_node_path : IDisposable { internal IntPtr _data; @@ -222,7 +222,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal struct godot_signal : IDisposable + public struct godot_signal : IDisposable { [FieldOffset(0)] public godot_string_name _name; @@ -241,7 +241,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal struct godot_callable : IDisposable + public struct godot_callable : IDisposable { [FieldOffset(0)] public godot_string_name _method; @@ -265,7 +265,7 @@ namespace Godot.NativeInterop // 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 - internal struct godot_array : IDisposable + public struct godot_array : IDisposable { internal unsafe ArrayPrivate* _p; @@ -304,7 +304,7 @@ namespace Godot.NativeInterop // 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 - internal struct godot_dictionary : IDisposable + public struct godot_dictionary : IDisposable { internal IntPtr _p; @@ -319,7 +319,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_byte_array : IDisposable + public struct godot_packed_byte_array : IDisposable { internal IntPtr _writeProxy; internal unsafe byte* _ptr; @@ -337,7 +337,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_int32_array : IDisposable + public struct godot_packed_int32_array : IDisposable { internal IntPtr _writeProxy; internal unsafe int* _ptr; @@ -355,7 +355,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_int64_array : IDisposable + public struct godot_packed_int64_array : IDisposable { internal IntPtr _writeProxy; internal unsafe long* _ptr; @@ -373,7 +373,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_float32_array : IDisposable + public struct godot_packed_float32_array : IDisposable { internal IntPtr _writeProxy; internal unsafe float* _ptr; @@ -391,7 +391,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_float64_array : IDisposable + public struct godot_packed_float64_array : IDisposable { internal IntPtr _writeProxy; internal unsafe double* _ptr; @@ -409,7 +409,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_string_array : IDisposable + public struct godot_packed_string_array : IDisposable { internal IntPtr _writeProxy; internal unsafe godot_string* _ptr; @@ -427,7 +427,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_vector2_array : IDisposable + public struct godot_packed_vector2_array : IDisposable { internal IntPtr _writeProxy; internal unsafe Vector2* _ptr; @@ -445,7 +445,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_vector3_array : IDisposable + public struct godot_packed_vector3_array : IDisposable { internal IntPtr _writeProxy; internal unsafe Vector3* _ptr; @@ -463,7 +463,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_color_array : IDisposable + public struct godot_packed_color_array : IDisposable { internal IntPtr _writeProxy; internal unsafe Color* _ptr; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index 08d49bb937..5d53006140 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -1,5 +1,9 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.Bridge; + +// ReSharper disable InconsistentNaming namespace Godot.NativeInterop { @@ -7,21 +11,105 @@ namespace Godot.NativeInterop { public static Object UnmanagedGetManaged(IntPtr unmanaged) { - // TODO: Move to C# - return internal_unmanaged_get_managed(unmanaged); + // The native pointer may be null + if (unmanaged == IntPtr.Zero) + return null; + + IntPtr gcHandlePtr; + bool has_cs_script_instance = false; + + // First try to get the tied managed instance from a CSharpInstance script instance + + unsafe + { + gcHandlePtr = unmanaged_get_script_instance_managed(unmanaged, &has_cs_script_instance); + } + + if (gcHandlePtr != IntPtr.Zero) + return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + + // Otherwise, if the object has a CSharpInstance script instance, return null + + if (has_cs_script_instance) + return null; + + // If it doesn't have a CSharpInstance script instance, try with native instance bindings + + gcHandlePtr = 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 = unmanaged_instance_binding_create_managed(unmanaged, gcHandlePtr); + + return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null; } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern Object internal_unmanaged_get_managed(IntPtr unmanaged); + private static extern unsafe IntPtr unmanaged_get_script_instance_managed(IntPtr p_unmanaged, + bool* r_has_cs_script_instance); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); - public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged) + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, + IntPtr oldGCHandlePtr); + + public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged, + StringName nativeName, bool refCounted, Type type, Type nativeType) { - // TODO: Move to C# - internal_tie_managed_to_unmanaged(managed, unmanaged); + var gcHandle = GCHandle.Alloc(managed, refCounted ? GCHandleType.Weak : GCHandleType.Normal); + + if (type == nativeType) + { + unsafe + { + godot_string_name nativeNameAux = nativeName.NativeValue; + internal_tie_native_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, + &nativeNameAux, refCounted); + } + } + else + { + IntPtr scriptPtr = internal_new_csharp_script(); + + ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type); + + // IMPORTANT: This must be called after AddScriptWithTypeBridge + internal_tie_user_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, + scriptPtr, refCounted); + } } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_tie_managed_to_unmanaged(Object managed, IntPtr unmanaged); + private static extern unsafe void internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, godot_string_name* nativeName, bool refCounted); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, IntPtr scriptPtr, bool refCounted); + + public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged, + Type type, Type nativeType) + { + if (type == nativeType) + return; + + var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal); + internal_tie_managed_to_unmanaged_with_pre_setup(GCHandle.ToIntPtr(strongGCHandle), unmanaged); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_tie_managed_to_unmanaged_with_pre_setup( + IntPtr gcHandleIntPtr, IntPtr unmanaged); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr internal_new_csharp_script(); public static unsafe Object EngineGetSingleton(string name) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index e0819b2857..eae644af85 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -10,14 +10,8 @@ namespace Godot.NativeInterop { // We want to use full name qualifiers here even if redundant for clarity [SuppressMessage("ReSharper", "RedundantNameQualifier")] - internal static class Marshaling + public static class Marshaling { - public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value) - { - var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType); - fieldInfo.SetValue(obj, valueObj); - } - public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) { r_nil_is_variant = false; @@ -242,10 +236,6 @@ namespace Godot.NativeInterop return mono_object_to_variant_impl(p_obj); } - // TODO: Only called from C++. Remove once no longer needed. - private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret) - => *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err); - private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) { if (p_obj == null) @@ -457,7 +447,7 @@ namespace Godot.NativeInterop // TODO: Validate element type is compatible with Variant #if NET var nativeGodotArray = - mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); + (godot_array)mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); #else // With .NET Standard we need a package reference for Microsoft.CSharp in order to // use dynamic, so we have this workaround for now until we switch to .NET 5/6. @@ -500,12 +490,12 @@ namespace Godot.NativeInterop case Variant.Type.String: { // We avoid the internal call if the stored type is the same we want. - return mono_string_from_godot(&(*p_var)._data._m_string); + return mono_string_from_godot((*p_var)._data._m_string); } default: { using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); - return mono_string_from_godot(&godotString); + return mono_string_from_godot(godotString); } } } @@ -877,7 +867,7 @@ namespace Godot.NativeInterop #endif } case Variant.Type.String: - return mono_string_from_godot(&(*p_var)._data._m_string); + return mono_string_from_godot((*p_var)._data._m_string); case Variant.Type.Vector2: return (*p_var)._data._m_vector2; case Variant.Type.Vector2i: @@ -1007,14 +997,14 @@ namespace Godot.NativeInterop } } - public static unsafe string mono_string_from_godot(godot_string* p_string) + public static unsafe string mono_string_from_godot(in godot_string p_string) { - if ((*p_string)._ptr == IntPtr.Zero) + if (p_string._ptr == IntPtr.Zero) return string.Empty; const int sizeOfChar32 = 4; - byte* bytes = (byte*)(*p_string)._ptr; - int size = (*p_string).Size; + byte* bytes = (byte*)p_string._ptr; + int size = p_string.Size; if (size == 0) return string.Empty; size -= 1; // zero at the end @@ -1283,7 +1273,7 @@ namespace Godot.NativeInterop int size = (*p_array).Size; var array = new string[size]; for (int i = 0; i < size; i++) - array[i] = mono_string_from_godot(&buffer[i]); + array[i] = mono_string_from_godot(buffer[i]); return array; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index adbf5eb9b6..73ac837fe1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -10,7 +10,7 @@ namespace Godot.NativeInterop // The attribute is not available with .NET Core and it's not needed there. [System.Security.SuppressUnmanagedCodeSecurity] #endif - internal static unsafe partial class NativeFuncs + public static unsafe partial class NativeFuncs { private const string GodotDllName = "__Internal"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs index 6001b3a0de..089883c7e8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; namespace Godot.NativeInterop { - internal static unsafe partial class NativeFuncs + public static unsafe partial class NativeFuncs { public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 67f9e23893..91ba864687 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; namespace Godot.NativeInterop { - internal static class VariantUtils + public static class VariantUtils { public static godot_variant CreateFromRID(RID from) => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 541364b281..824f29558f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -42,7 +42,7 @@ namespace Godot /// public sealed class NodePath : IDisposable { - internal godot_node_path NativeValue; + public godot_node_path NativeValue; ~NodePath() { @@ -140,7 +140,7 @@ namespace Godot godot_node_path src = NativeValue; NativeFuncs.godotsharp_node_path_as_string(&dest, &src); using (dest) - return Marshaling.mono_string_from_godot(&dest); + return Marshaling.mono_string_from_godot(dest); } /// @@ -179,7 +179,7 @@ namespace Godot { using godot_string names = default; NativeFuncs.godotsharp_node_path_get_concatenated_names(ref NativeValue, &names); - return Marshaling.mono_string_from_godot(&names); + return Marshaling.mono_string_from_godot(names); } /// @@ -197,7 +197,7 @@ namespace Godot { using godot_string subNames = default; NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames); - return Marshaling.mono_string_from_godot(&subNames); + return Marshaling.mono_string_from_godot(subNames); } /// @@ -217,7 +217,7 @@ namespace Godot { using godot_string name = default; NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name); - return Marshaling.mono_string_from_godot(&name); + return Marshaling.mono_string_from_godot(name); } /// @@ -240,7 +240,7 @@ namespace Godot { using godot_string subName = default; NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName); - return Marshaling.mono_string_from_godot(&subName); + return Marshaling.mono_string_from_godot(subName); } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 7bbaef62fa..763483a11f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Reflection; using System.Runtime.CompilerServices; using Godot.NativeInterop; @@ -7,6 +9,7 @@ namespace Godot public partial class Object : IDisposable { private bool _disposed = false; + private Type _cachedType = typeof(Object); internal IntPtr NativePtr; internal bool MemoryOwn; @@ -21,12 +24,18 @@ namespace Godot #if NET unsafe { - ptr = NativeCtor(); + NativePtr = NativeCtor(); } #else NativePtr = _gd__invoke_class_constructor(NativeCtor); #endif - NativeInterop.InteropUtils.TieManagedToUnmanaged(this, NativePtr); + InteropUtils.TieManagedToUnmanaged(this, NativePtr, + NativeName, refCounted: false, GetType(), _cachedType); + } + else + { + InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr, + GetType(), _cachedType); } _InitializeGodotScriptInstanceInternals(); @@ -34,12 +43,32 @@ namespace Godot internal void _InitializeGodotScriptInstanceInternals() { - godot_icall_Object_ConnectEventSignals(NativePtr); + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + foreach (var eventSignal in top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any())) + { + unsafe + { + using var eventSignalName = new StringName(eventSignal.Name); + godot_string_name eventSignalNameAux = eventSignalName.NativeValue; + godot_icall_Object_ConnectEventSignal(NativePtr, &eventSignalNameAux); + } + } + + top = top.BaseType; + } } internal Object(bool memoryOwn) { - this.MemoryOwn = memoryOwn; + MemoryOwn = memoryOwn; } /// @@ -85,11 +114,11 @@ namespace Godot if (MemoryOwn) { MemoryOwn = false; - godot_icall_RefCounted_Disposed(this, NativePtr, !disposing); + godot_icall_RefCounted_Disposed(NativePtr, !disposing); } else { - godot_icall_Object_Disposed(this, NativePtr); + godot_icall_Object_Disposed(NativePtr); } this.NativePtr = IntPtr.Zero; @@ -106,7 +135,7 @@ namespace Godot { using godot_string str = default; NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str); - return Marshaling.mono_string_from_godot(&str); + return Marshaling.mono_string_from_godot(str); } /// @@ -141,13 +170,219 @@ namespace Godot return new SignalAwaiter(source, signal, this); } + internal static Type InternalGetClassNativeBase(Type t) + { + do + { + var assemblyName = t.Assembly.GetName(); + + if (assemblyName.Name == "GodotSharp") + return t; + + if (assemblyName.Name == "GodotSharpEditor") + return t; + } while ((t = t.BaseType) != null); + + return null; + } + + internal static bool InternalIsClassNativeBase(Type t) + { + var assemblyName = t.Assembly.GetName(); + return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor"; + } + + internal unsafe bool InternalGodotScriptCallViaReflection(string method, godot_variant** args, int argCount, + out godot_variant ret) + { + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var methodInfo = top.GetMethod(method, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (methodInfo != null) + { + var parameters = methodInfo.GetParameters(); + int paramCount = parameters.Length; + + if (argCount == paramCount) + { + object[] invokeParams = new object[paramCount]; + + for (int i = 0; i < paramCount; i++) + { + invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameters[i].ParameterType); + } + + object retObj = methodInfo.Invoke(this, invokeParams); + + ret = Marshaling.mono_object_to_variant(retObj); + return true; + } + } + + top = top.BaseType; + } + + ret = default; + return false; + } + + internal unsafe bool InternalGodotScriptSetFieldOrPropViaReflection(string name, godot_variant* value) + { + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var fieldInfo = top.GetField(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (fieldInfo != null) + { + object valueManaged = Marshaling.variant_to_mono_object_of_type(value, fieldInfo.FieldType); + fieldInfo.SetValue(this, valueManaged); + + return true; + } + + var propertyInfo = top.GetProperty(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (propertyInfo != null) + { + object valueManaged = Marshaling.variant_to_mono_object_of_type(value, propertyInfo.PropertyType); + propertyInfo.SetValue(this, valueManaged); + + return true; + } + + top = top.BaseType; + } + + return false; + } + + internal bool InternalGodotScriptGetFieldOrPropViaReflection(string name, out godot_variant value) + { + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var fieldInfo = top.GetField(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (fieldInfo != null) + { + object valueManaged = fieldInfo.GetValue(this); + value = Marshaling.mono_object_to_variant(valueManaged); + return true; + } + + var propertyInfo = top.GetProperty(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (propertyInfo != null) + { + object valueManaged = propertyInfo.GetValue(this); + value = Marshaling.mono_object_to_variant(valueManaged); + return true; + } + + top = top.BaseType; + } + + value = default; + return false; + } + + internal unsafe void InternalRaiseEventSignal(godot_string_name* eventSignalName, godot_variant** args, + int argc) + { + // Performance is not critical here as this will be replaced with source generators. + + using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(eventSignalName)); + string eventSignalNameStr = stringName.ToString(); + + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any()) + .Select(ev => ev.Name); + + var fields = top.GetFields( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + var eventSignalField = fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name)) + .FirstOrDefault(f => f.Name == eventSignalNameStr); + + if (eventSignalField != null) + { + var @delegate = (Delegate)eventSignalField.GetValue(this); + + if (@delegate == null) + continue; + + var delegateType = eventSignalField.FieldType; + + var invokeMethod = delegateType.GetMethod("Invoke"); + + if (invokeMethod == null) + throw new MissingMethodException(delegateType.FullName, "Invoke"); + + var parameterInfos = invokeMethod.GetParameters(); + var paramsLength = parameterInfos.Length; + + if (argc != paramsLength) + { + throw new InvalidOperationException( + $"The event delegate expects {paramsLength} arguments, but received {argc}."); + } + + var managedArgs = new object[argc]; + + for (uint i = 0; i < argc; i++) + { + managedArgs[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameterInfos[i].ParameterType); + } + + invokeMethod.Invoke(@delegate, managedArgs); + return; + } + + top = top.BaseType; + } + } + internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) { IntPtr methodBind; fixed (char* methodChars = method) { - methodBind = NativeInterop.NativeFuncs - .godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); + methodBind = NativeFuncs.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); } if (methodBind == IntPtr.Zero) @@ -157,11 +392,10 @@ namespace Godot } #if NET - internal static unsafe delegate* unmanaged _gd__ClassDB_get_constructor(StringName type) + internal static unsafe delegate* unmanaged ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeInterop.NativeFuncs - .godotsharp_get_class_constructor(ref type.NativeValue); + var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); if (nativeConstructor == null) throw new NativeConstructorNotFoundException(type); @@ -172,8 +406,7 @@ namespace Godot internal static IntPtr ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeInterop.NativeFuncs - .godotsharp_get_class_constructor(ref type.NativeValue); + var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); if (nativeConstructor == IntPtr.Zero) throw new NativeConstructorNotFoundException(type); @@ -182,16 +415,17 @@ namespace Godot } internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr) - => NativeInterop.NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); + => NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); #endif [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr); + internal static extern void godot_icall_Object_Disposed(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + internal static extern void godot_icall_RefCounted_Disposed(IntPtr ptr, bool isFinalizer); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj); + internal static extern unsafe void godot_icall_Object_ConnectEventSignal(IntPtr obj, + godot_string_name* eventSignal); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs deleted file mode 100644 index e92688f5bb..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Godot -{ - internal class ScriptManager - { - internal static void FrameCallback() - { - Dispatcher.DefaultGodotTaskScheduler?.Activate(); - } - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index e485207fb4..fd6636e410 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Godot.NativeInterop; namespace Godot @@ -12,11 +13,13 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, + Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this))); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter); + internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, + IntPtr target, IntPtr awaiterHandlePtr); public bool IsCompleted => _completed; @@ -29,11 +32,30 @@ namespace Godot public IAwaiter GetAwaiter() => this; - internal void SignalCallback(object[] args) + internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, + godot_variant** args, int argCount, + bool* r_awaiterIsNull) { - _completed = true; - _result = args; - _action?.Invoke(); + var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target; + + if (awaiter == null) + { + *r_awaiterIsNull = true; + return; + } + + *r_awaiterIsNull = false; + + awaiter._completed = true; + + object[] signalArgs = new object[argCount]; + + for (int i = 0; i < argCount; i++) + signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); + + awaiter._result = signalArgs; + + awaiter._action?.Invoke(); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 6c3d673fdc..dfdef81f9e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -1096,7 +1096,7 @@ namespace Godot using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); using godot_string md5Text = default; NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text); - return Marshaling.mono_string_from_godot(&md5Text); + return Marshaling.mono_string_from_godot(md5Text); } /// @@ -1345,7 +1345,7 @@ namespace Godot using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); using godot_string sha256Text = default; NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text); - return Marshaling.mono_string_from_godot(&sha256Text); + return Marshaling.mono_string_from_godot(sha256Text); } /// @@ -1401,7 +1401,7 @@ namespace Godot using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); using godot_string simplifiedPath = default; NativeFuncs.godotsharp_string_simplify_path(&instanceStr, &simplifiedPath); - return Marshaling.mono_string_from_godot(&simplifiedPath); + return Marshaling.mono_string_from_godot(simplifiedPath); } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index 40d282eab4..84b0ab623c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -13,7 +13,7 @@ namespace Godot /// public sealed class StringName : IDisposable { - internal godot_string_name NativeValue; + public godot_string_name NativeValue; ~StringName() { @@ -86,7 +86,7 @@ namespace Godot godot_string_name src = NativeValue; NativeFuncs.godotsharp_string_name_as_string(&dest, &src); using (dest) - return Marshaling.mono_string_from_godot(&dest); + return Marshaling.mono_string_from_godot(dest); } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 36faf92144..6a529de99b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -32,6 +32,9 @@ + + + @@ -71,7 +74,6 @@ - -- cgit v1.2.3 From f9a67ee9da1d6cc3562fa5a7443a2a66a673bd8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 20:23:05 +0200 Subject: C#: Begin move to .NET Core We're targeting .NET 5 for now to make development easier while .NET 6 is not yet released. TEMPORARY REGRESSIONS --------------------- Assembly unloading is not implemented yet. As such, many Godot resources are leaked at exit. This will be re-implemented later together with assembly hot-reloading. --- .../GodotSharp/GodotPlugins/GodotPlugins.csproj | 17 + modules/mono/glue/GodotSharp/GodotPlugins/Main.cs | 197 +++++++ .../GodotSharp/GodotPlugins/PluginLoadContext.cs | 61 ++ modules/mono/glue/GodotSharp/GodotSharp.sln | 6 + .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 2 +- .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 205 ++++--- .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 15 +- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 72 +++ .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 653 +++++++++++++-------- .../GodotSharp/GodotSharp/Core/DebuggingUtils.cs | 20 +- .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 62 +- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 27 +- .../glue/GodotSharp/GodotSharp/Core/Dispatcher.cs | 16 +- .../Core/Extensions/SceneTreeExtensions.cs | 18 +- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 4 +- .../Core/NativeInterop/ExceptionUtils.cs | 74 +++ .../Core/NativeInterop/InteropStructs.cs | 34 +- .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 51 +- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 72 ++- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 63 +- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 134 +++-- .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 2 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 39 +- .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 47 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 4 +- .../GodotSharp/Properties/AssemblyInfo.cs | 1 + .../GodotSharpEditor/GodotSharpEditor.csproj | 2 +- 27 files changed, 1315 insertions(+), 583 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj create mode 100644 modules/mono/glue/GodotSharp/GodotPlugins/Main.cs create mode 100644 modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj new file mode 100644 index 0000000000..38cd2ece4e --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj @@ -0,0 +1,17 @@ + + + + net5.0 + 9 + enable + true + + + true + + + + + + + diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs new file mode 100644 index 0000000000..9f938373c4 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using Godot.NativeInterop; + +namespace GodotPlugins +{ + public static class Main + { + private static readonly List SharedAssemblies = new(); + private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly; + private static Assembly? _editorApiAssembly; + + private static readonly AssemblyLoadContext MainLoadContext = + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? + AssemblyLoadContext.Default; + + // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later. + [UnmanagedCallersOnly] + internal static unsafe godot_bool Initialize(godot_bool editorHint, + PluginsCallbacks* pluginsCallbacks, Godot.Bridge.ManagedCallbacks* managedCallbacks) + { + try + { + SharedAssemblies.Add(CoreApiAssembly.GetName()); + + if (editorHint.ToBool()) + { + _editorApiAssembly = Assembly.Load("GodotSharpEditor"); + SharedAssemblies.Add(_editorApiAssembly.GetName()); + } + + NativeLibrary.SetDllImportResolver(CoreApiAssembly, OnResolveDllImport); + + *pluginsCallbacks = new() + { + LoadProjectAssemblyCallback = &LoadProjectAssembly, + LoadToolsAssemblyCallback = &LoadToolsAssembly, + }; + + *managedCallbacks = Godot.Bridge.ManagedCallbacks.Create(); + + return godot_bool.True; + } + catch (Exception e) + { + Console.Error.WriteLine(e); + *pluginsCallbacks = default; + *managedCallbacks = default; + return false.ToGodotBool(); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PluginsCallbacks + { + public unsafe delegate* unmanaged LoadProjectAssemblyCallback; + public unsafe delegate* unmanaged LoadToolsAssemblyCallback; + } + + [UnmanagedCallersOnly] + internal static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath) + { + try + { + string assemblyPath = new(nAssemblyPath); + + var assembly = LoadPlugin(assemblyPath); + + var method = CoreApiAssembly.GetType("Godot.Bridge.ScriptManagerBridge")? + .GetMethod("LookupScriptsInAssembly", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + + if (method == null) + { + throw new MissingMethodException("Godot.Bridge.ScriptManagerBridge", + "LookupScriptsInAssembly"); + } + + method.Invoke(null, new object[] { assembly }); + + return godot_bool.True; + } + catch (Exception e) + { + Console.Error.WriteLine(e); + return false.ToGodotBool(); + } + } + + [UnmanagedCallersOnly] + internal static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath) + { + 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, OnResolveDllImport); + + 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, null) ?? IntPtr.Zero; + } + catch (Exception e) + { + Console.Error.WriteLine(e); + return IntPtr.Zero; + } + } + + private static Assembly LoadPlugin(string assemblyPath) + { + string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); + + var sharedAssemblies = new List(); + + foreach (var sharedAssembly in SharedAssemblies) + { + string? sharedAssemblyName = sharedAssembly.Name; + if (sharedAssemblyName != null) + sharedAssemblies.Add(sharedAssemblyName); + } + + var loadContext = new PluginLoadContext(assemblyPath, sharedAssemblies, MainLoadContext); + return loadContext.LoadFromAssemblyName(new AssemblyName(assemblyName)); + } + + public static 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 Linux.dlopen(IntPtr.Zero, Linux.RTLD_LAZY); + } + 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 Linux + { + // libdl.so was resulting in DllNotFoundException, for some reason... + // libcoreclr.so should work with both CoreCLR and the .NET Core version of Mono. + private const string SystemLibrary = "libcoreclr.so"; + + 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/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs new file mode 100644 index 0000000000..1b969716aa --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs @@ -0,0 +1,61 @@ +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 _sharedAssemblies; + private readonly AssemblyLoadContext _mainLoadContext; + + public PluginLoadContext(string pluginPath, ICollection sharedAssemblies, + AssemblyLoadContext mainLoadContext) + { + Console.WriteLine(pluginPath); + Console.Out.Flush(); + _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) + { + // 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..fc4e6e91f1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp.sln +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln @@ -4,6 +4,8 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ 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 EndGlobalSection EndGlobal diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a2a97e0a3e..2aa2ece803 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -95,7 +95,7 @@ namespace Godot.Collections public Array Duplicate(bool deep = false) { godot_array newArray; - NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep, out newArray); + NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep.ToGodotBool(), out newArray); return CreateTakingOwnershipOfDisposableValue(newArray); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 16fde2a900..db27989bdb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -6,118 +6,167 @@ namespace Godot.Bridge { internal static class CSharpInstanceBridge { - private static unsafe void Call(IntPtr godotObjectGCHandle, godot_string_name* method, - godot_variant** args, int argCount, godot_variant_call_error* ref_callError, godot_variant* r_ret) + [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) { - // Performance is not critical here as this will be replaced with source generators. - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - - if (godotObject == null) + try { - *r_ret = default; - (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; - return; + // Performance is not critical here as this will be replaced with source generators. + 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 false.ToGodotBool(); + } + + using godot_string dest = default; + NativeFuncs.godotsharp_string_name_as_string(&dest, method); + string methodStr = Marshaling.mono_string_from_godot(dest); + + bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, 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 false.ToGodotBool(); + } + + *ret = retValue; + return true.ToGodotBool(); } - - using godot_string dest = default; - NativeFuncs.godotsharp_string_name_as_string(&dest, method); - string methodStr = Marshaling.mono_string_from_godot(dest); - - bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant outRet); - - if (!methodInvoked) + catch (Exception e) { - *r_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. - (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; - return; + ExceptionUtils.DebugPrintUnhandledException(e); + *ret = default; + return false.ToGodotBool(); } - - *r_ret = outRet; } - private static unsafe bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value) + [UnmanagedCallersOnly] + internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value) { - // Performance is not critical here as this will be replaced with source generators. - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + try + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - if (godotObject == null) - throw new InvalidOperationException(); + if (godotObject == null) + throw new InvalidOperationException(); - var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(name)); + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); - if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) - return true; + if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) + return true.ToGodotBool(); - object valueManaged = Marshaling.variant_to_mono_object(value); + object valueManaged = Marshaling.variant_to_mono_object(value); - return godotObject._Set(nameManaged, valueManaged); + return godotObject._Set(nameManaged, valueManaged).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + return false.ToGodotBool(); + } } - private static unsafe bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* r_retValue) + [UnmanagedCallersOnly] + internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, + godot_variant* outRet) { - // Performance is not critical here as this will be replaced with source generators. - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + try + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - if (godotObject == null) - throw new InvalidOperationException(); + if (godotObject == null) + throw new InvalidOperationException(); - var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(name)); + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); - if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), - out godot_variant outRet)) - { - *r_retValue = outRet; - return true; - } + if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), + out godot_variant outRetValue)) + { + *outRet = outRetValue; + return true.ToGodotBool(); + } - object ret = godotObject._Get(nameManaged); + object ret = godotObject._Get(nameManaged); - if (ret == null) + if (ret == null) + { + *outRet = default; + return false.ToGodotBool(); + } + + *outRet = Marshaling.mono_object_to_variant(ret); + return true.ToGodotBool(); + } + catch (Exception e) { - *r_retValue = default; - return false; + ExceptionUtils.DebugPrintUnhandledException(e); + *outRet = default; + return false.ToGodotBool(); } - - *r_retValue = Marshaling.mono_object_to_variant(ret); - return true; } - private static void CallDispose(IntPtr godotObjectGCHandle, bool okIfNull) + [UnmanagedCallersOnly] + internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull) { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + try + { + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - if (okIfNull) - godotObject?.Dispose(); - else - godotObject!.Dispose(); + if (okIfNull.ToBool()) + godotObject?.Dispose(); + else + godotObject!.Dispose(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + } } - private static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* r_res, bool* r_valid) + [UnmanagedCallersOnly] + internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid) { - var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - - if (self == null) + try { - *r_res = default; - *r_valid = false; - return; + var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (self == null) + { + *outRes = default; + *outValid = false.ToGodotBool(); + return; + } + + var resultStr = self.ToString(); + + if (resultStr == null) + { + *outRes = default; + *outValid = false.ToGodotBool(); + return; + } + + *outRes = Marshaling.mono_string_to_godot(resultStr); + *outValid = true.ToGodotBool(); } - - var resultStr = self.ToString(); - - if (resultStr == null) + catch (Exception e) { - *r_res = default; - *r_valid = false; - return; + ExceptionUtils.DebugPrintUnhandledException(e); + *outRes = default; + *outValid = false.ToGodotBool(); } - - *r_res = Marshaling.mono_string_to_godot(resultStr); - *r_valid = true; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs index aa9e434b07..c6f2e8f77d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -1,11 +1,22 @@ using System; using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot.Bridge { internal static class GCHandleBridge { - private static void FreeGCHandle(IntPtr gcHandlePtr) - => GCHandle.FromIntPtr(gcHandlePtr).Free(); + [UnmanagedCallersOnly] + internal static void FreeGCHandle(IntPtr gcHandlePtr) + { + try + { + GCHandle.FromIntPtr(gcHandlePtr).Free(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + } + } } } 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..1d19376cdd --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -0,0 +1,72 @@ +using System; +using System.Runtime.InteropServices; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct ManagedCallbacks + { + // @formatter:off + public delegate* unmanaged SignalAwaiter_SignalCallback; + public delegate* unmanaged DelegateUtils_InvokeWithVariantArgs; + public delegate* unmanaged DelegateUtils_DelegateEquals; + public delegate* unmanaged ScriptManagerBridge_FrameCallback; + public delegate* unmanaged ScriptManagerBridge_CreateManagedForGodotObjectBinding; + public delegate* unmanaged ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; + public delegate* unmanaged ScriptManagerBridge_GetScriptNativeName; + public delegate* unmanaged ScriptManagerBridge_SetGodotObjectPtr; + public delegate* unmanaged ScriptManagerBridge_RaiseEventSignal; + public delegate* unmanaged ScriptManagerBridge_GetScriptSignalList; + public delegate* unmanaged ScriptManagerBridge_HasScriptSignal; + public delegate* unmanaged ScriptManagerBridge_HasMethodUnknownParams; + public delegate* unmanaged ScriptManagerBridge_ScriptIsOrInherits; + public delegate* unmanaged ScriptManagerBridge_AddScriptBridge; + public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; + public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; + public delegate* unmanaged ScriptManagerBridge_SwapGCHandleForType; + public delegate* unmanaged CSharpInstanceBridge_Call; + public delegate* unmanaged CSharpInstanceBridge_Set; + public delegate* unmanaged CSharpInstanceBridge_Get; + public delegate* unmanaged CSharpInstanceBridge_CallDispose; + public delegate* unmanaged CSharpInstanceBridge_CallToString; + public delegate* unmanaged GCHandleBridge_FreeGCHandle; + public delegate* unmanaged DebuggingUtils_InstallTraceListener; + public delegate* unmanaged Dispatcher_InitializeDefaultGodotTaskScheduler; + // @formatter:on + + public static ManagedCallbacks Create() + { + return new() + { + // @formatter:off + SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback, + DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs, + DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals, + 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_GetScriptSignalList = &ScriptManagerBridge.GetScriptSignalList, + ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal, + ScriptManagerBridge_HasMethodUnknownParams = &ScriptManagerBridge.HasMethodUnknownParams, + ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits, + ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge, + ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge, + ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo, + ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType, + CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call, + CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set, + CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get, + CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose, + CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString, + GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle, + DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener, + Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler, + // @formatter:on + }; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index a39da68a02..9655887e52 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -27,99 +27,150 @@ namespace Godot.Bridge } }; + [UnmanagedCallersOnly] internal static void FrameCallback() { - Dispatcher.DefaultGodotTaskScheduler?.Activate(); + try + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } } + [UnmanagedCallersOnly] internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName, IntPtr godotObject) { - Type nativeType = TypeGetProxyClass(nativeTypeName); - var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); + try + { + Type nativeType = TypeGetProxyClass(nativeTypeName); + var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); - obj.NativePtr = godotObject; + obj.NativePtr = godotObject; - var ctor = nativeType.GetConstructor( - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, - null, Type.EmptyTypes, null); - _ = ctor!.Invoke(obj, null); + var ctor = nativeType.GetConstructor( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, + null, Type.EmptyTypes, null); + _ = ctor!.Invoke(obj, null); - return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + return IntPtr.Zero; + } } - internal static unsafe void CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, IntPtr godotObject, + [UnmanagedCallersOnly] + internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, + IntPtr godotObject, godot_variant** args, int argCount) { - // Performance is not critical here as this will be replaced with source generators. - Type scriptType = _scriptBridgeMap[scriptPtr]; - var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); + try + { + // Performance is not critical here as this will be replaced with source generators. + Type scriptType = _scriptBridgeMap[scriptPtr]; + var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); - obj.NativePtr = godotObject; + obj.NativePtr = godotObject; - var ctor = scriptType - .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(c => c.GetParameters().Length == argCount) - .FirstOrDefault(); + 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 + if (ctor == null) { - throw new MissingMemberException( - $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters."); + 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 parameters = ctor.GetParameters(); + int paramCount = parameters.Length; - object[] invokeParams = new object[paramCount]; + object[] invokeParams = new object[paramCount]; - for (int i = 0; i < paramCount; i++) + for (int i = 0; i < paramCount; i++) + { + invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameters[i].ParameterType); + } + + ctor.Invoke(obj, invokeParams); + return true.ToGodotBool(); + } + catch (Exception e) { - invokeParams[i] = Marshaling.variant_to_mono_object_of_type( - args[i], parameters[i].ParameterType); + ExceptionUtils.DebugPrintUnhandledException(e); + return false.ToGodotBool(); } - - ctor.Invoke(obj, invokeParams); } - private static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* r_res) + [UnmanagedCallersOnly] + internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes) { - // Performance is not critical here as this will be replaced with source generators. - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + try { - *r_res = default; - return; - } + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + { + *outRes = default; + return; + } - var native = Object.InternalGetClassNativeBase(scriptType); + var native = Object.InternalGetClassNativeBase(scriptType); - var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic); + var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); - if (field == null) - { - *r_res = default; - return; - } + if (field == null) + { + *outRes = default; + return; + } - var nativeName = (StringName)field.GetValue(null); + var nativeName = (StringName)field.GetValue(null); - *r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + if (nativeName == null) + { + *outRes = default; + return; + } + + *outRes = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outRes = default; + } } - private static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr) + [UnmanagedCallersOnly] + internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr) { - var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; - if (target != null) - target.NativePtr = newPtr; + try + { + var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + if (target != null) + target.NativePtr = newPtr; + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } } private static unsafe Type TypeGetProxyClass(godot_string_name* nativeTypeName) @@ -152,7 +203,9 @@ namespace Godot.Bridge return wrapperType; } - internal static void LookupScriptsInAssembly(Assembly assembly) + // Called from GodotPlugins + // ReSharper disable once UnusedMember.Local + private static void LookupScriptsInAssembly(Assembly assembly) { static void LookupScriptForClass(Type type) { @@ -208,294 +261,398 @@ namespace Godot.Bridge } } + [UnmanagedCallersOnly] internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr, - godot_string_name* eventSignalName, godot_variant** args, int argCount, bool* r_ownerIsNull) + godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull) { - var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; - - if (owner == null) + try { - *r_ownerIsNull = true; - return; - } + var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; + + if (owner == null) + { + *outOwnerIsNull = true.ToGodotBool(); + return; + } - *r_ownerIsNull = false; + *outOwnerIsNull = false.ToGodotBool(); - owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + *outOwnerIsNull = false.ToGodotBool(); + } } - internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* r_retSignals) + [UnmanagedCallersOnly] + internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* outRetSignals) { - // Performance is not critical here as this will be replaced with source generators. - using var signals = new Dictionary(); - - Type top = _scriptBridgeMap[scriptPtr]; - Type native = Object.InternalGetClassNativeBase(top); - - while (top != null && top != native) + try { - // Legacy signals + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); - foreach (var signalDelegate in top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any())) + while (top != null && top != native) { - var invokeMethod = signalDelegate.GetMethod("Invoke"); + // Legacy signals - if (invokeMethod == null) - throw new MissingMethodException(signalDelegate.FullName, "Invoke"); + foreach (var signalDelegate in top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any())) + { + var invokeMethod = signalDelegate.GetMethod("Invoke"); - var signalParams = new Collections.Array(); + if (invokeMethod == null) + throw new MissingMethodException(signalDelegate.FullName, "Invoke"); - foreach (var parameters in invokeMethod.GetParameters()) - { - var paramType = Marshaling.managed_to_variant_type( - parameters.ParameterType, out bool nilIsVariant); - signalParams.Add(new Dictionary() + var signalParams = new Collections.Array(); + + foreach (var parameters in invokeMethod.GetParameters()) { - { "name", parameters.Name }, - { "type", paramType }, - { "nil_is_variant", nilIsVariant } - }); + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(signalDelegate.Name, signalParams); } - signals.Add(signalDelegate.Name, signalParams); - } + // Event signals - // Event signals + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any()) + .Select(ev => ev.Name); - var foundEventSignals = top.GetEvents( + var fields = top.GetFields( BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any()) - .Select(ev => ev.Name); - - var fields = top.GetFields( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); + BindingFlags.NonPublic | BindingFlags.Public); - foreach (var eventSignalField in fields - .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) - .Where(f => foundEventSignals.Contains(f.Name))) - { - var delegateType = eventSignalField.FieldType; - var invokeMethod = delegateType.GetMethod("Invoke"); + foreach (var eventSignalField in fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name))) + { + var delegateType = eventSignalField.FieldType; + var invokeMethod = delegateType.GetMethod("Invoke"); - if (invokeMethod == null) - throw new MissingMethodException(delegateType.FullName, "Invoke"); + if (invokeMethod == null) + throw new MissingMethodException(delegateType.FullName, "Invoke"); - var signalParams = new Collections.Array(); + var signalParams = new Collections.Array(); - foreach (var parameters in invokeMethod.GetParameters()) - { - var paramType = Marshaling.managed_to_variant_type( - parameters.ParameterType, out bool nilIsVariant); - signalParams.Add(new Dictionary() + foreach (var parameters in invokeMethod.GetParameters()) { - { "name", parameters.Name }, - { "type", paramType }, - { "nil_is_variant", nilIsVariant } - }); + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(eventSignalField.Name, signalParams); } - signals.Add(eventSignalField.Name, signalParams); + top = top.BaseType; } - top = top.BaseType; + *outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outRetSignals = NativeFuncs.godotsharp_dictionary_new(); } - - *r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); } - internal static unsafe bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) + [UnmanagedCallersOnly] + internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) { - // Performance is not critical here as this will be replaced with source generators. - using var signals = new Dictionary(); - - string signalNameStr = Marshaling.mono_string_from_godot(*signalName); + try + { + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); - Type top = _scriptBridgeMap[scriptPtr]; - Type native = Object.InternalGetClassNativeBase(top); + string signalNameStr = Marshaling.mono_string_from_godot(*signalName); - while (top != null && top != native) - { - // Legacy signals + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); - if (top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any()) - .Any(signalDelegate => signalDelegate.Name == signalNameStr) - ) + while (top != null && top != native) { - return true; - } + // Legacy signals + + if (top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any()) + .Any(signalDelegate => signalDelegate.Name == signalNameStr) + ) + { + return true.ToGodotBool(); + } - // Event signals + // Event signals - if (top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any()) - .Any(eventSignal => eventSignal.Name == signalNameStr) - ) - { - return true; + if (top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any()) + .Any(eventSignal => eventSignal.Name == signalNameStr) + ) + { + return true.ToGodotBool(); + } + + top = top.BaseType; } - top = top.BaseType; + return false.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); } - - return false; } - internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, bool deep) + [UnmanagedCallersOnly] + internal static unsafe godot_bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, + godot_bool deep) { - // Performance is not critical here as this will be replaced with source generators. - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) - return false; - - string methodStr = Marshaling.mono_string_from_godot(*method); - - if (deep) + try { - Type top = scriptType; - Type native = Object.InternalGetClassNativeBase(scriptType); + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false.ToGodotBool(); - while (top != null && top != native) + string methodStr = Marshaling.mono_string_from_godot(*method); + + if (deep.ToBool()) { - var methodInfo = top.GetMethod(methodStr, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); + Type top = scriptType; + Type native = Object.InternalGetClassNativeBase(scriptType); - if (methodInfo != null) - return true; + while (top != null && top != native) + { + var methodInfo = top.GetMethod(methodStr, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); - top = top.BaseType; - } + if (methodInfo != null) + return true.ToGodotBool(); + + top = top.BaseType; + } + + top = native; + Type typeOfSystemObject = typeof(System.Object); + while (top != null && top != typeOfSystemObject) + { + bool found = top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(m => m.GetCustomAttributes(false).OfType() + .Where(a => a.MethodName == methodStr) + .Any()) + .Any(); + + if (found) + return true.ToGodotBool(); - return false; + top = top.BaseType; + } + + return false.ToGodotBool(); + } + else + { + var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + return (methodInfo != null).ToGodotBool(); + } } - else + catch (Exception e) { - var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - return methodInfo != null; + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); } } - internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) + [UnmanagedCallersOnly] + internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) { - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) - return false; + try + { + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false.ToGodotBool(); - if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) - return false; + if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) + return false.ToGodotBool(); - return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType); + return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); + } } - internal static unsafe bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) + [UnmanagedCallersOnly] + internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) { - string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); + try + { + string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); - if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) - return false; + if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) + return false.ToGodotBool(); - _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); + _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); - return true; + return true.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); + } } internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType) => _scriptBridgeMap.Add(scriptPtr, scriptType); + [UnmanagedCallersOnly] internal static void RemoveScriptBridge(IntPtr scriptPtr) - => _scriptBridgeMap.Remove(scriptPtr); - - internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, bool* r_tool, - godot_dictionary* r_rpcFunctionsDest) { - // Performance is not critical here as this will be replaced with source generators. - var scriptType = _scriptBridgeMap[scriptPtr]; - - *r_tool = scriptType.GetCustomAttributes(inherit: false) - .OfType() - .Any(); + try + { + _scriptBridgeMap.Remove(scriptPtr); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } + } - if (!*r_tool && scriptType.IsNested) + [UnmanagedCallersOnly] + internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool, + godot_dictionary* outRpcFunctionsDest) + { + try { - *r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false) + // Performance is not critical here as this will be replaced with source generators. + var scriptType = _scriptBridgeMap[scriptPtr]; + + *outTool = scriptType.GetCustomAttributes(inherit: false) .OfType() - .Any() ?? false; - } + .Any().ToGodotBool(); - if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools") - *r_tool = true; + if (!(*outTool).ToBool() && scriptType.IsNested) + { + *outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false) + .OfType() + .Any() ?? false).ToGodotBool(); + } - // RPC functions + if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools") + *outTool = true.ToGodotBool(); - Dictionary rpcFunctions = new(); + // RPC functions - Type top = _scriptBridgeMap[scriptPtr]; - Type native = Object.InternalGetClassNativeBase(top); + Dictionary rpcFunctions = new(); - while (top != null && top != native) - { - foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public)) + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) { - if (method.IsStatic) - continue; + foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public)) + { + if (method.IsStatic) + continue; - string methodName = method.Name; + string methodName = method.Name; - if (rpcFunctions.ContainsKey(methodName)) - continue; + if (rpcFunctions.ContainsKey(methodName)) + continue; - var rpcAttr = method.GetCustomAttributes(inherit: false) - .OfType().FirstOrDefault(); + var rpcAttr = method.GetCustomAttributes(inherit: false) + .OfType().FirstOrDefault(); - if (rpcAttr == null) - continue; + if (rpcAttr == null) + continue; - var rpcConfig = new Dictionary(); + var rpcConfig = new Dictionary(); - rpcConfig["rpc_mode"] = (long)rpcAttr.Mode; - rpcConfig["call_local"] = rpcAttr.CallLocal; - rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode; - rpcConfig["channel"] = rpcAttr.TransferChannel; + 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); + } - rpcFunctions.Add(methodName, rpcConfig); + top = top.BaseType; } - top = top.BaseType; + *outRpcFunctionsDest = + NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outTool = false.ToGodotBool(); + *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new(); } - - *r_rpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); } - internal static unsafe bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* r_newGCHandlePtr, - bool createWeak) + [UnmanagedCallersOnly] + internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr, + godot_bool createWeak) { - var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); + try + { + var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); - object target = oldGCHandle.Target; + object target = oldGCHandle.Target; - if (target == null) - { - oldGCHandle.Free(); - *r_newGCHandlePtr = IntPtr.Zero; - return false; // Called after the managed side was collected, so nothing to do here - } + if (target == null) + { + oldGCHandle.Free(); + *outNewGCHandlePtr = IntPtr.Zero; + return false.ToGodotBool(); // 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 = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal); + // Release the current weak handle and replace it with a strong handle. + var newGCHandle = GCHandle.Alloc(target, + createWeak.ToBool() ? GCHandleType.Weak : GCHandleType.Normal); - oldGCHandle.Free(); - *r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); - return true; + oldGCHandle.Free(); + *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); + return true.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outNewGCHandlePtr = IntPtr.Zero; + return false.ToGodotBool(); + } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index edfe3464ec..e446b3db1c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -1,7 +1,9 @@ using System; using System.Diagnostics; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; +using Godot.NativeInterop; namespace Godot { @@ -19,13 +21,23 @@ namespace Godot sb.Append(" "); } - public static void InstallTraceListener() + [UnmanagedCallersOnly] + internal static void InstallTraceListener() { - Trace.Listeners.Clear(); - Trace.Listeners.Add(new GodotTraceListener()); + try + { + Trace.Listeners.Clear(); + Trace.Listeners.Add(new GodotTraceListener()); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.PushError("Failed to install 'System.Diagnostics.Trace' listener."); + } } - public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl) + public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, + out string methodDecl) { fileName = frame.GetFileName(); fileLineNumber = frame.GetFileLineNumber(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 2a562d4d48..87c93e35f5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -11,38 +11,56 @@ namespace Godot { internal static class DelegateUtils { - internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) + [UnmanagedCallersOnly] + internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) { - var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; - var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; - return @delegateA == @delegateB; + try + { + var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; + var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; + return (@delegateA == @delegateB).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); + } } + [UnmanagedCallersOnly] internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, - godot_variant* ret) + godot_variant* outRet) { - // TODO: Optimize - var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; - var managedArgs = new object[argc]; + try + { + // TODO: Optimize + var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; + var managedArgs = new object[argc]; - var parameterInfos = @delegate.Method.GetParameters(); - var paramsLength = parameterInfos.Length; + var parameterInfos = @delegate!.Method.GetParameters(); + var paramsLength = parameterInfos.Length; - if (argc != paramsLength) - { - throw new InvalidOperationException( - $"The delegate expects {paramsLength} arguments, but received {argc}."); - } + if (argc != paramsLength) + { + throw new InvalidOperationException( + $"The delegate expects {paramsLength} arguments, but received {argc}."); + } - for (uint i = 0; i < argc; i++) - { - managedArgs[i] = Marshaling.variant_to_mono_object_of_type( - args[i], parameterInfos[i].ParameterType); - } + for (uint i = 0; i < argc; i++) + { + managedArgs[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameterInfos[i].ParameterType); + } - object invokeRet = @delegate.DynamicInvoke(managedArgs); + object invokeRet = @delegate.DynamicInvoke(managedArgs); - *ret = Marshaling.mono_object_to_variant(invokeRet); + *outRet = Marshaling.mono_object_to_variant(invokeRet); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + *outRet = default; + } } // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 8bc33837e6..c21d53b4d4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -76,7 +76,7 @@ namespace Godot.Collections public Dictionary Duplicate(bool deep = false) { godot_dictionary newDictionary; - NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep, out newDictionary); + NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep.ToGodotBool(), out newDictionary); return CreateTakingOwnershipOfDisposableValue(newDictionary); } @@ -137,7 +137,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey, - out godot_variant value)) + out godot_variant value).ToBool()) { using (value) return Marshaling.variant_to_mono_object(&value); @@ -165,7 +165,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey)) + if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool()) throw new ArgumentException("An element with the same key already exists", nameof(key)); using godot_variant variantValue = Marshaling.mono_object_to_variant(value); @@ -185,7 +185,7 @@ namespace Godot.Collections public unsafe bool Contains(object key) { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey); + return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool(); } /// @@ -432,7 +432,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant value)) + &variantKey, out godot_variant value).ToBool()) { using (value) return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); @@ -513,7 +513,7 @@ namespace Godot.Collections public unsafe bool Remove(TKey key) { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey).ToBool(); } /// @@ -526,7 +526,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue); + &variantKey, out godot_variant retValue).ToBool(); using (retValue) { @@ -566,7 +566,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue); + &variantKey, out godot_variant retValue).ToBool(); using (retValue) { @@ -574,7 +574,7 @@ namespace Godot.Collections return false; using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); - return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue); + return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool(); } } @@ -610,7 +610,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue); + &variantKey, out godot_variant retValue).ToBool(); using (retValue) { @@ -618,8 +618,11 @@ namespace Godot.Collections return false; using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); - if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue)) - return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool()) + { + return NativeFuncs.godotsharp_dictionary_remove_key( + ref _underlyingDict.NativeValue, &variantKey).ToBool(); + } return false; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index 5f84bb530f..e8cfb8e1b1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -1,12 +1,24 @@ +using System; +using System.Runtime.InteropServices; +using Godot.NativeInterop; + namespace Godot { public static class Dispatcher { internal static GodotTaskScheduler DefaultGodotTaskScheduler; - private static void InitializeDefaultGodotTaskScheduler() + [UnmanagedCallersOnly] + internal static void InitializeDefaultGodotTaskScheduler() { - DefaultGodotTaskScheduler = new GodotTaskScheduler(); + try + { + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } } public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index b939da8778..17bca19fab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -24,14 +24,16 @@ namespace Godot if (nativeBase) { // Native type - var field = typeOfT.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic); + var field = typeOfT.GetField("NativeName", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); var nativeName = (StringName)field!.GetValue(null); godot_string_name nativeNameAux = nativeName.NativeValue; godot_array inputAux = array.NativeValue; godot_array filteredArray; - godotsharp_array_filter_godot_objects_by_native(&nativeNameAux, &inputAux, &filteredArray); + NativeFuncs.godotsharp_array_filter_godot_objects_by_native( + &nativeNameAux, &inputAux, &filteredArray); return Array.CreateTakingOwnershipOfDisposableValue(filteredArray); } else @@ -39,7 +41,7 @@ namespace Godot // Custom derived type godot_array inputAux = array.NativeValue; godot_array filteredArray; - godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); + NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray); @@ -62,13 +64,5 @@ namespace Godot return resWrapped; } } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern unsafe void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, - godot_array* p_input, godot_array* r_output); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern unsafe void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, - godot_array* r_output); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index f428100ff7..39271d3daf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -30,7 +30,7 @@ namespace Godot { using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); using godot_variant ret = default; - NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects, &ret); + NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects.ToGodotBool(), &ret); return Marshaling.variant_to_mono_object(&ret); } @@ -561,7 +561,7 @@ namespace Godot { using var variant = Marshaling.mono_object_to_variant(var); using godot_packed_byte_array varBytes = default; - NativeFuncs.godotsharp_var2bytes(&variant, fullObjects, &varBytes); + NativeFuncs.godotsharp_var2bytes(&variant, fullObjects.ToGodotBool(), &varBytes); using (varBytes) return Marshaling.PackedByteArray_to_mono_array(&varBytes); } 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..2830d9c527 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -0,0 +1,74 @@ +using System; + +namespace Godot.NativeInterop +{ + internal static class ExceptionUtils + { + public static void PushError(string message) + { + GD.PushError(message); + } + + private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog) + { + // This better not throw + PushError("Exception thrown when trying to log another exception..."); + PushError("Exception:"); + PushError(exceptionToLog.ToString()); + PushError("Logger exception:"); + PushError(loggerException.ToString()); + } + + public static void DebugPrintUnhandledException(Exception e) + { + try + { + // TODO Not implemented (debug_print_unhandled_exception) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + + public static void DebugSendUnhandledExceptionError(Exception e) + { + try + { + // TODO Not implemented (debug_send_unhandled_exception_error) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + + public static void DebugUnhandledException(Exception e) + { + try + { + // TODO Not implemented (debug_unhandled_exception) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + + public static void PrintUnhandledException(Exception e) + { + try + { + // TODO Not implemented (print_unhandled_exception) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index d8931f8348..0942d8f722 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -1,26 +1,36 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; #if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; - #endif +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Godot.NativeInterop { - [StructLayout(LayoutKind.Sequential)] - // ReSharper disable once InconsistentNaming - public struct godot_bool + internal static class GodotBoolExtensions { - public byte _value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_bool ToGodotBool(this bool @bool) + { + return *(godot_bool*)&@bool; + } - public unsafe godot_bool(bool value) => _value = *(byte*)&value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool ToBool(this godot_bool godotBool) + { + return *(bool*)&godotBool; + } + } - public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value; - public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool); + // 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)] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index 5d53006140..5779421c69 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -16,13 +16,14 @@ namespace Godot.NativeInterop return null; IntPtr gcHandlePtr; - bool has_cs_script_instance = false; + godot_bool has_cs_script_instance = false.ToGodotBool(); // First try to get the tied managed instance from a CSharpInstance script instance unsafe { - gcHandlePtr = unmanaged_get_script_instance_managed(unmanaged, &has_cs_script_instance); + gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed( + unmanaged, &has_cs_script_instance); } if (gcHandlePtr != IntPtr.Zero) @@ -30,12 +31,12 @@ namespace Godot.NativeInterop // Otherwise, if the object has a CSharpInstance script instance, return null - if (has_cs_script_instance) + if (has_cs_script_instance.ToBool()) return null; // If it doesn't have a CSharpInstance script instance, try with native instance bindings - gcHandlePtr = unmanaged_get_instance_binding_managed(unmanaged); + gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged); object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null; @@ -44,22 +45,12 @@ namespace Godot.NativeInterop // If the native instance binding GC handle target was collected, create a new one - gcHandlePtr = unmanaged_instance_binding_create_managed(unmanaged, gcHandlePtr); + gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed( + unmanaged, gcHandlePtr); return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null; } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe IntPtr unmanaged_get_script_instance_managed(IntPtr p_unmanaged, - bool* r_has_cs_script_instance); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, - IntPtr oldGCHandlePtr); - public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged, StringName nativeName, bool refCounted, Type type, Type nativeType) { @@ -70,30 +61,22 @@ namespace Godot.NativeInterop unsafe { godot_string_name nativeNameAux = nativeName.NativeValue; - internal_tie_native_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, - &nativeNameAux, refCounted); + NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged( + GCHandle.ToIntPtr(gcHandle), unmanaged, &nativeNameAux, refCounted.ToGodotBool()); } } else { - IntPtr scriptPtr = internal_new_csharp_script(); + IntPtr scriptPtr = NativeFuncs.godotsharp_internal_new_csharp_script(); ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type); // IMPORTANT: This must be called after AddScriptWithTypeBridge - internal_tie_user_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, - scriptPtr, refCounted); + NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged( + GCHandle.ToIntPtr(gcHandle), unmanaged, scriptPtr, refCounted.ToGodotBool()); } } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, - IntPtr unmanaged, godot_string_name* nativeName, bool refCounted); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, - IntPtr unmanaged, IntPtr scriptPtr, bool refCounted); - public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged, Type type, Type nativeType) { @@ -101,16 +84,10 @@ namespace Godot.NativeInterop return; var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal); - internal_tie_managed_to_unmanaged_with_pre_setup(GCHandle.ToIntPtr(strongGCHandle), unmanaged); + NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( + GCHandle.ToIntPtr(strongGCHandle), unmanaged); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_tie_managed_to_unmanaged_with_pre_setup( - IntPtr gcHandleIntPtr, IntPtr unmanaged); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr internal_new_csharp_script(); - public static unsafe Object EngineGetSingleton(string name) { using godot_string src = Marshaling.mono_string_to_godot(name); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index eae644af85..74232425bb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -420,44 +421,18 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) { // TODO: Validate key and value types are compatible with Variant -#if NET - Collections.IGenericGodotDictionary genericGodotDictionary = - IDictionaryToGenericGodotDictionary((dynamic)p_obj); -#else - var genericArguments = type.GetGenericArguments(); - - // With .NET Standard we need a package reference for Microsoft.CSharp in order to - // use dynamic, so we have this workaround for now until we switch to .NET 5/6. - var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary), - BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! - .MakeGenericMethod(genericArguments[0], genericArguments[1]); + var godotDict = new Collections.Dictionary(); - var genericGodotDictionary = (Collections.IGenericGodotDictionary)method - .Invoke(null, new[] { p_obj }); -#endif + foreach (KeyValuePair entry in (IDictionary)p_obj) + godotDict.Add(entry.Key, entry.Value); - var godotDict = genericGodotDictionary.UnderlyingDictionary; - if (godotDict == null) - return new godot_variant(); return VariantUtils.CreateFromDictionary(godotDict.NativeValue); } if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { // TODO: Validate element type is compatible with Variant -#if NET - var nativeGodotArray = - (godot_array)mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); -#else - // With .NET Standard we need a package reference for Microsoft.CSharp in order to - // use dynamic, so we have this workaround for now until we switch to .NET 5/6. - // Also CollectionsMarshal.AsSpan is not available with .NET Standard. - - var collection = (System.Collections.ICollection)p_obj; - var array = new object[collection.Count]; - collection.CopyTo(array, 0); - var nativeGodotArray = mono_array_to_Array(array); -#endif + var nativeGodotArray = mono_array_to_Array((IList)p_obj); return VariantUtils.CreateFromArray(&nativeGodotArray); } } @@ -478,9 +453,6 @@ namespace Godot.NativeInterop } } - private static Collections.Dictionary IDictionaryToGenericGodotDictionary - (IDictionary dictionary) => new(dictionary); - public static unsafe string variant_to_mono_string(godot_variant* p_var) { switch ((*p_var)._type) @@ -855,7 +827,7 @@ namespace Godot.NativeInterop switch ((*p_var)._type) { case Variant.Type.Bool: - return (bool)(*p_var)._data._bool; + return (*p_var)._data._bool.ToBool(); case Variant.Type.Int: return (*p_var)._data._int; case Variant.Type.Float: @@ -1058,7 +1030,7 @@ namespace Godot.NativeInterop godot_string_name name; if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( - p_callable, &delegateGCHandle, &godotObject, &name)) + p_callable, &delegateGCHandle, &godotObject, &name).ToBool()) { if (delegateGCHandle != IntPtr.Zero) { @@ -1141,15 +1113,37 @@ namespace Godot.NativeInterop return ret; } - public static godot_array mono_array_to_Array(Span p_array) + public static godot_array mono_array_to_Array(object[] p_array) { - if (p_array.IsEmpty) + int length = p_array.Length; + + if (length == 0) return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); - array.Resize(p_array.Length); + array.Resize(length); - for (int i = 0; i < p_array.Length; i++) + for (int i = 0; i < length; i++) + array[i] = p_array[i]; + + godot_array src = array.NativeValue; + unsafe + { + return NativeFuncs.godotsharp_array_new_copy(&src); + } + } + + public static godot_array mono_array_to_Array(IList p_array) + { + int length = p_array.Count; + + if (length == 0) + return NativeFuncs.godotsharp_array_new(); + + using var array = new Collections.Array(); + array.Resize(length); + + for (int i = 0; i < length; i++) array[i] = p_array[i]; godot_array src = array.NativeValue; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 73ac837fe1..8bc785f375 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -20,20 +20,61 @@ namespace Godot.NativeInterop public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); -#if NET [DllImport(GodotDllName)] - public static extern delegate* unmanaged godotsharp_get_class_constructor(ref godot_string_name p_classname); -#else - // Workaround until we switch to .NET 5/6 + public static extern delegate* unmanaged godotsharp_get_class_constructor( + ref godot_string_name p_classname); + [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname); + public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func); -#endif + internal static extern void godotsharp_internal_object_disposed(IntPtr ptr); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); + internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, godot_bool isFinalizer); + + [DllImport(GodotDllName)] + internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj, + godot_string_name* eventSignal); + + [DllImport(GodotDllName)] + internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source, + ref godot_string_name signal, + IntPtr target, IntPtr awaiterHandlePtr); + + [DllImport(GodotDllName)] + public static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, godot_string_name* nativeName, godot_bool refCounted); + + [DllImport(GodotDllName)] + public static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, IntPtr scriptPtr, godot_bool refCounted); + + [DllImport(GodotDllName)] + public static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( + IntPtr gcHandleIntPtr, IntPtr unmanaged); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged, + godot_bool* r_has_cs_script_instance); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, + IntPtr oldGCHandlePtr); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_new_csharp_script(); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, + godot_array* p_input, godot_array* r_output); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, + godot_array* r_output); [DllImport(GodotDllName)] public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); @@ -509,12 +550,12 @@ namespace Godot.NativeInterop public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self); [DllImport(GodotDllName)] - public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); + public static extern godot_bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); // GD, etc [DllImport(GodotDllName)] - public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, bool p_allow_objects, + public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, godot_bool p_allow_objects, godot_variant* r_ret); [DllImport(GodotDllName)] @@ -578,7 +619,7 @@ namespace Godot.NativeInterop public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret); [DllImport(GodotDllName)] - public static extern void godotsharp_var2bytes(godot_variant* what, bool fullObjects, + public static extern void godotsharp_var2bytes(godot_variant* what, godot_bool fullObjects, godot_packed_byte_array* bytes); [DllImport(GodotDllName)] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 91ba864687..e52454a2e3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -8,46 +8,46 @@ namespace Godot.NativeInterop public static class VariantUtils { public static godot_variant CreateFromRID(RID from) - => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; + => new() { _type = Variant.Type.Rid, _data = { _m_rid = from } }; public static godot_variant CreateFromBool(bool from) - => new() {_type = Variant.Type.Bool, _data = {_bool = from}}; + => new() { _type = Variant.Type.Bool, _data = { _bool = from.ToGodotBool() } }; public static godot_variant CreateFromInt(long from) - => new() {_type = Variant.Type.Int, _data = {_int = from}}; + => new() { _type = Variant.Type.Int, _data = { _int = from } }; public static godot_variant CreateFromInt(ulong from) - => new() {_type = Variant.Type.Int, _data = {_int = (long)from}}; + => new() { _type = Variant.Type.Int, _data = { _int = (long)from } }; public static godot_variant CreateFromFloat(double from) - => new() {_type = Variant.Type.Float, _data = {_float = from}}; + => new() { _type = Variant.Type.Float, _data = { _float = from } }; public static godot_variant CreateFromVector2(Vector2 from) - => new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}}; + => new() { _type = Variant.Type.Vector2, _data = { _m_vector2 = from } }; public static godot_variant CreateFromVector2i(Vector2i from) - => new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}}; + => new() { _type = Variant.Type.Vector2i, _data = { _m_vector2i = from } }; public static godot_variant CreateFromVector3(Vector3 from) - => new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}}; + => new() { _type = Variant.Type.Vector3, _data = { _m_vector3 = from } }; public static godot_variant CreateFromVector3i(Vector3i from) - => new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}}; + => new() { _type = Variant.Type.Vector3i, _data = { _m_vector3i = from } }; public static godot_variant CreateFromRect2(Rect2 from) - => new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}}; + => new() { _type = Variant.Type.Rect2, _data = { _m_rect2 = from } }; public static godot_variant CreateFromRect2i(Rect2i from) - => new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}}; + => new() { _type = Variant.Type.Rect2i, _data = { _m_rect2i = from } }; public static godot_variant CreateFromQuaternion(Quaternion from) - => new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}}; + => new() { _type = Variant.Type.Quaternion, _data = { _m_quaternion = from } }; public static godot_variant CreateFromColor(Color from) - => new() {_type = Variant.Type.Color, _data = {_m_color = from}}; + => new() { _type = Variant.Type.Color, _data = { _m_color = from } }; public static godot_variant CreateFromPlane(Plane from) - => new() {_type = Variant.Type.Plane, _data = {_m_plane = from}}; + => new() { _type = Variant.Type.Plane, _data = { _m_plane = from } }; public static unsafe godot_variant CreateFromTransform2D(Transform2D from) { @@ -100,15 +100,15 @@ namespace Godot.NativeInterop // Explicit name to make it very clear public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from) - => new() {_type = Variant.Type.Callable, _data = {_m_callable = from}}; + => new() { _type = Variant.Type.Callable, _data = { _m_callable = from } }; // Explicit name to make it very clear public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from) - => new() {_type = Variant.Type.Signal, _data = {_m_signal = from}}; + => new() { _type = Variant.Type.Signal, _data = { _m_signal = from } }; // Explicit name to make it very clear public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from) - => new() {_type = Variant.Type.String, _data = {_m_string = from}}; + => new() { _type = Variant.Type.String, _data = { _m_string = from } }; public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from) { @@ -223,61 +223,97 @@ namespace Godot.NativeInterop // We avoid the internal call if the stored type is the same we want. public static unsafe bool ConvertToBool(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var); + => (*p_var)._type == Variant.Type.Bool ? + (*p_var)._data._bool.ToBool() : + NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool(); public static unsafe char ConvertToChar(godot_variant* p_var) - => (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (char)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe sbyte ConvertToInt8(godot_variant* p_var) - => (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (sbyte)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe Int16 ConvertToInt16(godot_variant* p_var) - => (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (Int16)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe Int32 ConvertToInt32(godot_variant* p_var) - => (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (Int32)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe Int64 ConvertToInt64(godot_variant* p_var) => (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var); public static unsafe byte ConvertToUInt8(godot_variant* p_var) - => (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (byte)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var) - => (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (UInt16)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var) - => (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (UInt32)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var) - => (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (UInt64)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe float ConvertToFloat32(godot_variant* p_var) - => (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var)); + => (float)((*p_var)._type == Variant.Type.Float ? + (*p_var)._data._float : + NativeFuncs.godotsharp_variant_as_float(p_var)); public static unsafe double ConvertToFloat64(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var); + => (*p_var)._type == Variant.Type.Float ? + (*p_var)._data._float : + NativeFuncs.godotsharp_variant_as_float(p_var); public static unsafe Vector2 ConvertToVector2(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var); + => (*p_var)._type == Variant.Type.Vector2 ? + (*p_var)._data._m_vector2 : + NativeFuncs.godotsharp_variant_as_vector2(p_var); public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var); + => (*p_var)._type == Variant.Type.Vector2i ? + (*p_var)._data._m_vector2i : + NativeFuncs.godotsharp_variant_as_vector2i(p_var); public static unsafe Rect2 ConvertToRect2(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var); + => (*p_var)._type == Variant.Type.Rect2 ? + (*p_var)._data._m_rect2 : + NativeFuncs.godotsharp_variant_as_rect2(p_var); public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var); + => (*p_var)._type == Variant.Type.Rect2i ? + (*p_var)._data._m_rect2i : + NativeFuncs.godotsharp_variant_as_rect2i(p_var); public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var); + => (*p_var)._type == Variant.Type.Transform2d ? + *(*p_var)._data._transform2d : + NativeFuncs.godotsharp_variant_as_transform2d(p_var); public static unsafe Vector3 ConvertToVector3(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var); + => (*p_var)._type == Variant.Type.Vector3 ? + (*p_var)._data._m_vector3 : + NativeFuncs.godotsharp_variant_as_vector3(p_var); public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var); + => (*p_var)._type == Variant.Type.Vector3i ? + (*p_var)._data._m_vector3i : + NativeFuncs.godotsharp_variant_as_vector3i(p_var); public static unsafe Vector4 ConvertToVector4(godot_variant* p_var) => (*p_var)._type == Variant.Type.Vector4 ? *(*p_var)._data._vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); @@ -286,31 +322,45 @@ namespace Godot.NativeInterop => (*p_var)._type == Variant.Type.Vector4i ? *(*p_var)._data._vector4i : NativeFuncs.godotsharp_variant_as_vector4i(p_var); public static unsafe Basis ConvertToBasis(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var); + => (*p_var)._type == Variant.Type.Basis ? + *(*p_var)._data._basis : + NativeFuncs.godotsharp_variant_as_basis(p_var); public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var); + => (*p_var)._type == Variant.Type.Quaternion ? + (*p_var)._data._m_quaternion : + NativeFuncs.godotsharp_variant_as_quaternion(p_var); public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var); + => (*p_var)._type == Variant.Type.Transform3d ? + *(*p_var)._data._transform3d : + NativeFuncs.godotsharp_variant_as_transform3d(p_var); public static unsafe Projection ConvertToProjection(godot_variant* p_var) => (*p_var)._type == Variant.Type.Projection ? *(*p_var)._data._projection : NativeFuncs.godotsharp_variant_as_projection(p_var); public static unsafe AABB ConvertToAABB(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var); + => (*p_var)._type == Variant.Type.Aabb ? + *(*p_var)._data._aabb : + NativeFuncs.godotsharp_variant_as_aabb(p_var); public static unsafe Color ConvertToColor(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var); + => (*p_var)._type == Variant.Type.Color ? + (*p_var)._data._m_color : + NativeFuncs.godotsharp_variant_as_color(p_var); public static unsafe Plane ConvertToPlane(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var); + => (*p_var)._type == Variant.Type.Plane ? + (*p_var)._data._m_plane : + NativeFuncs.godotsharp_variant_as_plane(p_var); public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var) => (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero; public static unsafe RID ConvertToRID(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var); + => (*p_var)._type == Variant.Type.Rid ? + (*p_var)._data._m_rid : + NativeFuncs.godotsharp_variant_as_rid(p_var); public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var) => (*p_var)._type == Variant.Type.StringName ? diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 824f29558f..b18606b47e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -263,7 +263,7 @@ namespace Godot /// If the is an absolute path. public bool IsAbsolute() { - return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue); + return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue).ToBool(); } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 763483a11f..98266ffdfc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -21,14 +21,11 @@ namespace Godot { if (NativePtr == IntPtr.Zero) { -#if NET unsafe { NativePtr = NativeCtor(); } -#else - NativePtr = _gd__invoke_class_constructor(NativeCtor); -#endif + InteropUtils.TieManagedToUnmanaged(this, NativePtr, NativeName, refCounted: false, GetType(), _cachedType); } @@ -58,7 +55,7 @@ namespace Godot { using var eventSignalName = new StringName(eventSignal.Name); godot_string_name eventSignalNameAux = eventSignalName.NativeValue; - godot_icall_Object_ConnectEventSignal(NativePtr, &eventSignalNameAux); + NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, &eventSignalNameAux); } } @@ -114,14 +111,14 @@ namespace Godot if (MemoryOwn) { MemoryOwn = false; - godot_icall_RefCounted_Disposed(NativePtr, !disposing); + NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool()); } else { - godot_icall_Object_Disposed(NativePtr); + NativeFuncs.godotsharp_internal_object_disposed(NativePtr); } - this.NativePtr = IntPtr.Zero; + NativePtr = IntPtr.Zero; } _disposed = true; @@ -391,7 +388,6 @@ namespace Godot return methodBind; } -#if NET internal static unsafe delegate* unmanaged ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' @@ -402,30 +398,5 @@ namespace Godot return nativeConstructor; } -#else - internal static IntPtr ClassDB_get_constructor(StringName type) - { - // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); - - if (nativeConstructor == IntPtr.Zero) - throw new NativeConstructorNotFoundException(type); - - return nativeConstructor; - } - - internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr) - => NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); -#endif - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Object_Disposed(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_RefCounted_Disposed(IntPtr ptr, bool isFinalizer); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe void godot_icall_Object_ConnectEventSignal(IntPtr obj, - godot_string_name* eventSignal); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index fd6636e410..62dec81582 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Godot.NativeInterop; @@ -13,14 +12,10 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, + NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this))); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, - IntPtr target, IntPtr awaiterHandlePtr); - public bool IsCompleted => _completed; public void OnCompleted(Action action) @@ -32,30 +27,38 @@ namespace Godot public IAwaiter GetAwaiter() => this; - internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, - godot_variant** args, int argCount, - bool* r_awaiterIsNull) + [UnmanagedCallersOnly] + internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount, + godot_bool* outAwaiterIsNull) { - var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target; - - if (awaiter == null) + try { - *r_awaiterIsNull = true; - return; - } + var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target; + + if (awaiter == null) + { + *outAwaiterIsNull = true.ToGodotBool(); + return; + } - *r_awaiterIsNull = false; + *outAwaiterIsNull = false.ToGodotBool(); - awaiter._completed = true; + awaiter._completed = true; - object[] signalArgs = new object[argCount]; + object[] signalArgs = new object[argCount]; - for (int i = 0; i < argCount; i++) - signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); + for (int i = 0; i < argCount; i++) + signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); - awaiter._result = signalArgs; + awaiter._result = signalArgs; - awaiter._action?.Invoke(); + awaiter._action?.Invoke(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + *outAwaiterIsNull = false.ToGodotBool(); + } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 6a529de99b..763ded8809 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -4,7 +4,7 @@ bin/$(Configuration) false Godot - netstandard2.1 + net5.0 $(OutputPath)/$(AssemblyName).xml false true @@ -34,6 +34,7 @@ + @@ -58,6 +59,7 @@ + diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs index da6f293871..dbd98d0afb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("GodotSharpEditor")] +[assembly: InternalsVisibleTo("GodotPlugins")] diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index 1082c74448..c0c1b91dc9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -4,7 +4,7 @@ bin/$(Configuration) false Godot - netstandard2.1 + net5.0 $(OutputPath)/$(AssemblyName).xml false true -- cgit v1.2.3 From e5e7a795b14487e7eb0cfb011a8e0518769ce533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Tue, 28 Dec 2021 23:25:16 +0100 Subject: C#: Code cleanup and greatly reduce use of C# pointers --- modules/mono/glue/GodotSharp/.editorconfig | 8 + .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 142 +++-- .../mono/glue/GodotSharp/GodotSharp/Core/Basis.cs | 8 +- .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 31 +- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 41 +- .../glue/GodotSharp/GodotSharp/Core/Callable.cs | 66 +- .../mono/glue/GodotSharp/GodotSharp/Core/Color.cs | 8 +- .../GodotSharp/GodotSharp/Core/DebuggingUtils.cs | 14 +- .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 6 +- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 205 +++--- .../GodotSharp/Core/Extensions/ObjectExtensions.cs | 15 +- .../Core/Extensions/SceneTreeExtensions.cs | 18 +- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 115 ++-- .../Core/NativeInterop/InteropStructs.cs | 692 ++++++++++++++++++--- .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 27 +- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 599 +++++++++--------- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 364 ++++++----- .../Core/NativeInterop/NativeFuncs.extended.cs | 61 +- .../Core/NativeInterop/NativeVariantPtrArgs.cs | 20 + .../Core/NativeInterop/VariantSpanHelpers.cs | 10 +- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 350 +++++------ .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 70 ++- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 53 +- .../glue/GodotSharp/GodotSharp/Core/Quaternion.cs | 4 +- .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 14 +- .../glue/GodotSharp/GodotSharp/Core/SignalInfo.cs | 2 +- .../GodotSharp/GodotSharp/Core/StringExtensions.cs | 75 +-- .../glue/GodotSharp/GodotSharp/Core/StringName.cs | 22 +- .../glue/GodotSharp/GodotSharp/Core/Transform2D.cs | 4 +- .../glue/GodotSharp/GodotSharp/Core/Transform3D.cs | 12 +- .../glue/GodotSharp/GodotSharp/Core/Vector2.cs | 4 +- .../glue/GodotSharp/GodotSharp/Core/Vector2i.cs | 4 +- .../glue/GodotSharp/GodotSharp/Core/Vector3.cs | 4 +- .../glue/GodotSharp/GodotSharp/Core/Vector3i.cs | 4 +- .../glue/GodotSharp/GodotSharp/FodyWeavers.xml | 3 + .../GodotSharp/GenerateGodotCustomUnsafe.targets | 93 +++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 9 + 37 files changed, 1968 insertions(+), 1209 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/.editorconfig create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/FodyWeavers.xml create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/GenerateGodotCustomUnsafe.targets (limited to 'modules/mono/glue/GodotSharp') 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/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 2aa2ece803..9fa221a0cc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot.Collections @@ -14,14 +15,14 @@ namespace Godot.Collections /// public sealed class Array : IList, IDisposable { - public godot_array NativeValue; + internal godot_array.movable NativeValue; /// /// Constructs a new empty . /// public Array() { - NativeValue = NativeFuncs.godotsharp_array_new(); + NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); } /// @@ -32,7 +33,7 @@ namespace Godot.Collections public Array(IEnumerable collection) : this() { if (collection == null) - throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + throw new ArgumentNullException(nameof(collection)); foreach (object element in collection) Add(element); @@ -47,9 +48,9 @@ namespace Godot.Collections public Array(params object[] array) : this() { if (array == null) - throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); + throw new ArgumentNullException(nameof(array)); - NativeValue = NativeFuncs.godotsharp_array_new(); + NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); int length = array.Length; Resize(length); @@ -60,7 +61,9 @@ namespace Godot.Collections private Array(godot_array nativeValueToOwn) { - NativeValue = nativeValueToOwn; + NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ? + nativeValueToOwn : + NativeFuncs.godotsharp_array_new()); } // Explicit name to make it very clear @@ -84,7 +87,7 @@ namespace Godot.Collections public void Dispose(bool disposing) { // Always dispose `NativeValue` even if disposing is true - NativeValue.Dispose(); + NativeValue.DangerousSelfRef.Dispose(); } /// @@ -95,7 +98,8 @@ namespace Godot.Collections public Array Duplicate(bool deep = false) { godot_array newArray; - NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep.ToGodotBool(), out newArray); + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_duplicate(ref self, deep.ToGodotBool(), out newArray); return CreateTakingOwnershipOfDisposableValue(newArray); } @@ -104,12 +108,20 @@ namespace Godot.Collections /// /// The new size of the array. /// if successful, or an error code. - public Error Resize(int newSize) => NativeFuncs.godotsharp_array_resize(ref NativeValue, newSize); + public Error Resize(int newSize) + { + var self = (godot_array)NativeValue; + return NativeFuncs.godotsharp_array_resize(ref self, newSize); + } /// /// Shuffles the contents of this into a random order. /// - public void Shuffle() => NativeFuncs.godotsharp_array_shuffle(ref NativeValue); + public void Shuffle() + { + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_shuffle(ref self); + } /// /// Concatenates these two s. @@ -119,6 +131,17 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { + if (left == null) + { + if (right == null) + return new Array(); + + return right.Duplicate(deep: false); + } + + if (right == null) + return left.Duplicate(deep: false); + int leftCount = left.Count; int rightCount = right.Count; @@ -146,14 +169,15 @@ namespace Godot.Collections get { GetVariantBorrowElementAt(index, out godot_variant borrowElem); - return Marshaling.variant_to_mono_object(&borrowElem); + return Marshaling.ConvertVariantToManagedObject(borrowElem); } set { if (index < 0 || index >= Count) - throw new IndexOutOfRangeException(); - godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref NativeValue); - ptrw[index] = Marshaling.mono_object_to_variant(value); + throw new ArgumentOutOfRangeException(nameof(index)); + var self = (godot_array)NativeValue; + godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); + ptrw[index] = Marshaling.ConvertManagedObjectToVariant(value); } } @@ -163,10 +187,11 @@ namespace Godot.Collections /// /// The object to add. /// The new size after adding the object. - public unsafe int Add(object value) + public int Add(object value) { - using godot_variant variantValue = Marshaling.mono_object_to_variant(value); - return NativeFuncs.godotsharp_array_add(ref NativeValue, &variantValue); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + var self = (godot_array)NativeValue; + return NativeFuncs.godotsharp_array_add(ref self, variantValue); } /// @@ -187,10 +212,11 @@ namespace Godot.Collections /// /// The object to search for. /// The index of the object, or -1 if not found. - public unsafe int IndexOf(object value) + public int IndexOf(object value) { - using godot_variant variantValue = Marshaling.mono_object_to_variant(value); - return NativeFuncs.godotsharp_array_index_of(ref NativeValue, &variantValue); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + var self = (godot_array)NativeValue; + return NativeFuncs.godotsharp_array_index_of(ref self, variantValue); } /// @@ -201,13 +227,14 @@ namespace Godot.Collections /// /// The index to insert at. /// The object to insert. - public unsafe void Insert(int index, object value) + public void Insert(int index, object value) { if (index < 0 || index > Count) - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); - using godot_variant variantValue = Marshaling.mono_object_to_variant(value); - NativeFuncs.godotsharp_array_insert(ref NativeValue, index, &variantValue); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_insert(ref self, index, variantValue); } /// @@ -229,9 +256,10 @@ namespace Godot.Collections public void RemoveAt(int index) { if (index < 0 || index > Count) - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); - NativeFuncs.godotsharp_array_remove_at(ref NativeValue, index); + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_remove_at(ref self, index); } // ICollection @@ -241,7 +269,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// /// The number of elements. - public int Count => NativeValue.Size; + public int Count => NativeValue.DangerousSelfRef.Size; object ICollection.SyncRoot => this; @@ -252,21 +280,21 @@ namespace Godot.Collections /// untyped C# array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int destIndex) + /// The index to start at. + public void CopyTo(System.Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (destIndex < 0) + if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(destIndex), + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); } int count = Count; - if (array.Length < (destIndex + count)) + if (array.Length < (index + count)) { throw new ArgumentException( "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); @@ -276,9 +304,9 @@ namespace Godot.Collections { for (int i = 0; i < count; i++) { - object obj = Marshaling.variant_to_mono_object(&(*NativeValue._p)._arrayVector._ptr[i]); - array.SetValue(obj, destIndex); - destIndex++; + object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]); + array.SetValue(obj, index); + index++; } } } @@ -303,11 +331,12 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this array. - public override unsafe string ToString() + public override string ToString() { - using godot_string str = default; - NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str); - return Marshaling.mono_string_from_godot(str); + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_to_string(ref self, out godot_string str); + using (str) + return Marshaling.ConvertStringToManaged(str); } /// @@ -316,7 +345,7 @@ namespace Godot.Collections internal void GetVariantBorrowElementAt(int index, out godot_variant elem) { if (index < 0 || index >= Count) - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); GetVariantBorrowElementAtUnchecked(index, out elem); } @@ -325,7 +354,7 @@ namespace Godot.Collections /// internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem) { - elem = (*NativeValue._p)._arrayVector._ptr[index]; + elem = NativeValue.DangerousSelfRef.Elements[index]; } } @@ -344,11 +373,16 @@ namespace Godot.Collections /// /// The type of the array. [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] + [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")] public sealed class Array : IList, ICollection, IEnumerable, IGenericGodotArray { private readonly Array _underlyingArray; - internal ref godot_array NativeValue => ref _underlyingArray.NativeValue; + internal ref godot_array.movable NativeValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _underlyingArray.NativeValue; + } // ReSharper disable StaticMemberInGenericType // Warning is about unique static fields being created for each generic type combination: @@ -376,7 +410,7 @@ namespace Godot.Collections public Array(IEnumerable collection) { if (collection == null) - throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + throw new ArgumentNullException(nameof(collection)); _underlyingArray = new Array(collection); } @@ -389,9 +423,7 @@ namespace Godot.Collections public Array(params T[] array) : this() { if (array == null) - { - throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); - } + throw new ArgumentNullException(nameof(array)); _underlyingArray = new Array(array); } @@ -415,7 +447,7 @@ namespace Godot.Collections /// The typed array to convert. public static explicit operator Array(Array from) { - return from._underlyingArray; + return from?._underlyingArray; } /// @@ -454,6 +486,17 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { + if (left == null) + { + if (right == null) + return new Array(); + + return right.Duplicate(deep: false); + } + + if (right == null) + return left.Duplicate(deep: false); + return new Array(left._underlyingArray + right._underlyingArray); } @@ -468,10 +511,7 @@ namespace Godot.Collections get { _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); - unsafe - { - return (T)Marshaling.variant_to_mono_object_of_type(&borrowElem, TypeOfElements); - } + return (T)Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, TypeOfElements); } set => _underlyingArray[index] = value; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 437878818c..e1e2df199b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -167,7 +167,7 @@ namespace Godot case 2: return Column2; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(column)); } } set @@ -184,7 +184,7 @@ namespace Godot Column2 = value; return; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(column)); } } } @@ -386,7 +386,7 @@ namespace Godot case 2: return Row2; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } @@ -413,7 +413,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/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index db27989bdb..6bb1ba27e9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -18,22 +18,24 @@ namespace Godot.Bridge if (godotObject == null) { *ret = default; - (*refCallError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; + (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; return false.ToGodotBool(); } - using godot_string dest = default; - NativeFuncs.godotsharp_string_name_as_string(&dest, method); - string methodStr = Marshaling.mono_string_from_godot(dest); + NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, CustomUnsafe.AsRef(method)); + string methodStr; + using (dest) + methodStr = Marshaling.ConvertStringToManaged(dest); - bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant retValue); + bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, 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; + (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; return false.ToGodotBool(); } @@ -60,12 +62,15 @@ namespace Godot.Bridge throw new InvalidOperationException(); var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(name)); + NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); - if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) + if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection( + nameManaged.ToString(), CustomUnsafe.AsRef(value))) + { return true.ToGodotBool(); + } - object valueManaged = Marshaling.variant_to_mono_object(value); + object valueManaged = Marshaling.ConvertVariantToManagedObject(CustomUnsafe.AsRef(value)); return godotObject._Set(nameManaged, valueManaged).ToGodotBool(); } @@ -89,10 +94,10 @@ namespace Godot.Bridge throw new InvalidOperationException(); var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(name)); + NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), - out godot_variant outRetValue)) + out godot_variant outRetValue)) { *outRet = outRetValue; return true.ToGodotBool(); @@ -106,7 +111,7 @@ namespace Godot.Bridge return false.ToGodotBool(); } - *outRet = Marshaling.mono_object_to_variant(ret); + *outRet = Marshaling.ConvertManagedObjectToVariant(ret); return true.ToGodotBool(); } catch (Exception e) @@ -158,7 +163,7 @@ namespace Godot.Bridge return; } - *outRes = Marshaling.mono_string_to_godot(resultStr); + *outRes = Marshaling.ConvertStringToNative(resultStr); *outValid = true.ToGodotBool(); } catch (Exception e) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 9655887e52..689d6cddbb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -104,8 +104,8 @@ namespace Godot.Bridge for (int i = 0; i < paramCount; i++) { - invokeParams[i] = Marshaling.variant_to_mono_object_of_type( - args[i], parameters[i].ParameterType); + invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType( + *args[i], parameters[i].ParameterType); } ctor.Invoke(obj, invokeParams); @@ -149,7 +149,7 @@ namespace Godot.Bridge return; } - *outRes = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + *outRes = NativeFuncs.godotsharp_string_name_new_copy((godot_string_name)nativeName.NativeValue); } catch (Exception e) { @@ -177,7 +177,7 @@ namespace Godot.Bridge { // Performance is not critical here as this will be replaced with a generated dictionary. using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(nativeTypeName)); + NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(nativeTypeName))); string nativeTypeNameStr = stringName.ToString(); if (nativeTypeNameStr[0] == '_') @@ -277,7 +277,8 @@ namespace Godot.Bridge *outOwnerIsNull = false.ToGodotBool(); - owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + owner.InternalRaiseEventSignal(CustomUnsafe.AsRef(eventSignalName), + new NativeVariantPtrArgs(args), argCount); } catch (Exception e) { @@ -302,9 +303,10 @@ namespace Godot.Bridge // Legacy signals foreach (var signalDelegate in top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any())) + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | + BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any())) { var invokeMethod = signalDelegate.GetMethod("Invoke"); @@ -315,7 +317,7 @@ namespace Godot.Bridge foreach (var parameters in invokeMethod.GetParameters()) { - var paramType = Marshaling.managed_to_variant_type( + var paramType = Marshaling.ConvertManagedTypeToVariantType( parameters.ParameterType, out bool nilIsVariant); signalParams.Add(new Dictionary() { @@ -341,8 +343,8 @@ namespace Godot.Bridge BindingFlags.NonPublic | BindingFlags.Public); foreach (var eventSignalField in fields - .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) - .Where(f => foundEventSignals.Contains(f.Name))) + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name))) { var delegateType = eventSignalField.FieldType; var invokeMethod = delegateType.GetMethod("Invoke"); @@ -354,7 +356,7 @@ namespace Godot.Bridge foreach (var parameters in invokeMethod.GetParameters()) { - var paramType = Marshaling.managed_to_variant_type( + var paramType = Marshaling.ConvertManagedTypeToVariantType( parameters.ParameterType, out bool nilIsVariant); signalParams.Add(new Dictionary() { @@ -370,7 +372,7 @@ namespace Godot.Bridge top = top.BaseType; } - *outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); + *outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy((godot_dictionary)signals.NativeValue); } catch (Exception e) { @@ -387,7 +389,7 @@ namespace Godot.Bridge // Performance is not critical here as this will be replaced with source generators. using var signals = new Dictionary(); - string signalNameStr = Marshaling.mono_string_from_godot(*signalName); + string signalNameStr = Marshaling.ConvertStringToManaged(*signalName); Type top = _scriptBridgeMap[scriptPtr]; Type native = Object.InternalGetClassNativeBase(top); @@ -401,7 +403,7 @@ namespace Godot.Bridge .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any()) .Any(signalDelegate => signalDelegate.Name == signalNameStr) - ) + ) { return true.ToGodotBool(); } @@ -413,7 +415,7 @@ namespace Godot.Bridge BindingFlags.NonPublic | BindingFlags.Public) .Where(ev => ev.GetCustomAttributes().OfType().Any()) .Any(eventSignal => eventSignal.Name == signalNameStr) - ) + ) { return true.ToGodotBool(); } @@ -440,7 +442,7 @@ namespace Godot.Bridge if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) return false.ToGodotBool(); - string methodStr = Marshaling.mono_string_from_godot(*method); + string methodStr = Marshaling.ConvertStringToManaged(*method); if (deep.ToBool()) { @@ -517,7 +519,7 @@ namespace Godot.Bridge { try { - string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); + string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath); if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) return false.ToGodotBool(); @@ -612,7 +614,8 @@ namespace Godot.Bridge } *outRpcFunctionsDest = - NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); + NativeFuncs.godotsharp_dictionary_new_copy( + (godot_dictionary)((Dictionary)rpcFunctions).NativeValue); } catch (Exception e) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs index 2722b64e6d..ef75ff446a 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 /// } /// /// - 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. /// public Object Target => _target; + /// /// Name of the method that will be called. /// public StringName Method => _method; + /// /// Delegate of the method that will be called. /// @@ -73,15 +77,43 @@ namespace Godot _delegate = @delegate; } + private const int VarArgsSpanThreshold = 5; + /// /// Calls the method represented by this . /// Arguments can be passed and should match the method's signature. /// /// Arguments that will be passed to the method call. /// The value returned by the method. - public object Call(params object[] args) + public unsafe object Call(params object[] args) { - return godot_icall_Callable_Call(ref this, args); + using godot_callable callable = Marshaling.ConvertCallableToNative(this); + + int argc = args.Length; + + Span argsStoreSpan = argc <= VarArgsSpanThreshold ? + stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() : + new godot_variant.movable[argc]; + + Span 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] = Marshaling.ConvertManagedObjectToVariant(args[i]); + argsPtr[i] = new IntPtr(&varargs[i]); + } + + using godot_variant ret = NativeFuncs.godotsharp_callable_call(callable, + (godot_variant**)argsPtr, argc, out _); + return Marshaling.ConvertVariantToManagedObject(ret); + } } /// @@ -89,9 +121,33 @@ namespace Godot /// Arguments can be passed and should match the method's signature. /// /// Arguments that will be passed to the method call. - public void CallDeferred(params object[] args) + public unsafe void CallDeferred(params object[] args) { - godot_icall_Callable_CallDeferred(ref this, args); + using godot_callable callable = Marshaling.ConvertCallableToNative(this); + + int argc = args.Length; + + Span argsStoreSpan = argc <= VarArgsSpanThreshold ? + stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() : + new godot_variant.movable[argc]; + + Span 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] = Marshaling.ConvertManagedObjectToVariant(args[i]); + 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/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index e446b3db1c..39904b6154 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -18,7 +18,7 @@ namespace Godot else sb.Append(type); - sb.Append(" "); + sb.Append(' '); } [UnmanagedCallersOnly] @@ -55,15 +55,15 @@ namespace Godot if (methodBase is MethodInfo) sb.AppendTypeName(((MethodInfo)methodBase).ReturnType); - sb.Append(methodBase.DeclaringType.FullName); - sb.Append("."); + sb.Append(methodBase.DeclaringType?.FullName ?? ""); + 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++) { @@ -73,10 +73,10 @@ namespace Godot sb.AppendTypeName(genericParams[j]); } - sb.Append(">"); + sb.Append('>'); } - sb.Append("("); + sb.Append('('); bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0; @@ -93,7 +93,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 87c93e35f5..02ae392f47 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -48,13 +48,13 @@ namespace Godot for (uint i = 0; i < argc; i++) { - managedArgs[i] = Marshaling.variant_to_mono_object_of_type( - args[i], parameterInfos[i].ParameterType); + managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType( + *args[i], parameterInfos[i].ParameterType); } object invokeRet = @delegate.DynamicInvoke(managedArgs); - *outRet = Marshaling.mono_object_to_variant(invokeRet); + *outRet = Marshaling.ConvertManagedObjectToVariant(invokeRet); } catch (Exception e) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index c21d53b4d4..89fc2210b8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace Godot.Collections { @@ -15,14 +16,14 @@ namespace Godot.Collections IDictionary, IDisposable { - public godot_dictionary NativeValue; + internal godot_dictionary.movable NativeValue; /// /// Constructs a new empty . /// public Dictionary() { - NativeValue = NativeFuncs.godotsharp_dictionary_new(); + NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new(); } /// @@ -33,7 +34,7 @@ namespace Godot.Collections public Dictionary(IDictionary dictionary) : this() { if (dictionary == null) - throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + throw new ArgumentNullException(nameof(dictionary)); foreach (DictionaryEntry entry in dictionary) Add(entry.Key, entry.Value); @@ -41,7 +42,9 @@ namespace Godot.Collections private Dictionary(godot_dictionary nativeValueToOwn) { - NativeValue = nativeValueToOwn; + NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ? + nativeValueToOwn : + NativeFuncs.godotsharp_dictionary_new()); } // Explicit name to make it very clear @@ -65,7 +68,7 @@ namespace Godot.Collections public void Dispose(bool disposing) { // Always dispose `NativeValue` even if disposing is true - NativeValue.Dispose(); + NativeValue.DangerousSelfRef.Dispose(); } /// @@ -76,7 +79,8 @@ namespace Godot.Collections public Dictionary Duplicate(bool deep = false) { godot_dictionary newDictionary; - NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep.ToGodotBool(), out newDictionary); + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_duplicate(ref self, deep.ToGodotBool(), out newDictionary); return CreateTakingOwnershipOfDisposableValue(newDictionary); } @@ -90,7 +94,8 @@ namespace Godot.Collections get { godot_array keysArray; - NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray); return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -103,22 +108,25 @@ namespace Godot.Collections get { godot_array valuesArray; - NativeFuncs.godotsharp_dictionary_values(ref NativeValue, out valuesArray); + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } private (Array keys, Array values, int count) GetKeyValuePairs() { + var self = (godot_dictionary)NativeValue; + godot_array keysArray; - NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); + NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray); var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); godot_array valuesArray; - NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_keys(ref self, out valuesArray); var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); - int count = NativeFuncs.godotsharp_dictionary_count(ref NativeValue); + int count = NativeFuncs.godotsharp_dictionary_count(ref self); return (keys, values, count); } @@ -131,16 +139,17 @@ namespace Godot.Collections /// Returns the object at the given . /// /// The object at the given . - public unsafe object this[object key] + public object this[object key] { get { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey, - out godot_variant value).ToBool()) + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)NativeValue; + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey, + out godot_variant value).ToBool()) { using (value) - return Marshaling.variant_to_mono_object(&value); + return Marshaling.ConvertVariantToManagedObject(value); } else { @@ -149,9 +158,10 @@ namespace Godot.Collections } set { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - using godot_variant variantValue = Marshaling.mono_object_to_variant(value); - NativeFuncs.godotsharp_dictionary_set_value(ref NativeValue, &variantKey, &variantValue); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_set_value(ref self, variantKey, variantValue); } } @@ -161,31 +171,38 @@ namespace Godot.Collections /// /// The key at which to add the object. /// The object to add. - public unsafe void Add(object key, object value) + public void Add(object key, object value) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + + var self = (godot_dictionary)NativeValue; - if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool()) + if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool()) throw new ArgumentException("An element with the same key already exists", nameof(key)); - using godot_variant variantValue = Marshaling.mono_object_to_variant(value); - NativeFuncs.godotsharp_dictionary_add(ref NativeValue, &variantKey, &variantValue); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue); } /// /// Erases all items from this . /// - public void Clear() => NativeFuncs.godotsharp_dictionary_clear(ref NativeValue); + public void Clear() + { + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_clear(ref self); + } /// /// Checks if this contains the given key. /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public unsafe bool Contains(object key) + public bool Contains(object key) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool(); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)NativeValue; + return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool(); } /// @@ -198,10 +215,11 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public unsafe void Remove(object key) + public void Remove(object key) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - NativeFuncs.godotsharp_dictionary_remove_key(ref NativeValue, &variantKey); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey); } // ICollection @@ -215,7 +233,14 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count => NativeFuncs.godotsharp_dictionary_count(ref NativeValue); + public int Count + { + get + { + var self = (godot_dictionary)NativeValue; + return NativeFuncs.godotsharp_dictionary_count(ref self); + } + } /// /// Copies the elements of this to the given @@ -279,17 +304,19 @@ namespace Godot.Collections } } - private unsafe void UpdateEntry() + private void UpdateEntry() { _dirty = false; - NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _dictionary.NativeValue, _index, + var self = (godot_dictionary)_dictionary.NativeValue; + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, _index, out godot_variant key, out godot_variant value); using (key) using (value) { - _entry = new DictionaryEntry(Marshaling.variant_to_mono_object(&key), - Marshaling.variant_to_mono_object(&value)); + // FIXME: DictionaryEntry keys cannot be null, but Godot dictionaries allow null keys + _entry = new DictionaryEntry(Marshaling.ConvertVariantToManagedObject(key)!, + Marshaling.ConvertVariantToManagedObject(value)); } } @@ -315,11 +342,12 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this dictionary. - public override unsafe string ToString() + public override string ToString() { - using godot_string str = default; - NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str); - return Marshaling.mono_string_from_godot(str); + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_to_string(ref self, out godot_string str); + using (str) + return Marshaling.ConvertStringToManaged(str); } } @@ -345,7 +373,11 @@ namespace Godot.Collections { private readonly Dictionary _underlyingDict; - internal ref godot_dictionary NativeValue => ref _underlyingDict.NativeValue; + internal ref godot_dictionary.movable NativeValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _underlyingDict.NativeValue; + } // ReSharper disable StaticMemberInGenericType // Warning is about unique static fields being created for each generic type combination: @@ -375,10 +407,10 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary(IDictionary dictionary) { - _underlyingDict = new Dictionary(); - if (dictionary == null) - throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + throw new ArgumentNullException(nameof(dictionary)); + + _underlyingDict = new Dictionary(); foreach (KeyValuePair entry in dictionary) Add(entry.Key, entry.Value); @@ -405,7 +437,7 @@ namespace Godot.Collections /// The typed dictionary to convert. public static explicit operator Dictionary(Dictionary from) { - return from._underlyingDict; + return from?._underlyingDict; } /// @@ -428,19 +460,17 @@ namespace Godot.Collections { get { - unsafe + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)_underlyingDict.NativeValue; + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self, + variantKey, out godot_variant value).ToBool()) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant value).ToBool()) - { - using (value) - return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); - } - else - { - throw new KeyNotFoundException(); - } + using (value) + return (TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues); + } + else + { + throw new KeyNotFoundException(); } } set => _underlyingDict[key] = value; @@ -454,7 +484,8 @@ namespace Godot.Collections get { godot_array keyArray; - NativeFuncs.godotsharp_dictionary_keys(ref _underlyingDict.NativeValue, out keyArray); + var self = (godot_dictionary)_underlyingDict.NativeValue; + NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray); return Array.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -467,21 +498,23 @@ namespace Godot.Collections get { godot_array valuesArray; - NativeFuncs.godotsharp_dictionary_values(ref _underlyingDict.NativeValue, out valuesArray); + var self = (godot_dictionary)_underlyingDict.NativeValue; + NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } - private unsafe KeyValuePair GetKeyValuePair(int index) + private KeyValuePair GetKeyValuePair(int index) { - NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _underlyingDict.NativeValue, index, + 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)Marshaling.variant_to_mono_object(&key), - (TValue)Marshaling.variant_to_mono_object(&value)); + return new KeyValuePair((TKey)Marshaling.ConvertVariantToManagedObject(key), + (TValue)Marshaling.ConvertVariantToManagedObject(value)); } } @@ -510,10 +543,11 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public unsafe bool Remove(TKey key) + public bool Remove(TKey key) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey).ToBool(); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)_underlyingDict.NativeValue; + return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); } /// @@ -522,16 +556,17 @@ namespace Godot.Collections /// The key of the element to get. /// The value at the given . /// If an object was found for the given . - public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue).ToBool(); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(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 ? - (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : + (TValue)Marshaling.ConvertVariantToManagedObjectOfType(retValue, TypeOfValues) : default; } @@ -562,19 +597,20 @@ namespace Godot.Collections _underlyingDict.Clear(); } - unsafe bool ICollection>.Contains(KeyValuePair item) + bool ICollection>.Contains(KeyValuePair item) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue).ToBool(); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(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 godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); - return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool(); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); } } @@ -606,22 +642,23 @@ namespace Godot.Collections } } - unsafe bool ICollection>.Remove(KeyValuePair item) + bool ICollection>.Remove(KeyValuePair item) { - using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue).ToBool(); + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(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 godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); - if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool()) + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) { return NativeFuncs.godotsharp_dictionary_remove_key( - ref _underlyingDict.NativeValue, &variantKey).ToBool(); + ref self, variantKey).ToBool(); } return false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs index 340780bb45..4094ceeb22 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs @@ -35,17 +35,14 @@ namespace Godot if (!IsInstanceValid(obj)) return null; - using godot_ref weakRef = default; - - unsafe + NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef); + using (weakRef) { - NativeFuncs.godotsharp_weakref(GetPtr(obj), &weakRef); - } + if (weakRef.IsNull) + return null; - if (weakRef.IsNull) - return null; - - return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef._reference); + 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 index 17bca19fab..fa8a1c6402 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using Godot.Collections; using Godot.NativeInterop; @@ -11,7 +10,7 @@ namespace Godot /// Returns a list of all nodes assigned to the given . /// /// The type to cast to. Should be a descendant of . - public unsafe Array GetNodesInGroup(StringName group) where T : class + public Array GetNodesInGroup(StringName group) where T : class { var array = GetNodesInGroup(group); @@ -29,19 +28,18 @@ namespace Godot BindingFlags.Public | BindingFlags.NonPublic); var nativeName = (StringName)field!.GetValue(null); - godot_string_name nativeNameAux = nativeName.NativeValue; - godot_array inputAux = array.NativeValue; - godot_array filteredArray; - NativeFuncs.godotsharp_array_filter_godot_objects_by_native( - &nativeNameAux, &inputAux, &filteredArray); + var nativeNameSelf = (godot_string_name)nativeName!.NativeValue; + var inputSelf = (godot_array)array.NativeValue; + NativeFuncs.godotsharp_array_filter_godot_objects_by_native(nativeNameSelf, inputSelf, + out godot_array filteredArray); return Array.CreateTakingOwnershipOfDisposableValue(filteredArray); } else { // Custom derived type - godot_array inputAux = array.NativeValue; - godot_array filteredArray; - NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); + var inputSelf = (godot_array)array.NativeValue; + NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(inputSelf, + out godot_array filteredArray); var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 39271d3daf..02ae32f46e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -26,12 +26,12 @@ namespace Godot /// Byte array that will be decoded to a Variant. /// If objects should be decoded. /// The decoded Variant. - public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false) + public static object Bytes2Var(byte[] bytes, bool allowObjects = false) { - using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); - using godot_variant ret = default; - NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects.ToGodotBool(), &ret); - return Marshaling.variant_to_mono_object(&ret); + using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes); + NativeFuncs.godotsharp_bytes2var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret); + using (ret) + return Marshaling.ConvertVariantToManagedObject(ret); } /// @@ -49,12 +49,12 @@ namespace Godot /// /// /// The Variant converted to the given . - public static unsafe object Convert(object what, Variant.Type type) + public static object Convert(object what, Variant.Type type) { - using var whatVariant = Marshaling.mono_object_to_variant(what); - using godot_variant ret = default; - NativeFuncs.godotsharp_convert(&whatVariant, (int)type, &ret); - return Marshaling.variant_to_mono_object(&ret); + using var whatVariant = Marshaling.ConvertManagedObjectToVariant(what); + NativeFuncs.godotsharp_convert(whatVariant, (int)type, out godot_variant ret); + using (ret) + return Marshaling.ConvertVariantToManagedObject(ret); } /// @@ -88,10 +88,10 @@ namespace Godot /// /// Variable that will be hashed. /// Hash of the variable passed. - public static unsafe int Hash(object var) + public static int Hash(object var) { - using var variant = Marshaling.mono_object_to_variant(var); - return NativeFuncs.godotsharp_hash(&variant); + using var variant = Marshaling.ConvertManagedObjectToVariant(var); + return NativeFuncs.godotsharp_hash(variant); } /// @@ -207,10 +207,10 @@ namespace Godot /// /// /// Error message. - public static unsafe void PushError(string message) + public static void PushError(string message) { - using var godotStr = Marshaling.mono_string_to_godot(message); - NativeFuncs.godotsharp_pusherror(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(message); + NativeFuncs.godotsharp_pusherror(godotStr); } /// @@ -220,10 +220,10 @@ namespace Godot /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call /// /// Warning message. - public static unsafe void PushWarning(string message) + public static void PushWarning(string message) { - using var godotStr = Marshaling.mono_string_to_godot(message); - NativeFuncs.godotsharp_pushwarning(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(message); + NativeFuncs.godotsharp_pushwarning(godotStr); } /// @@ -242,11 +242,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static unsafe void Print(params object[] what) + public static void Print(params object[] what) { string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.mono_string_to_godot(str); - NativeFuncs.godotsharp_print(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_print(godotStr); } /// @@ -273,11 +273,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static unsafe void PrintRich(params object[] what) + public static void PrintRich(params object[] what) { string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.mono_string_to_godot(str); - NativeFuncs.godotsharp_print_rich(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_print_rich(godotStr); } /// @@ -297,11 +297,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static unsafe void PrintErr(params object[] what) + public static void PrintErr(params object[] what) { string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.mono_string_to_godot(str); - NativeFuncs.godotsharp_printerr(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_printerr(godotStr); } /// @@ -319,11 +319,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static unsafe void PrintRaw(params object[] what) + public static void PrintRaw(params object[] what) { string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.mono_string_to_godot(str); - NativeFuncs.godotsharp_printraw(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_printraw(godotStr); } /// @@ -335,11 +335,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static unsafe void PrintS(params object[] what) + public static void PrintS(params object[] what) { string str = string.Join(' ', GetPrintParams(what)); - using var godotStr = Marshaling.mono_string_to_godot(str); - NativeFuncs.godotsharp_prints(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_prints(godotStr); } /// @@ -351,11 +351,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static unsafe void PrintT(params object[] what) + public static void PrintT(params object[] what) { string str = string.Join('\t', GetPrintParams(what)); - using var godotStr = Marshaling.mono_string_to_godot(str); - NativeFuncs.godotsharp_printt(&godotStr); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_printt(godotStr); } /// @@ -520,12 +520,12 @@ namespace Godot /// /// Arguments that will converted to string. /// The string formed by the given arguments. - public static unsafe string Str(params object[] what) + public static string Str(params object[] what) { - using var whatGodotArray = Marshaling.mono_array_to_Array(what); - using godot_string ret = default; - NativeFuncs.godotsharp_str(&whatGodotArray, &ret); - return Marshaling.mono_string_from_godot(ret); + using var whatGodotArray = Marshaling.ConvertSystemArrayToNativeGodotArray(what); + NativeFuncs.godotsharp_str(whatGodotArray, out godot_string ret); + using (ret) + return Marshaling.ConvertStringToManaged(ret); } /// @@ -540,12 +540,12 @@ namespace Godot /// /// String that will be converted to Variant. /// The decoded Variant. - public static unsafe object Str2Var(string str) + public static object Str2Var(string str) { - using var godotStr = Marshaling.mono_string_to_godot(str); - using godot_variant ret = default; - NativeFuncs.godotsharp_str2var(&godotStr, &ret); - return Marshaling.variant_to_mono_object(&ret); + using var godotStr = Marshaling.ConvertStringToNative(str); + NativeFuncs.godotsharp_str2var(godotStr, out godot_variant ret); + using (ret) + return Marshaling.ConvertVariantToManagedObject(ret); } /// @@ -557,13 +557,12 @@ namespace Godot /// Variant that will be encoded. /// If objects should be serialized. /// The Variant encoded as an array of bytes. - public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false) + public static byte[] Var2Bytes(object var, bool fullObjects = false) { - using var variant = Marshaling.mono_object_to_variant(var); - using godot_packed_byte_array varBytes = default; - NativeFuncs.godotsharp_var2bytes(&variant, fullObjects.ToGodotBool(), &varBytes); + using var variant = Marshaling.ConvertManagedObjectToVariant(var); + NativeFuncs.godotsharp_var2bytes(variant, fullObjects.ToGodotBool(), out var varBytes); using (varBytes) - return Marshaling.PackedByteArray_to_mono_array(&varBytes); + return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); } /// @@ -583,12 +582,12 @@ namespace Godot /// /// Variant that will be converted to string. /// The Variant encoded as a string. - public static unsafe string Var2Str(object var) + public static string Var2Str(object var) { - using var variant = Marshaling.mono_object_to_variant(var); - using godot_string ret = default; - NativeFuncs.godotsharp_var2str(&variant, &ret); - return Marshaling.mono_string_from_godot(ret); + using var variant = Marshaling.ConvertManagedObjectToVariant(var); + NativeFuncs.godotsharp_var2str(variant, out godot_string ret); + using (ret) + return Marshaling.ConvertStringToManaged(ret); } /// @@ -597,7 +596,7 @@ namespace Godot /// The for the given . public static Variant.Type TypeToVariantType(Type type) { - return Marshaling.managed_to_variant_type(type, out bool _); + return Marshaling.ConvertManagedTypeToVariantType(type, out bool _); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 0942d8f722..ef20819d62 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -10,7 +10,10 @@ using System.Runtime.InteropServices; namespace Godot.NativeInterop { - internal static class GodotBoolExtensions + // 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) @@ -35,9 +38,9 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - public struct godot_ref : IDisposable + public ref struct godot_ref { - internal IntPtr _reference; + private IntPtr _reference; public void Dispose() { @@ -47,7 +50,17 @@ namespace Godot.NativeInterop _reference = IntPtr.Zero; } - public bool IsNull => _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")] @@ -63,41 +76,53 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - public struct godot_variant_call_error + public ref struct godot_variant_call_error { - public godot_variant_call_error_error error; - public int argument; - public int expected; + 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 struct godot_variant : IDisposable + public ref struct godot_variant { // 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)] internal godot_variant_data _data; - - public Variant.Type _type - { - get => (Variant.Type)_typeField; - set => _typeField = (int)value; - } + [FieldOffset(8)] private godot_variant_data _data; [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal unsafe struct godot_variant_data + 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 Transform2D* _transform2D; [FieldOffset(0)] public AABB* _aabb; [FieldOffset(0)] public Basis* _basis; - [FieldOffset(0)] public Transform3D* _transform3d; + [FieldOffset(0)] public Transform3D* _transform3D; [FieldOffset(0)] public Vector4* _vector4; [FieldOffset(0)] public Vector4i* _vector4i; [FieldOffset(0)] public Projection* _projection; @@ -125,7 +150,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_variant_obj_data + public struct godot_variant_obj_data { public UInt64 id; public IntPtr obj; @@ -133,7 +158,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - private struct godot_variant_data_mem + public struct godot_variant_data_mem { #pragma warning disable 169 private real_t _mem0; @@ -144,9 +169,225 @@ namespace Godot.NativeInterop } } + 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) + switch (Type) { case Variant.Type.Nil: case Variant.Type.Bool: @@ -166,15 +407,36 @@ namespace Godot.NativeInterop } NativeFuncs.godotsharp_variant_destroy(ref this); - _type = Variant.Type.Nil; + 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 struct godot_string : IDisposable + public ref struct godot_string { - internal IntPtr _ptr; + private IntPtr _ptr; public void Dispose() { @@ -184,15 +446,25 @@ namespace Godot.NativeInterop _ptr = IntPtr.Zero; } + public readonly IntPtr Buffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _ptr; + } + // Size including the null termination character - public unsafe int Size => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0; + public readonly unsafe int Size + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0; + } } [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - public struct godot_string_name : IDisposable + public ref struct godot_string_name { - internal IntPtr _data; + private IntPtr _data; public void Dispose() { @@ -202,18 +474,41 @@ namespace Godot.NativeInterop _data = IntPtr.Zero; } - // An static method because an instance method could result in a hidden copy if called on an `in` parameter. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmpty(in godot_string_name name) => + 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++. - name._data == IntPtr.Zero; + 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_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 struct godot_node_path : IDisposable + public ref struct godot_node_path { - internal IntPtr _data; + private IntPtr _data; public void Dispose() { @@ -223,49 +518,98 @@ namespace Godot.NativeInterop _data = IntPtr.Zero; } - // An static method because an instance method could result in a hidden copy if called on an `in` parameter. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmpty(in godot_node_path nodePath) => + 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. - nodePath._data == IntPtr.Zero; + 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 struct godot_signal : IDisposable + public ref struct godot_signal { - [FieldOffset(0)] public godot_string_name _name; + [FieldOffset(0)] private godot_string_name _name; // There's padding here on 32-bit - [FieldOffset(8)] public UInt64 _objectId; + [FieldOffset(8)] private UInt64 _objectId; + + public godot_signal(godot_string_name name, ulong objectId) + { + _name = name; + _objectId = objectId; + } + + public godot_string_name Name + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _name; + } + + public UInt64 ObjectId + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _objectId; + } public void Dispose() { - if (_name._data == IntPtr.Zero) + if (!_name.IsAllocated) return; NativeFuncs.godotsharp_signal_destroy(ref this); - _name._data = IntPtr.Zero; + _name = default; } } [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - public struct godot_callable : IDisposable + public ref struct godot_callable { - [FieldOffset(0)] public godot_string_name _method; + [FieldOffset(0)] private godot_string_name _method; // There's padding here on 32-bit - [FieldOffset(8)] public UInt64 _objectId; - [FieldOffset(8)] public IntPtr _custom; + // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable + [FieldOffset(8)] private UInt64 _objectId; + [FieldOffset(8)] private IntPtr _custom; + + public godot_callable(godot_string_name method, ulong objectId) : this() + { + _method = method; + _objectId = objectId; + } public void Dispose() { - if (_method._data == IntPtr.Zero && _custom == IntPtr.Zero) + // _custom needs freeing as well + if (!_method.IsAllocated && _custom == IntPtr.Zero) return; NativeFuncs.godotsharp_callable_destroy(ref this); - _method._data = IntPtr.Zero; + _method = default; _custom = IntPtr.Zero; } } @@ -275,29 +619,55 @@ namespace Godot.NativeInterop // 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 struct godot_array : IDisposable + public ref struct godot_array { - internal unsafe ArrayPrivate* _p; + private unsafe ArrayPrivate* _p; [StructLayout(LayoutKind.Sequential)] - internal struct ArrayPrivate + private struct ArrayPrivate { private uint _safeRefCount; - internal VariantVector _arrayVector; - // There's more here, but we don't care as we never store this in C# + 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)] - internal struct VariantVector + private struct VariantVector { - internal IntPtr _writeProxy; - internal unsafe godot_variant* _ptr; + private IntPtr _writeProxy; + public unsafe godot_variant* _ptr; - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 unsafe int Size => _p != null ? _p->_arrayVector.Size : 0; + 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() { @@ -306,6 +676,22 @@ namespace Godot.NativeInterop 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: @@ -314,9 +700,15 @@ namespace Godot.NativeInterop // 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 struct godot_dictionary : IDisposable + public ref struct godot_dictionary { - internal IntPtr _p; + private IntPtr _p; + + public readonly bool IsAllocated + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _p != IntPtr.Zero; + } public void Dispose() { @@ -325,14 +717,30 @@ namespace Godot.NativeInterop 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 struct godot_packed_byte_array : IDisposable + public ref struct godot_packed_byte_array { - internal IntPtr _writeProxy; - internal unsafe byte* _ptr; + private IntPtr _writeProxy; + private unsafe byte* _ptr; public unsafe void Dispose() { @@ -342,15 +750,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_int32_array : IDisposable + public ref struct godot_packed_int32_array { - internal IntPtr _writeProxy; - internal unsafe int* _ptr; + private IntPtr _writeProxy; + private unsafe int* _ptr; public unsafe void Dispose() { @@ -360,15 +778,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0; + 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 struct godot_packed_int64_array : IDisposable + public ref struct godot_packed_int64_array { - internal IntPtr _writeProxy; - internal unsafe long* _ptr; + private IntPtr _writeProxy; + private unsafe long* _ptr; public unsafe void Dispose() { @@ -378,15 +806,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_float32_array : IDisposable + public ref struct godot_packed_float32_array { - internal IntPtr _writeProxy; - internal unsafe float* _ptr; + private IntPtr _writeProxy; + private unsafe float* _ptr; public unsafe void Dispose() { @@ -396,15 +834,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_float64_array : IDisposable + public ref struct godot_packed_float64_array { - internal IntPtr _writeProxy; - internal unsafe double* _ptr; + private IntPtr _writeProxy; + private unsafe double* _ptr; public unsafe void Dispose() { @@ -414,15 +862,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_string_array : IDisposable + public ref struct godot_packed_string_array { - internal IntPtr _writeProxy; - internal unsafe godot_string* _ptr; + private IntPtr _writeProxy; + private unsafe godot_string* _ptr; public unsafe void Dispose() { @@ -432,15 +890,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_vector2_array : IDisposable + public ref struct godot_packed_vector2_array { - internal IntPtr _writeProxy; - internal unsafe Vector2* _ptr; + private IntPtr _writeProxy; + private unsafe Vector2* _ptr; public unsafe void Dispose() { @@ -450,15 +918,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_vector3_array : IDisposable + public ref struct godot_packed_vector3_array { - internal IntPtr _writeProxy; - internal unsafe Vector3* _ptr; + private IntPtr _writeProxy; + private unsafe Vector3* _ptr; public unsafe void Dispose() { @@ -468,15 +946,25 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 struct godot_packed_color_array : IDisposable + public ref struct godot_packed_color_array { - internal IntPtr _writeProxy; - internal unsafe Color* _ptr; + private IntPtr _writeProxy; + private unsafe Color* _ptr; public unsafe void Dispose() { @@ -486,6 +974,16 @@ namespace Godot.NativeInterop _ptr = null; } - public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + 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 index 5779421c69..8ddf28ba16 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Godot.Bridge; @@ -16,22 +15,19 @@ namespace Godot.NativeInterop return null; IntPtr gcHandlePtr; - godot_bool has_cs_script_instance = false.ToGodotBool(); + godot_bool hasCsScriptInstance; // First try to get the tied managed instance from a CSharpInstance script instance - unsafe - { - gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed( - unmanaged, &has_cs_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 (has_cs_script_instance.ToBool()) + if (hasCsScriptInstance.ToBool()) return null; // If it doesn't have a CSharpInstance script instance, try with native instance bindings @@ -58,12 +54,9 @@ namespace Godot.NativeInterop if (type == nativeType) { - unsafe - { - godot_string_name nativeNameAux = nativeName.NativeValue; - NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged( - GCHandle.ToIntPtr(gcHandle), unmanaged, &nativeNameAux, refCounted.ToGodotBool()); - } + var nativeNameSelf = (godot_string_name)nativeName.NativeValue; + NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged( + GCHandle.ToIntPtr(gcHandle), unmanaged, nativeNameSelf, refCounted.ToGodotBool()); } else { @@ -88,10 +81,10 @@ namespace Godot.NativeInterop GCHandle.ToIntPtr(strongGCHandle), unmanaged); } - public static unsafe Object EngineGetSingleton(string name) + public static Object EngineGetSingleton(string name) { - using godot_string src = Marshaling.mono_string_to_godot(name); - return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(&src)); + 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 index 74232425bb..d1a1450f04 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -1,19 +1,21 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Reflection; 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 { - // We want to use full name qualifiers here even if redundant for clarity - [SuppressMessage("ReSharper", "RedundantNameQualifier")] public static class Marshaling { - public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) + internal static Variant.Type ConvertManagedTypeToVariantType(Type type, out bool r_nil_is_variant) { r_nil_is_variant = false; @@ -106,7 +108,7 @@ namespace Godot.NativeInterop if (type.IsArray || type.IsSZArray) { - if (type == typeof(Byte[])) + if (type == typeof(byte[])) return Variant.Type.PackedByteArray; if (type == typeof(Int32[])) @@ -133,6 +135,15 @@ namespace Godot.NativeInterop 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; @@ -161,6 +172,9 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) return Variant.Type.Array; + + if (typeof(Godot.Object).IsAssignableFrom(type)) + return Variant.Type.Object; } else if (type == typeof(object)) { @@ -200,44 +214,9 @@ namespace Godot.NativeInterop return Variant.Type.Nil; } - public static bool try_get_array_element_type(Type p_array_type, out Type r_elem_type) - { - if (p_array_type.IsArray || p_array_type.IsSZArray) - { - r_elem_type = p_array_type.GetElementType(); - return true; - } - else if (p_array_type.IsGenericType) - { - var genericTypeDefinition = p_array_type.GetGenericTypeDefinition(); - - if (typeof(Collections.Array) == genericTypeDefinition || - typeof(System.Collections.Generic.List<>) == genericTypeDefinition || - typeof(System.Collections.ICollection) == genericTypeDefinition || - typeof(System.Collections.IEnumerable) == genericTypeDefinition) - { - r_elem_type = p_array_type.GetGenericArguments()[0]; - return true; - } - } - - r_elem_type = null; - return false; - } - /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ - public static godot_variant mono_object_to_variant(object p_obj) - { - return mono_object_to_variant_impl(p_obj); - } - - public static godot_variant mono_object_to_variant_no_err(object p_obj) - { - return mono_object_to_variant_impl(p_obj); - } - - private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) + public static godot_variant ConvertManagedObjectToVariant(object? p_obj) { if (p_obj == null) return new godot_variant(); @@ -248,7 +227,7 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromBool(@bool); case char @char: return VariantUtils.CreateFromInt(@char); - case SByte @int8: + case sbyte @int8: return VariantUtils.CreateFromInt(@int8); case Int16 @int16: return VariantUtils.CreateFromInt(@int16); @@ -256,7 +235,7 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromInt(@int32); case Int64 @int64: return VariantUtils.CreateFromInt(@int64); - case Byte @uint8: + case byte @uint8: return VariantUtils.CreateFromInt(@uint8); case UInt16 @uint16: return VariantUtils.CreateFromInt(@uint16); @@ -311,58 +290,72 @@ namespace Godot.NativeInterop case string @string: { return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue( - mono_string_to_godot(@string)); + ConvertStringToNative(@string)); } - case Byte[] byteArray: + case byte[] byteArray: { - using godot_packed_byte_array array = mono_array_to_PackedByteArray(byteArray); - return VariantUtils.CreateFromPackedByteArray(&array); + using godot_packed_byte_array array = ConvertSystemArrayToNativePackedByteArray(byteArray); + return VariantUtils.CreateFromPackedByteArray(array); } case Int32[] int32Array: { - using godot_packed_int32_array array = mono_array_to_PackedInt32Array(int32Array); - return VariantUtils.CreateFromPackedInt32Array(&array); + using godot_packed_int32_array array = ConvertSystemArrayToNativePackedInt32Array(int32Array); + return VariantUtils.CreateFromPackedInt32Array(array); } case Int64[] int64Array: { - using godot_packed_int64_array array = mono_array_to_PackedInt64Array(int64Array); - return VariantUtils.CreateFromPackedInt64Array(&array); + using godot_packed_int64_array array = ConvertSystemArrayToNativePackedInt64Array(int64Array); + return VariantUtils.CreateFromPackedInt64Array(array); } case float[] floatArray: { - using godot_packed_float32_array array = mono_array_to_PackedFloat32Array(floatArray); - return VariantUtils.CreateFromPackedFloat32Array(&array); + using godot_packed_float32_array array = ConvertSystemArrayToNativePackedFloat32Array(floatArray); + return VariantUtils.CreateFromPackedFloat32Array(array); } case double[] doubleArray: { - using godot_packed_float64_array array = mono_array_to_PackedFloat64Array(doubleArray); - return VariantUtils.CreateFromPackedFloat64Array(&array); + using godot_packed_float64_array array = ConvertSystemArrayToNativePackedFloat64Array(doubleArray); + return VariantUtils.CreateFromPackedFloat64Array(array); } case string[] stringArray: { - using godot_packed_string_array array = mono_array_to_PackedStringArray(stringArray); - return VariantUtils.CreateFromPackedStringArray(&array); + using godot_packed_string_array array = ConvertSystemArrayToNativePackedStringArray(stringArray); + return VariantUtils.CreateFromPackedStringArray(array); } case Vector2[] vector2Array: { - using godot_packed_vector2_array array = mono_array_to_PackedVector2Array(vector2Array); - return VariantUtils.CreateFromPackedVector2Array(&array); + using godot_packed_vector2_array array = ConvertSystemArrayToNativePackedVector2Array(vector2Array); + return VariantUtils.CreateFromPackedVector2Array(array); } case Vector3[] vector3Array: { - using godot_packed_vector3_array array = mono_array_to_PackedVector3Array(vector3Array); - return VariantUtils.CreateFromPackedVector3Array(&array); + using godot_packed_vector3_array array = ConvertSystemArrayToNativePackedVector3Array(vector3Array); + return VariantUtils.CreateFromPackedVector3Array(array); } case Color[] colorArray: { - using godot_packed_color_array array = mono_array_to_PackedColorArray(colorArray); - return VariantUtils.CreateFromPackedColorArray(&array); + using godot_packed_color_array array = ConvertSystemArrayToNativePackedColorArray(colorArray); + return VariantUtils.CreateFromPackedColorArray(array); + } + case StringName[] stringNameArray: + { + using godot_array array = ConvertSystemArrayToNativeGodotArray(stringNameArray); + return VariantUtils.CreateFromArray(array); + } + case NodePath[] nodePathArray: + { + using godot_array array = ConvertSystemArrayToNativeGodotArray(nodePathArray); + return VariantUtils.CreateFromArray(array); + } + case RID[] ridArray: + { + using godot_array array = ConvertSystemArrayToNativeGodotArray(ridArray); + return VariantUtils.CreateFromArray(array); } case Godot.Object[] godotObjectArray: { - // ReSharper disable once CoVariantArrayConversion - using godot_array array = mono_array_to_Array(godotObjectArray); - return VariantUtils.CreateFromArray(&array); + using godot_array array = ConvertSystemArrayToNativeGodotArray(godotObjectArray); + return VariantUtils.CreateFromArray(array); } case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] { @@ -370,45 +363,38 @@ namespace Godot.NativeInterop // so we need to check the actual type to make sure it's truly `object[]`. if (objectArray.GetType() == typeof(object[])) { - using godot_array array = mono_array_to_Array(objectArray); - return VariantUtils.CreateFromArray(&array); + using godot_array array = ConvertSystemArrayToNativeGodotArray(objectArray); + return VariantUtils.CreateFromArray(array); } - if (p_fail_with_err) - { - GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); - return new godot_variant(); - } - else - { - return new godot_variant(); - } + GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); + return new godot_variant(); } case Godot.Object godotObject: return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance); case StringName stringName: - return VariantUtils.CreateFromStringName(ref stringName.NativeValue); + return VariantUtils.CreateFromStringName(stringName.NativeValue.DangerousSelfRef); case NodePath nodePath: - return VariantUtils.CreateFromNodePath(ref nodePath.NativeValue); + return VariantUtils.CreateFromNodePath((godot_node_path)nodePath.NativeValue); case RID rid: return VariantUtils.CreateFromRID(rid); case Collections.Dictionary godotDictionary: - return VariantUtils.CreateFromDictionary(godotDictionary.NativeValue); + return VariantUtils.CreateFromDictionary((godot_dictionary)godotDictionary.NativeValue); case Collections.Array godotArray: - return VariantUtils.CreateFromArray(godotArray.NativeValue); + return VariantUtils.CreateFromArray((godot_array)godotArray.NativeValue); case Collections.IGenericGodotDictionary genericGodotDictionary: { var godotDict = genericGodotDictionary.UnderlyingDictionary; if (godotDict == null) return new godot_variant(); - return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + return VariantUtils.CreateFromDictionary((godot_dictionary)godotDict.NativeValue); } case Collections.IGenericGodotArray genericGodotArray: { var godotArray = genericGodotArray.UnderlyingArray; if (godotArray == null) return new godot_variant(); - return VariantUtils.CreateFromArray(godotArray.NativeValue); + return VariantUtils.CreateFromArray((godot_array)godotArray.NativeValue); } default: { @@ -426,14 +412,14 @@ namespace Godot.NativeInterop foreach (KeyValuePair entry in (IDictionary)p_obj) godotDict.Add(entry.Key, entry.Value); - return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + return VariantUtils.CreateFromDictionary((godot_dictionary)godotDict.NativeValue); } if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { // TODO: Validate element type is compatible with Variant - var nativeGodotArray = mono_array_to_Array((IList)p_obj); - return VariantUtils.CreateFromArray(&nativeGodotArray); + using var nativeGodotArray = ConvertIListToNativeGodotArray((IList)p_obj); + return VariantUtils.CreateFromArray(nativeGodotArray); } } @@ -441,38 +427,31 @@ namespace Godot.NativeInterop } } - if (p_fail_with_err) - { - GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + - p_obj.GetType().FullName + "."); - return new godot_variant(); - } - else - { - return new godot_variant(); - } + GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + + p_obj.GetType().FullName + "."); + return new godot_variant(); } - public static unsafe string variant_to_mono_string(godot_variant* p_var) + private static string? ConvertVariantToManagedString(in godot_variant p_var) { - switch ((*p_var)._type) + switch (p_var.Type) { case Variant.Type.Nil: return null; // 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 mono_string_from_godot((*p_var)._data._m_string); + return ConvertStringToManaged(p_var.String); } default: { using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); - return mono_string_from_godot(godotString); + return ConvertStringToManaged(godotString); } } } - public static unsafe object variant_to_mono_object_of_type(godot_variant* p_var, Type type) + 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)) @@ -502,7 +481,7 @@ namespace Godot.NativeInterop case TypeCode.Double: return VariantUtils.ConvertToFloat64(p_var); case TypeCode.String: - return variant_to_mono_string(p_var); + return ConvertVariantToManagedString(p_var); default: { if (type == typeof(Vector2)) @@ -556,13 +535,13 @@ namespace Godot.NativeInterop if (type == typeof(Callable)) { using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var); - return ConvertCallableToManaged(&callable); + return ConvertCallableToManaged(in callable); } if (type == typeof(SignalInfo)) { using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var); - return ConvertSignalToManaged(&signal); + return ConvertSignalToManaged(in signal); } if (type.IsEnum) @@ -597,12 +576,12 @@ namespace Godot.NativeInterop } if (type.IsArray || type.IsSZArray) - return variant_to_mono_array_of_type(p_var, type); + return ConvertVariantToSystemArrayOfType(p_var, type); else if (type.IsGenericType) - return variant_to_mono_object_of_genericinst(p_var, type); + return ConvertVariantToManagedObjectOfGenericType(p_var, type); else if (type == typeof(object)) - return variant_to_mono_object(p_var); - if (variant_to_mono_object_of_class(p_var, type, out object res)) + return ConvertVariantToManagedObject(p_var); + if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res)) return res; break; @@ -614,72 +593,90 @@ namespace Godot.NativeInterop return null; } - private static unsafe object variant_to_mono_array_of_type(godot_variant* p_var, Type type) + private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type) { - if (type == typeof(Byte[])) + if (type == typeof(byte[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); - return PackedByteArray_to_mono_array(&packedArray); + return ConvertNativePackedByteArrayToSystemArray(packedArray); } if (type == typeof(Int32[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); - return PackedInt32Array_to_mono_array(&packedArray); + return ConvertNativePackedInt32ArrayToSystemArray(packedArray); } if (type == typeof(Int64[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); - return PackedInt64Array_to_mono_array(&packedArray); + return ConvertNativePackedInt64ArrayToSystemArray(packedArray); } if (type == typeof(float[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); - return PackedFloat32Array_to_mono_array(&packedArray); + return ConvertNativePackedFloat32ArrayToSystemArray(packedArray); } if (type == typeof(double[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); - return PackedFloat64Array_to_mono_array(&packedArray); + return ConvertNativePackedFloat64ArrayToSystemArray(packedArray); } if (type == typeof(string[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); - return PackedStringArray_to_mono_array(&packedArray); + return ConvertNativePackedStringArrayToSystemArray(packedArray); } if (type == typeof(Vector2[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); - return PackedVector2Array_to_mono_array(&packedArray); + return ConvertNativePackedVector2ArrayToSystemArray(packedArray); } if (type == typeof(Vector3[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); - return PackedVector3Array_to_mono_array(&packedArray); + return ConvertNativePackedVector3ArrayToSystemArray(packedArray); } if (type == typeof(Color[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); - return PackedColorArray_to_mono_array(&packedArray); + return ConvertNativePackedColorArrayToSystemArray(packedArray); + } + + if (type == typeof(StringName[])) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); + } + + if (type == typeof(NodePath[])) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); + } + + if (type == typeof(RID[])) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); } if (typeof(Godot.Object[]).IsAssignableFrom(type)) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Array_to_mono_array_of_godot_object_type(&godotArray, type); + return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); } if (type == typeof(object[])) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Array_to_mono_array(&godotArray); + return ConvertNativeGodotArrayToSystemArray(godotArray); } GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + @@ -687,7 +684,8 @@ namespace Godot.NativeInterop return null; } - private static unsafe bool variant_to_mono_object_of_class(godot_variant* p_var, Type type, out object res) + private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type, + out object? res) { if (typeof(Godot.Object).IsAssignableFrom(type)) { @@ -735,33 +733,33 @@ namespace Godot.NativeInterop return false; } - private static unsafe object variant_to_mono_object_of_genericinst(godot_variant* p_var, Type type) + private static object? ConvertVariantToManagedObjectOfGenericType(in godot_variant p_var, Type type) { - static object variant_to_generic_godot_collections_dictionary(godot_variant* p_var, Type fullType) + static object ConvertVariantToGenericGodotCollectionsDictionary(in godot_variant p_var, Type fullType) { var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToDictionary(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] { underlyingDict }, null); + args: new object[] { underlyingDict }, null)!; } - static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) + static object ConvertVariantToGenericGodotCollectionsArray(in godot_variant p_var, Type fullType) { var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToArray(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] { underlyingArray }, null); + args: new object[] { underlyingArray }, null)!; } var genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) - return variant_to_generic_godot_collections_dictionary(p_var, type); + return ConvertVariantToGenericGodotCollectionsDictionary(p_var, type); if (genericTypeDefinition == typeof(Collections.Array<>)) - return variant_to_generic_godot_collections_array(p_var, type); + return ConvertVariantToGenericGodotCollectionsArray(p_var, type); if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) { @@ -773,7 +771,7 @@ namespace Godot.NativeInterop args: new object[] { /* capacity: */ godotDictionary.Count - }, null); + }, null)!; foreach (System.Collections.DictionaryEntry pair in godotDictionary) dictionary.Add(pair.Key, pair.Value); @@ -791,7 +789,7 @@ namespace Godot.NativeInterop args: new object[] { /* capacity: */ godotArray.Count - }, null); + }, null)!; foreach (object elem in godotArray) list.Add(elem); @@ -807,7 +805,7 @@ namespace Godot.NativeInterop var genericGodotDictionaryType = typeof(Collections.Dictionary<,>) .MakeGenericType(keyType, valueType); - return variant_to_generic_godot_collections_dictionary(p_var, genericGodotDictionaryType); + return ConvertVariantToGenericGodotCollectionsDictionary(p_var, genericGodotDictionaryType); } if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) @@ -816,138 +814,141 @@ namespace Godot.NativeInterop var genericGodotArrayType = typeof(Collections.Array<>) .MakeGenericType(elementType); - return variant_to_generic_godot_collections_array(p_var, genericGodotArrayType); + return ConvertVariantToGenericGodotCollectionsArray(p_var, genericGodotArrayType); } + if (typeof(Godot.Object).IsAssignableFrom(type)) + return InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); + return null; } - public static unsafe object variant_to_mono_object(godot_variant* p_var) + public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var) { - switch ((*p_var)._type) + switch (p_var.Type) { case Variant.Type.Bool: - return (*p_var)._data._bool.ToBool(); + return p_var.Bool.ToBool(); case Variant.Type.Int: - return (*p_var)._data._int; + return p_var.Int; case Variant.Type.Float: { #if REAL_T_IS_DOUBLE - return (*p_var)._data._float; + return p_var.Float; #else - return (float)(*p_var)._data._float; + return (float)p_var.Float; #endif } case Variant.Type.String: - return mono_string_from_godot((*p_var)._data._m_string); + return ConvertStringToManaged(p_var.String); case Variant.Type.Vector2: - return (*p_var)._data._m_vector2; + return p_var.Vector2; case Variant.Type.Vector2i: - return (*p_var)._data._m_vector2i; + return p_var.Vector2i; case Variant.Type.Rect2: - return (*p_var)._data._m_rect2; + return p_var.Rect2; case Variant.Type.Rect2i: - return (*p_var)._data._m_rect2i; + return p_var.Rect2i; case Variant.Type.Vector3: - return (*p_var)._data._m_vector3; + return p_var.Vector3; case Variant.Type.Vector3i: - return (*p_var)._data._m_vector3i; + return p_var.Vector3i; case Variant.Type.Transform2d: - return *(*p_var)._data._transform2d; + return *p_var.Transform2D; case Variant.Type.Vector4: - return *(*p_var)._data._vector4; + return *p_var.Vector4; case Variant.Type.Vector4i: - return *(*p_var)._data._vector4i; + return *p_var.Vector4i; case Variant.Type.Plane: - return (*p_var)._data._m_plane; + return p_var.Plane; case Variant.Type.Quaternion: - return (*p_var)._data._m_quaternion; + return p_var.Quaternion; case Variant.Type.Aabb: - return *(*p_var)._data._aabb; + return *p_var.AABB; case Variant.Type.Basis: - return *(*p_var)._data._basis; + return *p_var.Basis; case Variant.Type.Transform3d: - return *(*p_var)._data._transform3d; + return *p_var.Transform3D; case Variant.Type.Projection: - return *(*p_var)._data._projection; + return *p_var.Projection; case Variant.Type.Color: - return (*p_var)._data._m_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)._data._m_string_name)); + 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)._data._m_node_path)); + NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath)); } case Variant.Type.Rid: - return (*p_var)._data._m_rid; + return p_var.RID; case Variant.Type.Object: - return InteropUtils.UnmanagedGetManaged((*p_var)._data._m_obj_data.obj); + return InteropUtils.UnmanagedGetManaged(p_var.Object); case Variant.Type.Callable: - return ConvertCallableToManaged(&(*p_var)._data._m_callable); + return ConvertCallableToManaged(p_var.Callable); case Variant.Type.Signal: - return ConvertSignalToManaged(&(*p_var)._data._m_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)._data._m_dictionary)); + 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)._data._m_array)); + NativeFuncs.godotsharp_array_new_copy(p_var.Array)); } case Variant.Type.PackedByteArray: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); - return PackedByteArray_to_mono_array(&packedArray); + return ConvertNativePackedByteArrayToSystemArray(packedArray); } case Variant.Type.PackedInt32Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); - return PackedInt32Array_to_mono_array(&packedArray); + return ConvertNativePackedInt32ArrayToSystemArray(packedArray); } case Variant.Type.PackedInt64Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); - return PackedInt64Array_to_mono_array(&packedArray); + return ConvertNativePackedInt64ArrayToSystemArray(packedArray); } case Variant.Type.PackedFloat32Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); - return PackedFloat32Array_to_mono_array(&packedArray); + return ConvertNativePackedFloat32ArrayToSystemArray(packedArray); } case Variant.Type.PackedFloat64Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); - return PackedFloat64Array_to_mono_array(&packedArray); + return ConvertNativePackedFloat64ArrayToSystemArray(packedArray); } case Variant.Type.PackedStringArray: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); - return PackedStringArray_to_mono_array(&packedArray); + return ConvertNativePackedStringArrayToSystemArray(packedArray); } case Variant.Type.PackedVector2Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); - return PackedVector2Array_to_mono_array(&packedArray); + return ConvertNativePackedVector2ArrayToSystemArray(packedArray); } case Variant.Type.PackedVector3Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); - return PackedVector3Array_to_mono_array(&packedArray); + return ConvertNativePackedVector3ArrayToSystemArray(packedArray); } case Variant.Type.PackedColorArray: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); - return PackedColorArray_to_mono_array(&packedArray); + return ConvertNativePackedColorArrayToSystemArray(packedArray); } default: return null; @@ -956,26 +957,25 @@ namespace Godot.NativeInterop // String - public static unsafe godot_string mono_string_to_godot(string p_mono_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) { - godot_string dest; - NativeFuncs.godotsharp_string_new_with_utf16_chars(&dest, methodChars); + NativeFuncs.godotsharp_string_new_with_utf16_chars(out godot_string dest, methodChars); return dest; } } - public static unsafe string mono_string_from_godot(in godot_string p_string) + public static unsafe string ConvertStringToManaged(in godot_string p_string) { - if (p_string._ptr == IntPtr.Zero) + if (p_string.Buffer == IntPtr.Zero) return string.Empty; const int sizeOfChar32 = 4; - byte* bytes = (byte*)p_string._ptr; + byte* bytes = (byte*)p_string.Buffer; int size = p_string.Size; if (size == 0) return string.Empty; @@ -987,54 +987,45 @@ namespace Godot.NativeInterop // Callable public static godot_callable ConvertCallableToNative(ref Callable p_managed_callable) + => ConvertCallableToNative(p_managed_callable); + + public static godot_callable ConvertCallableToNative(Callable p_managed_callable) { if (p_managed_callable.Delegate != null) { - unsafe - { - godot_callable callable; - NativeFuncs.godotsharp_callable_new_with_delegate( - GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), &callable); - return callable; - } + NativeFuncs.godotsharp_callable_new_with_delegate( + GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), + out godot_callable callable); + return callable; } else { - unsafe - { - godot_string_name method; - - if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty) - { - godot_string_name src = p_managed_callable.Method.NativeValue; - method = NativeFuncs.godotsharp_string_name_new_copy(&src); - } - else - { - method = default; - } + godot_string_name method; - return new godot_callable - { - _method = method, // Takes ownership of disposable - _objectId = p_managed_callable.Target.GetInstanceId() - }; + 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 unsafe Callable ConvertCallableToManaged(godot_callable* p_callable) + public static Callable ConvertCallableToManaged(in godot_callable p_callable) { - IntPtr delegateGCHandle; - IntPtr godotObject; - godot_string_name name; - - if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( - p_callable, &delegateGCHandle, &godotObject, &name).ToBool()) + 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); + return new Callable((Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target); } else { @@ -1053,39 +1044,32 @@ namespace Godot.NativeInterop public static godot_signal ConvertSignalToNative(ref SignalInfo p_managed_signal) { ulong ownerId = p_managed_signal.Owner.GetInstanceId(); - unsafe - { - godot_string_name name; - - if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty) - { - godot_string_name src = p_managed_signal.Name.NativeValue; - name = NativeFuncs.godotsharp_string_name_new_copy(&src); - } - else - { - name = default; - } + godot_string_name name; - return new godot_signal() - { - _name = name, - _objectId = ownerId - }; + 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 unsafe SignalInfo ConvertSignalToManaged(godot_signal* p_signal) + public static SignalInfo ConvertSignalToManaged(in godot_signal p_signal) { - var owner = GD.InstanceFromId((*p_signal)._objectId); + var owner = GD.InstanceFromId(p_signal.ObjectId); var name = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(&(*p_signal)._name)); + NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name)); return new SignalInfo(owner, name); } // Array - public static unsafe object[] Array_to_mono_array(godot_array* p_array) + public static object[] ConvertNativeGodotArrayToSystemArray(in godot_array p_array) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); @@ -1093,27 +1077,27 @@ namespace Godot.NativeInterop int length = array.Count; var ret = new object[length]; - array.CopyTo(ret, 0); // variant_to_mono_object handled by Collections.Array + array.CopyTo(ret, 0); // ConvertVariantToManagedObject handled by Collections.Array return ret; } - public static unsafe object Array_to_mono_array_of_godot_object_type(godot_array* p_array, Type type) + private static object ConvertNativeGodotArrayToSystemArrayOfType(in godot_array p_array, Type type) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); int length = array.Count; - object ret = Activator.CreateInstance(type, length); + object ret = Activator.CreateInstance(type, length)!; - // variant_to_mono_object handled by Collections.Array - // variant_to_mono_object_of_type is not needed because target element types are Godot.Object (or derived) + // ConvertVariantToManagedObject handled by Collections.Array + // ConvertVariantToManagedObjectOfType is not needed because target element types are Godot.Object (or derived) array.CopyTo((object[])ret, 0); return ret; } - public static godot_array mono_array_to_Array(object[] p_array) + public static godot_array ConvertSystemArrayToNativeGodotArray(object[] p_array) { int length = p_array.Length; @@ -1126,14 +1110,28 @@ namespace Godot.NativeInterop for (int i = 0; i < length; i++) array[i] = p_array[i]; - godot_array src = array.NativeValue; - unsafe - { - return NativeFuncs.godotsharp_array_new_copy(&src); - } + var src = (godot_array)array.NativeValue; + return NativeFuncs.godotsharp_array_new_copy(src); + } + + public static godot_array ConvertSystemArrayToNativeGodotArray(T[] p_array) + { + int length = p_array.Length; + + if (length == 0) + return NativeFuncs.godotsharp_array_new(); + + using var array = new Collections.Array(); + array.Resize(length); + + for (int i = 0; i < length; i++) + array[i] = p_array[i]; + + var src = (godot_array)array.NativeValue; + return NativeFuncs.godotsharp_array_new_copy(src); } - public static godot_array mono_array_to_Array(IList p_array) + public static godot_array ConvertIListToNativeGodotArray(IList p_array) { int length = p_array.Count; @@ -1146,26 +1144,25 @@ namespace Godot.NativeInterop for (int i = 0; i < length; i++) array[i] = p_array[i]; - godot_array src = array.NativeValue; - unsafe - { - return NativeFuncs.godotsharp_array_new_copy(&src); - } + var src = (godot_array)array.NativeValue; + return NativeFuncs.godotsharp_array_new_copy(src); } // PackedByteArray - public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array) + public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array) { - byte* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + byte* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); var array = new byte[size]; fixed (byte* dest = array) Buffer.MemoryCopy(buffer, dest, size, size); return array; } - public static unsafe godot_packed_byte_array mono_array_to_PackedByteArray(Span p_array) + public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(Span p_array) { if (p_array.IsEmpty) return new godot_packed_byte_array(); @@ -1175,10 +1172,12 @@ namespace Godot.NativeInterop // PackedInt32Array - public static unsafe int[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array) + public static unsafe int[] ConvertNativePackedInt32ArrayToSystemArray(godot_packed_int32_array p_array) { - int* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + int* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(int); var array = new int[size]; fixed (int* dest = array) @@ -1186,7 +1185,7 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_int32_array mono_array_to_PackedInt32Array(Span p_array) + public static unsafe godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_int32_array(); @@ -1196,10 +1195,12 @@ namespace Godot.NativeInterop // PackedInt64Array - public static unsafe long[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array) + public static unsafe long[] ConvertNativePackedInt64ArrayToSystemArray(godot_packed_int64_array p_array) { - long* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + long* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(long); var array = new long[size]; fixed (long* dest = array) @@ -1207,7 +1208,7 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_int64_array mono_array_to_PackedInt64Array(Span p_array) + public static unsafe godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_int64_array(); @@ -1217,10 +1218,12 @@ namespace Godot.NativeInterop // PackedFloat32Array - public static unsafe float[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array) + public static unsafe float[] ConvertNativePackedFloat32ArrayToSystemArray(godot_packed_float32_array p_array) { - float* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + float* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(float); var array = new float[size]; fixed (float* dest = array) @@ -1228,7 +1231,8 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_float32_array mono_array_to_PackedFloat32Array(Span p_array) + public static unsafe godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array( + Span p_array) { if (p_array.IsEmpty) return new godot_packed_float32_array(); @@ -1238,10 +1242,12 @@ namespace Godot.NativeInterop // PackedFloat64Array - public static unsafe double[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array) + public static unsafe double[] ConvertNativePackedFloat64ArrayToSystemArray(godot_packed_float64_array p_array) { - double* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + double* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(double); var array = new double[size]; fixed (double* dest = array) @@ -1249,7 +1255,8 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_float64_array mono_array_to_PackedFloat64Array(Span p_array) + public static unsafe godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array( + Span p_array) { if (p_array.IsEmpty) return new godot_packed_float64_array(); @@ -1259,19 +1266,19 @@ namespace Godot.NativeInterop // PackedStringArray - public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) + public static unsafe string[] ConvertNativePackedStringArrayToSystemArray(godot_packed_string_array p_array) { - godot_string* buffer = (*p_array)._ptr; - if (buffer == null) - return new string[] { }; - int size = (*p_array).Size; + godot_string* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); var array = new string[size]; for (int i = 0; i < size; i++) - array[i] = mono_string_from_godot(buffer[i]); + array[i] = ConvertStringToManaged(buffer[i]); return array; } - public static unsafe godot_packed_string_array mono_array_to_PackedStringArray(Span p_array) + public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(Span p_array) { godot_packed_string_array dest = new godot_packed_string_array(); @@ -1283,8 +1290,8 @@ namespace Godot.NativeInterop for (int i = 0; i < p_array.Length; i++) { - using godot_string godotStrElem = mono_string_to_godot(p_array[i]); - NativeFuncs.godotsharp_packed_string_array_add(&dest, &godotStrElem); + using godot_string godotStrElem = ConvertStringToNative(p_array[i]); + NativeFuncs.godotsharp_packed_string_array_add(ref dest, godotStrElem); } return dest; @@ -1292,10 +1299,12 @@ namespace Godot.NativeInterop // PackedVector2Array - public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array) + public static unsafe Vector2[] ConvertNativePackedVector2ArrayToSystemArray(godot_packed_vector2_array p_array) { - Vector2* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + Vector2* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(Vector2); var array = new Vector2[size]; fixed (Vector2* dest = array) @@ -1303,7 +1312,8 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_vector2_array mono_array_to_PackedVector2Array(Span p_array) + public static unsafe godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array( + Span p_array) { if (p_array.IsEmpty) return new godot_packed_vector2_array(); @@ -1313,10 +1323,12 @@ namespace Godot.NativeInterop // PackedVector3Array - public static unsafe Vector3[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array) + public static unsafe Vector3[] ConvertNativePackedVector3ArrayToSystemArray(godot_packed_vector3_array p_array) { - Vector3* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + Vector3* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(Vector3); var array = new Vector3[size]; fixed (Vector3* dest = array) @@ -1324,7 +1336,8 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_vector3_array mono_array_to_PackedVector3Array(Span p_array) + public static unsafe godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array( + Span p_array) { if (p_array.IsEmpty) return new godot_packed_vector3_array(); @@ -1334,10 +1347,12 @@ namespace Godot.NativeInterop // PackedColorArray - public static unsafe Color[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array) + public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array) { - Color* buffer = (*p_array)._ptr; - int size = (*p_array).Size; + Color* buffer = p_array.Buffer; + int size = p_array.Size; + if (size == 0) + return Array.Empty(); int sizeInBytes = size * sizeof(Color); var array = new Color[size]; fixed (Color* dest = array) @@ -1345,7 +1360,7 @@ namespace Godot.NativeInterop return array; } - public static unsafe godot_packed_color_array mono_array_to_PackedColorArray(Span p_array) + public static unsafe godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(Span p_array) { if (p_array.IsEmpty) return new godot_packed_color_array(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 8bc785f375..f1cccfed0a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -1,15 +1,23 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; // ReSharper disable InconsistentNaming namespace Godot.NativeInterop { -#if !NET - // This improves P/Invoke performance. - // The attribute is not available with .NET Core and it's not needed there. - [System.Security.SuppressUnmanagedCodeSecurity] -#endif + /* + * TODO: + * P/Invoke pins by-ref parameters in case the managed memory is moved. + * That's not needed here since we use "ref structs" which are stack-only. + * Unfortunately, the runtime is not smart enough and pins them anyway. + * We want to avoid pinning, so we must wrap these DllImports in methods + * that reinterpret refs and pointers with CustomUnsafe.AsPointer/AsRef. + * I wish such unnecessary boilerplate wasn't needed... + */ + + [SuppressMessage("Interoperability", "CA1401", + MessageId = "P/Invokes should not be visible" /* Meh. What are you gonna do about it? */)] public static unsafe partial class NativeFuncs { private const string GodotDllName = "__Internal"; @@ -17,15 +25,15 @@ namespace Godot.NativeInterop // Custom functions [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, + public static extern IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname, char* p_methodname); [DllImport(GodotDllName)] public static extern delegate* unmanaged godotsharp_get_class_constructor( - ref godot_string_name p_classname); + in godot_string_name p_classname); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); + public static extern IntPtr godotsharp_engine_get_singleton(in godot_string p_name); [DllImport(GodotDllName)] internal static extern void godotsharp_internal_object_disposed(IntPtr ptr); @@ -35,61 +43,64 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj, - godot_string_name* eventSignal); + in godot_string_name eventSignal); [DllImport(GodotDllName)] internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source, - ref godot_string_name signal, + in godot_string_name signal, IntPtr target, IntPtr awaiterHandlePtr); [DllImport(GodotDllName)] - public static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, - IntPtr unmanaged, godot_string_name* nativeName, godot_bool refCounted); + internal static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, in godot_string_name nativeName, godot_bool refCounted); [DllImport(GodotDllName)] - public static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, + internal static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, IntPtr unmanaged, IntPtr scriptPtr, godot_bool refCounted); [DllImport(GodotDllName)] - public static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( + internal static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( IntPtr gcHandleIntPtr, IntPtr unmanaged); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged, - godot_bool* r_has_cs_script_instance); + internal static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged, + out godot_bool r_has_cs_script_instance); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); + internal static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, + internal static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, IntPtr oldGCHandlePtr); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_internal_new_csharp_script(); + internal static extern IntPtr godotsharp_internal_new_csharp_script(); [DllImport(GodotDllName)] - public static extern void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, - godot_array* p_input, godot_array* r_output); + internal static extern 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); [DllImport(GodotDllName)] - public static extern void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, - godot_array* r_output); + internal static extern void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input, + out godot_array r_output); [DllImport(GodotDllName)] public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); [DllImport(GodotDllName)] - public static extern void godotsharp_string_name_new_from_string(godot_string_name* dest, godot_string* name); + public static extern void godotsharp_string_name_new_from_string(out godot_string_name r_dest, + in godot_string p_name); [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_new_from_string(godot_node_path* dest, godot_string* name); + public static extern void godotsharp_node_path_new_from_string(out godot_node_path r_dest, + in godot_string p_name); [DllImport(GodotDllName)] - public static extern void godotsharp_string_name_as_string(godot_string* r_dest, godot_string_name* p_name); + public static extern void + godotsharp_string_name_as_string(out godot_string r_dest, in godot_string_name p_name); [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np); + public static extern void godotsharp_node_path_as_string(out godot_string r_dest, in godot_node_path p_np); [DllImport(GodotDllName)] public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, @@ -124,16 +135,24 @@ namespace Godot.NativeInterop int p_length); [DllImport(GodotDllName)] - public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, - godot_string* p_element); + public static extern void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest, + in godot_string p_element); [DllImport(GodotDllName)] public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, - godot_callable* r_callable); + out godot_callable r_callable); + + [DllImport(GodotDllName)] + internal static extern 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); [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, - IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); + internal static extern 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); + + [DllImport(GodotDllName)] + internal static extern void godotsharp_callable_call_deferred(in godot_callable p_callable, + godot_variant** p_args, int p_arg_count); // GDNative functions @@ -145,219 +164,223 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, - godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); + godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error); // variant.h [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_string_name(godot_variant* r_dest, godot_string_name* p_s); + public static extern void + godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_node_path(godot_variant* r_dest, godot_node_path* p_np); + public static extern void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_object(godot_variant* r_dest, IntPtr p_obj); + public static extern void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_transform2d(godot_variant* r_dest, Transform2D* p_t2d); + public static extern void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_vector4(godot_variant* r_dest, Vector4* p_vec4); + public static extern void godotsharp_variant_new_vector4(out godot_variant r_dest, in Vector4 p_vec4); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_vector4i(godot_variant* r_dest, Vector4i* p_vec4i); + public static extern void godotsharp_variant_new_vector4i(out godot_variant r_dest, in Vector4i p_vec4i); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_basis(godot_variant* r_dest, Basis* p_basis); + public static extern void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_transform3d(godot_variant* r_dest, Transform3D* p_trans); + public static extern void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_projection(godot_variant* r_dest, Projection* p_proj); + public static extern void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_aabb(godot_variant* r_dest, AABB* p_aabb); + public static extern void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_dictionary(godot_variant* r_dest, godot_dictionary* p_dict); + public static extern void godotsharp_variant_new_dictionary(out godot_variant r_dest, + in godot_dictionary p_dict); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr); + public static extern void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, - godot_packed_byte_array* p_pba); + public static extern void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest, + in godot_packed_byte_array p_pba); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, - godot_packed_int32_array* p_pia); + public static extern void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest, + in godot_packed_int32_array p_pia); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, - godot_packed_int64_array* p_pia); + public static extern void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest, + in godot_packed_int64_array p_pia); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, - godot_packed_float32_array* p_pra); + public static extern void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest, + in godot_packed_float32_array p_pra); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, - godot_packed_float64_array* p_pra); + public static extern void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest, + in godot_packed_float64_array p_pra); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, - godot_packed_string_array* p_psa); + public static extern void godotsharp_variant_new_packed_string_array(out godot_variant r_dest, + in godot_packed_string_array p_psa); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, - godot_packed_vector2_array* p_pv2a); + public static extern void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest, + in godot_packed_vector2_array p_pv2a); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, - godot_packed_vector3_array* p_pv3a); + public static extern void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest, + in godot_packed_vector3_array p_pv3a); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, - godot_packed_color_array* p_pca); + public static extern void godotsharp_variant_new_packed_color_array(out godot_variant r_dest, + in godot_packed_color_array p_pca); [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_variant_as_bool(godot_variant* p_self); + public static extern godot_bool godotsharp_variant_as_bool(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); + public static extern Int64 godotsharp_variant_as_int(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern double godotsharp_variant_as_float(godot_variant* p_self); + public static extern double godotsharp_variant_as_float(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_string godotsharp_variant_as_string(godot_variant* p_self); + public static extern godot_string godotsharp_variant_as_string(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Vector2 godotsharp_variant_as_vector2(godot_variant* p_self); + public static extern Vector2 godotsharp_variant_as_vector2(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Vector2i godotsharp_variant_as_vector2i(godot_variant* p_self); + public static extern Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Rect2 godotsharp_variant_as_rect2(godot_variant* p_self); + public static extern Rect2 godotsharp_variant_as_rect2(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Rect2i godotsharp_variant_as_rect2i(godot_variant* p_self); + public static extern Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Vector3 godotsharp_variant_as_vector3(godot_variant* p_self); + public static extern Vector3 godotsharp_variant_as_vector3(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Vector3i godotsharp_variant_as_vector3i(godot_variant* p_self); + public static extern Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Transform2D godotsharp_variant_as_transform2d(godot_variant* p_self); + public static extern Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Vector4 godotsharp_variant_as_vector4(godot_variant* p_self); + public static extern Vector4 godotsharp_variant_as_vector4(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Vector4i godotsharp_variant_as_vector4i(godot_variant* p_self); + public static extern Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Plane godotsharp_variant_as_plane(godot_variant* p_self); + public static extern Plane godotsharp_variant_as_plane(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Quaternion godotsharp_variant_as_quaternion(godot_variant* p_self); + public static extern Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern AABB godotsharp_variant_as_aabb(godot_variant* p_self); + public static extern AABB godotsharp_variant_as_aabb(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Basis godotsharp_variant_as_basis(godot_variant* p_self); + public static extern Basis godotsharp_variant_as_basis(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Transform3D godotsharp_variant_as_transform3d(godot_variant* p_self); + public static extern Transform3D godotsharp_variant_as_transform3d(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Projection godotsharp_variant_as_projection(godot_variant* p_self); + public static extern Projection godotsharp_variant_as_projection(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern Color godotsharp_variant_as_color(godot_variant* p_self); + public static extern Color godotsharp_variant_as_color(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_string_name godotsharp_variant_as_string_name(godot_variant* p_self); + public static extern godot_string_name godotsharp_variant_as_string_name(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_node_path godotsharp_variant_as_node_path(godot_variant* p_self); + public static extern godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern RID godotsharp_variant_as_rid(godot_variant* p_self); + public static extern RID godotsharp_variant_as_rid(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_callable godotsharp_variant_as_callable(godot_variant* p_self); + public static extern godot_callable godotsharp_variant_as_callable(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_signal godotsharp_variant_as_signal(godot_variant* p_self); + public static extern godot_signal godotsharp_variant_as_signal(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_dictionary godotsharp_variant_as_dictionary(godot_variant* p_self); + public static extern godot_dictionary godotsharp_variant_as_dictionary(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_array godotsharp_variant_as_array(godot_variant* p_self); + public static extern godot_array godotsharp_variant_as_array(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(godot_variant* p_self); + public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(godot_variant* p_self); + public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self); + public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(in godot_variant p_self); [DllImport(GodotDllName)] public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array( - godot_variant* p_self); + in godot_variant p_self); [DllImport(GodotDllName)] public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array( - godot_variant* p_self); + in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self); + public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array( + in godot_variant p_self); [DllImport(GodotDllName)] public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array( - godot_variant* p_self); + in godot_variant p_self); [DllImport(GodotDllName)] public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array( - godot_variant* p_self); + in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self); + public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self); [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_variant_equals(godot_variant* p_a, godot_variant* p_b); + public static extern godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b); // string.h [DllImport(GodotDllName)] - public static extern void godotsharp_string_new_with_utf16_chars(godot_string* r_dest, char* p_contents); + public static extern void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents); // string_name.h [DllImport(GodotDllName)] - public static extern void godotsharp_string_name_new_copy(godot_string_name* r_dest, godot_string_name* p_src); + public static extern void godotsharp_string_name_new_copy(out godot_string_name r_dest, + in godot_string_name p_src); // node_path.h [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_new_copy(godot_node_path* r_dest, godot_node_path* p_src); + public static extern void godotsharp_node_path_new_copy(out godot_node_path r_dest, in godot_node_path p_src); // array.h [DllImport(GodotDllName)] - public static extern void godotsharp_array_new(godot_array* p_self); + public static extern void godotsharp_array_new(out godot_array r_dest); [DllImport(GodotDllName)] - public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + public static extern void godotsharp_array_new_copy(out godot_array r_dest, in godot_array p_src); [DllImport(GodotDllName)] public static extern godot_variant* godotsharp_array_ptrw(ref godot_array p_self); @@ -365,10 +388,11 @@ namespace Godot.NativeInterop // dictionary.h [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_new(godot_dictionary* p_self); + public static extern void godotsharp_dictionary_new(out godot_dictionary r_dest); [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); + public static extern void godotsharp_dictionary_new_copy(out godot_dictionary r_dest, + in godot_dictionary p_src); // destroy functions @@ -426,17 +450,17 @@ namespace Godot.NativeInterop // Array [DllImport(GodotDllName)] - public static extern int godotsharp_array_add(ref godot_array p_self, godot_variant* p_item); + public static extern int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item); [DllImport(GodotDllName)] public static extern void godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest); [DllImport(GodotDllName)] - public static extern int godotsharp_array_index_of(ref godot_array p_self, godot_variant* p_item); + public static extern int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item); [DllImport(GodotDllName)] - public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, godot_variant* p_item); + public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, in godot_variant p_item); [DllImport(GodotDllName)] public static extern void godotsharp_array_remove_at(ref godot_array p_self, int p_index); @@ -448,18 +472,18 @@ namespace Godot.NativeInterop public static extern Error godotsharp_array_shuffle(ref godot_array p_self); [DllImport(GodotDllName)] - public static extern void godotsharp_array_to_string(ref godot_array p_self, godot_string* r_str); + public static extern void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str); // Dictionary [DllImport(GodotDllName)] public static extern godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self, - godot_variant* p_key, + in godot_variant p_key, out godot_variant r_value); [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, godot_variant* p_key, - godot_variant* p_value); + public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key, + in godot_variant p_value); [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest); @@ -475,15 +499,15 @@ namespace Godot.NativeInterop out godot_variant r_key, out godot_variant r_value); [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, godot_variant* p_key, - godot_variant* p_value); + public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key, + in godot_variant p_value); [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_clear(ref godot_dictionary p_self); [DllImport(GodotDllName)] public static extern godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self, - godot_variant* p_key); + in godot_variant p_key); [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, @@ -491,149 +515,153 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, - godot_variant* p_key); + in godot_variant p_key); [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, godot_string* r_str); + public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str); // StringExtensions [DllImport(GodotDllName)] - public static extern void godotsharp_string_md5_buffer(godot_string* p_self, - godot_packed_byte_array* r_md5_buffer); + public static extern void godotsharp_string_md5_buffer(in godot_string p_self, + out godot_packed_byte_array r_md5_buffer); [DllImport(GodotDllName)] - public static extern void godotsharp_string_md5_text(godot_string* p_self, godot_string* r_md5_text); + public static extern void godotsharp_string_md5_text(in godot_string p_self, out godot_string r_md5_text); [DllImport(GodotDllName)] - public static extern int godotsharp_string_rfind(godot_string* p_self, godot_string* p_what, int p_from); + public static extern int godotsharp_string_rfind(in godot_string p_self, in godot_string p_what, int p_from); [DllImport(GodotDllName)] - public static extern int godotsharp_string_rfindn(godot_string* p_self, godot_string* p_what, int p_from); + public static extern int godotsharp_string_rfindn(in godot_string p_self, in godot_string p_what, int p_from); [DllImport(GodotDllName)] - public static extern void godotsharp_string_sha256_buffer(godot_string* p_self, - godot_packed_byte_array* r_sha256_buffer); + public static extern void godotsharp_string_sha256_buffer(in godot_string p_self, + out godot_packed_byte_array r_sha256_buffer); [DllImport(GodotDllName)] - public static extern void godotsharp_string_sha256_text(godot_string* p_self, godot_string* r_sha256_text); + public static extern void godotsharp_string_sha256_text(in godot_string p_self, + out godot_string r_sha256_text); [DllImport(GodotDllName)] - public static extern void godotsharp_string_simplify_path(godot_string* p_self, godot_string* r_simplified_path); + public static extern void godotsharp_string_simplify_path(in godot_string p_self, + out godot_string r_simplified_path); // NodePath [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_as_property_path(ref godot_node_path p_self, + public static extern void godotsharp_node_path_get_as_property_path(in godot_node_path p_self, ref godot_node_path r_dest); [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_concatenated_names(ref godot_node_path p_self, - godot_string* r_names); + public static extern void godotsharp_node_path_get_concatenated_names(in godot_node_path p_self, + out godot_string r_names); [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_concatenated_subnames(ref godot_node_path p_self, - godot_string* r_subnames); + public static extern void godotsharp_node_path_get_concatenated_subnames(in godot_node_path p_self, + out godot_string r_subnames); [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_name(ref godot_node_path p_self, int p_idx, - godot_string* r_name); + public static extern void godotsharp_node_path_get_name(in godot_node_path p_self, int p_idx, + out godot_string r_name); [DllImport(GodotDllName)] - public static extern int godotsharp_node_path_get_name_count(ref godot_node_path p_self); + public static extern int godotsharp_node_path_get_name_count(in godot_node_path p_self); [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_subname(ref godot_node_path p_self, int p_idx, - godot_string* r_subname); + public static extern void godotsharp_node_path_get_subname(in godot_node_path p_self, int p_idx, + out godot_string r_subname); [DllImport(GodotDllName)] - public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self); + public static extern int godotsharp_node_path_get_subname_count(in godot_node_path p_self); [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); + public static extern godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self); // GD, etc [DllImport(GodotDllName)] - public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, godot_bool p_allow_objects, - godot_variant* r_ret); + internal static extern void godotsharp_bytes2var(in godot_packed_byte_array p_bytes, + godot_bool p_allow_objects, + out godot_variant r_ret); [DllImport(GodotDllName)] - public static extern void godotsharp_convert(godot_variant* p_what, int p_type, godot_variant* r_ret); + internal static extern void godotsharp_convert(in godot_variant p_what, int p_type, + out godot_variant r_ret); [DllImport(GodotDllName)] - public static extern int godotsharp_hash(godot_variant* var); + internal static extern int godotsharp_hash(in godot_variant p_var); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_instance_from_id(ulong instanceId); + internal static extern IntPtr godotsharp_instance_from_id(ulong p_instance_id); [DllImport(GodotDllName)] - public static extern void godotsharp_print(godot_string* p_what); + internal static extern void godotsharp_print(in godot_string p_what); [DllImport(GodotDllName)] - public static extern void godotsharp_print_rich(godot_string* p_what); + public static extern void godotsharp_print_rich(in godot_string p_what); [DllImport(GodotDllName)] - public static extern void godotsharp_printerr(godot_string* p_what); + internal static extern void godotsharp_printerr(in godot_string p_what); [DllImport(GodotDllName)] - public static extern void godotsharp_printraw(godot_string* p_what); + internal static extern void godotsharp_printraw(in godot_string p_what); [DllImport(GodotDllName)] - public static extern void godotsharp_prints(godot_string* p_what); + internal static extern void godotsharp_prints(in godot_string p_what); [DllImport(GodotDllName)] - public static extern void godotsharp_printt(godot_string* p_what); + internal static extern void godotsharp_printt(in godot_string p_what); [DllImport(GodotDllName)] - public static extern float godotsharp_randf(); + internal static extern float godotsharp_randf(); [DllImport(GodotDllName)] - public static extern uint godotsharp_randi(); + internal static extern uint godotsharp_randi(); [DllImport(GodotDllName)] - public static extern void godotsharp_randomize(); + internal static extern void godotsharp_randomize(); [DllImport(GodotDllName)] - public static extern double godotsharp_randf_range(double from, double to); + internal static extern double godotsharp_randf_range(double from, double to); [DllImport(GodotDllName)] - public static extern double godotsharp_randfn(double mean, double deviation); + internal static extern double godotsharp_randfn(double mean, double deviation); [DllImport(GodotDllName)] - public static extern int godotsharp_randi_range(int from, int to); + internal static extern int godotsharp_randi_range(int from, int to); [DllImport(GodotDllName)] - public static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); + internal static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); [DllImport(GodotDllName)] - public static extern void godotsharp_seed(ulong seed); + internal static extern void godotsharp_seed(ulong seed); [DllImport(GodotDllName)] - public static extern void godotsharp_weakref(IntPtr obj, godot_ref* r_weak_ref); + internal static extern void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref); [DllImport(GodotDllName)] - public static extern string godotsharp_str(godot_array* p_what, godot_string* r_ret); + internal static extern void godotsharp_str(in godot_array p_what, out godot_string r_ret); [DllImport(GodotDllName)] - public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret); + internal static extern void godotsharp_str2var(in godot_string p_str, out godot_variant r_ret); [DllImport(GodotDllName)] - public static extern void godotsharp_var2bytes(godot_variant* what, godot_bool fullObjects, - godot_packed_byte_array* bytes); + internal static extern void godotsharp_var2bytes(in godot_variant p_what, godot_bool p_full_objects, + out godot_packed_byte_array r_bytes); [DllImport(GodotDllName)] - public static extern void godotsharp_var2str(godot_variant* var, godot_string* r_ret); + internal static extern void godotsharp_var2str(in godot_variant p_var, out godot_string r_ret); [DllImport(GodotDllName)] - public static extern void godotsharp_pusherror(godot_string* type); + internal static extern void godotsharp_pusherror(in godot_string p_str); [DllImport(GodotDllName)] - public static extern void godotsharp_pushwarning(godot_string* type); + internal static extern void godotsharp_pushwarning(in godot_string p_str); // Object [DllImport(GodotDllName)] - public static extern string godotsharp_object_to_string(IntPtr ptr, godot_string* r_str); + public static extern 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 index 089883c7e8..0c49660cf0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -1,83 +1,60 @@ -using System; -using System.Runtime.CompilerServices; - // ReSharper disable InconsistentNaming namespace Godot.NativeInterop { - public static unsafe partial class NativeFuncs + public static partial class NativeFuncs { - public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src) + public static godot_string_name godotsharp_string_name_new_copy(in godot_string_name src) { - godot_string_name ret; - godotsharp_string_name_new_copy(&ret, src); + if (src.IsEmpty) + return default; + godotsharp_string_name_new_copy(out godot_string_name ret, src); return ret; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_string_name godotsharp_string_name_new_copy(godot_string_name src) - => godotsharp_string_name_new_copy(&src); - - public static godot_node_path godotsharp_node_path_new_copy(godot_node_path* src) + public static godot_node_path godotsharp_node_path_new_copy(in godot_node_path src) { - godot_node_path ret; - godotsharp_node_path_new_copy(&ret, src); + if (src.IsEmpty) + return default; + godotsharp_node_path_new_copy(out godot_node_path ret, src); return ret; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_node_path godotsharp_node_path_new_copy(godot_node_path src) - => godotsharp_node_path_new_copy(&src); - public static godot_array godotsharp_array_new() { - godot_array ret; - godotsharp_array_new(&ret); + godotsharp_array_new(out godot_array ret); return ret; } - public static godot_array godotsharp_array_new_copy(godot_array* src) + public static godot_array godotsharp_array_new_copy(in godot_array src) { - godot_array ret; - godotsharp_array_new_copy(&ret, src); + godotsharp_array_new_copy(out godot_array ret, src); return ret; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_array godotsharp_array_new_copy(godot_array src) - => godotsharp_array_new_copy(&src); - public static godot_dictionary godotsharp_dictionary_new() { - godot_dictionary ret; - godotsharp_dictionary_new(&ret); + godotsharp_dictionary_new(out godot_dictionary ret); return ret; } - public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src) + public static godot_dictionary godotsharp_dictionary_new_copy(in godot_dictionary src) { - godot_dictionary ret; - godotsharp_dictionary_new_copy(&ret, src); + godotsharp_dictionary_new_copy(out godot_dictionary ret, src); return ret; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary src) - => godotsharp_dictionary_new_copy(&src); - public static godot_string_name godotsharp_string_name_new_from_string(string name) { - godot_string_name ret; - using godot_string src = Marshaling.mono_string_to_godot(name); - godotsharp_string_name_new_from_string(&ret, &src); + 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) { - godot_node_path ret; - using godot_string src = Marshaling.mono_string_to_godot(name); - godotsharp_node_path_new_from_string(&ret, &src); + 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/VariantSpanHelpers.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs index 2814f9d506..46f31bbf4e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs @@ -2,13 +2,13 @@ using System; namespace Godot.NativeInterop { - internal ref struct VariantSpanDisposer + internal readonly ref struct VariantSpanDisposer { - private readonly Span _variantSpan; + private readonly Span _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 variantSpan) + public VariantSpanDisposer(Span variantSpan) { _variantSpan = variantSpan; } @@ -16,7 +16,7 @@ namespace Godot.NativeInterop public void Dispose() { for (int i = 0; i < _variantSpan.Length; i++) - _variantSpan[i].Dispose(); + _variantSpan[i].DangerousSelfRef.Dispose(); } } @@ -24,7 +24,7 @@ namespace Godot.NativeInterop { // 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 Cleared(this Span span) + public static Span Cleared(this Span 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 index e52454a2e3..f69cc40314 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; // ReSharper disable InconsistentNaming @@ -8,378 +7,353 @@ namespace Godot.NativeInterop public static class VariantUtils { public static godot_variant CreateFromRID(RID from) - => new() { _type = Variant.Type.Rid, _data = { _m_rid = from } }; + => new() { Type = Variant.Type.Rid, RID = from }; public static godot_variant CreateFromBool(bool from) - => new() { _type = Variant.Type.Bool, _data = { _bool = from.ToGodotBool() } }; + => new() { Type = Variant.Type.Bool, Bool = from.ToGodotBool() }; public static godot_variant CreateFromInt(long from) - => new() { _type = Variant.Type.Int, _data = { _int = from } }; + => new() { Type = Variant.Type.Int, Int = from }; public static godot_variant CreateFromInt(ulong from) - => new() { _type = Variant.Type.Int, _data = { _int = (long)from } }; + => new() { Type = Variant.Type.Int, Int = (long)from }; public static godot_variant CreateFromFloat(double from) - => new() { _type = Variant.Type.Float, _data = { _float = from } }; + => new() { Type = Variant.Type.Float, Float = from }; public static godot_variant CreateFromVector2(Vector2 from) - => new() { _type = Variant.Type.Vector2, _data = { _m_vector2 = from } }; + => new() { Type = Variant.Type.Vector2, Vector2 = from }; public static godot_variant CreateFromVector2i(Vector2i from) - => new() { _type = Variant.Type.Vector2i, _data = { _m_vector2i = from } }; + => new() { Type = Variant.Type.Vector2i, Vector2i = from }; public static godot_variant CreateFromVector3(Vector3 from) - => new() { _type = Variant.Type.Vector3, _data = { _m_vector3 = from } }; + => new() { Type = Variant.Type.Vector3, Vector3 = from }; public static godot_variant CreateFromVector3i(Vector3i from) - => new() { _type = Variant.Type.Vector3i, _data = { _m_vector3i = from } }; + => new() { Type = Variant.Type.Vector3i, Vector3i = from }; public static godot_variant CreateFromRect2(Rect2 from) - => new() { _type = Variant.Type.Rect2, _data = { _m_rect2 = from } }; + => new() { Type = Variant.Type.Rect2, Rect2 = from }; public static godot_variant CreateFromRect2i(Rect2i from) - => new() { _type = Variant.Type.Rect2i, _data = { _m_rect2i = from } }; + => new() { Type = Variant.Type.Rect2i, Rect2i = from }; public static godot_variant CreateFromQuaternion(Quaternion from) - => new() { _type = Variant.Type.Quaternion, _data = { _m_quaternion = from } }; + => new() { Type = Variant.Type.Quaternion, Quaternion = from }; public static godot_variant CreateFromColor(Color from) - => new() { _type = Variant.Type.Color, _data = { _m_color = from } }; + => new() { Type = Variant.Type.Color, Color = from }; public static godot_variant CreateFromPlane(Plane from) - => new() { _type = Variant.Type.Plane, _data = { _m_plane = from } }; + => new() { Type = Variant.Type.Plane, Plane = from }; - public static unsafe godot_variant CreateFromTransform2D(Transform2D from) + public static godot_variant CreateFromTransform2D(Transform2D from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_transform2d(&ret, &from); + NativeFuncs.godotsharp_variant_new_transform2d(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromVector4(Vector4 from) + public static godot_variant CreateFromVector4(Vector4 from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_vector4(&ret, &from); + NativeFuncs.godotsharp_variant_new_vector4(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromVector4i(Vector4i from) + public static godot_variant CreateFromVector4i(Vector4i from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_vector4i(&ret, &from); + NativeFuncs.godotsharp_variant_new_vector4i(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromBasis(Basis from) + public static godot_variant CreateFromBasis(Basis from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_basis(&ret, &from); + NativeFuncs.godotsharp_variant_new_basis(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromTransform3D(Transform3D from) + public static godot_variant CreateFromTransform3D(Transform3D from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_transform3d(&ret, &from); + NativeFuncs.godotsharp_variant_new_transform3d(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromProjection(Projection from) + public static godot_variant CreateFromProjection(Projection from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_projection(&ret, &from); + NativeFuncs.godotsharp_variant_new_projection(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromAABB(AABB from) + public static godot_variant CreateFromAABB(AABB from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_aabb(&ret, &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, _data = { _m_callable = from } }; + => new() { Type = Variant.Type.Callable, Callable = from }; // Explicit name to make it very clear public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from) - => new() { _type = Variant.Type.Signal, _data = { _m_signal = from } }; + => new() { Type = Variant.Type.Signal, Signal = from }; // Explicit name to make it very clear public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from) - => new() { _type = Variant.Type.String, _data = { _m_string = from } }; + => new() { Type = Variant.Type.String, String = from }; - public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from) + public static godot_variant CreateFromPackedByteArray(in godot_packed_byte_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_byte_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_byte_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedInt32Array(godot_packed_int32_array* from) + public static godot_variant CreateFromPackedInt32Array(in godot_packed_int32_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_int32_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_int32_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedInt64Array(godot_packed_int64_array* from) + public static godot_variant CreateFromPackedInt64Array(in godot_packed_int64_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_int64_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_int64_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedFloat32Array(godot_packed_float32_array* from) + public static godot_variant CreateFromPackedFloat32Array(in godot_packed_float32_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_float32_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_float32_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedFloat64Array(godot_packed_float64_array* from) + public static godot_variant CreateFromPackedFloat64Array(in godot_packed_float64_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_float64_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_float64_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedStringArray(godot_packed_string_array* from) + public static godot_variant CreateFromPackedStringArray(in godot_packed_string_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_string_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_string_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedVector2Array(godot_packed_vector2_array* from) + public static godot_variant CreateFromPackedVector2Array(in godot_packed_vector2_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_vector2_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_vector2_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedVector3Array(godot_packed_vector3_array* from) + public static godot_variant CreateFromPackedVector3Array(in godot_packed_vector3_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_vector3_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_vector3_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromPackedColorArray(godot_packed_color_array* from) + public static godot_variant CreateFromPackedColorArray(in godot_packed_color_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_packed_color_array(&ret, from); + NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromArray(godot_array* from) + public static godot_variant CreateFromArray(godot_array from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_array(&ret, from); + NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from); return ret; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe godot_variant CreateFromArray(godot_array from) - => CreateFromArray(&from); - - public static unsafe godot_variant CreateFromDictionary(godot_dictionary* from) + public static godot_variant CreateFromDictionary(godot_dictionary from) { - godot_variant ret; - NativeFuncs.godotsharp_variant_new_dictionary(&ret, from); + NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from); return ret; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe godot_variant CreateFromDictionary(godot_dictionary from) - => CreateFromDictionary(&from); - - public static unsafe godot_variant CreateFromStringName(ref godot_string_name arg1) + public static godot_variant CreateFromStringName(godot_string_name from) { - godot_variant ret; - godot_string_name src = arg1; - NativeFuncs.godotsharp_variant_new_string_name(&ret, &src); + NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromNodePath(ref godot_node_path arg1) + public static godot_variant CreateFromNodePath(godot_node_path from) { - godot_variant ret; - godot_node_path src = arg1; - NativeFuncs.godotsharp_variant_new_node_path(&ret, &src); + NativeFuncs.godotsharp_variant_new_node_path(out godot_variant ret, from); return ret; } - public static unsafe godot_variant CreateFromGodotObject(IntPtr from) + public static godot_variant CreateFromGodotObject(IntPtr from) { if (from == IntPtr.Zero) return new godot_variant(); - godot_variant ret; - NativeFuncs.godotsharp_variant_new_object(&ret, from); + NativeFuncs.godotsharp_variant_new_object(out godot_variant ret, from); return ret; } // We avoid the internal call if the stored type is the same we want. - public static unsafe bool ConvertToBool(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Bool ? - (*p_var)._data._bool.ToBool() : + 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 unsafe char ConvertToChar(godot_variant* p_var) - => (char)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + 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 unsafe sbyte ConvertToInt8(godot_variant* p_var) - => (sbyte)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + 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 unsafe Int16 ConvertToInt16(godot_variant* p_var) - => (Int16)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + public static Int16 ConvertToInt16(in godot_variant p_var) + => (Int16)(p_var.Type == Variant.Type.Int ? + p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static unsafe Int32 ConvertToInt32(godot_variant* p_var) - => (Int32)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + public static Int32 ConvertToInt32(in godot_variant p_var) + => (Int32)(p_var.Type == Variant.Type.Int ? + p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static unsafe Int64 ConvertToInt64(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var); + public static Int64 ConvertToInt64(in godot_variant p_var) + => p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var); - public static unsafe byte ConvertToUInt8(godot_variant* p_var) - => (byte)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + 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 unsafe UInt16 ConvertToUInt16(godot_variant* p_var) - => (UInt16)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + public static UInt16 ConvertToUInt16(in godot_variant p_var) + => (UInt16)(p_var.Type == Variant.Type.Int ? + p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var) - => (UInt32)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + public static UInt32 ConvertToUInt32(in godot_variant p_var) + => (UInt32)(p_var.Type == Variant.Type.Int ? + p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var) - => (UInt64)((*p_var)._type == Variant.Type.Int ? - (*p_var)._data._int : + public static UInt64 ConvertToUInt64(in godot_variant p_var) + => (UInt64)(p_var.Type == Variant.Type.Int ? + p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var)); - public static unsafe float ConvertToFloat32(godot_variant* p_var) - => (float)((*p_var)._type == Variant.Type.Float ? - (*p_var)._data._float : + 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 unsafe double ConvertToFloat64(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Float ? - (*p_var)._data._float : + 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 unsafe Vector2 ConvertToVector2(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector2 ? - (*p_var)._data._m_vector2 : + 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 unsafe Vector2i ConvertToVector2i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector2i ? - (*p_var)._data._m_vector2i : + 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 unsafe Rect2 ConvertToRect2(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rect2 ? - (*p_var)._data._m_rect2 : + 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 unsafe Rect2i ConvertToRect2i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rect2i ? - (*p_var)._data._m_rect2i : + 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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Transform2d ? - *(*p_var)._data._transform2d : + 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 unsafe Vector3 ConvertToVector3(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector3 ? - (*p_var)._data._m_vector3 : + 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 unsafe Vector3i ConvertToVector3i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector3i ? - (*p_var)._data._m_vector3i : + 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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector4 ? *(*p_var)._data._vector4 : NativeFuncs.godotsharp_variant_as_vector4(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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector4i ? *(*p_var)._data._vector4i : NativeFuncs.godotsharp_variant_as_vector4i(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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Basis ? - *(*p_var)._data._basis : + 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 unsafe Quaternion ConvertToQuaternion(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Quaternion ? - (*p_var)._data._m_quaternion : + 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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Transform3d ? - *(*p_var)._data._transform3d : + 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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Projection ? *(*p_var)._data._projection : NativeFuncs.godotsharp_variant_as_projection(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(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Aabb ? - *(*p_var)._data._aabb : + 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 unsafe Color ConvertToColor(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Color ? - (*p_var)._data._m_color : + 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 unsafe Plane ConvertToPlane(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Plane ? - (*p_var)._data._m_plane : + 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 unsafe IntPtr ConvertToGodotObject(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero; + public static IntPtr ConvertToGodotObject(in godot_variant p_var) + => p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero; - public static unsafe RID ConvertToRID(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rid ? - (*p_var)._data._m_rid : + 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 unsafe godot_string_name ConvertToStringName(godot_variant* p_var) - => (*p_var)._type == Variant.Type.StringName ? - NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name) : + 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); - public static unsafe godot_node_path ConvertToNodePath(godot_variant* p_var) - => (*p_var)._type == Variant.Type.NodePath ? - NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path) : + 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); - public static unsafe godot_array ConvertToArray(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Array ? - NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array) : + 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); - public static unsafe godot_dictionary ConvertToDictionary(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Dictionary ? - NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary) : + 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); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index b18606b47e..d7b736fbcf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot @@ -42,7 +41,7 @@ namespace Godot /// public sealed class NodePath : IDisposable { - public godot_node_path NativeValue; + internal godot_node_path.movable NativeValue; ~NodePath() { @@ -61,12 +60,12 @@ namespace Godot public void Dispose(bool disposing) { // Always dispose `NativeValue` even if disposing is true - NativeValue.Dispose(); + NativeValue.DangerousSelfRef.Dispose(); } private NodePath(godot_node_path nativeValueToOwn) { - NativeValue = nativeValueToOwn; + NativeValue = (godot_node_path.movable)nativeValueToOwn; } // Explicit name to make it very clear @@ -112,7 +111,7 @@ namespace Godot public NodePath(string path) { if (!string.IsNullOrEmpty(path)) - NativeValue = NativeFuncs.godotsharp_node_path_new_from_string(path); + NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path); } /// @@ -125,22 +124,21 @@ namespace Godot /// Converts this to a string. /// /// The to convert. - public static implicit operator string(NodePath from) => from.ToString(); + public static implicit operator string(NodePath from) => from?.ToString(); /// /// Converts this to a string. /// /// A string representation of this . - public override unsafe string ToString() + public override string ToString() { if (IsEmpty) return string.Empty; - godot_string dest; - godot_node_path src = NativeValue; - NativeFuncs.godotsharp_node_path_as_string(&dest, &src); + var src = (godot_node_path)NativeValue; + NativeFuncs.godotsharp_node_path_as_string(out godot_string dest, src); using (dest) - return Marshaling.mono_string_from_godot(dest); + return Marshaling.ConvertStringToManaged(dest); } /// @@ -161,7 +159,8 @@ namespace Godot public NodePath GetAsPropertyPath() { godot_node_path propertyPath = default; - NativeFuncs.godotsharp_node_path_get_as_property_path(ref NativeValue, ref propertyPath); + var self = (godot_node_path)NativeValue; + NativeFuncs.godotsharp_node_path_get_as_property_path(self, ref propertyPath); return CreateTakingOwnershipOfDisposableValue(propertyPath); } @@ -175,11 +174,12 @@ namespace Godot /// /// /// The names concatenated with /. - public unsafe string GetConcatenatedNames() + public string GetConcatenatedNames() { - using godot_string names = default; - NativeFuncs.godotsharp_node_path_get_concatenated_names(ref NativeValue, &names); - return Marshaling.mono_string_from_godot(names); + var self = (godot_node_path)NativeValue; + NativeFuncs.godotsharp_node_path_get_concatenated_names(self, out godot_string names); + using (names) + return Marshaling.ConvertStringToManaged(names); } /// @@ -193,11 +193,12 @@ namespace Godot /// /// /// The subnames concatenated with :. - public unsafe string GetConcatenatedSubNames() + public string GetConcatenatedSubNames() { - using godot_string subNames = default; - NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames); - return Marshaling.mono_string_from_godot(subNames); + var self = (godot_node_path)NativeValue; + NativeFuncs.godotsharp_node_path_get_concatenated_subnames(self, out godot_string subNames); + using (subNames) + return Marshaling.ConvertStringToManaged(subNames); } /// @@ -213,11 +214,12 @@ namespace Godot /// /// The name index. /// The name at the given index . - public unsafe string GetName(int idx) + public string GetName(int idx) { - using godot_string name = default; - NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name); - return Marshaling.mono_string_from_godot(name); + var self = (godot_node_path)NativeValue; + NativeFuncs.godotsharp_node_path_get_name(self, idx, out godot_string name); + using (name) + return Marshaling.ConvertStringToManaged(name); } /// @@ -228,7 +230,8 @@ namespace Godot /// The number of node names which make up the path. public int GetNameCount() { - return NativeFuncs.godotsharp_node_path_get_name_count(ref NativeValue); + var self = (godot_node_path)NativeValue; + return NativeFuncs.godotsharp_node_path_get_name_count(self); } /// @@ -236,11 +239,12 @@ namespace Godot /// /// The subname index. /// The subname at the given index . - public unsafe string GetSubName(int idx) + public string GetSubName(int idx) { - using godot_string subName = default; - NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName); - return Marshaling.mono_string_from_godot(subName); + var self = (godot_node_path)NativeValue; + NativeFuncs.godotsharp_node_path_get_subname(self, idx, out godot_string subName); + using (subName) + return Marshaling.ConvertStringToManaged(subName); } /// @@ -251,7 +255,8 @@ namespace Godot /// The number of subnames in the path. public int GetSubNameCount() { - return NativeFuncs.godotsharp_node_path_get_subname_count(ref NativeValue); + var self = (godot_node_path)NativeValue; + return NativeFuncs.godotsharp_node_path_get_subname_count(self); } /// @@ -263,13 +268,14 @@ namespace Godot /// If the is an absolute path. public bool IsAbsolute() { - return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue).ToBool(); + var self = (godot_node_path)NativeValue; + return NativeFuncs.godotsharp_node_path_is_absolute(self).ToBool(); } /// /// Returns if the node path is empty. /// /// If the is empty. - public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); + 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 98266ffdfc..5f4dc50c72 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot @@ -47,16 +46,13 @@ namespace Godot while (top != null && top != native) { foreach (var eventSignal in top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any())) + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any())) { - unsafe - { - using var eventSignalName = new StringName(eventSignal.Name); - godot_string_name eventSignalNameAux = eventSignalName.NativeValue; - NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, &eventSignalNameAux); - } + using var eventSignalName = new StringName(eventSignal.Name); + var eventSignalNameSelf = (godot_string_name)eventSignalName.NativeValue; + NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, eventSignalNameSelf); } top = top.BaseType; @@ -128,11 +124,11 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this object. - public override unsafe string ToString() + public override string ToString() { - using godot_string str = default; - NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str); - return Marshaling.mono_string_from_godot(str); + NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str); + using (str) + return Marshaling.ConvertStringToManaged(str); } /// @@ -189,7 +185,7 @@ namespace Godot return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor"; } - internal unsafe bool InternalGodotScriptCallViaReflection(string method, godot_variant** args, int argCount, + internal bool InternalGodotScriptCallViaReflection(string method, NativeVariantPtrArgs args, int argCount, out godot_variant ret) { // Performance is not critical here as this will be replaced with source generators. @@ -213,13 +209,13 @@ namespace Godot for (int i = 0; i < paramCount; i++) { - invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType( args[i], parameters[i].ParameterType); } object retObj = methodInfo.Invoke(this, invokeParams); - ret = Marshaling.mono_object_to_variant(retObj); + ret = Marshaling.ConvertManagedObjectToVariant(retObj); return true; } } @@ -231,7 +227,7 @@ namespace Godot return false; } - internal unsafe bool InternalGodotScriptSetFieldOrPropViaReflection(string name, godot_variant* value) + internal bool InternalGodotScriptSetFieldOrPropViaReflection(string name, in godot_variant value) { // Performance is not critical here as this will be replaced with source generators. Type top = GetType(); @@ -245,7 +241,7 @@ namespace Godot if (fieldInfo != null) { - object valueManaged = Marshaling.variant_to_mono_object_of_type(value, fieldInfo.FieldType); + object valueManaged = Marshaling.ConvertVariantToManagedObjectOfType(value, fieldInfo.FieldType); fieldInfo.SetValue(this, valueManaged); return true; @@ -257,7 +253,8 @@ namespace Godot if (propertyInfo != null) { - object valueManaged = Marshaling.variant_to_mono_object_of_type(value, propertyInfo.PropertyType); + object valueManaged = + Marshaling.ConvertVariantToManagedObjectOfType(value, propertyInfo.PropertyType); propertyInfo.SetValue(this, valueManaged); return true; @@ -284,7 +281,7 @@ namespace Godot if (fieldInfo != null) { object valueManaged = fieldInfo.GetValue(this); - value = Marshaling.mono_object_to_variant(valueManaged); + value = Marshaling.ConvertManagedObjectToVariant(valueManaged); return true; } @@ -295,7 +292,7 @@ namespace Godot if (propertyInfo != null) { object valueManaged = propertyInfo.GetValue(this); - value = Marshaling.mono_object_to_variant(valueManaged); + value = Marshaling.ConvertManagedObjectToVariant(valueManaged); return true; } @@ -306,7 +303,7 @@ namespace Godot return false; } - internal unsafe void InternalRaiseEventSignal(godot_string_name* eventSignalName, godot_variant** args, + internal unsafe void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args, int argc) { // Performance is not critical here as this will be replaced with source generators. @@ -360,9 +357,9 @@ namespace Godot var managedArgs = new object[argc]; - for (uint i = 0; i < argc; i++) + for (int i = 0; i < argc; i++) { - managedArgs[i] = Marshaling.variant_to_mono_object_of_type( + managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType( args[i], parameterInfos[i].ParameterType); } @@ -379,7 +376,8 @@ namespace Godot IntPtr methodBind; fixed (char* methodChars = method) { - methodBind = NativeFuncs.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); + var typeSelf = (godot_string_name)type.NativeValue; + methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodChars); } if (methodBind == IntPtr.Zero) @@ -391,7 +389,8 @@ namespace Godot internal static unsafe delegate* unmanaged ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); + var typeSelf = (godot_string_name)type.NativeValue; + var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf); if (nativeConstructor == null) throw new NativeConstructorNotFoundException(type); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index e38dca414f..369b95c7cb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -73,7 +73,7 @@ namespace Godot case 3: return w; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } set @@ -93,7 +93,7 @@ namespace Godot w = value; break; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 62dec81582..c8bf686afa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -8,19 +8,21 @@ namespace Godot { private bool _completed; private object[] _result; - private Action _action; + private Action _continuation; public SignalAwaiter(Object source, StringName signal, Object target) { - NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), ref signal.NativeValue, + 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(GCHandle.Alloc(this))); } public bool IsCompleted => _completed; - public void OnCompleted(Action action) + public void OnCompleted(Action continuation) { - this._action = action; + _continuation = continuation; } public object[] GetResult() => _result; @@ -48,11 +50,11 @@ namespace Godot object[] signalArgs = new object[argCount]; for (int i = 0; i < argCount; i++) - signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); + signalArgs[i] = Marshaling.ConvertVariantToManagedObject(*args[i]); awaiter._result = signalArgs; - awaiter._action?.Invoke(); + awaiter._continuation?.Invoke(); } catch (Exception e) { 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 /// /// Represents a signal defined in an object. /// - 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 dfdef81f9e..ce89e134b8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -1,12 +1,13 @@ 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 { /// @@ -1077,12 +1078,12 @@ namespace Godot /// /// The string to hash. /// The MD5 hash of the string. - public static unsafe byte[] MD5Buffer(this string instance) + public static byte[] MD5Buffer(this string instance) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_packed_byte_array md5Buffer = default; - NativeFuncs.godotsharp_string_md5_buffer(&instanceStr, &md5Buffer); - return Marshaling.PackedByteArray_to_mono_array(&md5Buffer); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_md5_buffer(instanceStr, out var md5Buffer); + using (md5Buffer) + return Marshaling.ConvertNativePackedByteArrayToSystemArray(md5Buffer); } /// @@ -1091,12 +1092,12 @@ namespace Godot /// /// The string to hash. /// The MD5 hash of the string. - public static unsafe string MD5Text(this string instance) + public static string MD5Text(this string instance) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_string md5Text = default; - NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text); - return Marshaling.mono_string_from_godot(md5Text); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_md5_text(instanceStr, out var md5Text); + using (md5Text) + return Marshaling.ConvertStringToManaged(md5Text); } /// @@ -1251,11 +1252,11 @@ namespace Godot /// The substring to search in the string. /// The position at which to start searching. /// The position at which the substring was found, or -1 if not found. - public static unsafe int RFind(this string instance, string what, int from = -1) + public static int RFind(this string instance, string what, int from = -1) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_string whatStr = Marshaling.mono_string_to_godot(instance); - return NativeFuncs.godotsharp_string_rfind(&instanceStr, &whatStr, from); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + using godot_string whatStr = Marshaling.ConvertStringToNative(instance); + return NativeFuncs.godotsharp_string_rfind(instanceStr, whatStr, from); } /// @@ -1267,11 +1268,11 @@ namespace Godot /// The substring to search in the string. /// The position at which to start searching. /// The position at which the substring was found, or -1 if not found. - public static unsafe int RFindN(this string instance, string what, int from = -1) + public static int RFindN(this string instance, string what, int from = -1) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_string whatStr = Marshaling.mono_string_to_godot(instance); - return NativeFuncs.godotsharp_string_rfindn(&instanceStr, &whatStr, from); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + using godot_string whatStr = Marshaling.ConvertStringToNative(instance); + return NativeFuncs.godotsharp_string_rfindn(instanceStr, whatStr, from); } /// @@ -1326,12 +1327,12 @@ namespace Godot /// /// The string to hash. /// The SHA-256 hash of the string. - public static unsafe byte[] SHA256Buffer(this string instance) + public static byte[] SHA256Buffer(this string instance) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_packed_byte_array sha256Buffer = default; - NativeFuncs.godotsharp_string_sha256_buffer(&instanceStr, &sha256Buffer); - return Marshaling.PackedByteArray_to_mono_array(&sha256Buffer); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_sha256_buffer(instanceStr, out var sha256Buffer); + using (sha256Buffer) + return Marshaling.ConvertNativePackedByteArrayToSystemArray(sha256Buffer); } /// @@ -1340,12 +1341,12 @@ namespace Godot /// /// The string to hash. /// The SHA-256 hash of the string. - public static unsafe string SHA256Text(this string instance) + public static string SHA256Text(this string instance) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_string sha256Text = default; - NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text); - return Marshaling.mono_string_from_godot(sha256Text); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_sha256_text(instanceStr, out var sha256Text); + using (sha256Text) + return Marshaling.ConvertStringToManaged(sha256Text); } /// @@ -1396,12 +1397,12 @@ namespace Godot /// /// Returns a simplified canonical path. /// - public static unsafe string SimplifyPath(this string instance) + public static string SimplifyPath(this string instance) { - using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); - using godot_string simplifiedPath = default; - NativeFuncs.godotsharp_string_simplify_path(&instanceStr, &simplifiedPath); - return Marshaling.mono_string_from_godot(simplifiedPath); + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_simplify_path(instanceStr, out godot_string simplifiedPath); + using (simplifiedPath) + return Marshaling.ConvertStringToManaged(simplifiedPath); } /// @@ -1602,7 +1603,7 @@ namespace Godot /// /// The string to escape. /// The escaped string. - public static string XMLEscape(this string instance) + public static string? XMLEscape(this string instance) { return SecurityElement.Escape(instance); } @@ -1614,9 +1615,9 @@ namespace Godot /// /// The string to unescape. /// The unescaped string. - 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 84b0ab623c..e8bda9b219 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot @@ -13,7 +12,7 @@ namespace Godot /// public sealed class StringName : IDisposable { - public godot_string_name NativeValue; + internal godot_string_name.movable NativeValue; ~StringName() { @@ -32,12 +31,12 @@ namespace Godot public void Dispose(bool disposing) { // Always dispose `NativeValue` even if disposing is true - NativeValue.Dispose(); + NativeValue.DangerousSelfRef.Dispose(); } private StringName(godot_string_name nativeValueToOwn) { - NativeValue = nativeValueToOwn; + NativeValue = (godot_string_name.movable)nativeValueToOwn; } // Explicit name to make it very clear @@ -58,7 +57,7 @@ namespace Godot public StringName(string name) { if (!string.IsNullOrEmpty(name)) - NativeValue = NativeFuncs.godotsharp_string_name_new_from_string(name); + NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name); } /// @@ -71,28 +70,27 @@ namespace Godot /// Converts a to a string. /// /// The to convert. - public static implicit operator string(StringName from) => from.ToString(); + public static implicit operator string(StringName from) => from?.ToString(); /// /// Converts this to a string. /// /// A string representation of this . - public override unsafe string ToString() + public override string ToString() { if (IsEmpty) return string.Empty; - godot_string dest; - godot_string_name src = NativeValue; - NativeFuncs.godotsharp_string_name_as_string(&dest, &src); + var src = (godot_string_name)NativeValue; + NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, src); using (dest) - return Marshaling.mono_string_from_godot(dest); + return Marshaling.ConvertStringToManaged(dest); } /// /// Check whether this is empty. /// /// If the is empty. - public bool IsEmpty => godot_string_name.IsEmpty(in NativeValue); + public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 68d097eb4e..1e3a13ba8e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -93,7 +93,7 @@ namespace Godot case 2: return origin; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(column)); } } set @@ -110,7 +110,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..27de87d7d4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -52,7 +52,7 @@ namespace Godot case 3: return origin; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(column)); } } set @@ -72,7 +72,7 @@ namespace Godot origin = value; return; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(column)); } } } @@ -168,7 +168,7 @@ namespace Godot /// The object to look at. /// The relative up direction. /// The resulting transform. - 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 +194,7 @@ namespace Godot /// The axis to rotate around. Must be normalized. /// The angle to rotate, in radians. /// The rotated transformation matrix. - 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 +443,7 @@ namespace Godot /// /// The object to compare with. /// Whether or not the transform and the object are exactly equal. - public override bool Equals(object obj) + public override readonly bool Equals(object obj) { if (obj is Transform3D) { @@ -460,7 +460,7 @@ namespace Godot /// /// The other transform to compare. /// Whether or not the matrices are exactly equal. - 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/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 67f70390dd..6223f3ae53 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -62,7 +62,7 @@ namespace Godot case 1: return y; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } set @@ -76,7 +76,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..e4c3f869b1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -62,7 +62,7 @@ namespace Godot case 1: return y; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } set @@ -76,7 +76,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..7e98969d34 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -74,7 +74,7 @@ namespace Godot case 2: return z; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } set @@ -91,7 +91,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..88b58e20cd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -74,7 +74,7 @@ namespace Godot case 2: return z; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } set @@ -91,7 +91,7 @@ namespace Godot z = value; return; default: - throw new IndexOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index)); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/FodyWeavers.xml b/modules/mono/glue/GodotSharp/GodotSharp/FodyWeavers.xml new file mode 100644 index 0000000000..d7abb3c982 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GenerateGodotCustomUnsafe.targets b/modules/mono/glue/GodotSharp/GodotSharp/GenerateGodotCustomUnsafe.targets new file mode 100644 index 0000000000..f572f8a75c --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/GenerateGodotCustomUnsafe.targets @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %(GodotInteropStructs.Identity) + + + + + diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 763ded8809..a06b448136 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -10,6 +10,8 @@ true 9 + Recommended + CS1591 @@ -20,6 +22,12 @@ + + + + + + @@ -62,6 +70,7 @@ + -- cgit v1.2.3 From 4d710bf659c0bea5b8f3d6ec65eda047bada0e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Tue, 28 Dec 2021 23:25:16 +0100 Subject: C#: Add initial implementation of source generator for script members This replaces the way we invoke methods and set/get properties. This first iteration rids us of runtime type checking in those cases, as it's now done at compile time. Later it will also stop needing the use of reflection. After that, we will only depend on reflection for generic Godot Array and Dictionary. We're stuck with reflection in generic collections for now as C# doesn't support generic/template specialization. This is only the initial implementation. Further iterations are coming, specially once we switch to the native extension system which completely changes the way members are accessed/invoked. For example, with the native extension system we will likely need to create `UnmanagedCallersOnly` invoke wrapper methods and return function pointers to the engine. Other kind of members, like event signals will be receiving the same treatment in the future. --- .../Attributes/DisableGodotGeneratorsAttribute.cs | 10 -- .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 23 ++-- .../Core/NativeInterop/InteropStructs.cs | 25 ++++ .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 2 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 126 ++------------------- .../glue/GodotSharp/GodotSharp/Core/StringName.cs | 46 ++++++++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 - 7 files changed, 90 insertions(+), 143 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs (limited to 'modules/mono/glue/GodotSharp') 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 -{ - /// - /// An attribute that disables Godot Generators. - /// - [AttributeUsage(AttributeTargets.Class)] - public class DisableGodotGeneratorsAttribute : Attribute { } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 6bb1ba27e9..f28d7b1c51 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -12,7 +12,6 @@ namespace Godot.Bridge { try { - // Performance is not critical here as this will be replaced with source generators. var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) @@ -27,7 +26,7 @@ namespace Godot.Bridge using (dest) methodStr = Marshaling.ConvertStringToManaged(dest); - bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, new NativeVariantPtrArgs(args), + bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method), new NativeVariantPtrArgs(args), argCount, out godot_variant retValue); if (!methodInvoked) @@ -55,21 +54,19 @@ namespace Godot.Bridge { try { - // Performance is not critical here as this will be replaced with source generators. var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) throw new InvalidOperationException(); - var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); - - if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection( - nameManaged.ToString(), CustomUnsafe.AsRef(value))) + if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value))) { return true.ToGodotBool(); } + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); + object valueManaged = Marshaling.ConvertVariantToManagedObject(CustomUnsafe.AsRef(value)); return godotObject._Set(nameManaged, valueManaged).ToGodotBool(); @@ -87,22 +84,20 @@ namespace Godot.Bridge { try { - // Performance is not critical here as this will be replaced with source generators. var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) throw new InvalidOperationException(); - var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); - - if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), - out godot_variant outRetValue)) + if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue)) { *outRet = outRetValue; return true.ToGodotBool(); } + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); + object ret = godotObject._Get(nameManaged); if (ret == null) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index ef20819d62..f15dc941c9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -487,6 +487,31 @@ namespace Godot.NativeInterop 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 diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index f1cccfed0a..c2812b8919 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -26,7 +26,7 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname, - char* p_methodname); + in godot_string_name p_methodname); [DllImport(GodotDllName)] public static extern delegate* unmanaged godotsharp_get_class_constructor( diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 5f4dc50c72..dbffd1d5d1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -185,125 +185,20 @@ namespace Godot return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor"; } - internal bool InternalGodotScriptCallViaReflection(string method, NativeVariantPtrArgs args, int argCount, - out godot_variant ret) + // ReSharper disable once VirtualMemberNeverOverridden.Global + protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) { - // Performance is not critical here as this will be replaced with source generators. - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - var methodInfo = top.GetMethod(method, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (methodInfo != null) - { - var parameters = methodInfo.GetParameters(); - int paramCount = parameters.Length; - - if (argCount == paramCount) - { - object[] invokeParams = new object[paramCount]; - - for (int i = 0; i < paramCount; i++) - { - invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType( - args[i], parameters[i].ParameterType); - } - - object retObj = methodInfo.Invoke(this, invokeParams); - - ret = Marshaling.ConvertManagedObjectToVariant(retObj); - return true; - } - } - - top = top.BaseType; - } - - ret = default; - return false; - } - - internal bool InternalGodotScriptSetFieldOrPropViaReflection(string name, in godot_variant value) - { - // Performance is not critical here as this will be replaced with source generators. - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - var fieldInfo = top.GetField(name, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (fieldInfo != null) - { - object valueManaged = Marshaling.ConvertVariantToManagedObjectOfType(value, fieldInfo.FieldType); - fieldInfo.SetValue(this, valueManaged); - - return true; - } - - var propertyInfo = top.GetProperty(name, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (propertyInfo != null) - { - object valueManaged = - Marshaling.ConvertVariantToManagedObjectOfType(value, propertyInfo.PropertyType); - propertyInfo.SetValue(this, valueManaged); - - return true; - } - - top = top.BaseType; - } - return false; } - internal bool InternalGodotScriptGetFieldOrPropViaReflection(string name, out godot_variant value) + // ReSharper disable once VirtualMemberNeverOverridden.Global + protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) { - // Performance is not critical here as this will be replaced with source generators. - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - var fieldInfo = top.GetField(name, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (fieldInfo != null) - { - object valueManaged = fieldInfo.GetValue(this); - value = Marshaling.ConvertManagedObjectToVariant(valueManaged); - return true; - } - - var propertyInfo = top.GetProperty(name, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (propertyInfo != null) - { - object valueManaged = propertyInfo.GetValue(this); - value = Marshaling.ConvertManagedObjectToVariant(valueManaged); - return true; - } - - top = top.BaseType; - } - value = default; return false; } - internal unsafe void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args, + internal void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args, int argc) { // Performance is not critical here as this will be replaced with source generators. @@ -371,14 +266,11 @@ namespace Godot } } - internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) + internal static IntPtr ClassDB_get_method(StringName type, StringName method) { - IntPtr methodBind; - fixed (char* methodChars = method) - { - var typeSelf = (godot_string_name)type.NativeValue; - methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodChars); - } + 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); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index e8bda9b219..3a415d3deb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -92,5 +92,51 @@ namespace Godot /// /// If the is empty. public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty; + + public static bool operator ==(StringName left, StringName right) + { + if (left is null) + return right is null; + return left.Equals(right); + } + + public static bool operator !=(StringName left, StringName right) + { + return !(left == right); + } + + public bool Equals(StringName other) + { + if (other is null) + return false; + return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef; + } + + public static bool operator ==(StringName left, in godot_string_name right) + { + if (left is null) + return right.IsEmpty; + return left.Equals(right); + } + + 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; + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index a06b448136..d5bbbfb7ca 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -32,7 +32,6 @@ - -- cgit v1.2.3 From f88d8902cfc0d6a9441e794eb47611ef4ed0d46c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Tue, 28 Dec 2021 23:25:16 +0100 Subject: C#: Ensure native handles are freed after switch to .NET Core Finalizers are longer guaranteed to be called on exit now that we switched to .NET Core. This results in native instances leaking. The only solution I can think of so far is to keep a list of all instances alive to dispose when the AssemblyLoadContext.Unloading event is raised. --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 11 ++- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 2 + .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 17 +++- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 5 + .../GodotSharp/Core/DisposablesTracker.cs | 101 +++++++++++++++++++++ .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 7 ++ .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 20 +++- .../glue/GodotSharp/GodotSharp/Core/StringName.cs | 17 ++++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 + 9 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 9fa221a0cc..cd6655b857 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -13,16 +13,21 @@ namespace Godot.Collections /// interfacing with the engine. Otherwise prefer .NET collections /// such as or . /// - public sealed class Array : IList, IDisposable + public sealed class Array : + IList, + IDisposable { internal godot_array.movable NativeValue; + private WeakReference _weakReferenceToSelf; + /// /// Constructs a new empty . /// public Array() { NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } /// @@ -51,6 +56,8 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array)); NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + int length = array.Length; Resize(length); @@ -64,6 +71,7 @@ namespace Godot.Collections NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ? nativeValueToOwn : NativeFuncs.godotsharp_array_new()); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } // Explicit name to make it very clear @@ -88,6 +96,7 @@ namespace Godot.Collections { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 1d19376cdd..bd939ef27d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -33,6 +33,7 @@ namespace Godot.Bridge public delegate* unmanaged GCHandleBridge_FreeGCHandle; public delegate* unmanaged DebuggingUtils_InstallTraceListener; public delegate* unmanaged Dispatcher_InitializeDefaultGodotTaskScheduler; + public delegate* unmanaged DisposablesTracker_OnGodotShuttingDown; // @formatter:on public static ManagedCallbacks Create() @@ -65,6 +66,7 @@ namespace Godot.Bridge GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle, DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener, Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler, + DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown, // @formatter:on }; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 689d6cddbb..e87b7f4d4b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -44,16 +44,19 @@ namespace Godot.Bridge internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName, IntPtr godotObject) { + // TODO: Optimize with source generators and delegate pointers + try { Type nativeType = TypeGetProxyClass(nativeTypeName); var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); - obj.NativePtr = godotObject; - var ctor = nativeType.GetConstructor( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); + + obj.NativePtr = godotObject; + _ = ctor!.Invoke(obj, null); return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); @@ -70,14 +73,14 @@ namespace Godot.Bridge 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 = _scriptBridgeMap[scriptPtr]; var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); - obj.NativePtr = godotObject; - var ctor = scriptType .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(c => c.GetParameters().Length == argCount) @@ -108,7 +111,11 @@ namespace Godot.Bridge *args[i], parameters[i].ParameterType); } - ctor.Invoke(obj, invokeParams); + obj.NativePtr = godotObject; + + _ = ctor.Invoke(obj, invokeParams); + + return true.ToGodotBool(); } catch (Exception e) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 89fc2210b8..4a99872e7b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -18,12 +18,15 @@ namespace Godot.Collections { internal godot_dictionary.movable NativeValue; + private WeakReference _weakReferenceToSelf; + /// /// Constructs a new empty . /// public Dictionary() { NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new(); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } /// @@ -45,6 +48,7 @@ namespace Godot.Collections NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ? nativeValueToOwn : NativeFuncs.godotsharp_dictionary_new()); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } // Explicit name to make it very clear @@ -69,6 +73,7 @@ namespace Godot.Collections { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); } /// 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..bf3b3b083a --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using Godot.NativeInterop; + +#nullable enable + +namespace Godot +{ + internal static class DisposablesTracker + { + static DisposablesTracker() + { + AssemblyLoadContext.Default.Unloading += _ => OnUnloading(); + } + + [UnmanagedCallersOnly] + internal static void OnGodotShuttingDown() + { + try + { + OnUnloading(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } + } + + private static void OnUnloading() + { + 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 item in GodotObjectInstances.Keys) + { + if (item.TryGetTarget(out Object? self)) + self.Dispose(); + } + + foreach (WeakReference 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, object?> GodotObjectInstances { get; } = + new(); + + private static ConcurrentDictionary, object?> OtherInstances { get; } = + new(); + + public static WeakReference RegisterGodotObject(Object godotObject) + { + var weakReferenceToSelf = new WeakReference(godotObject); + GodotObjectInstances.TryAdd(weakReferenceToSelf, null); + return weakReferenceToSelf; + } + + public static WeakReference RegisterDisposable(IDisposable disposable) + { + var weakReferenceToSelf = new WeakReference(disposable); + OtherInstances.TryAdd(weakReferenceToSelf, null); + return weakReferenceToSelf; + } + + public static void UnregisterGodotObject(WeakReference weakReference) + { + if (!GodotObjectInstances.TryRemove(weakReference, out _)) + throw new ArgumentException("Godot Object not registered", nameof(weakReference)); + } + + public static void UnregisterDisposable(WeakReference weakReference) + { + if (!OtherInstances.TryRemove(weakReference, out _)) + throw new ArgumentException("Disposable not registered", nameof(weakReference)); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index d7b736fbcf..6edc19c4d6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -43,6 +43,8 @@ namespace Godot { internal godot_node_path.movable NativeValue; + private WeakReference _weakReferenceToSelf; + ~NodePath() { Dispose(false); @@ -61,11 +63,13 @@ namespace Godot { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); } private NodePath(godot_node_path nativeValueToOwn) { NativeValue = (godot_node_path.movable)nativeValueToOwn; + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } // Explicit name to make it very clear @@ -111,7 +115,10 @@ namespace Godot public NodePath(string path) { if (!string.IsNullOrEmpty(path)) + { NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + } } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index dbffd1d5d1..a3a4c2599e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -11,7 +11,9 @@ namespace Godot private Type _cachedType = typeof(Object); internal IntPtr NativePtr; - internal bool MemoryOwn; + private bool _memoryOwn; + + private WeakReference _weakReferenceToSelf; /// /// Constructs a new . @@ -34,6 +36,8 @@ namespace Godot GetType(), _cachedType); } + _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this); + _InitializeGodotScriptInstanceInternals(); } @@ -61,7 +65,7 @@ namespace Godot internal Object(bool memoryOwn) { - MemoryOwn = memoryOwn; + _memoryOwn = memoryOwn; } /// @@ -74,7 +78,12 @@ namespace Godot 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.NativePtr; @@ -104,9 +113,8 @@ namespace Godot if (NativePtr != IntPtr.Zero) { - if (MemoryOwn) + if (_memoryOwn) { - MemoryOwn = false; NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool()); } else @@ -117,6 +125,8 @@ namespace Godot NativePtr = IntPtr.Zero; } + DisposablesTracker.UnregisterGodotObject(_weakReferenceToSelf); + _disposed = true; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index 3a415d3deb..b993a1b3e9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -14,6 +14,8 @@ namespace Godot { internal godot_string_name.movable NativeValue; + private WeakReference _weakReferenceToSelf; + ~StringName() { Dispose(false); @@ -32,11 +34,13 @@ namespace Godot { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); } private StringName(godot_string_name nativeValueToOwn) { NativeValue = (godot_string_name.movable)nativeValueToOwn; + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); } // Explicit name to make it very clear @@ -57,7 +61,10 @@ namespace Godot public StringName(string name) { if (!string.IsNullOrEmpty(name)) + { NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name); + _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this); + } } /// @@ -138,5 +145,15 @@ namespace Godot { 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/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index d5bbbfb7ca..f1a397f8fa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -60,6 +60,7 @@ + -- cgit v1.2.3 From 88e367a4066773a6fbfe2ea25dc2e81d2035d791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Tue, 28 Dec 2021 23:25:16 +0100 Subject: C#/netcore: Add base desktop game export implementation This base implementation is still very barebones but it defines the path for how exporting will work (at least when embedding the .NET runtime). Many manual steps are still needed, which should be automatized in the future. For example, in addition to the API assemblies, now you also need to copy the GodotPlugins assembly to each game project. --- modules/mono/glue/GodotSharp/GodotPlugins/Main.cs | 93 ++++------------------ .../GodotSharp/GodotPlugins/PluginLoadContext.cs | 2 - .../Core/Attributes/GodotMethodAttribute.cs | 24 ------ .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 27 +++++-- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 9 ++- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 66 +-------------- .../Core/NativeInterop/GodotDllImportResolver.cs | 64 +++++++++++++++ .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 34 +++++--- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 2 +- .../GodotSharp/Properties/AssemblyInfo.cs | 1 - 10 files changed, 136 insertions(+), 186 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs index 9f938373c4..0cb9e57530 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -4,6 +4,7 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; +using Godot.Bridge; using Godot.NativeInterop; namespace GodotPlugins @@ -13,6 +14,7 @@ namespace GodotPlugins private static readonly List SharedAssemblies = new(); private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly; private static Assembly? _editorApiAssembly; + private static Assembly? _projectAssembly; private static readonly AssemblyLoadContext MainLoadContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? @@ -20,67 +22,59 @@ namespace GodotPlugins // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later. [UnmanagedCallersOnly] - internal static unsafe godot_bool Initialize(godot_bool editorHint, - PluginsCallbacks* pluginsCallbacks, Godot.Bridge.ManagedCallbacks* managedCallbacks) + // ReSharper disable once UnusedMember.Local + private static unsafe godot_bool InitializeFromEngine(godot_bool editorHint, + PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks) { try { SharedAssemblies.Add(CoreApiAssembly.GetName()); + NativeLibrary.SetDllImportResolver(CoreApiAssembly, GodotDllImportResolver.OnResolveDllImport); if (editorHint.ToBool()) { _editorApiAssembly = Assembly.Load("GodotSharpEditor"); SharedAssemblies.Add(_editorApiAssembly.GetName()); + NativeLibrary.SetDllImportResolver(_editorApiAssembly, GodotDllImportResolver.OnResolveDllImport); } - NativeLibrary.SetDllImportResolver(CoreApiAssembly, OnResolveDllImport); - *pluginsCallbacks = new() { LoadProjectAssemblyCallback = &LoadProjectAssembly, LoadToolsAssemblyCallback = &LoadToolsAssembly, }; - *managedCallbacks = Godot.Bridge.ManagedCallbacks.Create(); + *managedCallbacks = ManagedCallbacks.Create(); return godot_bool.True; } catch (Exception e) { Console.Error.WriteLine(e); - *pluginsCallbacks = default; - *managedCallbacks = default; return false.ToGodotBool(); } } [StructLayout(LayoutKind.Sequential)] - internal struct PluginsCallbacks + private struct PluginsCallbacks { public unsafe delegate* unmanaged LoadProjectAssemblyCallback; public unsafe delegate* unmanaged LoadToolsAssemblyCallback; } [UnmanagedCallersOnly] - internal static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath) + private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath) { try { - string assemblyPath = new(nAssemblyPath); + if (_projectAssembly != null) + return godot_bool.True; // Already loaded - var assembly = LoadPlugin(assemblyPath); - - var method = CoreApiAssembly.GetType("Godot.Bridge.ScriptManagerBridge")? - .GetMethod("LookupScriptsInAssembly", - BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + string assemblyPath = new(nAssemblyPath); - if (method == null) - { - throw new MissingMethodException("Godot.Bridge.ScriptManagerBridge", - "LookupScriptsInAssembly"); - } + _projectAssembly = LoadPlugin(assemblyPath); - method.Invoke(null, new object[] { assembly }); + ScriptManagerBridge.LookupScriptsInAssembly(_projectAssembly); return godot_bool.True; } @@ -92,7 +86,7 @@ namespace GodotPlugins } [UnmanagedCallersOnly] - internal static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath) + private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath) { try { @@ -103,7 +97,7 @@ namespace GodotPlugins var assembly = LoadPlugin(assemblyPath); - NativeLibrary.SetDllImportResolver(assembly, OnResolveDllImport); + NativeLibrary.SetDllImportResolver(assembly, GodotDllImportResolver.OnResolveDllImport); var method = assembly.GetType("GodotTools.GodotSharpEditor")? .GetMethod("InternalCreateInstance", @@ -140,58 +134,5 @@ namespace GodotPlugins var loadContext = new PluginLoadContext(assemblyPath, sharedAssemblies, MainLoadContext); return loadContext.LoadFromAssemblyName(new AssemblyName(assemblyName)); } - - public static 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 Linux.dlopen(IntPtr.Zero, Linux.RTLD_LAZY); - } - 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 Linux - { - // libdl.so was resulting in DllNotFoundException, for some reason... - // libcoreclr.so should work with both CoreCLR and the .NET Core version of Mono. - private const string SystemLibrary = "libcoreclr.so"; - - 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/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs index 1b969716aa..982549fff7 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs @@ -15,8 +15,6 @@ namespace GodotPlugins public PluginLoadContext(string pluginPath, ICollection sharedAssemblies, AssemblyLoadContext mainLoadContext) { - Console.WriteLine(pluginPath); - Console.Out.Flush(); _resolver = new AssemblyDependencyResolver(pluginPath); _sharedAssemblies = sharedAssemblies; _mainLoadContext = mainLoadContext; 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 -{ - /// - /// An attribute for a method. - /// - [AttributeUsage(AttributeTargets.Method)] - internal class GodotMethodAttribute : Attribute - { - private string methodName; - - public string MethodName { get { return methodName; } } - - /// - /// Constructs a new GodotMethodAttribute instance. - /// - /// The name of the method. - public GodotMethodAttribute(string methodName) - { - this.methodName = methodName; - } - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index f28d7b1c51..266df07d63 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -21,12 +21,8 @@ namespace Godot.Bridge return false.ToGodotBool(); } - NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, CustomUnsafe.AsRef(method)); - string methodStr; - using (dest) - methodStr = Marshaling.ConvertStringToManaged(dest); - - bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method), new NativeVariantPtrArgs(args), + bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method), + new NativeVariantPtrArgs(args), argCount, out godot_variant retValue); if (!methodInvoked) @@ -168,5 +164,24 @@ namespace Godot.Bridge *outValid = false.ToGodotBool(); } } + + [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 false.ToGodotBool(); + + return godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(method)).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + return false.ToGodotBool(); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index bd939ef27d..fb1efa0ac8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -5,7 +5,7 @@ using Godot.NativeInterop; namespace Godot.Bridge { [StructLayout(LayoutKind.Sequential)] - internal unsafe struct ManagedCallbacks + public unsafe struct ManagedCallbacks { // @formatter:off public delegate* unmanaged SignalAwaiter_SignalCallback; @@ -19,7 +19,6 @@ namespace Godot.Bridge public delegate* unmanaged ScriptManagerBridge_RaiseEventSignal; public delegate* unmanaged ScriptManagerBridge_GetScriptSignalList; public delegate* unmanaged ScriptManagerBridge_HasScriptSignal; - public delegate* unmanaged ScriptManagerBridge_HasMethodUnknownParams; public delegate* unmanaged ScriptManagerBridge_ScriptIsOrInherits; public delegate* unmanaged ScriptManagerBridge_AddScriptBridge; public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; @@ -30,6 +29,7 @@ namespace Godot.Bridge public delegate* unmanaged CSharpInstanceBridge_Get; public delegate* unmanaged CSharpInstanceBridge_CallDispose; public delegate* unmanaged CSharpInstanceBridge_CallToString; + public delegate* unmanaged CSharpInstanceBridge_HasMethodUnknownParams; public delegate* unmanaged GCHandleBridge_FreeGCHandle; public delegate* unmanaged DebuggingUtils_InstallTraceListener; public delegate* unmanaged Dispatcher_InitializeDefaultGodotTaskScheduler; @@ -52,7 +52,6 @@ namespace Godot.Bridge ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal, ScriptManagerBridge_GetScriptSignalList = &ScriptManagerBridge.GetScriptSignalList, ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal, - ScriptManagerBridge_HasMethodUnknownParams = &ScriptManagerBridge.HasMethodUnknownParams, ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits, ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge, ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge, @@ -63,6 +62,7 @@ namespace Godot.Bridge CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get, CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose, CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString, + CSharpInstanceBridge_HasMethodUnknownParams = &CSharpInstanceBridge.HasMethodUnknownParams, GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle, DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener, Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler, @@ -70,5 +70,8 @@ namespace Godot.Bridge // @formatter:on }; } + + public static void Create(IntPtr outManagedCallbacks) + => *(ManagedCallbacks*)outManagedCallbacks = Create(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index e87b7f4d4b..b86ced55cb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -8,7 +8,7 @@ using Godot.NativeInterop; namespace Godot.Bridge { - internal static class ScriptManagerBridge + public static class ScriptManagerBridge { private static System.Collections.Generic.Dictionary _scriptLookupMap = new(); private static System.Collections.Generic.Dictionary _scriptBridgeMap = new(); @@ -212,7 +212,7 @@ namespace Godot.Bridge // Called from GodotPlugins // ReSharper disable once UnusedMember.Local - private static void LookupScriptsInAssembly(Assembly assembly) + public static void LookupScriptsInAssembly(Assembly assembly) { static void LookupScriptForClass(Type type) { @@ -439,68 +439,6 @@ namespace Godot.Bridge } } - [UnmanagedCallersOnly] - internal static unsafe godot_bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, - godot_bool deep) - { - try - { - // Performance is not critical here as this will be replaced with source generators. - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) - return false.ToGodotBool(); - - string methodStr = Marshaling.ConvertStringToManaged(*method); - - if (deep.ToBool()) - { - Type top = scriptType; - Type native = Object.InternalGetClassNativeBase(scriptType); - - while (top != null && top != native) - { - var methodInfo = top.GetMethod(methodStr, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (methodInfo != null) - return true.ToGodotBool(); - - top = top.BaseType; - } - - top = native; - Type typeOfSystemObject = typeof(System.Object); - while (top != null && top != typeOfSystemObject) - { - bool found = top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(m => m.GetCustomAttributes(false).OfType() - .Where(a => a.MethodName == methodStr) - .Any()) - .Any(); - - if (found) - return true.ToGodotBool(); - - top = top.BaseType; - } - - return false.ToGodotBool(); - } - else - { - var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - return (methodInfo != null).ToGodotBool(); - } - } - catch (Exception e) - { - ExceptionUtils.DebugUnhandledException(e); - return false.ToGodotBool(); - } - } - [UnmanagedCallersOnly] internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) { 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..c4a90625c3 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs @@ -0,0 +1,64 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#nullable enable + +namespace Godot.NativeInterop +{ + public static class GodotDllImportResolver + { + public static 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 Linux.dlopen(IntPtr.Zero, Linux.RTLD_LAZY); + } + 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 Linux + { + // libdl.so was resulting in DllNotFoundException, for some reason... + // libcoreclr.so should work with both CoreCLR and the .NET Core version of Mono. + private const string SystemLibrary = "libcoreclr.so"; + + 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/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index a3a4c2599e..6255ffbbc7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -8,7 +8,7 @@ namespace Godot public partial class Object : IDisposable { private bool _disposed = false; - private Type _cachedType = typeof(Object); + private static readonly Type CachedType = typeof(Object); internal IntPtr NativePtr; private bool _memoryOwn; @@ -19,21 +19,31 @@ namespace Godot /// Constructs a new . /// public Object() : this(false) + { + unsafe + { + _ConstructAndInitialize(NativeCtor, NativeName, CachedType, refCounted: false); + } + } + + internal unsafe void _ConstructAndInitialize( + delegate* unmanaged nativeCtor, + StringName nativeName, + Type cachedType, + bool refCounted + ) { if (NativePtr == IntPtr.Zero) { - unsafe - { - NativePtr = NativeCtor(); - } + NativePtr = nativeCtor(); InteropUtils.TieManagedToUnmanaged(this, NativePtr, - NativeName, refCounted: false, GetType(), _cachedType); + nativeName, refCounted, GetType(), cachedType); } else { InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr, - GetType(), _cachedType); + GetType(), cachedType); } _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this); @@ -191,8 +201,14 @@ namespace Godot internal static bool InternalIsClassNativeBase(Type t) { - var assemblyName = t.Assembly.GetName(); - return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor"; + // Check whether the type is declared in the GodotSharp or GodotSharpEditor assemblies + var typeAssembly = t.Assembly; + + if (typeAssembly == CachedType.Assembly) + return true; + + var typeAssemblyName = t.Assembly.GetName(); + return typeAssemblyName.Name == "GodotSharpEditor"; } // ReSharper disable once VirtualMemberNeverOverridden.Global diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index f1a397f8fa..b7fbb81f9c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -33,7 +33,6 @@ - @@ -68,6 +67,7 @@ + diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs index dbd98d0afb..da6f293871 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs @@ -1,4 +1,3 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("GodotSharpEditor")] -[assembly: InternalsVisibleTo("GodotPlugins")] -- cgit v1.2.3 From 92503ae8dbdf8f0f543dd785b79d3ec13b19092f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 27 Feb 2022 21:57:30 +0100 Subject: C#: Add source generator for properties and exports default values The editor no longer needs to create temporary instances to get the default values. The initializer values of the exported properties are still evaluated at runtime. For example, in the following example, `GetInitialValue()` will be called when first looks for default values: ``` [Export] int MyValue = GetInitialValue(); ``` Exporting fields with a non-supported type now results in a compiler error rather than a runtime error when the script is used. --- .../GodotSharp/Core/Attributes/ExportAttribute.cs | 2 +- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 4 + .../GodotSharp/Core/Bridge/PropertyInfo.cs | 26 +++ .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 255 ++++++++++++++++++++- .../GodotSharp/GodotSharp/Core/MarshalUtils.cs | 13 -- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 7 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 21 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 2 +- 8 files changed, 308 insertions(+), 22 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs (limited to 'modules/mono/glue/GodotSharp') 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. /// [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/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index fb1efa0ac8..7a6748b4eb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -24,6 +24,8 @@ namespace Godot.Bridge public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; public delegate* unmanaged ScriptManagerBridge_SwapGCHandleForType; + public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyInfoList; + public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyDefaultValues; public delegate* unmanaged CSharpInstanceBridge_Call; public delegate* unmanaged CSharpInstanceBridge_Set; public delegate* unmanaged CSharpInstanceBridge_Get; @@ -57,6 +59,8 @@ namespace Godot.Bridge ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge, 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, 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..cfdfe2dab3 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs @@ -0,0 +1,26 @@ +using System; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + 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 index b86ced55cb..8348598b65 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -1,6 +1,8 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using Godot.Collections; @@ -523,7 +525,7 @@ namespace Godot.Bridge Dictionary rpcFunctions = new(); - Type top = _scriptBridgeMap[scriptPtr]; + Type top = scriptType; Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -602,5 +604,256 @@ namespace Godot.Bridge return false.ToGodotBool(); } } + + // 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 addPropInfoFunc) + { + try + { + Type scriptType = _scriptBridgeMap[scriptPtr]; + GetPropertyInfoListForType(scriptType, scriptPtr, addPropInfoFunc); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } + } + + private static unsafe void GetPropertyInfoListForType(Type type, IntPtr scriptPtr, + delegate* unmanaged addPropInfoFunc) + { + try + { + var getGodotPropertiesMetadataMethod = type.GetMethod( + "GetGodotPropertiesMetadata", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.NonPublic | BindingFlags.Public); + + if (getGodotPropertiesMetadataMethod == null) + return; + + var properties = (System.Collections.Generic.List) + getGodotPropertiesMetadataMethod.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 + { +#if NET6_0_OR_GREATER + interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc(length))!; +#else + interopProperties = ((godotsharp_property_info*)Marshal.AllocHGlobal(length))!; +#endif + } + + 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, ¤tClassName, interopProperties, length); + + // We're borrowing the StringName's without making an owning copy, so the + // managed collection needs to be kept alive until `addPropInfoFunc` returns. + GC.KeepAlive(properties); + } + finally + { + for (int i = 0; i < length; i++) + interopProperties[i].Dispose(); + + if (!useStack) + { +#if NET6_0_OR_GREATER + NativeMemory.Free(interopProperties); +#else + Marshal.FreeHGlobal((IntPtr)interopProperties); +#endif + } + } + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(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 addDefValFunc) + { + try + { + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + GetPropertyDefaultValuesForType(top, scriptPtr, addDefValFunc); + + top = top.BaseType; + } + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } + } + + [SkipLocalsInit] + private static unsafe void GetPropertyDefaultValuesForType(Type type, IntPtr scriptPtr, + delegate* unmanaged addDefValFunc) + { + try + { + var getGodotPropertyDefaultValuesMethod = type.GetMethod( + "GetGodotPropertyDefaultValues", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.NonPublic | BindingFlags.Public); + + if (getGodotPropertyDefaultValuesMethod == null) + return; + + var defaultValues = (System.Collections.Generic.Dictionary) + 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 + { +#if NET6_0_OR_GREATER + interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc(length))!; +#else + interopDefaultValues = ((godotsharp_property_def_val_pair*)Marshal.AllocHGlobal(length))!; +#endif + } + + 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) + { +#if NET6_0_OR_GREATER + NativeMemory.Free(interopDefaultValues); +#else + Marshal.FreeHGlobal((IntPtr)interopDefaultValues); +#endif + } + } + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(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 733a8ac114..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Godot -{ - internal static class MarshalUtils - { - /// - /// Returns if the is applied to the given type. - /// - private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false); - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index c2812b8919..01add1bf45 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -36,10 +36,13 @@ namespace Godot.NativeInterop public static extern IntPtr godotsharp_engine_get_singleton(in godot_string p_name); [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_object_disposed(IntPtr ptr); + internal static extern IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr); [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, godot_bool isFinalizer); + internal static extern void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree); + + [DllImport(GodotDllName)] + internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree, godot_bool isFinalizer); [DllImport(GodotDllName)] internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 6255ffbbc7..71a620716f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using Godot.NativeInterop; namespace Godot @@ -121,23 +122,35 @@ namespace Godot if (_disposed) return; + _disposed = true; + if (NativePtr != IntPtr.Zero) { + 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) { - NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool()); + NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree, + (!disposing).ToGodotBool()); } else { - NativeFuncs.godotsharp_internal_object_disposed(NativePtr); + NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree); } NativePtr = IntPtr.Zero; } DisposablesTracker.UnregisterGodotObject(_weakReferenceToSelf); - - _disposed = true; } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index b7fbb81f9c..8b0c421829 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -41,6 +41,7 @@ + @@ -63,7 +64,6 @@ - -- cgit v1.2.3 From 67db89988d3c9283d4d58b328b959e1531b16dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 27 Feb 2022 21:57:36 +0100 Subject: C#: Ensure we only create one CSharpScript per type Previously, for each scripts class instance that was created from code rather than by the engine, we were constructing, configuring and assigning a new CSharpScript. This has changed now and we make sure there's only one CSharpScript associated to each type. --- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 2 + .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 94 ++++++++++++++-------- .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 18 +++-- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 13 ++- 4 files changed, 85 insertions(+), 42 deletions(-) (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 7a6748b4eb..a348776874 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -21,6 +21,7 @@ namespace Godot.Bridge public delegate* unmanaged ScriptManagerBridge_HasScriptSignal; public delegate* unmanaged ScriptManagerBridge_ScriptIsOrInherits; public delegate* unmanaged ScriptManagerBridge_AddScriptBridge; + public delegate* unmanaged ScriptManagerBridge_GetOrCreateScriptBridgeForPath; public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; public delegate* unmanaged ScriptManagerBridge_SwapGCHandleForType; @@ -56,6 +57,7 @@ namespace Godot.Bridge ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal, ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits, ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge, + ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath, ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge, ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo, ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 8348598b65..d812626fab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -12,22 +12,11 @@ namespace Godot.Bridge { public static class ScriptManagerBridge { - private static System.Collections.Generic.Dictionary _scriptLookupMap = new(); - private static System.Collections.Generic.Dictionary _scriptBridgeMap = new(); + private static System.Collections.Generic.Dictionary _pathScriptMap = new(); - private struct ScriptLookupInfo - { - public string ClassNamespace { get; private set; } - public string ClassName { get; private set; } - public Type ScriptType { get; private set; } - - public ScriptLookupInfo(string classNamespace, string className, Type scriptType) - { - ClassNamespace = classNamespace; - ClassName = className; - ScriptType = scriptType; - } - }; + private static readonly object ScriptBridgeLock = new(); + private static System.Collections.Generic.Dictionary _scriptTypeMap = new(); + private static System.Collections.Generic.Dictionary _typeScriptMap = new(); [UnmanagedCallersOnly] internal static void FrameCallback() @@ -80,7 +69,7 @@ namespace Godot.Bridge try { // Performance is not critical here as this will be replaced with source generators. - Type scriptType = _scriptBridgeMap[scriptPtr]; + Type scriptType = _scriptTypeMap[scriptPtr]; var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); var ctor = scriptType @@ -133,7 +122,7 @@ namespace Godot.Bridge try { // Performance is not critical here as this will be replaced with source generators. - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + if (!_scriptTypeMap.TryGetValue(scriptPtr, out var scriptType)) { *outRes = default; return; @@ -225,7 +214,7 @@ namespace Godot.Bridge if (scriptPathAttr == null) return; - _scriptLookupMap[scriptPathAttr.Path] = new ScriptLookupInfo(type.Namespace, type.Name, type); + _pathScriptMap[scriptPathAttr.Path] = type; } var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false) @@ -304,7 +293,7 @@ namespace Godot.Bridge // Performance is not critical here as this will be replaced with source generators. using var signals = new Dictionary(); - Type top = _scriptBridgeMap[scriptPtr]; + Type top = _scriptTypeMap[scriptPtr]; Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -400,7 +389,7 @@ namespace Godot.Bridge string signalNameStr = Marshaling.ConvertStringToManaged(*signalName); - Type top = _scriptBridgeMap[scriptPtr]; + Type top = _scriptTypeMap[scriptPtr]; Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -446,10 +435,10 @@ namespace Godot.Bridge { try { - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + if (!_scriptTypeMap.TryGetValue(scriptPtr, out var scriptType)) return false.ToGodotBool(); - if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) + if (!_scriptTypeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) return false.ToGodotBool(); return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool(); @@ -466,12 +455,19 @@ namespace Godot.Bridge { try { - string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath); + lock (ScriptBridgeLock) + { + if (!_scriptTypeMap.ContainsKey(scriptPtr)) + { + string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath); - if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) - return false.ToGodotBool(); + if (!_pathScriptMap.TryGetValue(scriptPathStr, out Type scriptType)) + return false.ToGodotBool(); - _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); + _scriptTypeMap.Add(scriptPtr, scriptType); + _typeScriptMap.Add(scriptType, scriptPtr); + } + } return true.ToGodotBool(); } @@ -482,15 +478,49 @@ namespace Godot.Bridge } } - internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType) - => _scriptBridgeMap.Add(scriptPtr, scriptType); + [UnmanagedCallersOnly] + internal static unsafe void GetOrCreateScriptBridgeForPath(godot_string* scriptPath, godot_ref* outScript) + { + string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath); + + if (!_pathScriptMap.TryGetValue(scriptPathStr, out Type scriptType)) + { + NativeFuncs.godotsharp_internal_new_csharp_script(outScript); + return; + } + + GetOrCreateScriptBridgeForType(scriptType, outScript); + } + + internal static unsafe void GetOrCreateScriptBridgeForType(Type scriptType, godot_ref* outScript) + { + lock (ScriptBridgeLock) + { + if (_typeScriptMap.TryGetValue(scriptType, out IntPtr scriptPtr)) + { + NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr); + return; + } + + NativeFuncs.godotsharp_internal_new_csharp_script(outScript); + scriptPtr = outScript->Reference; + + _scriptTypeMap.Add(scriptPtr, scriptType); + _typeScriptMap.Add(scriptType, scriptPtr); + + NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr); + } + } [UnmanagedCallersOnly] internal static void RemoveScriptBridge(IntPtr scriptPtr) { try { - _scriptBridgeMap.Remove(scriptPtr); + lock (ScriptBridgeLock) + { + _ = _scriptTypeMap.Remove(scriptPtr); + } } catch (Exception e) { @@ -505,7 +535,7 @@ namespace Godot.Bridge try { // Performance is not critical here as this will be replaced with source generators. - var scriptType = _scriptBridgeMap[scriptPtr]; + var scriptType = _scriptTypeMap[scriptPtr]; *outTool = scriptType.GetCustomAttributes(inherit: false) .OfType() @@ -630,7 +660,7 @@ namespace Godot.Bridge { try { - Type scriptType = _scriptBridgeMap[scriptPtr]; + Type scriptType = _scriptTypeMap[scriptPtr]; GetPropertyInfoListForType(scriptType, scriptPtr, addPropInfoFunc); } catch (Exception e) @@ -752,7 +782,7 @@ namespace Godot.Bridge { try { - Type top = _scriptBridgeMap[scriptPtr]; + Type top = _scriptTypeMap[scriptPtr]; Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index 8ddf28ba16..f6f0186016 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -60,13 +60,17 @@ namespace Godot.NativeInterop } else { - IntPtr scriptPtr = NativeFuncs.godotsharp_internal_new_csharp_script(); - - ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type); - - // IMPORTANT: This must be called after AddScriptWithTypeBridge - NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged( - GCHandle.ToIntPtr(gcHandle), unmanaged, scriptPtr, refCounted.ToGodotBool()); + 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.GetOrCreateScriptBridgeForType(type, &script); + + // IMPORTANT: This must be called after GetOrCreateScriptBridgeForType + NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged( + GCHandle.ToIntPtr(gcHandle), unmanaged, &script, refCounted.ToGodotBool()); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 01add1bf45..2c81ae7816 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -42,7 +42,8 @@ namespace Godot.NativeInterop internal static extern void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree); [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree, godot_bool isFinalizer); + internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree, + godot_bool isFinalizer); [DllImport(GodotDllName)] internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj, @@ -59,7 +60,7 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] internal static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, - IntPtr unmanaged, IntPtr scriptPtr, godot_bool refCounted); + IntPtr unmanaged, godot_ref* scriptPtr, godot_bool refCounted); [DllImport(GodotDllName)] internal static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( @@ -77,7 +78,10 @@ namespace Godot.NativeInterop IntPtr oldGCHandlePtr); [DllImport(GodotDllName)] - internal static extern IntPtr godotsharp_internal_new_csharp_script(); + internal static extern void godotsharp_internal_new_csharp_script(godot_ref* r_script); + + [DllImport(GodotDllName)] + internal static extern void godotsharp_internal_reload_registered_script(IntPtr scriptPtr); [DllImport(GodotDllName)] internal static extern void godotsharp_array_filter_godot_objects_by_native(in godot_string_name p_native_name, @@ -87,6 +91,9 @@ namespace Godot.NativeInterop internal static extern void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input, out godot_array r_output); + [DllImport(GodotDllName)] + public static extern void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest, IntPtr p_ref_counted_ptr); + [DllImport(GodotDllName)] public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); -- cgit v1.2.3 From 778007a358961f30ccadd738300a353edf0a0c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 27 Feb 2022 21:57:39 +0100 Subject: C#: Re-introduce exception logging and error stack traces in editor These two had been disabled while moving to .NET 5, as the previous implementation relied on Mono embedding APIs. --- .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 12 +-- .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 2 +- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 8 +- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 34 +++--- .../GodotSharp/GodotSharp/Core/DebuggingUtils.cs | 93 +++++++++++++--- .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 4 +- .../glue/GodotSharp/GodotSharp/Core/Dispatcher.cs | 12 +-- .../GodotSharp/Core/DisposablesTracker.cs | 2 +- .../Core/GodotUnhandledExceptionEvent.cs | 30 ++++-- .../Core/NativeInterop/ExceptionUtils.cs | 117 ++++++++++++++++----- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 20 +++- .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 2 +- .../GodotSharp/Core/UnhandledExceptionArgs.cs | 20 ---- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 3 +- 14 files changed, 248 insertions(+), 111 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 266df07d63..db53a508ef 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -39,7 +39,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); *ret = default; return false.ToGodotBool(); } @@ -69,7 +69,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } @@ -107,7 +107,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); *outRet = default; return false.ToGodotBool(); } @@ -127,7 +127,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -159,7 +159,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); *outRes = default; *outValid = false.ToGodotBool(); } @@ -179,7 +179,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs index c6f2e8f77d..bd11811c7d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -15,7 +15,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index a348776874..9f9d740659 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -34,9 +34,9 @@ namespace Godot.Bridge public delegate* unmanaged CSharpInstanceBridge_CallToString; public delegate* unmanaged CSharpInstanceBridge_HasMethodUnknownParams; public delegate* unmanaged GCHandleBridge_FreeGCHandle; - public delegate* unmanaged DebuggingUtils_InstallTraceListener; - public delegate* unmanaged Dispatcher_InitializeDefaultGodotTaskScheduler; + public delegate* unmanaged DebuggingUtils_GetCurrentStackInfo; public delegate* unmanaged DisposablesTracker_OnGodotShuttingDown; + public delegate* unmanaged GD_OnCoreApiAssemblyLoaded; // @formatter:on public static ManagedCallbacks Create() @@ -70,9 +70,9 @@ namespace Godot.Bridge CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString, CSharpInstanceBridge_HasMethodUnknownParams = &CSharpInstanceBridge.HasMethodUnknownParams, GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle, - DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener, - Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler, + DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo, DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown, + GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded, // @formatter:on }; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index d812626fab..20d0c57179 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -27,7 +27,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -54,7 +54,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); return IntPtr.Zero; } } @@ -111,7 +111,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } @@ -151,7 +151,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); *outRes = default; } } @@ -167,7 +167,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -280,7 +280,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); *outOwnerIsNull = false.ToGodotBool(); } } @@ -374,7 +374,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); *outRetSignals = NativeFuncs.godotsharp_dictionary_new(); } } @@ -425,7 +425,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } @@ -445,7 +445,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } @@ -473,7 +473,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } @@ -524,7 +524,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -596,7 +596,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); *outTool = false.ToGodotBool(); *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new(); } @@ -629,7 +629,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); *outNewGCHandlePtr = IntPtr.Zero; return false.ToGodotBool(); } @@ -665,7 +665,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -757,7 +757,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -794,7 +794,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } @@ -882,7 +882,7 @@ namespace Godot.Bridge } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index 39904b6154..6fbc04702a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -1,15 +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); @@ -21,28 +24,94 @@ namespace Godot sb.Append(' '); } - [UnmanagedCallersOnly] internal static void InstallTraceListener() + { + Trace.Listeners.Clear(); + Trace.Listeners.Add(new GodotTraceListener()); + } + + [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 + { + 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; + } + } + + [UnmanagedCallersOnly] + internal static unsafe void GetCurrentStackInfo(void* destVector) { try { - Trace.Listeners.Clear(); - Trace.Listeners.Add(new GodotTraceListener()); + 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.DebugPrintUnhandledException(e); - ExceptionUtils.PushError("Failed to install 'System.Diagnostics.Trace' listener."); + ExceptionUtils.LogException(e); } } - public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, - out string methodDecl) + internal static void GetStackFrameMethodDecl(StackFrame frame, out string methodDecl) { - fileName = frame.GetFileName(); - fileLineNumber = frame.GetFileLineNumber(); - - MethodBase methodBase = frame.GetMethod(); + MethodBase? methodBase = frame.GetMethod(); if (methodBase == null) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 02ae392f47..fb5e3c6dda 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -22,7 +22,7 @@ namespace Godot } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); return false.ToGodotBool(); } } @@ -58,7 +58,7 @@ namespace Godot } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); *outRet = default; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index e8cfb8e1b1..e6cb4171a7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -8,18 +8,8 @@ namespace Godot { internal static GodotTaskScheduler DefaultGodotTaskScheduler; - [UnmanagedCallersOnly] internal static void InitializeDefaultGodotTaskScheduler() - { - try - { - DefaultGodotTaskScheduler = new GodotTaskScheduler(); - } - catch (Exception e) - { - ExceptionUtils.DebugUnhandledException(e); - } - } + => 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 index bf3b3b083a..4e15b37e44 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs @@ -24,7 +24,7 @@ namespace Godot } catch (Exception e) { - ExceptionUtils.DebugUnhandledException(e); + ExceptionUtils.LogException(e); } } 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 { - /// - /// Fires when an unhandled exception occurs, regardless of project settings. - /// - public static event EventHandler 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/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index 2830d9c527..7a2f205632 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -1,4 +1,9 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +#nullable enable namespace Godot.NativeInterop { @@ -10,47 +15,102 @@ namespace Godot.NativeInterop } private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog) - { - // This better not throw - PushError("Exception thrown when trying to log another exception..."); - PushError("Exception:"); - PushError(exceptionToLog.ToString()); - PushError("Logger exception:"); - PushError(loggerException.ToString()); - } - - public static void DebugPrintUnhandledException(Exception e) { try { - // TODO Not implemented (debug_print_unhandled_exception) - GD.PushError(e.ToString()); + // 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 unexpected) + catch (Exception) { - OnExceptionLoggerException(unexpected, e); + // Well, too bad... } } - public static void DebugSendUnhandledExceptionError(Exception e) + private record struct StackInfoTuple(string? File, string Func, int Line); + + private static void CollectExceptionInfo(Exception exception, List globalFrames, + StringBuilder excMsg) { - try + if (excMsg.Length > 0) + excMsg.Append(" ---> "); + excMsg.Append(exception.GetType().FullName); + excMsg.Append(": "); + excMsg.Append(exception.Message); + + var innerExc = exception.InnerException; + + if (innerExc != null) { - // TODO Not implemented (debug_send_unhandled_exception_error) - GD.PushError(e.ToString()); + CollectExceptionInfo(innerExc, globalFrames, excMsg); + globalFrames.Add(new("", "--- End of inner exception stack trace ---", 0)); } - catch (Exception unexpected) + + var stackTrace = new StackTrace(exception, fNeedFileInfo: true); + + foreach (StackFrame frame in stackTrace.GetFrames()) { - OnExceptionLoggerException(unexpected, e); + DebuggingUtils.GetStackFrameMethodDecl(frame, out string methodDecl); + globalFrames.Add(new(frame.GetFileName(), methodDecl, frame.GetFileLineNumber())); + } + } + + private static void SendToScriptDebugger(Exception e) + { + var globalFrames = new List(); + + 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: false.ToGodotBool(), stackInfoVector); } } - public static void DebugUnhandledException(Exception e) + public static void LogException(Exception e) { try { - // TODO Not implemented (debug_unhandled_exception) - GD.PushError(e.ToString()); + if (NativeFuncs.godotsharp_internal_script_debugger_is_active()) + { + SendToScriptDebugger(e); + } + else + { + GD.PushError(e.ToString()); + } } catch (Exception unexpected) { @@ -58,12 +118,17 @@ namespace Godot.NativeInterop } } - public static void PrintUnhandledException(Exception e) + public static void LogUnhandledException(Exception e) { try { - // TODO Not implemented (print_unhandled_exception) - GD.PushError(e.ToString()); + 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) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 2c81ae7816..d7c57fa260 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -35,6 +35,23 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern IntPtr godotsharp_engine_get_singleton(in godot_string p_name); + + [DllImport(GodotDllName)] + internal static extern Error godotsharp_stack_info_vector_resize( + ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector, int p_size); + + [DllImport(GodotDllName)] + internal static extern void godotsharp_stack_info_vector_destroy( + ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector); + + [DllImport(GodotDllName)] + internal static extern 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); + + [DllImport(GodotDllName)] + internal static extern bool godotsharp_internal_script_debugger_is_active(); + [DllImport(GodotDllName)] internal static extern IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr); @@ -92,7 +109,8 @@ namespace Godot.NativeInterop out godot_array r_output); [DllImport(GodotDllName)] - public static extern void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest, IntPtr p_ref_counted_ptr); + public static extern void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest, + IntPtr p_ref_counted_ptr); [DllImport(GodotDllName)] public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index c8bf686afa..fb72d706c7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -58,7 +58,7 @@ namespace Godot } catch (Exception e) { - ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.LogException(e); *outAwaiterIsNull = false.ToGodotBool(); } } 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 -{ - /// - /// Event arguments for when unhandled exceptions occur. - /// - public class UnhandledExceptionArgs - { - /// - /// Exception object. - /// - public Exception Exception { get; private set; } - - internal UnhandledExceptionArgs(Exception exception) - { - Exception = exception; - } - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 8b0c421829..cc38ad31b6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -8,7 +8,7 @@ $(OutputPath)/$(AssemblyName).xml false true - 9 + 10 Recommended @@ -91,7 +91,6 @@ - -- cgit v1.2.3 From e22dd3bc6a0934d26ba5c406505b2c60c43445fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 27 Feb 2022 21:57:46 +0100 Subject: C#: Static marshaling for bindings and source generators Previously, we added source generators for invoking/accessing methods, properties and fields in scripts. This freed us from the overhead of reflection. However, the generated code still used our dynamic marshaling functions, which do runtime type checking and box value types. This commit changes the bindings and source generators to include 'static' marshaling. Based on the types known at compile time, now we generate the appropriate marshaling call for each type. --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 60 ++-- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 2 +- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 397 +++++++++------------ .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 361 ++++++++++++++++++- 4 files changed, 565 insertions(+), 255 deletions(-) (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index cd6655b857..a09ba09e95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -320,6 +320,37 @@ namespace Godot.Collections } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object GetAtAsType(int index, Type type) + { + GetVariantBorrowElementAt(index, out godot_variant borrowElem); + return Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, type); + } + + internal void CopyToGeneric(T[] array, int arrayIndex, Type type = null) + { + 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."); + + var typeOfElements = type ?? typeof(T); + + 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."); + + for (int i = 0; i < count; i++) + { + array[arrayIndex] = (T)GetAtAsType(i, typeOfElements); + arrayIndex++; + } + } + // IEnumerable /// @@ -517,11 +548,7 @@ namespace Godot.Collections /// The value at the given . public T this[int index] { - get - { - _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); - return (T)Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, TypeOfElements); - } + get => (T)_underlyingArray.GetAtAsType(index, TypeOfElements); set => _underlyingArray[index] = value; } @@ -604,27 +631,8 @@ namespace Godot.Collections /// /// The C# array to copy to. /// The index to start at. - public void CopyTo(T[] 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 = _underlyingArray.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."); - - for (int i = 0; i < count; i++) - { - array[arrayIndex] = this[i]; - arrayIndex++; - } - } + public void CopyTo(T[] array, int arrayIndex) => + _underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements); /// /// Removes the first occurrence of the specified value diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 4a99872e7b..2bea2f3b4f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -373,7 +373,7 @@ namespace Godot.Collections /// /// The type of the dictionary's keys. /// The type of the dictionary's values. - public sealed class Dictionary : + public class Dictionary : IDictionary, IGenericGodotDictionary { private readonly Dictionary _underlyingDict; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index d1a1450f04..fc11f56680 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -11,6 +11,8 @@ using System.Runtime.InteropServices; #nullable enable +// TODO: Consider removing support for IEnumerable + namespace Godot.NativeInterop { public static class Marshaling @@ -280,121 +282,74 @@ namespace Godot.NativeInterop case Plane @plane: return VariantUtils.CreateFromPlane(@plane); case Callable @callable: - return VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue( - ConvertCallableToNative(ref @callable)); + return VariantUtils.CreateFromCallable(@callable); case SignalInfo @signalInfo: - return VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue( - ConvertSignalToNative(ref @signalInfo)); + return VariantUtils.CreateFromSignalInfo(@signalInfo); case Enum @enum: return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); case string @string: - { - return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue( - ConvertStringToNative(@string)); - } + return VariantUtils.CreateFromString(@string); case byte[] byteArray: - { - using godot_packed_byte_array array = ConvertSystemArrayToNativePackedByteArray(byteArray); - return VariantUtils.CreateFromPackedByteArray(array); - } + return VariantUtils.CreateFromPackedByteArray(byteArray); case Int32[] int32Array: - { - using godot_packed_int32_array array = ConvertSystemArrayToNativePackedInt32Array(int32Array); - return VariantUtils.CreateFromPackedInt32Array(array); - } + return VariantUtils.CreateFromPackedInt32Array(int32Array); case Int64[] int64Array: - { - using godot_packed_int64_array array = ConvertSystemArrayToNativePackedInt64Array(int64Array); - return VariantUtils.CreateFromPackedInt64Array(array); - } + return VariantUtils.CreateFromPackedInt64Array(int64Array); case float[] floatArray: - { - using godot_packed_float32_array array = ConvertSystemArrayToNativePackedFloat32Array(floatArray); - return VariantUtils.CreateFromPackedFloat32Array(array); - } + return VariantUtils.CreateFromPackedFloat32Array(floatArray); case double[] doubleArray: - { - using godot_packed_float64_array array = ConvertSystemArrayToNativePackedFloat64Array(doubleArray); - return VariantUtils.CreateFromPackedFloat64Array(array); - } + return VariantUtils.CreateFromPackedFloat64Array(doubleArray); case string[] stringArray: - { - using godot_packed_string_array array = ConvertSystemArrayToNativePackedStringArray(stringArray); - return VariantUtils.CreateFromPackedStringArray(array); - } + return VariantUtils.CreateFromPackedStringArray(stringArray); case Vector2[] vector2Array: - { - using godot_packed_vector2_array array = ConvertSystemArrayToNativePackedVector2Array(vector2Array); - return VariantUtils.CreateFromPackedVector2Array(array); - } + return VariantUtils.CreateFromPackedVector2Array(vector2Array); case Vector3[] vector3Array: - { - using godot_packed_vector3_array array = ConvertSystemArrayToNativePackedVector3Array(vector3Array); - return VariantUtils.CreateFromPackedVector3Array(array); - } + return VariantUtils.CreateFromPackedVector3Array(vector3Array); case Color[] colorArray: - { - using godot_packed_color_array array = ConvertSystemArrayToNativePackedColorArray(colorArray); - return VariantUtils.CreateFromPackedColorArray(array); - } + return VariantUtils.CreateFromPackedColorArray(colorArray); case StringName[] stringNameArray: - { - using godot_array array = ConvertSystemArrayToNativeGodotArray(stringNameArray); - return VariantUtils.CreateFromArray(array); - } + return VariantUtils.CreateFromSystemArrayOfSupportedType(stringNameArray); case NodePath[] nodePathArray: - { - using godot_array array = ConvertSystemArrayToNativeGodotArray(nodePathArray); - return VariantUtils.CreateFromArray(array); - } + return VariantUtils.CreateFromSystemArrayOfSupportedType(nodePathArray); case RID[] ridArray: - { - using godot_array array = ConvertSystemArrayToNativeGodotArray(ridArray); - return VariantUtils.CreateFromArray(array); - } + return VariantUtils.CreateFromSystemArrayOfSupportedType(ridArray); case Godot.Object[] godotObjectArray: - { - using godot_array array = ConvertSystemArrayToNativeGodotArray(godotObjectArray); - return VariantUtils.CreateFromArray(array); - } + return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray); case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] { // The pattern match for `object[]` catches arrays on any reference type, // so we need to check the actual type to make sure it's truly `object[]`. if (objectArray.GetType() == typeof(object[])) - { - using godot_array array = ConvertSystemArrayToNativeGodotArray(objectArray); - return VariantUtils.CreateFromArray(array); - } + return VariantUtils.CreateFromSystemArrayOfVariant(objectArray); GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); return new godot_variant(); } case Godot.Object godotObject: - return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance); + return VariantUtils.CreateFromGodotObject(godotObject); case StringName stringName: - return VariantUtils.CreateFromStringName(stringName.NativeValue.DangerousSelfRef); + return VariantUtils.CreateFromStringName(stringName); case NodePath nodePath: - return VariantUtils.CreateFromNodePath((godot_node_path)nodePath.NativeValue); + return VariantUtils.CreateFromNodePath(nodePath); case RID rid: return VariantUtils.CreateFromRID(rid); case Collections.Dictionary godotDictionary: - return VariantUtils.CreateFromDictionary((godot_dictionary)godotDictionary.NativeValue); + return VariantUtils.CreateFromDictionary(godotDictionary); case Collections.Array godotArray: - return VariantUtils.CreateFromArray((godot_array)godotArray.NativeValue); + return VariantUtils.CreateFromArray(godotArray); case Collections.IGenericGodotDictionary genericGodotDictionary: { var godotDict = genericGodotDictionary.UnderlyingDictionary; if (godotDict == null) return new godot_variant(); - return VariantUtils.CreateFromDictionary((godot_dictionary)godotDict.NativeValue); + return VariantUtils.CreateFromDictionary(godotDict); } case Collections.IGenericGodotArray genericGodotArray: { var godotArray = genericGodotArray.UnderlyingArray; if (godotArray == null) return new godot_variant(); - return VariantUtils.CreateFromArray((godot_array)godotArray.NativeValue); + return VariantUtils.CreateFromArray(godotArray); } default: { @@ -412,13 +367,13 @@ namespace Godot.NativeInterop foreach (KeyValuePair entry in (IDictionary)p_obj) godotDict.Add(entry.Key, entry.Value); - return VariantUtils.CreateFromDictionary((godot_dictionary)godotDict.NativeValue); + return VariantUtils.CreateFromDictionary(godotDict); } if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { // TODO: Validate element type is compatible with Variant - using var nativeGodotArray = ConvertIListToNativeGodotArray((IList)p_obj); + using var nativeGodotArray = ConvertICollectionToNativeGodotArray((ICollection)p_obj); return VariantUtils.CreateFromArray(nativeGodotArray); } } @@ -432,25 +387,6 @@ namespace Godot.NativeInterop return new godot_variant(); } - private static string? ConvertVariantToManagedString(in godot_variant p_var) - { - switch (p_var.Type) - { - case Variant.Type.Nil: - return null; // 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 ConvertStringToManaged(p_var.String); - } - default: - { - using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); - return ConvertStringToManaged(godotString); - } - } - } - 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. @@ -481,7 +417,7 @@ namespace Godot.NativeInterop case TypeCode.Double: return VariantUtils.ConvertToFloat64(p_var); case TypeCode.String: - return ConvertVariantToManagedString(p_var); + return VariantUtils.ConvertToStringObject(p_var); default: { if (type == typeof(Vector2)) @@ -533,16 +469,10 @@ namespace Godot.NativeInterop return VariantUtils.ConvertToPlane(p_var); if (type == typeof(Callable)) - { - using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var); - return ConvertCallableToManaged(in callable); - } + return VariantUtils.ConvertToCallableManaged(p_var); if (type == typeof(SignalInfo)) - { - using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var); - return ConvertSignalToManaged(in signal); - } + return VariantUtils.ConvertToSignalInfo(p_var); if (type.IsEnum) { @@ -596,88 +526,46 @@ namespace Godot.NativeInterop private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type) { if (type == typeof(byte[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); - return ConvertNativePackedByteArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); if (type == typeof(Int32[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); - return ConvertNativePackedInt32ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); if (type == typeof(Int64[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); - return ConvertNativePackedInt64ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); if (type == typeof(float[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); - return ConvertNativePackedFloat32ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var); if (type == typeof(double[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); - return ConvertNativePackedFloat64ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var); if (type == typeof(string[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); - return ConvertNativePackedStringArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var); if (type == typeof(Vector2[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); - return ConvertNativePackedVector2ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var); if (type == typeof(Vector3[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); - return ConvertNativePackedVector3ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var); if (type == typeof(Color[])) - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); - return ConvertNativePackedColorArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); if (type == typeof(StringName[])) - { - using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); - } + return VariantUtils.ConvertToSystemArrayOfSupportedType(p_var); if (type == typeof(NodePath[])) - { - using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); - } + return VariantUtils.ConvertToSystemArrayOfSupportedType(p_var); if (type == typeof(RID[])) - { - using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); - } + return VariantUtils.ConvertToSystemArrayOfSupportedType(p_var); if (typeof(Godot.Object[]).IsAssignableFrom(type)) - { - using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type); - } + return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type); if (type == typeof(object[])) - { - using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return ConvertNativeGodotArrayToSystemArray(godotArray); - } + return VariantUtils.ConvertToSystemArrayOfVariant(p_var); GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + type.GetElementType()!.FullName + "."); @@ -689,21 +577,29 @@ namespace Godot.NativeInterop { if (typeof(Godot.Object).IsAssignableFrom(type)) { - res = InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); + 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}`."); + res = null; + return false; + } + + res = godotObject; return true; } if (typeof(StringName) == type) { - res = StringName.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToStringName(p_var)); + res = VariantUtils.ConvertToStringNameObject(p_var); return true; } if (typeof(NodePath) == type) { - res = NodePath.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToNodePath(p_var)); + res = VariantUtils.ConvertToNodePathObject(p_var); return true; } @@ -715,8 +611,7 @@ namespace Godot.NativeInterop if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) { - res = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToDictionary(p_var)); + res = VariantUtils.ConvertToDictionaryObject(p_var); return true; } @@ -724,8 +619,7 @@ namespace Godot.NativeInterop typeof(System.Collections.ICollection) == type || typeof(System.Collections.IEnumerable) == type) { - res = Collections.Array.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToArray(p_var)); + res = VariantUtils.ConvertToArrayObject(p_var); return true; } @@ -737,8 +631,7 @@ namespace Godot.NativeInterop { static object ConvertVariantToGenericGodotCollectionsDictionary(in godot_variant p_var, Type fullType) { - var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToDictionary(p_var)); + var underlyingDict = VariantUtils.ConvertToDictionaryObject(p_var); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, args: new object[] { underlyingDict }, null)!; @@ -746,8 +639,7 @@ namespace Godot.NativeInterop static object ConvertVariantToGenericGodotCollectionsArray(in godot_variant p_var, Type fullType) { - var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToArray(p_var)); + var underlyingArray = VariantUtils.ConvertToArrayObject(p_var); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, args: new object[] { underlyingArray }, null)!; @@ -763,8 +655,7 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) { - using var godotDictionary = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToDictionary(p_var)); + using var godotDictionary = VariantUtils.ConvertToDictionaryObject(p_var); var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.Instance, null, @@ -781,8 +672,7 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { - using var godotArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( - VariantUtils.ConvertToArray(p_var)); + using var godotArray = VariantUtils.ConvertToArrayObject(p_var); var list = (System.Collections.IList)Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.Instance, null, @@ -818,7 +708,18 @@ namespace Godot.NativeInterop } if (typeof(Godot.Object).IsAssignableFrom(type)) - return InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); + { + 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; } @@ -906,50 +807,23 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_array_new_copy(p_var.Array)); } case Variant.Type.PackedByteArray: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); - return ConvertNativePackedByteArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); case Variant.Type.PackedInt32Array: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); - return ConvertNativePackedInt32ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); case Variant.Type.PackedInt64Array: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); - return ConvertNativePackedInt64ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); case Variant.Type.PackedFloat32Array: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); - return ConvertNativePackedFloat32ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var); case Variant.Type.PackedFloat64Array: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); - return ConvertNativePackedFloat64ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var); case Variant.Type.PackedStringArray: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); - return ConvertNativePackedStringArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var); case Variant.Type.PackedVector2Array: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); - return ConvertNativePackedVector2ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var); case Variant.Type.PackedVector3Array: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); - return ConvertNativePackedVector3ArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var); case Variant.Type.PackedColorArray: - { - using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); - return ConvertNativePackedColorArrayToSystemArray(packedArray); - } + return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); default: return null; } @@ -986,10 +860,7 @@ namespace Godot.NativeInterop // Callable - public static godot_callable ConvertCallableToNative(ref Callable p_managed_callable) - => ConvertCallableToNative(p_managed_callable); - - public static godot_callable ConvertCallableToNative(Callable p_managed_callable) + public static godot_callable ConvertCallableToNative(in Callable p_managed_callable) { if (p_managed_callable.Delegate != null) { @@ -1041,7 +912,7 @@ namespace Godot.NativeInterop // SignalInfo - public static godot_signal ConvertSignalToNative(ref SignalInfo p_managed_signal) + public static godot_signal ConvertSignalToNative(in SignalInfo p_managed_signal) { ulong ownerId = p_managed_signal.Owner.GetInstanceId(); godot_string_name name; @@ -1082,17 +953,46 @@ namespace Godot.NativeInterop return ret; } - private static object ConvertNativeGodotArrayToSystemArrayOfType(in godot_array p_array, Type type) + internal static T[] ConvertNativeGodotArrayToSystemArrayOfType(in godot_array p_array) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + var ret = new T[length]; + + // ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric + array.CopyToGeneric(ret, 0); + + return ret; + } + + internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(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; - object ret = Activator.CreateInstance(type, length)!; + var ret = new T[length]; - // ConvertVariantToManagedObject handled by Collections.Array - // ConvertVariantToManagedObjectOfType is not needed because target element types are Godot.Object (or derived) - array.CopyTo((object[])ret, 0); + // ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric + array.CopyToGeneric(ret, 0); + + return ret; + } + + 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)!; + + // ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric + array.CopyToGeneric(ret, 0, type.GetElementType()); return ret; } @@ -1131,7 +1031,7 @@ namespace Godot.NativeInterop return NativeFuncs.godotsharp_array_new_copy(src); } - public static godot_array ConvertIListToNativeGodotArray(IList p_array) + public static godot_array ConvertICollectionToNativeGodotArray(ICollection p_array) { int length = p_array.Count; @@ -1141,13 +1041,62 @@ namespace Godot.NativeInterop using var array = new Collections.Array(); array.Resize(length); - for (int i = 0; i < length; i++) - array[i] = p_array[i]; + int i = 0; + foreach (var elem in p_array) + { + array[i] = elem; + i++; + } var src = (godot_array)array.NativeValue; return NativeFuncs.godotsharp_array_new_copy(src); } + public static godot_array ConvertGenericICollectionToNativeGodotArray(ICollection p_array) + { + int length = p_array.Count; + + if (length == 0) + return NativeFuncs.godotsharp_array_new(); + + var array = new Collections.Array(); + using var underlyingArray = (Collections.Array)array; + array.Resize(length); + + int i = 0; + foreach (var elem in p_array) + { + array[i] = elem; + i++; + } + + var src = (godot_array)underlyingArray.NativeValue; + return NativeFuncs.godotsharp_array_new_copy(src); + } + + public static godot_array ConvertIEnumerableToNativeGodotArray(IEnumerable p_array) + { + using var array = new Collections.Array(); + + foreach (var elem in p_array) + array.Add(elem); + + var src = (godot_array)array.NativeValue; + return NativeFuncs.godotsharp_array_new_copy(src); + } + + public static godot_array ConvertGenericIEnumerableToNativeGodotArray(IEnumerable p_array) + { + var array = new Collections.Array(); + using var underlyingArray = (Collections.Array)array; + + foreach (var elem in p_array) + array.Add(elem); + + var src = (godot_array)underlyingArray.NativeValue; + return NativeFuncs.godotsharp_array_new_copy(src); + } + // PackedByteArray public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index f69cc40314..72be871f16 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -1,7 +1,11 @@ using System; +using System.Runtime.CompilerServices; +using Godot.Collections; // ReSharper disable InconsistentNaming +#nullable enable + namespace Godot.NativeInterop { public static class VariantUtils @@ -94,14 +98,28 @@ namespace Godot.NativeInterop 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); @@ -156,31 +174,192 @@ namespace Godot.NativeInterop return ret; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedByteArray(Span from) + => CreateFromPackedByteArray(Marshaling.ConvertSystemArrayToNativePackedByteArray(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedInt32Array(Span from) + => CreateFromPackedInt32Array(Marshaling.ConvertSystemArrayToNativePackedInt32Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedInt64Array(Span from) + => CreateFromPackedInt64Array(Marshaling.ConvertSystemArrayToNativePackedInt64Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedFloat32Array(Span from) + => CreateFromPackedFloat32Array(Marshaling.ConvertSystemArrayToNativePackedFloat32Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedFloat64Array(Span from) + => CreateFromPackedFloat64Array(Marshaling.ConvertSystemArrayToNativePackedFloat64Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedStringArray(Span from) + => CreateFromPackedStringArray(Marshaling.ConvertSystemArrayToNativePackedStringArray(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedVector2Array(Span from) + => CreateFromPackedVector2Array(Marshaling.ConvertSystemArrayToNativePackedVector2Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedVector3Array(Span from) + => CreateFromPackedVector3Array(Marshaling.ConvertSystemArrayToNativePackedVector3Array(from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromPackedColorArray(Span from) + => CreateFromPackedColorArray(Marshaling.ConvertSystemArrayToNativePackedColorArray(from)); + + public static godot_variant CreateFromSystemArrayOfSupportedType(T[]? from) + { + if (from == null) + return default; // Nil + using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from); + return CreateFromArray(array); + } + + // ReSharper disable once RedundantNameQualifier + public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from) + { + if (from == null) + return default; // Nil + using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from); + return CreateFromArray(array); + } + + public static godot_variant CreateFromSystemArrayOfVariant(object[]? from) + { + if (from == null) + return default; // Nil + using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from); + return CreateFromArray(array); + } + public static godot_variant CreateFromArray(godot_array from) { NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from); return ret; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_variant CreateFromArray(Collections.Array? from) + => from != null ? CreateFromArray((godot_array)from.NativeValue) : default; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable once RedundantNameQualifier + public static godot_variant CreateFromArray(Collections.Array? from) + => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default; + + public static godot_variant CreateFromSystemICollection(System.Collections.ICollection? from) + { + if (from == null) + return default; // Nil + using var nativeGodotArray = Marshaling.ConvertICollectionToNativeGodotArray(from); + return CreateFromArray(nativeGodotArray); + } + + public static godot_variant CreateFromSystemGenericICollection( + System.Collections.Generic.ICollection? from) + { + if (from == null) + return default; // Nil + using var nativeGodotArray = Marshaling.ConvertGenericICollectionToNativeGodotArray(from); + return CreateFromArray(nativeGodotArray); + } + + public static godot_variant CreateFromSystemIEnumerable(System.Collections.IEnumerable? from) + { + if (from == null) + return default; // Nil + using var nativeGodotArray = Marshaling.ConvertIEnumerableToNativeGodotArray(from); + return CreateFromArray(nativeGodotArray); + } + + public static godot_variant CreateFromSystemGenericIEnumerable( + System.Collections.Generic.IEnumerable? from) + { + if (from == null) + return default; // Nil + using var nativeGodotArray = Marshaling.ConvertGenericIEnumerableToNativeGodotArray(from); + return CreateFromArray(nativeGodotArray); + } + 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(Dictionary? from) + => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default; + + public static godot_variant CreateFromSystemDictionary( + System.Collections.Generic.Dictionary? from) where TKey : notnull + { + if (from == null) + return default; // Nil + + var godotDict = new Dictionary(); + + foreach (var entry in from) + godotDict.Add(entry.Key, entry.Value); + + return CreateFromDictionary(godotDict); + } + + public static godot_variant CreateFromSystemIDictionary(System.Collections.IDictionary? from) + { + if (from == null) + return default; // Nil + + var godotDict = new Dictionary(); + + foreach (var entry in from) + godotDict.Add(entry, entry); + + return CreateFromDictionary(godotDict); + } + + public static godot_variant CreateFromSystemGenericIDictionary( + System.Collections.Generic.IDictionary? from) + { + if (from == null) + return default; // Nil + + var godotDict = new Dictionary(); + + foreach (var entry in from) + godotDict.Add(entry.Key, entry.Value); + + return CreateFromDictionary(godotDict); + } + 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; } - public static godot_variant CreateFromGodotObject(IntPtr from) + [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(); @@ -188,6 +367,11 @@ namespace Godot.NativeInterop 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) @@ -328,32 +512,201 @@ namespace Godot.NativeInterop p_var.Plane : NativeFuncs.godotsharp_variant_as_plane(p_var); - public static IntPtr ConvertToGodotObject(in godot_variant p_var) - => p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero; - 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)); + 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)); + + 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 T[] ConvertToSystemArrayOfSupportedType(in godot_variant p_var) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Marshaling.ConvertNativeGodotArrayToSystemArrayOfType(godotArray); + } + + public static T[] ConvertToSystemArrayOfGodotObject(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(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); + } + + public static object[] ConvertToSystemArrayOfVariant(in godot_variant p_var) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Marshaling.ConvertNativeGodotArrayToSystemArray(godotArray); + } + + public static Array ConvertToGenericArrayObject(in godot_variant p_var) => + new(ConvertToArrayObject(p_var)); + + public static Dictionary ConvertToGenericDictionaryObject(in godot_variant p_var) => + new(ConvertToDictionaryObject(p_var)); + + public static System.Collections.Generic.List ConvertToSystemGenericList(in godot_variant p_var) + { + var godotArray = ConvertToArrayObject(p_var); + + var res = new System.Collections.Generic.List(godotArray.Count); + + foreach (object elem in godotArray) + res.Add((T)elem); + + return res; + } + + public static System.Collections.Generic.Dictionary + ConvertToSystemGenericDictionary(in godot_variant p_var) + where TKey : notnull + { + var godotDictionary = ConvertToDictionaryObject(p_var); + + var res = new System.Collections.Generic.Dictionary(godotDictionary.Count); + + foreach (System.Collections.Generic.KeyValuePair pair in godotDictionary) + res.Add(pair.Key, pair.Value); + + return res; + } } } -- cgit v1.2.3 From 18f805b3aad2be838a7396f18d4ebd99182b6935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 27 Feb 2022 21:57:50 +0100 Subject: C#: Upgrade to .NET 6 (5.0 -> 6.0) --- .../GodotSharp/GodotPlugins/GodotPlugins.csproj | 4 ++-- .../mono/glue/GodotSharp/GodotSharp/Core/AABB.cs | 5 ----- .../mono/glue/GodotSharp/GodotSharp/Core/Basis.cs | 5 ----- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 24 ++-------------------- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 5 ----- .../mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/MathfEx.cs | 5 ----- .../Core/NativeInterop/InteropStructs.cs | 5 ----- .../mono/glue/GodotSharp/GodotSharp/Core/Plane.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Projection.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Quaternion.cs | 5 ----- .../mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs | 5 ----- .../GodotSharp/GodotSharp/Core/StringExtensions.cs | 2 +- .../glue/GodotSharp/GodotSharp/Core/Transform2D.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Transform3D.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Vector2.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Vector2i.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Vector3.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Vector3i.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Vector4.cs | 5 ----- .../glue/GodotSharp/GodotSharp/Core/Vector4i.cs | 5 ----- .../glue/GodotSharp/GodotSharp/GlobalUsings.cs | 5 +++++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 3 ++- .../GodotSharpEditor/GodotSharpEditor.csproj | 4 ++-- 24 files changed, 14 insertions(+), 118 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj index 38cd2ece4e..e720d3878c 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj +++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj @@ -1,8 +1,8 @@ - net5.0 - 9 + net6.0 + 10 enable true 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/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index e1e2df199b..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; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 20d0c57179..71736a65c7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -706,11 +706,7 @@ namespace Godot.Bridge } else { -#if NET6_0_OR_GREATER - interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc(length))!; -#else - interopProperties = ((godotsharp_property_info*)Marshal.AllocHGlobal(length))!; -#endif + interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length))!; } try @@ -746,13 +742,7 @@ namespace Godot.Bridge interopProperties[i].Dispose(); if (!useStack) - { -#if NET6_0_OR_GREATER NativeMemory.Free(interopProperties); -#else - Marshal.FreeHGlobal((IntPtr)interopProperties); -#endif - } } } catch (Exception e) @@ -836,11 +826,7 @@ namespace Godot.Bridge } else { -#if NET6_0_OR_GREATER - interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc(length))!; -#else - interopDefaultValues = ((godotsharp_property_def_val_pair*)Marshal.AllocHGlobal(length))!; -#endif + interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length))!; } try @@ -871,13 +857,7 @@ namespace Godot.Bridge interopDefaultValues[i].Dispose(); if (!useStack) - { -#if NET6_0_OR_GREATER NativeMemory.Free(interopDefaultValues); -#else - Marshal.FreeHGlobal((IntPtr)interopDefaultValues); -#endif - } } } catch (Exception e) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 02ae32f46e..206a8f1c0a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.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.Collections.Generic; using Godot.NativeInterop; 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/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index f15dc941c9..8a0dc9b235 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.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.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; 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 369b95c7cb..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; 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/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index ce89e134b8..f0bc5949df 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -1603,7 +1603,7 @@ namespace Godot /// /// The string to escape. /// The escaped string. - public static string? XMLEscape(this string instance) + public static string XMLEscape(this string instance) { return SecurityElement.Escape(instance); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 1e3a13ba8e..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; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 27de87d7d4..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; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 6223f3ae53..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; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs index e4c3f869b1..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; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 7e98969d34..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; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs index 88b58e20cd..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; 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 cc38ad31b6..ce1a53cff2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -4,7 +4,7 @@ bin/$(Configuration) false Godot - net5.0 + net6.0 $(OutputPath)/$(AssemblyName).xml false true @@ -97,6 +97,7 @@ + CS1591 + + Godot C# Core API. + Godot Engine contributors + + GodotSharp + 4.0.0 + $(PackageVersion_GodotSharp) + https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp + $(RepositoryUrl) + MIT + + true + + + + + SdkPackageVersions.props + + $(DefineConstants);GODOT diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index aebf3a4a18..ebf09aab7b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -10,6 +10,19 @@ true 10 + + Godot C# Editor API. + Godot Engine contributors + + GodotSharpEditor + 4.0.0 + $(PackageVersion_GodotSharp) + https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor + $(RepositoryUrl) + MIT + + true + $(DefineConstants);GODOT -- cgit v1.2.3 From e235cef09f71d0cd752ba4931640be24dcb551ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sat, 28 May 2022 04:56:46 +0200 Subject: C#: Re-implement assembly reloading with ALCs --- .../System.Runtime.InteropServices.xml | 5 + modules/mono/glue/GodotSharp/GodotPlugins/Main.cs | 147 +++++++++- .../GodotSharp/GodotPlugins/PluginLoadContext.cs | 5 + .../glue/GodotSharp/GodotSharp.sln.DotSettings | 1 + .../GodotSharp/Core/Bridge/AlcReloadCfg.cs | 18 ++ .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 104 +++++-- .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 2 +- .../Core/Bridge/GodotSerializationInfo.cs | 67 +++++ .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 12 +- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 315 ++++++++++++++++----- .../Core/Bridge/ScriptManagerBridge.types.cs | 92 ++++++ .../GodotSharp/GodotSharp/Core/CustomGCHandle.cs | 98 +++++++ .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 176 ++++++++---- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 18 ++ .../GodotSharp/Core/DisposablesTracker.cs | 24 +- .../Core/NativeInterop/ExceptionUtils.cs | 2 +- .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 8 +- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 28 +- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 5 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 75 ++++- .../GodotSharp/GodotSharp/Core/ReflectionUtils.cs | 16 ++ .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 9 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 5 + 23 files changed, 1050 insertions(+), 182 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs (limited to 'modules/mono/glue/GodotSharp') 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 @@ + + + + + diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs index 2a2e147eaa..395cc9bf66 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -2,6 +2,7 @@ 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; @@ -11,10 +12,55 @@ 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 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 SharedAssemblies = new(); private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly; private static Assembly? _editorApiAssembly; - private static Assembly? _projectAssembly; + private static PluginLoadContextWrapper? _projectLoadContext; private static readonly AssemblyLoadContext MainLoadContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? @@ -35,6 +81,8 @@ namespace GodotPlugins SharedAssemblies.Add(CoreApiAssembly.GetName()); NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver); + AlcReloadCfg.Configure(alcReloadEnabled: editorHint.ToBool()); + if (editorHint.ToBool()) { _editorApiAssembly = Assembly.Load("GodotSharpEditor"); @@ -46,6 +94,7 @@ namespace GodotPlugins { LoadProjectAssemblyCallback = &LoadProjectAssembly, LoadToolsAssemblyCallback = &LoadToolsAssembly, + UnloadProjectPluginCallback = &UnloadProjectPlugin, }; *managedCallbacks = ManagedCallbacks.Create(); @@ -55,37 +104,41 @@ namespace GodotPlugins catch (Exception e) { Console.Error.WriteLine(e); - return false.ToGodotBool(); + return godot_bool.False; } } [StructLayout(LayoutKind.Sequential)] private struct PluginsCallbacks { - public unsafe delegate* unmanaged LoadProjectAssemblyCallback; + public unsafe delegate* unmanaged LoadProjectAssemblyCallback; public unsafe delegate* unmanaged LoadToolsAssemblyCallback; + public unsafe delegate* unmanaged UnloadProjectPluginCallback; } [UnmanagedCallersOnly] - private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath) + private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath, godot_string* outLoadedAssemblyPath) { try { - if (_projectAssembly != null) + if (_projectLoadContext != null) return godot_bool.True; // Already loaded string assemblyPath = new(nAssemblyPath); - _projectAssembly = LoadPlugin(assemblyPath); + (var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath); + + string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath; + *outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath); - ScriptManagerBridge.LookupScriptsInAssembly(_projectAssembly); + ScriptManagerBridge.LookupScriptsInAssembly(projectAssembly); return godot_bool.True; } catch (Exception e) { Console.Error.WriteLine(e); - return false.ToGodotBool(); + return godot_bool.False; } } @@ -99,7 +152,7 @@ namespace GodotPlugins if (_editorApiAssembly == null) throw new InvalidOperationException("The Godot editor API assembly is not loaded"); - var assembly = LoadPlugin(assemblyPath); + var (assembly, _) = LoadPlugin(assemblyPath); NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!); @@ -122,7 +175,7 @@ namespace GodotPlugins } } - private static Assembly LoadPlugin(string assemblyPath) + private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath) { string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); @@ -135,8 +188,78 @@ namespace GodotPlugins sharedAssemblies.Add(sharedAssemblyName); } - var loadContext = new PluginLoadContext(assemblyPath, sharedAssemblies, MainLoadContext); - return loadContext.LoadFromAssemblyName(new AssemblyName(assemblyName)); + 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 index 982549fff7..dcd572c65e 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs @@ -12,8 +12,11 @@ namespace GodotPlugins private readonly ICollection _sharedAssemblies; private readonly AssemblyLoadContext _mainLoadContext; + public string? AssemblyLoadedPath { get; private set; } + public PluginLoadContext(string pluginPath, ICollection sharedAssemblies, AssemblyLoadContext mainLoadContext) + : base(isCollectible: true) { _resolver = new AssemblyDependencyResolver(pluginPath); _sharedAssemblies = sharedAssemblies; @@ -31,6 +34,8 @@ namespace GodotPlugins 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"); diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings index 3103fa78c7..ba65b61e95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings @@ -1,5 +1,6 @@ GC + True True True True 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 index db53a508ef..9ede67b285 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -18,7 +18,7 @@ namespace Godot.Bridge { *ret = default; (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; - return false.ToGodotBool(); + return godot_bool.False; } bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method), @@ -31,17 +31,17 @@ namespace Godot.Bridge // 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 false.ToGodotBool(); + return godot_bool.False; } *ret = retValue; - return true.ToGodotBool(); + return godot_bool.True; } catch (Exception e) { ExceptionUtils.LogException(e); *ret = default; - return false.ToGodotBool(); + return godot_bool.False; } } @@ -57,7 +57,7 @@ namespace Godot.Bridge if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value))) { - return true.ToGodotBool(); + return godot_bool.True; } var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( @@ -70,7 +70,7 @@ namespace Godot.Bridge catch (Exception e) { ExceptionUtils.LogException(e); - return false.ToGodotBool(); + return godot_bool.False; } } @@ -88,7 +88,7 @@ namespace Godot.Bridge if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue)) { *outRet = outRetValue; - return true.ToGodotBool(); + return godot_bool.True; } var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( @@ -99,17 +99,17 @@ namespace Godot.Bridge if (ret == null) { *outRet = default; - return false.ToGodotBool(); + return godot_bool.False; } *outRet = Marshaling.ConvertManagedObjectToVariant(ret); - return true.ToGodotBool(); + return godot_bool.True; } catch (Exception e) { ExceptionUtils.LogException(e); *outRet = default; - return false.ToGodotBool(); + return godot_bool.False; } } @@ -141,7 +141,7 @@ namespace Godot.Bridge if (self == null) { *outRes = default; - *outValid = false.ToGodotBool(); + *outValid = godot_bool.False; return; } @@ -150,18 +150,18 @@ namespace Godot.Bridge if (resultStr == null) { *outRes = default; - *outValid = false.ToGodotBool(); + *outValid = godot_bool.False; return; } *outRes = Marshaling.ConvertStringToNative(resultStr); - *outValid = true.ToGodotBool(); + *outValid = godot_bool.True; } catch (Exception e) { ExceptionUtils.LogException(e); *outRes = default; - *outValid = false.ToGodotBool(); + *outValid = godot_bool.False; } } @@ -173,14 +173,86 @@ namespace Godot.Bridge var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) - return false.ToGodotBool(); + return godot_bool.False; return godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(method)).ToGodotBool(); } catch (Exception e) { ExceptionUtils.LogException(e); - return false.ToGodotBool(); + 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 + + var info = new GodotSerializationInfo( + Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_dictionary_new_copy(*propertiesState)), + Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_dictionary_new_copy(*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 + + var info = new GodotSerializationInfo( + Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_dictionary_new_copy(*propertiesState)), + Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_dictionary_new_copy(*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 index bd11811c7d..456a118b90 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -11,7 +11,7 @@ namespace Godot.Bridge { try { - GCHandle.FromIntPtr(gcHandlePtr).Free(); + CustomGCHandle.Free(GCHandle.FromIntPtr(gcHandlePtr)); } catch (Exception 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..26fbed8cac --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Godot.Bridge; + +public class GodotSerializationInfo +{ + private readonly Collections.Dictionary _properties = new(); + private readonly Collections.Dictionary _signalEvents = new(); + + internal GodotSerializationInfo() + { + } + + internal GodotSerializationInfo( + Collections.Dictionary properties, + Collections.Dictionary signalEvents + ) + { + _properties = properties; + _signalEvents = signalEvents; + } + + public void AddProperty(StringName name, object value) + { + _properties[name] = value; + } + + public bool TryGetProperty(StringName name, [MaybeNullWhen(false)] out T value) + { + return _properties.TryGetValueAsType(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 Delegate GetSignalEventDelegate(StringName name) + { + if (DelegateUtils.TryDeserializeDelegate(_signalEvents[name], out var eventDelegate)) + { + return eventDelegate; + } + else if (OS.IsStdoutVerbose()) + { + Console.WriteLine($"Failed to deserialize event signal delegate: {name}"); + } + + return null; + } + + public IEnumerable GetSignalEventsList() + { + return _signalEvents.Keys; + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 9f9d740659..a6e5f6da1a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -11,6 +11,8 @@ namespace Godot.Bridge public delegate* unmanaged SignalAwaiter_SignalCallback; public delegate* unmanaged DelegateUtils_InvokeWithVariantArgs; public delegate* unmanaged DelegateUtils_DelegateEquals; + public delegate* unmanaged DelegateUtils_TrySerializeDelegateWithGCHandle; + public delegate* unmanaged DelegateUtils_TryDeserializeDelegateWithGCHandle; public delegate* unmanaged ScriptManagerBridge_FrameCallback; public delegate* unmanaged ScriptManagerBridge_CreateManagedForGodotObjectBinding; public delegate* unmanaged ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; @@ -23,7 +25,8 @@ namespace Godot.Bridge public delegate* unmanaged ScriptManagerBridge_AddScriptBridge; public delegate* unmanaged ScriptManagerBridge_GetOrCreateScriptBridgeForPath; public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; - public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; + public delegate* unmanaged ScriptManagerBridge_TryReloadRegisteredScriptWithClass; + public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; public delegate* unmanaged ScriptManagerBridge_SwapGCHandleForType; public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyInfoList; public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyDefaultValues; @@ -33,6 +36,8 @@ namespace Godot.Bridge public delegate* unmanaged CSharpInstanceBridge_CallDispose; public delegate* unmanaged CSharpInstanceBridge_CallToString; public delegate* unmanaged CSharpInstanceBridge_HasMethodUnknownParams; + public delegate* unmanaged CSharpInstanceBridge_SerializeState; + public delegate* unmanaged CSharpInstanceBridge_DeserializeState; public delegate* unmanaged GCHandleBridge_FreeGCHandle; public delegate* unmanaged DebuggingUtils_GetCurrentStackInfo; public delegate* unmanaged DisposablesTracker_OnGodotShuttingDown; @@ -47,6 +52,8 @@ namespace Godot.Bridge 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, @@ -59,6 +66,7 @@ namespace Godot.Bridge 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, @@ -69,6 +77,8 @@ namespace Godot.Bridge 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, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 61987c6466..40f1235e7e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -1,22 +1,77 @@ +#nullable enable + using System; +using System.Collections.Concurrent; 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.Collections; using Godot.NativeInterop; namespace Godot.Bridge { - public static class ScriptManagerBridge + // TODO: Make class internal once we replace LookupScriptsInAssembly (the only public member) with source generators + public static partial class ScriptManagerBridge { - private static System.Collections.Generic.Dictionary _pathScriptMap = new(); + private static ConcurrentDictionary> + _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 readonly object ScriptBridgeLock = new(); - private static System.Collections.Generic.Dictionary _scriptTypeMap = new(); - private static System.Collections.Generic.Dictionary _typeScriptMap = new(); + private static ConcurrentDictionary + _scriptDataForReload = new(); [UnmanagedCallersOnly] internal static void FrameCallback() @@ -55,7 +110,7 @@ namespace Godot.Bridge _ = ctor!.Invoke(obj, null); - return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(obj)); } catch (Exception e) { @@ -74,7 +129,7 @@ namespace Godot.Bridge try { // Performance is not critical here as this will be replaced with source generators. - Type scriptType = _scriptTypeMap[scriptPtr]; + Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr); var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); var ctor = scriptType @@ -99,7 +154,7 @@ namespace Godot.Bridge var parameters = ctor.GetParameters(); int paramCount = parameters.Length; - object[] invokeParams = new object[paramCount]; + var invokeParams = new object?[paramCount]; for (int i = 0; i < paramCount; i++) { @@ -112,12 +167,12 @@ namespace Godot.Bridge _ = ctor.Invoke(obj, invokeParams); - return true.ToGodotBool(); + return godot_bool.True; } catch (Exception e) { ExceptionUtils.LogException(e); - return false.ToGodotBool(); + return godot_bool.False; } } @@ -127,7 +182,7 @@ namespace Godot.Bridge try { // Performance is not critical here as this will be replaced with source generators. - if (!_scriptTypeMap.TryGetValue(scriptPtr, out var scriptType)) + if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType)) { *outRes = default; return; @@ -144,7 +199,7 @@ namespace Godot.Bridge return; } - var nativeName = (StringName)field.GetValue(null); + var nativeName = (StringName?)field.GetValue(null); if (nativeName == null) { @@ -166,7 +221,7 @@ namespace Godot.Bridge { try { - var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + var target = (Object?)GCHandle.FromIntPtr(gcHandlePtr).Target; if (target != null) target.NativePtr = newPtr; } @@ -176,14 +231,14 @@ namespace Godot.Bridge } } - private static Type TypeGetProxyClass(string nativeTypeNameStr) + 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); + Type? wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr); if (wrapperType == null) { @@ -216,7 +271,12 @@ namespace Godot.Bridge if (scriptPathAttr == null) return; - _pathScriptMap[scriptPathAttr.Path] = type; + _pathTypeBiMap.Add(scriptPathAttr.Path, type); + + if (AlcReloadCfg.IsAlcReloadingEnabled) + { + AddTypeForAlcReloading(type); + } } var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false) @@ -267,15 +327,15 @@ namespace Godot.Bridge { try { - var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; + var owner = (Object?)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; if (owner == null) { - *outOwnerIsNull = true.ToGodotBool(); + *outOwnerIsNull = godot_bool.True; return; } - *outOwnerIsNull = false.ToGodotBool(); + *outOwnerIsNull = godot_bool.False; owner.InternalRaiseEventSignal(CustomUnsafe.AsRef(eventSignalName), new NativeVariantPtrArgs(args), argCount); @@ -283,7 +343,7 @@ namespace Godot.Bridge catch (Exception e) { ExceptionUtils.LogException(e); - *outOwnerIsNull = false.ToGodotBool(); + *outOwnerIsNull = godot_bool.False; } } @@ -295,7 +355,7 @@ namespace Godot.Bridge // Performance is not critical here as this will be replaced with source generators. using var signals = new Dictionary(); - Type top = _scriptTypeMap[scriptPtr]; + Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr); Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -391,7 +451,7 @@ namespace Godot.Bridge string signalNameStr = Marshaling.ConvertStringToManaged(*signalName); - Type top = _scriptTypeMap[scriptPtr]; + Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr); Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -405,7 +465,7 @@ namespace Godot.Bridge .Any(signalDelegate => signalDelegate.Name == signalNameStr) ) { - return true.ToGodotBool(); + return godot_bool.True; } // Event signals @@ -417,18 +477,18 @@ namespace Godot.Bridge .Any(eventSignal => eventSignal.Name == signalNameStr) ) { - return true.ToGodotBool(); + return godot_bool.True; } top = top.BaseType; } - return false.ToGodotBool(); + return godot_bool.False; } catch (Exception e) { ExceptionUtils.LogException(e); - return false.ToGodotBool(); + return godot_bool.False; } } @@ -437,18 +497,18 @@ namespace Godot.Bridge { try { - if (!_scriptTypeMap.TryGetValue(scriptPtr, out var scriptType)) - return false.ToGodotBool(); + if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType)) + return godot_bool.False; - if (!_scriptTypeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) - return false.ToGodotBool(); + 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 false.ToGodotBool(); + return godot_bool.False; } } @@ -457,26 +517,25 @@ namespace Godot.Bridge { try { - lock (ScriptBridgeLock) + lock (_scriptTypeBiMap.ReadWriteLock) { - if (!_scriptTypeMap.ContainsKey(scriptPtr)) + if (!_scriptTypeBiMap.IsScriptRegistered(scriptPtr)) { string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath); - if (!_pathScriptMap.TryGetValue(scriptPathStr, out Type scriptType)) - return false.ToGodotBool(); + if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType)) + return godot_bool.False; - _scriptTypeMap.Add(scriptPtr, scriptType); - _typeScriptMap.Add(scriptType, scriptPtr); + _scriptTypeBiMap.Add(scriptPtr, scriptType); } } - return true.ToGodotBool(); + return godot_bool.True; } catch (Exception e) { ExceptionUtils.LogException(e); - return false.ToGodotBool(); + return godot_bool.False; } } @@ -485,7 +544,7 @@ namespace Godot.Bridge { string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath); - if (!_pathScriptMap.TryGetValue(scriptPathStr, out Type scriptType)) + if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType)) { NativeFuncs.godotsharp_internal_new_csharp_script(outScript); return; @@ -494,34 +553,84 @@ namespace Godot.Bridge GetOrCreateScriptBridgeForType(scriptType, outScript); } - internal static unsafe void GetOrCreateScriptBridgeForType(Type scriptType, godot_ref* outScript) + private static unsafe void GetOrCreateScriptBridgeForType(Type scriptType, godot_ref* outScript) { - lock (ScriptBridgeLock) + lock (_scriptTypeBiMap.ReadWriteLock) { - if (_typeScriptMap.TryGetValue(scriptType, out IntPtr scriptPtr)) + if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr)) { + // Use existing NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr); return; } - NativeFuncs.godotsharp_internal_new_csharp_script(outScript); - scriptPtr = outScript->Reference; + // This path is slower, but it's only executed for the first instantiation of the type + CreateScriptBridgeForType(scriptType, outScript); + } + } - _scriptTypeMap.Add(scriptPtr, scriptType); - _typeScriptMap.Add(scriptType, scriptPtr); + 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; + } - NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr); + // 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 (ScriptBridgeLock) + lock (_scriptTypeBiMap.ReadWriteLock) { - _ = _scriptTypeMap.Remove(scriptPtr); + _scriptTypeBiMap.Remove(scriptPtr); } } catch (Exception e) @@ -530,14 +639,75 @@ namespace Godot.Bridge } } + [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_dictionary* outRpcFunctionsDest) + godot_dictionary* outRpcFunctionsDest, godot_ref* outBaseScript) { try { // Performance is not critical here as this will be replaced with source generators. - var scriptType = _scriptTypeMap[scriptPtr]; + var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr); *outTool = scriptType.GetCustomAttributes(inherit: false) .OfType() @@ -551,13 +721,13 @@ namespace Godot.Bridge } if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools") - *outTool = true.ToGodotBool(); + *outTool = godot_bool.True; // RPC functions Dictionary rpcFunctions = new(); - Type top = scriptType; + Type? top = scriptType; Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -595,11 +765,21 @@ namespace Godot.Bridge *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy( (godot_dictionary)((Dictionary)rpcFunctions).NativeValue); + + var baseType = scriptType.BaseType; + if (baseType != null && baseType != native) + { + GetOrLoadOrCreateScriptForType(baseType, outBaseScript); + } + else + { + *outBaseScript = default; + } } catch (Exception e) { ExceptionUtils.LogException(e); - *outTool = false.ToGodotBool(); + *outTool = godot_bool.False; *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new(); } } @@ -612,28 +792,29 @@ namespace Godot.Bridge { var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); - object target = oldGCHandle.Target; + object? target = oldGCHandle.Target; if (target == null) { - oldGCHandle.Free(); + CustomGCHandle.Free(oldGCHandle); *outNewGCHandlePtr = IntPtr.Zero; - return false.ToGodotBool(); // Called after the managed side was collected, so nothing to do here + 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 = GCHandle.Alloc(target, - createWeak.ToBool() ? GCHandleType.Weak : GCHandleType.Normal); + var newGCHandle = createWeak.ToBool() ? + CustomGCHandle.AllocWeak(target) : + CustomGCHandle.AllocStrong(target); - oldGCHandle.Free(); + CustomGCHandle.Free(oldGCHandle); *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); - return true.ToGodotBool(); + return godot_bool.True; } catch (Exception e) { ExceptionUtils.LogException(e); *outNewGCHandlePtr = IntPtr.Zero; - return false.ToGodotBool(); + return godot_bool.False; } } @@ -662,7 +843,7 @@ namespace Godot.Bridge { try { - Type scriptType = _scriptTypeMap[scriptPtr]; + Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr); GetPropertyInfoListForType(scriptType, scriptPtr, addPropInfoFunc); } catch (Exception e) @@ -684,7 +865,7 @@ namespace Godot.Bridge if (getGodotPropertiesMetadataMethod == null) return; - var properties = (System.Collections.Generic.List) + var properties = (System.Collections.Generic.List?) getGodotPropertiesMetadataMethod.Invoke(null, null); if (properties == null || properties.Count <= 0) @@ -774,7 +955,7 @@ namespace Godot.Bridge { try { - Type top = _scriptTypeMap[scriptPtr]; + Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr); Type native = Object.InternalGetClassNativeBase(top); while (top != null && top != native) @@ -804,7 +985,7 @@ namespace Godot.Bridge if (getGodotPropertyDefaultValuesMethod == null) return; - var defaultValues = (System.Collections.Generic.Dictionary) + var defaultValues = (System.Collections.Generic.Dictionary?) getGodotPropertyDefaultValuesMethod.Invoke(null, null); if (defaultValues == null || defaultValues.Count <= 0) 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 _scriptTypeMap = new(); + private System.Collections.Generic.Dictionary _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 _pathTypeMap = new(); + private System.Collections.Generic.Dictionary _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/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; + +/// +/// 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. +/// +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 _alcsBeingUnloaded = new(); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool IsAlcBeingUnloaded(AssemblyLoadContext alc) => _alcsBeingUnloaded.TryGetValue(alc, out _); + + // ReSharper disable once RedundantNameQualifier + private static ConcurrentDictionary< + AssemblyLoadContext, + ConcurrentDictionary + > _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/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index fb5e3c6dda..48eec66182 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -1,6 +1,8 @@ +#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; @@ -16,14 +18,14 @@ namespace Godot { try { - var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; - var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; - return (@delegateA == @delegateB).ToGodotBool(); + 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 false.ToGodotBool(); + return godot_bool.False; } } @@ -34,10 +36,10 @@ namespace Godot try { // TODO: Optimize - var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; - var managedArgs = new object[argc]; + var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!; + var managedArgs = new object?[argc]; - var parameterInfos = @delegate!.Method.GetParameters(); + var parameterInfos = @delegate.Method.GetParameters(); var paramsLength = parameterInfos.Length; if (argc != paramsLength) @@ -52,7 +54,7 @@ namespace Godot *args[i], parameterInfos[i].ParameterType); } - object invokeRet = @delegate.DynamicInvoke(managedArgs); + object? invokeRet = @delegate.DynamicInvoke(managedArgs); *outRet = Marshaling.ConvertManagedObjectToVariant(invokeRet); } @@ -72,10 +74,7 @@ namespace Godot CompilerGenerated } - internal static bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle, Collections.Array serializedData) - => TrySerializeDelegate((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target, serializedData); - - private static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) + internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) { if (@delegate is MulticastDelegate multicastDelegate) { @@ -98,7 +97,7 @@ namespace Godot } } - if (TrySerializeSingleDelegate(@delegate, out byte[] buffer)) + if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer)) { serializedData.Add(buffer); return true; @@ -107,11 +106,11 @@ namespace Godot 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) { @@ -200,9 +199,6 @@ namespace Godot private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo) { - if (methodInfo == null) - return false; - SerializeType(writer, methodInfo.DeclaringType); writer.Write(methodInfo.Name); @@ -241,7 +237,7 @@ namespace Godot return true; } - private static void SerializeType(BinaryWriter writer, Type type) + private static void SerializeType(BinaryWriter writer, Type? type) { if (type == null) { @@ -256,9 +252,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]); @@ -268,21 +263,62 @@ 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()); } } - private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, - out IntPtr delegateGCHandle) + [UnmanagedCallersOnly] + internal static unsafe godot_bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle, + godot_array* nSerializedData) { - bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate); - delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate)); - return res; + 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) { if (serializedData.Count == 1) { @@ -302,12 +338,12 @@ namespace Godot { 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); } } @@ -315,11 +351,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; @@ -332,14 +368,18 @@ 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: @@ -350,32 +390,37 @@ namespace Godot 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++) { @@ -383,12 +428,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: @@ -397,18 +447,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(); @@ -418,7 +472,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; @@ -432,15 +486,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 @@ -451,7 +513,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 2bea2f3b4f..2523728c8b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -578,6 +578,24 @@ namespace Godot.Collections return found; } + // TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace Sysme.Object with a Variant type. + internal bool TryGetValueAsType(TKey key, [MaybeNullWhen(false)] out TValueCustom value) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(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 ? + (TValueCustom)Marshaling.ConvertVariantToManagedObjectOfType(retValue, typeof(TValueCustom)) : + default; + } + + return found; + } + // ICollection> /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs index 4e15b37e44..75793ea446 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Runtime.InteropServices; -using System.Runtime.Loader; using Godot.NativeInterop; #nullable enable @@ -10,17 +9,12 @@ namespace Godot { internal static class DisposablesTracker { - static DisposablesTracker() - { - AssemblyLoadContext.Default.Unloading += _ => OnUnloading(); - } - [UnmanagedCallersOnly] internal static void OnGodotShuttingDown() { try { - OnUnloading(); + OnGodotShuttingDownImpl(); } catch (Exception e) { @@ -28,7 +22,7 @@ namespace Godot } } - private static void OnUnloading() + private static void OnGodotShuttingDownImpl() { bool isStdoutVerbose; @@ -66,30 +60,30 @@ namespace Godot } // ReSharper disable once RedundantNameQualifier - private static ConcurrentDictionary, object?> GodotObjectInstances { get; } = + private static ConcurrentDictionary, byte> GodotObjectInstances { get; } = new(); - private static ConcurrentDictionary, object?> OtherInstances { get; } = + private static ConcurrentDictionary, byte> OtherInstances { get; } = new(); public static WeakReference RegisterGodotObject(Object godotObject) { var weakReferenceToSelf = new WeakReference(godotObject); - GodotObjectInstances.TryAdd(weakReferenceToSelf, null); + GodotObjectInstances.TryAdd(weakReferenceToSelf, 0); return weakReferenceToSelf; } public static WeakReference RegisterDisposable(IDisposable disposable) { var weakReferenceToSelf = new WeakReference(disposable); - OtherInstances.TryAdd(weakReferenceToSelf, null); + OtherInstances.TryAdd(weakReferenceToSelf, 0); return weakReferenceToSelf; } - public static void UnregisterGodotObject(WeakReference weakReference) + public static void UnregisterGodotObject(Object godotObject, WeakReference weakReferenceToSelf) { - if (!GodotObjectInstances.TryRemove(weakReference, out _)) - throw new ArgumentException("Godot Object not registered", nameof(weakReference)); + if (!GodotObjectInstances.TryRemove(weakReferenceToSelf, out _)) + throw new ArgumentException("Godot Object not registered", nameof(weakReferenceToSelf)); } public static void UnregisterDisposable(WeakReference weakReference) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index 7a2f205632..5a0ea2ba13 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -95,7 +95,7 @@ namespace Godot.NativeInterop } NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line, - nErrorMsg, nExcMsg, p_warning: false.ToGodotBool(), stackInfoVector); + nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index f6f0186016..82f1c04d40 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -50,7 +50,9 @@ namespace Godot.NativeInterop public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged, StringName nativeName, bool refCounted, Type type, Type nativeType) { - var gcHandle = GCHandle.Alloc(managed, refCounted ? GCHandleType.Weak : GCHandleType.Normal); + var gcHandle = refCounted ? + CustomGCHandle.AllocWeak(managed) : + CustomGCHandle.AllocStrong(managed, type); if (type == nativeType) { @@ -65,7 +67,7 @@ namespace Godot.NativeInterop // 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.GetOrCreateScriptBridgeForType(type, &script); + ScriptManagerBridge.GetOrLoadOrCreateScriptForType(type, &script); // IMPORTANT: This must be called after GetOrCreateScriptBridgeForType NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged( @@ -80,7 +82,7 @@ namespace Godot.NativeInterop if (type == nativeType) return; - var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal); + var strongGCHandle = CustomGCHandle.AllocStrong(managed); NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( GCHandle.ToIntPtr(strongGCHandle), unmanaged); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index fc11f56680..1a0d9946d2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -577,7 +577,29 @@ namespace Godot.NativeInterop { if (typeof(Godot.Object).IsAssignableFrom(type)) { - var godotObject = VariantUtils.ConvertToGodotObject(p_var); + 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)) { @@ -864,9 +886,9 @@ namespace Godot.NativeInterop { if (p_managed_callable.Delegate != null) { + var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate); NativeFuncs.godotsharp_callable_new_with_delegate( - GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), - out godot_callable callable); + GCHandle.ToIntPtr(gcHandle), out godot_callable callable); return callable; } else diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index d7c57fa260..1ab2b4c0bf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -95,7 +95,10 @@ namespace Godot.NativeInterop IntPtr oldGCHandlePtr); [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_new_csharp_script(godot_ref* r_script); + internal static extern void godotsharp_internal_new_csharp_script(godot_ref* r_dest); + + [DllImport(GodotDllName)] + internal static extern godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest); [DllImport(GodotDllName)] internal static extern void godotsharp_internal_reload_registered_script(IntPtr scriptPtr); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 71a620716f..8d683c6d1e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using Godot.Bridge; using Godot.NativeInterop; namespace Godot @@ -150,7 +151,7 @@ namespace Godot NativePtr = IntPtr.Zero; } - DisposablesTracker.UnregisterGodotObject(_weakReferenceToSelf); + DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf); } /// @@ -328,5 +329,77 @@ namespace Godot return nativeConstructor; } + + protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info) + { + // Temporary solution via reflection until we add a signals events source generator + + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType().Any()) + .Select(ev => ev.Name); + + var fields = top.GetFields( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + foreach (var eventSignalField in fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name))) + { + var eventSignalDelegate = (Delegate)eventSignalField.GetValue(this); + info.AddSignalEventDelegate(eventSignalField.Name, eventSignalDelegate); + } + + top = top.BaseType; + } + } + + // TODO: Should this be a constructor overload? + protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info) + { + // Temporary solution via reflection until we add a signals events source generator + + void RestoreSignalEvent(StringName signalEventName) + { + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var foundEventSignal = top.GetEvent(signalEventName, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (foundEventSignal != null && + foundEventSignal.GetCustomAttributes().OfType().Any()) + { + var field = top.GetField(foundEventSignal.Name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (field != null && typeof(Delegate).IsAssignableFrom(field.FieldType)) + { + var eventSignalDelegate = info.GetSignalEventDelegate(signalEventName); + field.SetValue(this, eventSignalDelegate); + return; + } + } + + top = top.BaseType; + } + } + + foreach (var signalEventName in info.GetSignalEventsList()) + { + RestoreSignalEvent(signalEventName); + } + } } } 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 fb72d706c7..8ba3c403fa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -12,10 +12,11 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { + 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(GCHandle.Alloc(this))); + Object.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle)); } public bool IsCompleted => _completed; @@ -39,11 +40,11 @@ namespace Godot if (awaiter == null) { - *outAwaiterIsNull = true.ToGodotBool(); + *outAwaiterIsNull = godot_bool.True; return; } - *outAwaiterIsNull = false.ToGodotBool(); + *outAwaiterIsNull = godot_bool.False; awaiter._completed = true; @@ -59,7 +60,7 @@ namespace Godot catch (Exception e) { ExceptionUtils.LogException(e); - *outAwaiterIsNull = false.ToGodotBool(); + *outAwaiterIsNull = godot_bool.False; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 2e121bb789..d0897fe85e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -49,6 +49,8 @@ + + @@ -59,9 +61,11 @@ + + @@ -100,6 +104,7 @@ + -- cgit v1.2.3 From f033764ffe5892f963a9416e8cbcfd0fb5225103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sat, 28 May 2022 04:56:54 +0200 Subject: C#: Refactor Array & Dictionary interface implementations --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 93 ++++++--- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 208 +++++++++++++++------ .../GodotSharp/Core/NativeInterop/Marshaling.cs | 2 +- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 4 +- 4 files changed, 217 insertions(+), 90 deletions(-) (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a09ba09e95..3a1a8ac563 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -14,7 +14,9 @@ namespace Godot.Collections /// such as or . /// public sealed class Array : - IList, + IList, + IReadOnlyList, + ICollection, IDisposable { internal godot_array.movable NativeValue; @@ -163,12 +165,6 @@ namespace Godot.Collections return newArray; } - // IList - - bool IList.IsReadOnly => false; - - bool IList.IsFixedSize => false; - /// /// Returns the object at the given . /// @@ -194,21 +190,21 @@ namespace Godot.Collections /// Adds an object to the end of this . /// This is the same as append or push_back in GDScript. /// - /// The object to add. + /// The object to add. /// The new size after adding the object. - public int Add(object value) + public void Add(object item) { - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); var self = (godot_array)NativeValue; - return NativeFuncs.godotsharp_array_add(ref self, variantValue); + _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); } /// /// Checks if this contains the given object. /// - /// The item to look for. + /// The item to look for. /// Whether or not this array contains the given object. - public bool Contains(object value) => IndexOf(value) != -1; + public bool Contains(object item) => IndexOf(item) != -1; /// /// Erases all items from this . @@ -219,11 +215,11 @@ namespace Godot.Collections /// Searches this for an object /// and returns its index or -1 if not found. /// - /// The object to search for. + /// The object to search for. /// The index of the object, or -1 if not found. - public int IndexOf(object value) + public int IndexOf(object item) { - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); var self = (godot_array)NativeValue; return NativeFuncs.godotsharp_array_index_of(ref self, variantValue); } @@ -235,27 +231,32 @@ namespace Godot.Collections /// Existing items will be moved to the right. /// /// The index to insert at. - /// The object to insert. - public void Insert(int index, object value) + /// The object to insert. + public void Insert(int index, object item) { if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); var self = (godot_array)NativeValue; NativeFuncs.godotsharp_array_insert(ref self, index, variantValue); } /// - /// Removes the first occurrence of the specified + /// Removes the first occurrence of the specified /// from this . /// - /// The value to remove. - public void Remove(object value) + /// The value to remove. + public bool Remove(object item) { - int index = IndexOf(value); + int index = IndexOf(item); if (index >= 0) + { RemoveAt(index); + return true; + } + + return false; } /// @@ -280,17 +281,49 @@ namespace Godot.Collections /// The number of elements. public int Count => NativeValue.DangerousSelfRef.Size; - object ICollection.SyncRoot => this; + public bool IsSynchronized => false; - bool ICollection.IsSynchronized => false; + public object SyncRoot => false; + + public bool IsReadOnly => false; /// /// Copies the elements of this to the given /// untyped C# array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(object[] 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++) + { + object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]); + array[arrayIndex] = obj; + arrayIndex++; + } + } + } + + void ICollection.CopyTo(System.Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -357,7 +390,7 @@ namespace Godot.Collections /// Gets an enumerator for this . /// /// An enumerator. - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { int count = Count; @@ -367,6 +400,8 @@ namespace Godot.Collections } } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// /// Converts this to a string. /// @@ -632,7 +667,7 @@ namespace Godot.Collections /// The C# array to copy to. /// The index to start at. public void CopyTo(T[] array, int arrayIndex) => - _underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements); + _underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements); /// /// Removes the first occurrence of the specified value diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 2523728c8b..13aae72660 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -4,6 +4,7 @@ using System.Collections; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Linq; namespace Godot.Collections { @@ -13,7 +14,9 @@ namespace Godot.Collections /// interfacing with the engine. /// public sealed class Dictionary : + IDictionary, IDictionary, + IReadOnlyDictionary, IDisposable { internal godot_dictionary.movable NativeValue; @@ -94,7 +97,7 @@ namespace Godot.Collections /// /// Gets the collection of keys in this . /// - public ICollection Keys + public ICollection Keys { get { @@ -108,7 +111,7 @@ namespace Godot.Collections /// /// Gets the collection of elements in this . /// - public ICollection Values + public ICollection Values { get { @@ -119,6 +122,14 @@ namespace Godot.Collections } } + IEnumerable IReadOnlyDictionary.Keys => Keys; + + IEnumerable IReadOnlyDictionary.Values => Values; + + ICollection IDictionary.Keys => Keys.ToList(); + + ICollection IDictionary.Values => Values.ToList(); + private (Array keys, Array values, int count) GetKeyValuePairs() { var self = (godot_dictionary)NativeValue; @@ -189,6 +200,9 @@ namespace Godot.Collections NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue); } + void ICollection>.Add(KeyValuePair item) + => Add(item.Key, item.Value); + /// /// Erases all items from this . /// @@ -203,28 +217,72 @@ namespace Godot.Collections /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public bool Contains(object key) + public bool ContainsKey(object key) { using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool(); } - /// - /// Gets an enumerator for this . - /// - /// An enumerator. - public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + public bool Contains(KeyValuePair item) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); + 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; + + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); + } + } + + bool IDictionary.Contains(object key) + { + throw new NotImplementedException(); + } /// /// Removes an element from this by key. /// /// The key of the element to remove. - public void Remove(object key) + public bool Remove(object key) { using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; - NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey); + return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); + } + + public bool Remove(KeyValuePair item) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); + 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; + + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) + { + return NativeFuncs.godotsharp_dictionary_remove_key( + ref self, variantKey).ToBool(); + } + + return false; + } + } + + void IDictionary.Remove(object key) + { + _ = Remove(key); } // ICollection @@ -247,38 +305,91 @@ namespace Godot.Collections } } + public bool IsReadOnly => false; + + public bool TryGetValue(object key, out object value) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)NativeValue; + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, + variantKey, out godot_variant retValue).ToBool(); + + using (retValue) + { + value = found ? Marshaling.ConvertVariantToManagedObject(retValue) : default; + } + + return found; + } + /// - /// Copies the elements of this to the given - /// untyped C# array, starting at the given index. + /// Copies the elements of this to the given untyped + /// array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(KeyValuePair[] 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."); + + var (keys, values, count) = GetKeyValuePairs(); + + 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[arrayIndex] = new(keys[i], values[i]); + arrayIndex++; + } + } + + void ICollection.CopyTo(System.Array array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), + 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)) + 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.SetValue(new DictionaryEntry(keys[i], values[i]), arrayIndex); + arrayIndex++; } } // IEnumerable + /// + /// Gets an enumerator for this . + /// + /// An enumerator. + public IEnumerator> GetEnumerator() + { + for (int i = 0; i < Count; i++) + { + yield return GetKeyValuePair(i); + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IDictionaryEnumerator IDictionary.GetEnumerator() => new DictionaryEnumerator(this); + private class DictionaryEnumerator : IDictionaryEnumerator { private readonly Dictionary _dictionary; @@ -343,6 +454,20 @@ namespace Godot.Collections } } + private KeyValuePair 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); + using (key) + using (value) + { + return new KeyValuePair(Marshaling.ConvertVariantToManagedObject(key), + Marshaling.ConvertVariantToManagedObject(value)); + } + } + /// /// Converts this to a string. /// @@ -518,8 +643,9 @@ namespace Godot.Collections using (key) using (value) { - return new KeyValuePair((TKey)Marshaling.ConvertVariantToManagedObject(key), - (TValue)Marshaling.ConvertVariantToManagedObject(value)); + return new KeyValuePair( + (TKey)Marshaling.ConvertVariantToManagedObjectOfType(key, TypeOfKeys), + (TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues)); } } @@ -541,7 +667,7 @@ namespace Godot.Collections /// Whether or not this dictionary contains the given key. public bool ContainsKey(TKey key) { - return _underlyingDict.Contains(key); + return _underlyingDict.ContainsKey(key); } /// @@ -621,21 +747,7 @@ namespace Godot.Collections } bool ICollection>.Contains(KeyValuePair item) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(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 godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); - return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); - } - } + => _underlyingDict.Contains(new(item.Key, item.Value)); /// /// Copies the elements of this to the given @@ -666,27 +778,7 @@ namespace Godot.Collections } bool ICollection>.Remove(KeyValuePair item) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(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 godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); - if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) - { - return NativeFuncs.godotsharp_dictionary_remove_key( - ref self, variantKey).ToBool(); - } - - return false; - } - } + => _underlyingDict.Remove(new(item.Key, item.Value)); // IEnumerable> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 1a0d9946d2..563af91ae5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -686,7 +686,7 @@ namespace Godot.NativeInterop /* capacity: */ godotDictionary.Count }, null)!; - foreach (System.Collections.DictionaryEntry pair in godotDictionary) + foreach (KeyValuePair pair in godotDictionary) dictionary.Add(pair.Key, pair.Value); return dictionary; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 72be871f16..7aa27c2867 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -703,8 +703,8 @@ namespace Godot.NativeInterop var res = new System.Collections.Generic.Dictionary(godotDictionary.Count); - foreach (System.Collections.Generic.KeyValuePair pair in godotDictionary) - res.Add(pair.Key, pair.Value); + foreach (System.Collections.Generic.KeyValuePair pair in godotDictionary) + res.Add((TKey)pair.Key, (TValue)pair.Value); return res; } -- cgit v1.2.3 From 97713ff77a339faa72d54bd596e3d8c2b8520ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Thu, 28 Jul 2022 17:41:47 +0200 Subject: C#: Add source generator for signals as events Changed the signal declaration signal to: ``` // The following generates a MySignal event [Signal] public delegate void MySignalEventHandler(int param); ``` --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 6 +- .../Core/Bridge/GodotSerializationInfo.cs | 38 ++- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 6 +- .../GodotSharp/Core/Bridge/MethodInfo.cs | 25 ++ .../GodotSharp/Core/Bridge/PropertyInfo.cs | 40 ++- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 289 ++++++++------------- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 8 +- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 4 - .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 6 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 155 +---------- .../glue/GodotSharp/GodotSharp/Core/StringName.cs | 6 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 + 12 files changed, 198 insertions(+), 386 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 3a1a8ac563..afe295126e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -98,7 +98,11 @@ namespace Godot.Collections { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); - DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + + if (_weakReferenceToSelf != null) + { + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + } } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs index 26fbed8cac..53aeff8207 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs @@ -46,22 +46,34 @@ public class GodotSerializationInfo } } - public Delegate GetSignalEventDelegate(StringName name) + public bool TryGetSignalEventDelegate(StringName name, [MaybeNullWhen(false)] out T value) + where T : Delegate { - if (DelegateUtils.TryDeserializeDelegate(_signalEvents[name], out var eventDelegate)) + if (_signalEvents.TryGetValue(name, out Collections.Array serializedData)) { - return eventDelegate; - } - else if (OS.IsStdoutVerbose()) - { - Console.WriteLine($"Failed to deserialize event signal delegate: {name}"); - } + if (DelegateUtils.TryDeserializeDelegate(serializedData, out var eventDelegate)) + { + value = eventDelegate as T; - return null; - } + if (value == null) + { + Console.WriteLine($"Cannot cast the deserialized event signal delegate: {name}. " + + $"Expected '{typeof(T).FullName}'; got '{eventDelegate.GetType().FullName}'."); + return false; + } - public IEnumerable GetSignalEventsList() - { - return _signalEvents.Keys; + 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 index a6e5f6da1a..5d3f140cba 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -19,14 +19,12 @@ namespace Godot.Bridge public delegate* unmanaged ScriptManagerBridge_GetScriptNativeName; public delegate* unmanaged ScriptManagerBridge_SetGodotObjectPtr; public delegate* unmanaged ScriptManagerBridge_RaiseEventSignal; - public delegate* unmanaged ScriptManagerBridge_GetScriptSignalList; - public delegate* unmanaged ScriptManagerBridge_HasScriptSignal; public delegate* unmanaged ScriptManagerBridge_ScriptIsOrInherits; public delegate* unmanaged ScriptManagerBridge_AddScriptBridge; public delegate* unmanaged ScriptManagerBridge_GetOrCreateScriptBridgeForPath; public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; public delegate* unmanaged ScriptManagerBridge_TryReloadRegisteredScriptWithClass; - public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; + public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; public delegate* unmanaged ScriptManagerBridge_SwapGCHandleForType; public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyInfoList; public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyDefaultValues; @@ -60,8 +58,6 @@ namespace Godot.Bridge ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName, ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr, ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal, - ScriptManagerBridge_GetScriptSignalList = &ScriptManagerBridge.GetScriptSignalList, - ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal, ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits, ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge, ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath, 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..50260163bd --- /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? Arguments { get; init; } + public List? DefaultArguments { get; init; } + + public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags, List? arguments, + List? 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 index cfdfe2dab3..80d6f7b4a5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs @@ -1,26 +1,24 @@ -using System; -using Godot.NativeInterop; +namespace Godot.Bridge; -namespace Godot.Bridge +#nullable enable + +public struct PropertyInfo { - 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 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; - } + 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 index 40f1235e7e..4b9f851925 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -9,7 +10,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Loader; using System.Runtime.Serialization; -using Godot.Collections; using Godot.NativeInterop; namespace Godot.Bridge @@ -337,7 +337,7 @@ namespace Godot.Bridge *outOwnerIsNull = godot_bool.False; - owner.InternalRaiseEventSignal(CustomUnsafe.AsRef(eventSignalName), + owner.RaiseGodotClassSignalCallbacks(CustomUnsafe.AsRef(eventSignalName), new NativeVariantPtrArgs(args), argCount); } catch (Exception e) @@ -347,151 +347,6 @@ namespace Godot.Bridge } } - [UnmanagedCallersOnly] - internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* outRetSignals) - { - try - { - // Performance is not critical here as this will be replaced with source generators. - using var signals = new Dictionary(); - - Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr); - Type native = Object.InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - // Legacy signals - - foreach (var signalDelegate in top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | - BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any())) - { - var invokeMethod = signalDelegate.GetMethod("Invoke"); - - if (invokeMethod == null) - throw new MissingMethodException(signalDelegate.FullName, "Invoke"); - - var signalParams = new Collections.Array(); - - foreach (var parameters in invokeMethod.GetParameters()) - { - var paramType = Marshaling.ConvertManagedTypeToVariantType( - parameters.ParameterType, out bool nilIsVariant); - signalParams.Add(new Dictionary() - { - { "name", parameters.Name }, - { "type", paramType }, - { "nil_is_variant", nilIsVariant } - }); - } - - signals.Add(signalDelegate.Name, signalParams); - } - - // Event signals - - var foundEventSignals = top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any()) - .Select(ev => ev.Name); - - var fields = top.GetFields( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - foreach (var eventSignalField in fields - .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) - .Where(f => foundEventSignals.Contains(f.Name))) - { - var delegateType = eventSignalField.FieldType; - var invokeMethod = delegateType.GetMethod("Invoke"); - - if (invokeMethod == null) - throw new MissingMethodException(delegateType.FullName, "Invoke"); - - var signalParams = new Collections.Array(); - - foreach (var parameters in invokeMethod.GetParameters()) - { - var paramType = Marshaling.ConvertManagedTypeToVariantType( - parameters.ParameterType, out bool nilIsVariant); - signalParams.Add(new Dictionary() - { - { "name", parameters.Name }, - { "type", paramType }, - { "nil_is_variant", nilIsVariant } - }); - } - - signals.Add(eventSignalField.Name, signalParams); - } - - top = top.BaseType; - } - - *outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy((godot_dictionary)signals.NativeValue); - } - catch (Exception e) - { - ExceptionUtils.LogException(e); - *outRetSignals = NativeFuncs.godotsharp_dictionary_new(); - } - } - - [UnmanagedCallersOnly] - internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) - { - try - { - // Performance is not critical here as this will be replaced with source generators. - using var signals = new Dictionary(); - - string signalNameStr = Marshaling.ConvertStringToManaged(*signalName); - - Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr); - Type native = Object.InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - // Legacy signals - - if (top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType().Any()) - .Any(signalDelegate => signalDelegate.Name == signalNameStr) - ) - { - return godot_bool.True; - } - - // Event signals - - if (top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any()) - .Any(eventSignal => eventSignal.Name == signalNameStr) - ) - { - return godot_bool.True; - } - - top = top.BaseType; - } - - return godot_bool.False; - } - catch (Exception e) - { - ExceptionUtils.LogException(e); - return godot_bool.False; - } - } - [UnmanagedCallersOnly] internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) { @@ -702,7 +557,7 @@ namespace Godot.Bridge [UnmanagedCallersOnly] internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool, - godot_dictionary* outRpcFunctionsDest, godot_ref* outBaseScript) + godot_dictionary* outRpcFunctionsDest, godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript) { try { @@ -725,7 +580,7 @@ namespace Godot.Bridge // RPC functions - Dictionary rpcFunctions = new(); + Collections.Dictionary rpcFunctions = new(); Type? top = scriptType; Type native = Object.InternalGetClassNativeBase(top); @@ -749,7 +604,7 @@ namespace Godot.Bridge if (rpcAttr == null) continue; - var rpcConfig = new Dictionary(); + var rpcConfig = new Collections.Dictionary(); rpcConfig["rpc_mode"] = (long)rpcAttr.Mode; rpcConfig["call_local"] = rpcAttr.CallLocal; @@ -764,7 +619,54 @@ namespace Godot.Bridge *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy( - (godot_dictionary)((Dictionary)rpcFunctions).NativeValue); + (godot_dictionary)((Collections.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", param.Type }, + { "usage", 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) @@ -781,41 +683,22 @@ namespace Godot.Bridge ExceptionUtils.LogException(e); *outTool = godot_bool.False; *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new(); + *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new(); + *outBaseScript = default; } } - [UnmanagedCallersOnly] - internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr, - godot_bool createWeak) + private static List? GetSignalListForType(Type type) { - 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 - } + var getGodotSignalListMethod = type.GetMethod( + "GetGodotSignalList", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.NonPublic | BindingFlags.Public); - // Release the current weak handle and replace it with a strong handle. - var newGCHandle = createWeak.ToBool() ? - CustomGCHandle.AllocWeak(target) : - CustomGCHandle.AllocStrong(target); + if (getGodotSignalListMethod == null) + return null; - CustomGCHandle.Free(oldGCHandle); - *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); - return godot_bool.True; - } - catch (Exception e) - { - ExceptionUtils.LogException(e); - *outNewGCHandlePtr = IntPtr.Zero; - return godot_bool.False; - } + return (List?)getGodotSignalListMethod.Invoke(null, null); } // ReSharper disable once InconsistentNaming @@ -857,16 +740,16 @@ namespace Godot.Bridge { try { - var getGodotPropertiesMetadataMethod = type.GetMethod( - "GetGodotPropertiesMetadata", + var getGodotPropertyListMethod = type.GetMethod( + "GetGodotPropertyList", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - if (getGodotPropertiesMetadataMethod == null) + if (getGodotPropertyListMethod == null) return; - var properties = (System.Collections.Generic.List?) - getGodotPropertiesMetadataMethod.Invoke(null, null); + var properties = (List?) + getGodotPropertyListMethod.Invoke(null, null); if (properties == null || properties.Count <= 0) return; @@ -985,7 +868,7 @@ namespace Godot.Bridge if (getGodotPropertyDefaultValuesMethod == null) return; - var defaultValues = (System.Collections.Generic.Dictionary?) + var defaultValues = (Dictionary?) getGodotPropertyDefaultValuesMethod.Invoke(null, null); if (defaultValues == null || defaultValues.Count <= 0) @@ -1048,5 +931,39 @@ namespace Godot.Bridge 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/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 13aae72660..106cd7619f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -76,7 +76,11 @@ namespace Godot.Collections { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); - DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + + if (_weakReferenceToSelf != null) + { + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + } } /// @@ -704,7 +708,7 @@ namespace Godot.Collections return found; } - // TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace Sysme.Object with a Variant type. + // TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace System.Object with a Variant type. internal bool TryGetValueAsType(TKey key, [MaybeNullWhen(false)] out TValueCustom value) { using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 1ab2b4c0bf..b2df6e16b1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -62,10 +62,6 @@ namespace Godot.NativeInterop internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree, godot_bool isFinalizer); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj, - in godot_string_name eventSignal); - [DllImport(GodotDllName)] internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source, in godot_string_name signal, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 6edc19c4d6..b02bd167a1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -63,7 +63,11 @@ namespace Godot { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); - DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + + if (_weakReferenceToSelf != null) + { + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + } } private NodePath(godot_node_path nativeValueToOwn) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 8d683c6d1e..dd3f8477d9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -49,30 +49,6 @@ namespace Godot } _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this); - - _InitializeGodotScriptInstanceInternals(); - } - - internal void _InitializeGodotScriptInstanceInternals() - { - // Performance is not critical here as this will be replaced with source generators. - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - foreach (var eventSignal in top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any())) - { - using var eventSignalName = new StringName(eventSignal.Name); - var eventSignalNameSelf = (godot_string_name)eventSignalName.NativeValue; - NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, eventSignalNameSelf); - } - - top = top.BaseType; - } } internal Object(bool memoryOwn) @@ -238,72 +214,10 @@ namespace Godot return false; } - internal void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args, - int argc) + // ReSharper disable once VirtualMemberNeverOverridden.Global + protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal, + NativeVariantPtrArgs args, int argCount) { - // Performance is not critical here as this will be replaced with source generators. - - using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(eventSignalName)); - string eventSignalNameStr = stringName.ToString(); - - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - var foundEventSignals = top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any()) - .Select(ev => ev.Name); - - var fields = top.GetFields( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - var eventSignalField = fields - .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) - .Where(f => foundEventSignals.Contains(f.Name)) - .FirstOrDefault(f => f.Name == eventSignalNameStr); - - if (eventSignalField != null) - { - var @delegate = (Delegate)eventSignalField.GetValue(this); - - if (@delegate == null) - continue; - - var delegateType = eventSignalField.FieldType; - - var invokeMethod = delegateType.GetMethod("Invoke"); - - if (invokeMethod == null) - throw new MissingMethodException(delegateType.FullName, "Invoke"); - - var parameterInfos = invokeMethod.GetParameters(); - var paramsLength = parameterInfos.Length; - - if (argc != paramsLength) - { - throw new InvalidOperationException( - $"The event delegate expects {paramsLength} arguments, but received {argc}."); - } - - var managedArgs = new object[argc]; - - for (int i = 0; i < argc; i++) - { - managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType( - args[i], parameterInfos[i].ParameterType); - } - - invokeMethod.Invoke(@delegate, managedArgs); - return; - } - - top = top.BaseType; - } } internal static IntPtr ClassDB_get_method(StringName type, StringName method) @@ -332,74 +246,11 @@ namespace Godot protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info) { - // Temporary solution via reflection until we add a signals events source generator - - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - var foundEventSignals = top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType().Any()) - .Select(ev => ev.Name); - - var fields = top.GetFields( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - foreach (var eventSignalField in fields - .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) - .Where(f => foundEventSignals.Contains(f.Name))) - { - var eventSignalDelegate = (Delegate)eventSignalField.GetValue(this); - info.AddSignalEventDelegate(eventSignalField.Name, eventSignalDelegate); - } - - top = top.BaseType; - } } // TODO: Should this be a constructor overload? protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info) { - // Temporary solution via reflection until we add a signals events source generator - - void RestoreSignalEvent(StringName signalEventName) - { - Type top = GetType(); - Type native = InternalGetClassNativeBase(top); - - while (top != null && top != native) - { - var foundEventSignal = top.GetEvent(signalEventName, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (foundEventSignal != null && - foundEventSignal.GetCustomAttributes().OfType().Any()) - { - var field = top.GetField(foundEventSignal.Name, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - - if (field != null && typeof(Delegate).IsAssignableFrom(field.FieldType)) - { - var eventSignalDelegate = info.GetSignalEventDelegate(signalEventName); - field.SetValue(this, eventSignalDelegate); - return; - } - } - - top = top.BaseType; - } - } - - foreach (var signalEventName in info.GetSignalEventsList()) - { - RestoreSignalEvent(signalEventName); - } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index b993a1b3e9..10739c02a7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -34,7 +34,11 @@ namespace Godot { // Always dispose `NativeValue` even if disposing is true NativeValue.DangerousSelfRef.Dispose(); - DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + + if (_weakReferenceToSelf != null) + { + DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf); + } } private StringName(godot_string_name nativeValueToOwn) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index d0897fe85e..d95b839767 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -50,6 +50,7 @@ + -- cgit v1.2.3 From a9892f257153a2d760a5d221dc16e484e1428c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Thu, 28 Jul 2022 17:41:48 +0200 Subject: C#: Add source generator for method list --- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 2 +- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 75 +++++++++++++++++++--- 2 files changed, 68 insertions(+), 9 deletions(-) (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 5d3f140cba..57240624bc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -24,7 +24,7 @@ namespace Godot.Bridge public delegate* unmanaged ScriptManagerBridge_GetOrCreateScriptBridgeForPath; public delegate* unmanaged ScriptManagerBridge_RemoveScriptBridge; public delegate* unmanaged ScriptManagerBridge_TryReloadRegisteredScriptWithClass; - public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; + public delegate* unmanaged ScriptManagerBridge_UpdateScriptClassInfo; public delegate* unmanaged ScriptManagerBridge_SwapGCHandleForType; public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyInfoList; public delegate* unmanaged, void> ScriptManagerBridge_GetPropertyDefaultValues; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 4b9f851925..03094cbe81 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -557,7 +557,8 @@ namespace Godot.Bridge [UnmanagedCallersOnly] internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool, - godot_dictionary* outRpcFunctionsDest, godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript) + godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest, + godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript) { try { @@ -578,13 +579,59 @@ namespace Godot.Bridge if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools") *outTool = godot_bool.True; - // RPC functions + // Methods - Collections.Dictionary rpcFunctions = new(); + // 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", param.Type }, + { "usage", 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 | @@ -617,9 +664,8 @@ namespace Godot.Bridge top = top.BaseType; } - *outRpcFunctionsDest = - NativeFuncs.godotsharp_dictionary_new_copy( - (godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue); + *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy( + (godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue); // Event signals @@ -663,8 +709,8 @@ namespace Godot.Bridge top = top.BaseType; } - *outEventSignalsDest = - NativeFuncs.godotsharp_dictionary_new_copy((godot_dictionary)signals.NativeValue); + *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new_copy( + (godot_dictionary)signals.NativeValue); // Base script @@ -701,6 +747,19 @@ namespace Godot.Bridge return (List?)getGodotSignalListMethod.Invoke(null, null); } + private static List? GetMethodListForType(Type type) + { + var getGodotMethodListMethod = type.GetMethod( + "GetGodotMethodList", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.NonPublic | BindingFlags.Public); + + if (getGodotMethodListMethod == null) + return null; + + return (List?)getGodotMethodListMethod.Invoke(null, null); + } + // ReSharper disable once InconsistentNaming [SuppressMessage("ReSharper", "NotAccessedField.Local")] [StructLayout(LayoutKind.Sequential)] -- cgit v1.2.3 From 344f5028d48d4a5caf321abdf023c34f52aae0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Thu, 28 Jul 2022 17:41:49 +0200 Subject: C#: Add dedicated Variant struct, replacing System.Object --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 6 + .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 6 +- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 7 + .../GodotSharp/Core/NativeInterop/Marshaling.cs | 55 +- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 3 + .../Core/NativeInterop/NativeFuncs.extended.cs | 38 ++ .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 14 - .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 2 - .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 + modules/mono/glue/GodotSharp/GodotSharp/Variant.cs | 665 +++++++++++++++++++++ 10 files changed, 729 insertions(+), 68 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Variant.cs (limited to 'modules/mono/glue/GodotSharp') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index afe295126e..8ceb7ea882 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -714,5 +714,11 @@ namespace Godot.Collections /// /// A string representation of this array. public override string ToString() => _underlyingArray.ToString(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Array from) => Variant.From(from); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Array(Variant from) => from.AsGodotGenericArray(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 9ede67b285..3636a08377 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -63,7 +63,7 @@ namespace Godot.Bridge var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); - object valueManaged = Marshaling.ConvertVariantToManagedObject(CustomUnsafe.AsRef(value)); + Variant valueManaged = Variant.CreateCopyingBorrowed(*value); return godotObject._Set(nameManaged, valueManaged).ToGodotBool(); } @@ -94,9 +94,9 @@ namespace Godot.Bridge var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); - object ret = godotObject._Get(nameManaged); + Variant ret = godotObject._Get(nameManaged); - if (ret == null) + if (ret.VariantType == Variant.Type.Nil) { *outRet = default; return godot_bool.False; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 106cd7619f..a71ee1190e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -5,6 +5,7 @@ using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Linq; +using System.Runtime.CompilerServices; namespace Godot.Collections { @@ -805,5 +806,11 @@ namespace Godot.Collections /// /// A string representation of this dictionary. public override string ToString() => _underlyingDict.ToString(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Variant(Dictionary from) => Variant.From(from); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Dictionary(Variant from) => from.AsGodotGenericDictionary(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 563af91ae5..8802f229b3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -148,9 +148,6 @@ namespace Godot.NativeInterop if (typeof(Godot.Object[]).IsAssignableFrom(type)) return Variant.Type.Array; - - if (type == typeof(object[])) - return Variant.Type.Array; } else if (type.IsGenericType) { @@ -178,7 +175,7 @@ namespace Godot.NativeInterop if (typeof(Godot.Object).IsAssignableFrom(type)) return Variant.Type.Object; } - else if (type == typeof(object)) + else if (type == typeof(Variant)) { r_nil_is_variant = true; return Variant.Type.Nil; @@ -315,16 +312,6 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromSystemArrayOfSupportedType(ridArray); case Godot.Object[] godotObjectArray: return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray); - case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] - { - // The pattern match for `object[]` catches arrays on any reference type, - // so we need to check the actual type to make sure it's truly `object[]`. - if (objectArray.GetType() == typeof(object[])) - return VariantUtils.CreateFromSystemArrayOfVariant(objectArray); - - GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); - return new godot_variant(); - } case Godot.Object godotObject: return VariantUtils.CreateFromGodotObject(godotObject); case StringName stringName: @@ -337,6 +324,8 @@ namespace Godot.NativeInterop return VariantUtils.CreateFromDictionary(godotDictionary); case Collections.Array godotArray: return VariantUtils.CreateFromArray(godotArray); + case Variant variant: + return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar); case Collections.IGenericGodotDictionary genericGodotDictionary: { var godotDict = genericGodotDictionary.UnderlyingDictionary; @@ -509,8 +498,9 @@ namespace Godot.NativeInterop return ConvertVariantToSystemArrayOfType(p_var, type); else if (type.IsGenericType) return ConvertVariantToManagedObjectOfGenericType(p_var, type); - else if (type == typeof(object)) - return ConvertVariantToManagedObject(p_var); + else if (type == typeof(Variant)) + return Variant.CreateCopyingBorrowed(p_var); + if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res)) return res; @@ -564,9 +554,6 @@ namespace Godot.NativeInterop if (typeof(Godot.Object[]).IsAssignableFrom(type)) return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type); - if (type == typeof(object[])) - return VariantUtils.ConvertToSystemArrayOfVariant(p_var); - GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + type.GetElementType()!.FullName + "."); return null; @@ -962,19 +949,6 @@ namespace Godot.NativeInterop // Array - public static object[] ConvertNativeGodotArrayToSystemArray(in godot_array p_array) - { - var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_array_new_copy(p_array)); - - int length = array.Count; - var ret = new object[length]; - - array.CopyTo(ret, 0); // ConvertVariantToManagedObject handled by Collections.Array - - return ret; - } - internal static T[] ConvertNativeGodotArrayToSystemArrayOfType(in godot_array p_array) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( @@ -1019,23 +993,6 @@ namespace Godot.NativeInterop return ret; } - public static godot_array ConvertSystemArrayToNativeGodotArray(object[] p_array) - { - int length = p_array.Length; - - if (length == 0) - return NativeFuncs.godotsharp_array_new(); - - using var array = new Collections.Array(); - array.Resize(length); - - for (int i = 0; i < length; i++) - array[i] = p_array[i]; - - var src = (godot_array)array.NativeValue; - return NativeFuncs.godotsharp_array_new_copy(src); - } - public static godot_array ConvertSystemArrayToNativeGodotArray(T[] p_array) { int length = 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 index b2df6e16b1..e56e4944b0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -199,6 +199,9 @@ namespace Godot.NativeInterop public static extern void godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s); + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src); + [DllImport(GodotDllName)] public static extern void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs index 0c49660cf0..2ea3c18d26 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -4,6 +4,44 @@ namespace Godot.NativeInterop { public static partial class NativeFuncs { + public static godot_variant godotsharp_variant_new_copy(in godot_variant src) + { + switch (src.Type) + { + case Variant.Type.Nil: + return default; + case Variant.Type.Bool: + return new godot_variant() { Bool = src.Bool }; + case Variant.Type.Int: + return new godot_variant() { Int = src.Int }; + case Variant.Type.Float: + return new godot_variant() { Float = src.Float }; + case Variant.Type.Vector2: + return new godot_variant() { Vector2 = src.Vector2 }; + case Variant.Type.Vector2i: + return new godot_variant() { Vector2i = src.Vector2i }; + case Variant.Type.Rect2: + return new godot_variant() { Rect2 = src.Rect2 }; + case Variant.Type.Rect2i: + return new godot_variant() { Rect2i = src.Rect2i }; + case Variant.Type.Vector3: + return new godot_variant() { Vector3 = src.Vector3 }; + case Variant.Type.Vector3i: + return new godot_variant() { Vector3i = src.Vector3i }; + case Variant.Type.Plane: + return new godot_variant() { Plane = src.Plane }; + case Variant.Type.Quaternion: + return new godot_variant() { Quaternion = src.Quaternion }; + case Variant.Type.Color: + return new godot_variant() { Color = src.Color }; + case Variant.Type.Rid: + return new godot_variant() { RID = src.RID }; + } + + godotsharp_variant_new_copy(out godot_variant ret, src); + return ret; + } + public static godot_string_name godotsharp_string_name_new_copy(in godot_string_name src) { if (src.IsEmpty) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 7aa27c2867..fe0d7104ea 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -227,14 +227,6 @@ namespace Godot.NativeInterop return CreateFromArray(array); } - public static godot_variant CreateFromSystemArrayOfVariant(object[]? from) - { - if (from == null) - return default; // Nil - using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from); - return CreateFromArray(array); - } - public static godot_variant CreateFromArray(godot_array from) { NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from); @@ -671,12 +663,6 @@ namespace Godot.NativeInterop return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type); } - public static object[] ConvertToSystemArrayOfVariant(in godot_variant p_var) - { - using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Marshaling.ConvertNativeGodotArrayToSystemArray(godotArray); - } - public static Array ConvertToGenericArrayObject(in godot_variant p_var) => new(ConvertToArrayObject(p_var)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index dd3f8477d9..04920ccfab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,6 +1,4 @@ using System; -using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using Godot.Bridge; using Godot.NativeInterop; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index d95b839767..0a61069a1e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -124,6 +124,7 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %(GodotInteropStructs.Identity) - - - - - diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index c7881c7404..7022d96cfc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -41,11 +41,6 @@ - - - - - @@ -89,6 +84,7 @@ + -- cgit v1.2.3 From 2c180f62d985194060f1a8d2070c130081177c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Fri, 5 Aug 2022 03:32:59 +0200 Subject: C#: Replace P/Invoke with delegate pointers - Moves interop functions to UnmanagedCallbacks struct that contains the function pointers and is passed to C#. - Implements UnmanagedCallbacksGenerator, a C# source generator that generates the UnmanagedCallbacks struct in C# and the body for the NativeFuncs methods (their implementation just calls the function pointer in the UnmanagedCallbacks). The generated methods are needed because .NET pins byref parameters of native calls, even if they are 'ref struct's, which don't need pinning. The generated methods use `Unsafe.AsPointer` so that we can benefit from byref parameters without suffering overhead of pinning. Co-authored-by: Raul Santos --- .../CallbacksInfo.cs | 24 + .../Godot.SourceGenerators.Internal/Common.cs | 65 +++ .../ExtensionMethods.cs | 119 ++++ .../GeneratorClasses.cs | 6 + .../Godot.SourceGenerators.Internal.csproj | 11 + .../UnmanagedCallbacksGenerator.cs | 463 ++++++++++++++++ modules/mono/glue/GodotSharp/GodotPlugins/Main.cs | 13 +- modules/mono/glue/GodotSharp/GodotSharp.sln | 6 + .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 599 ++++++++------------- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 3 + 10 files changed, 918 insertions(+), 391 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs create mode 100644 modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs create mode 100644 modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs create mode 100644 modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs create mode 100644 modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj create mode 100644 modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs (limited to 'modules/mono/glue/GodotSharp') 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() + .ToImmutableArray(); + } + + public INamedTypeSymbol NativeTypeSymbol { get; } + + public INamedTypeSymbol FuncStructSymbol { get; } + + public ImmutableArray 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 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().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 @@ + + + netstandard2.0 + 10 + enable + + + + + + 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() + .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(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/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs index 395cc9bf66..dad7464410 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -72,7 +72,8 @@ namespace GodotPlugins [UnmanagedCallersOnly] // ReSharper disable once UnusedMember.Local private static unsafe godot_bool InitializeFromEngine(IntPtr godotDllHandle, godot_bool editorHint, - PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks) + PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks, + IntPtr unmanagedCallbacks, int unmanagedCallbacksSize) { try { @@ -82,6 +83,7 @@ namespace GodotPlugins NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver); AlcReloadCfg.Configure(alcReloadEnabled: editorHint.ToBool()); + NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize); if (editorHint.ToBool()) { @@ -112,7 +114,7 @@ namespace GodotPlugins private struct PluginsCallbacks { public unsafe delegate* unmanaged LoadProjectAssemblyCallback; - public unsafe delegate* unmanaged LoadToolsAssemblyCallback; + public unsafe delegate* unmanaged LoadToolsAssemblyCallback; public unsafe delegate* unmanaged UnloadProjectPluginCallback; } @@ -143,7 +145,8 @@ namespace GodotPlugins } [UnmanagedCallersOnly] - private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath) + private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath, + IntPtr unmanagedCallbacks, int unmanagedCallbacksSize) { try { @@ -166,7 +169,9 @@ namespace GodotPlugins "InternalCreateInstance"); } - return (IntPtr?)method.Invoke(null, null) ?? IntPtr.Zero; + return (IntPtr?)method + .Invoke(null, new object[] { unmanagedCallbacks, unmanagedCallbacksSize }) + ?? IntPtr.Zero; } catch (Exception e) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln index fc4e6e91f1..8db42c2d1a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp.sln +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln @@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSh 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 @@ -24,5 +26,9 @@ Global {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/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index e56e4944b0..6d2534e6f7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -1,697 +1,522 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Godot.SourceGenerators.Internal; // ReSharper disable InconsistentNaming namespace Godot.NativeInterop { /* - * TODO: - * P/Invoke pins by-ref parameters in case the managed memory is moved. - * That's not needed here since we use "ref structs" which are stack-only. - * Unfortunately, the runtime is not smart enough and pins them anyway. - * We want to avoid pinning, so we must wrap these DllImports in methods - * that reinterpret refs and pointers with CustomUnsafe.AsPointer/AsRef. - * I wish such unnecessary boilerplate wasn't needed... + * 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'. */ - [SuppressMessage("Interoperability", "CA1401", - MessageId = "P/Invokes should not be visible" /* Meh. What are you gonna do about it? */)] + [GenerateUnmanagedCallbacks(typeof(UnmanagedCallbacks))] public static unsafe partial class NativeFuncs { - private const string GodotDllName = "__Internal"; + 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((void*)unmanagedCallbacks); + } + + private partial struct UnmanagedCallbacks + { + } // Custom functions - [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname, + public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname, in godot_string_name p_methodname); - [DllImport(GodotDllName)] - public static extern delegate* unmanaged godotsharp_get_class_constructor( + public static partial delegate* unmanaged godotsharp_get_class_constructor( in godot_string_name p_classname); - [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_engine_get_singleton(in godot_string p_name); + public static partial IntPtr godotsharp_engine_get_singleton(in godot_string p_name); - [DllImport(GodotDllName)] - internal static extern Error godotsharp_stack_info_vector_resize( + internal static partial Error godotsharp_stack_info_vector_resize( ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector, int p_size); - [DllImport(GodotDllName)] - internal static extern void godotsharp_stack_info_vector_destroy( + internal static partial void godotsharp_stack_info_vector_destroy( ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_script_debugger_send_error(in godot_string p_func, + 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); - [DllImport(GodotDllName)] - internal static extern bool godotsharp_internal_script_debugger_is_active(); + internal static partial bool godotsharp_internal_script_debugger_is_active(); - [DllImport(GodotDllName)] - internal static extern IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr); + internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree); + internal static partial void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree, + internal static partial void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree, godot_bool isFinalizer); - [DllImport(GodotDllName)] - internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source, + internal static partial Error godotsharp_internal_signal_awaiter_connect(IntPtr source, in godot_string_name signal, IntPtr target, IntPtr awaiterHandlePtr); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, + internal static partial void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, IntPtr unmanaged, in godot_string_name nativeName, godot_bool refCounted); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, + internal static partial void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, IntPtr unmanaged, godot_ref* scriptPtr, godot_bool refCounted); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( + internal static partial void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( IntPtr gcHandleIntPtr, IntPtr unmanaged); - [DllImport(GodotDllName)] - internal static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged, + internal static partial IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged, out godot_bool r_has_cs_script_instance); - [DllImport(GodotDllName)] - internal static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); + internal static partial IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); - [DllImport(GodotDllName)] - internal static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, + internal static partial IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, IntPtr oldGCHandlePtr); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_new_csharp_script(godot_ref* r_dest); + internal static partial void godotsharp_internal_new_csharp_script(godot_ref* r_dest); - [DllImport(GodotDllName)] - internal static extern godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest); + internal static partial godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest); - [DllImport(GodotDllName)] - internal static extern void godotsharp_internal_reload_registered_script(IntPtr scriptPtr); + internal static partial void godotsharp_internal_reload_registered_script(IntPtr scriptPtr); - [DllImport(GodotDllName)] - internal static extern void godotsharp_array_filter_godot_objects_by_native(in godot_string_name p_native_name, + 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); - [DllImport(GodotDllName)] - internal static extern void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input, + internal static partial void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input, out godot_array r_output); - [DllImport(GodotDllName)] - public static extern void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest, + public static partial void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest, IntPtr p_ref_counted_ptr); - [DllImport(GodotDllName)] - public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); + public static partial void godotsharp_ref_destroy(ref godot_ref p_instance); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_name_new_from_string(out godot_string_name r_dest, + public static partial void godotsharp_string_name_new_from_string(out godot_string_name r_dest, in godot_string p_name); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_new_from_string(out godot_node_path r_dest, + public static partial void godotsharp_node_path_new_from_string(out godot_node_path r_dest, in godot_string p_name); - [DllImport(GodotDllName)] - public static extern void + public static partial void godotsharp_string_name_as_string(out godot_string r_dest, in godot_string_name p_name); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_as_string(out godot_string r_dest, in godot_node_path p_np); + public static partial void godotsharp_node_path_as_string(out godot_string r_dest, in godot_node_path p_np); - [DllImport(GodotDllName)] - public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, + public static partial godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, + public static partial godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, + public static partial godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, + public static partial godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, + public static partial godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, + public static partial godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, + public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, + public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest, + public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest, in godot_string p_element); - [DllImport(GodotDllName)] - public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, + public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, out godot_callable r_callable); - [DllImport(GodotDllName)] - internal static extern godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_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); - [DllImport(GodotDllName)] - internal static extern godot_variant godotsharp_callable_call(in godot_callable p_callable, + 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); - [DllImport(GodotDllName)] - internal static extern void godotsharp_callable_call_deferred(in godot_callable p_callable, + 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 - [DllImport(GodotDllName)] - public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, + public static partial void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret); - [DllImport(GodotDllName)] - public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, + 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 - [DllImport(GodotDllName)] - public static extern void + public static partial void godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src); + public static partial void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np); + public static partial void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj); + public static partial void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d); + public static partial void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_vector4(out godot_variant r_dest, in Vector4 p_vec4); + public static partial void godotsharp_variant_new_vector4(out godot_variant r_dest, in Vector4 p_vec4); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_vector4i(out godot_variant r_dest, in Vector4i p_vec4i); + public static partial void godotsharp_variant_new_vector4i(out godot_variant r_dest, in Vector4i p_vec4i); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis); + public static partial void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans); + public static partial void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj); + public static partial void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb); + public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_dictionary(out godot_variant r_dest, + public static partial void godotsharp_variant_new_dictionary(out godot_variant r_dest, in godot_dictionary p_dict); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr); + public static partial void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest, in godot_packed_byte_array p_pba); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest, in godot_packed_int32_array p_pia); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest, in godot_packed_int64_array p_pia); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest, in godot_packed_float32_array p_pra); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest, in godot_packed_float64_array p_pra); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_string_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_string_array(out godot_variant r_dest, in godot_packed_string_array p_psa); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest, in godot_packed_vector2_array p_pv2a); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest, in godot_packed_vector3_array p_pv3a); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_color_array(out godot_variant r_dest, + public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest, in godot_packed_color_array p_pca); - [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_variant_as_bool(in godot_variant p_self); + public static partial godot_bool godotsharp_variant_as_bool(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Int64 godotsharp_variant_as_int(in godot_variant p_self); + public static partial Int64 godotsharp_variant_as_int(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern double godotsharp_variant_as_float(in godot_variant p_self); + public static partial double godotsharp_variant_as_float(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_string godotsharp_variant_as_string(in godot_variant p_self); + public static partial godot_string godotsharp_variant_as_string(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Vector2 godotsharp_variant_as_vector2(in godot_variant p_self); + public static partial Vector2 godotsharp_variant_as_vector2(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self); + public static partial Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Rect2 godotsharp_variant_as_rect2(in godot_variant p_self); + public static partial Rect2 godotsharp_variant_as_rect2(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self); + public static partial Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Vector3 godotsharp_variant_as_vector3(in godot_variant p_self); + public static partial Vector3 godotsharp_variant_as_vector3(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self); + public static partial Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self); + public static partial Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Vector4 godotsharp_variant_as_vector4(in godot_variant p_self); + public static partial Vector4 godotsharp_variant_as_vector4(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self); + public static partial Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Plane godotsharp_variant_as_plane(in godot_variant p_self); + public static partial Plane godotsharp_variant_as_plane(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self); + public static partial Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern AABB godotsharp_variant_as_aabb(in godot_variant p_self); + public static partial AABB godotsharp_variant_as_aabb(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Basis godotsharp_variant_as_basis(in godot_variant p_self); + public static partial Basis godotsharp_variant_as_basis(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Transform3D godotsharp_variant_as_transform3d(in godot_variant p_self); + public static partial Transform3D godotsharp_variant_as_transform3d(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Projection godotsharp_variant_as_projection(in godot_variant p_self); + public static partial Projection godotsharp_variant_as_projection(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern Color godotsharp_variant_as_color(in godot_variant p_self); + public static partial Color godotsharp_variant_as_color(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_string_name godotsharp_variant_as_string_name(in godot_variant p_self); + public static partial godot_string_name godotsharp_variant_as_string_name(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self); + public static partial godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern RID godotsharp_variant_as_rid(in godot_variant p_self); + public static partial RID godotsharp_variant_as_rid(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_callable godotsharp_variant_as_callable(in godot_variant p_self); + public static partial godot_callable godotsharp_variant_as_callable(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_signal godotsharp_variant_as_signal(in godot_variant p_self); + public static partial godot_signal godotsharp_variant_as_signal(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_dictionary godotsharp_variant_as_dictionary(in godot_variant p_self); + public static partial godot_dictionary godotsharp_variant_as_dictionary(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_array godotsharp_variant_as_array(in godot_variant p_self); + public static partial godot_array godotsharp_variant_as_array(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self); + public static partial godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self); + public static partial godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(in godot_variant p_self); + public static partial godot_packed_int64_array godotsharp_variant_as_packed_int64_array(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array( + public static partial godot_packed_float32_array godotsharp_variant_as_packed_float32_array( in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array( + public static partial godot_packed_float64_array godotsharp_variant_as_packed_float64_array( in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array( + public static partial godot_packed_string_array godotsharp_variant_as_packed_string_array( in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array( + public static partial godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array( in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array( + public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array( in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self); + public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self); - [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b); + public static partial godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b); // string.h - [DllImport(GodotDllName)] - public static extern void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents); + public static partial void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents); // string_name.h - [DllImport(GodotDllName)] - public static extern void godotsharp_string_name_new_copy(out godot_string_name r_dest, + public static partial void godotsharp_string_name_new_copy(out godot_string_name r_dest, in godot_string_name p_src); // node_path.h - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_new_copy(out godot_node_path r_dest, in godot_node_path p_src); + public static partial void godotsharp_node_path_new_copy(out godot_node_path r_dest, in godot_node_path p_src); // array.h - [DllImport(GodotDllName)] - public static extern void godotsharp_array_new(out godot_array r_dest); + public static partial void godotsharp_array_new(out godot_array r_dest); - [DllImport(GodotDllName)] - public static extern void godotsharp_array_new_copy(out godot_array r_dest, in godot_array p_src); + public static partial void godotsharp_array_new_copy(out godot_array r_dest, in godot_array p_src); - [DllImport(GodotDllName)] - public static extern godot_variant* godotsharp_array_ptrw(ref godot_array p_self); + public static partial godot_variant* godotsharp_array_ptrw(ref godot_array p_self); // dictionary.h - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_new(out godot_dictionary r_dest); + public static partial void godotsharp_dictionary_new(out godot_dictionary r_dest); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_new_copy(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 - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self); + public static partial void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self); + public static partial void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self); + public static partial void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self); + public static partial void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self); + public static partial void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self); + public static partial void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self); + public static partial void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self); + public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self); + public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_variant_destroy(ref godot_variant p_self); + public static partial void godotsharp_variant_destroy(ref godot_variant p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_destroy(ref godot_string p_self); + public static partial void godotsharp_string_destroy(ref godot_string p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_name_destroy(ref godot_string_name p_self); + public static partial void godotsharp_string_name_destroy(ref godot_string_name p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_destroy(ref godot_node_path p_self); + public static partial void godotsharp_node_path_destroy(ref godot_node_path p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_signal_destroy(ref godot_signal p_self); + public static partial void godotsharp_signal_destroy(ref godot_signal p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_callable_destroy(ref godot_callable p_self); + public static partial void godotsharp_callable_destroy(ref godot_callable p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_array_destroy(ref godot_array p_self); + public static partial void godotsharp_array_destroy(ref godot_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + public static partial void godotsharp_dictionary_destroy(ref godot_dictionary p_self); // Array - [DllImport(GodotDllName)] - public static extern int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item); + public static partial int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item); - [DllImport(GodotDllName)] - public static extern void + public static partial void godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest); - [DllImport(GodotDllName)] - public static extern int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item); + public static partial int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item); - [DllImport(GodotDllName)] - public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, 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); - [DllImport(GodotDllName)] - public static extern void godotsharp_array_remove_at(ref godot_array p_self, int p_index); + public static partial void godotsharp_array_remove_at(ref godot_array p_self, int p_index); - [DllImport(GodotDllName)] - public static extern Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); + public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); - [DllImport(GodotDllName)] - public static extern Error godotsharp_array_shuffle(ref godot_array p_self); + public static partial Error godotsharp_array_shuffle(ref godot_array p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str); + public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str); // Dictionary - [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self, + 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); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key, + public static partial void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key, in godot_variant p_value); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest); + public static partial void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_values(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); - [DllImport(GodotDllName)] - public static extern int godotsharp_dictionary_count(ref godot_dictionary p_self); + public static partial int godotsharp_dictionary_count(ref godot_dictionary p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_key_value_pair_at(ref godot_dictionary p_self, int p_index, + 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); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key, + public static partial void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key, in godot_variant p_value); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_clear(ref godot_dictionary p_self); + public static partial void godotsharp_dictionary_clear(ref godot_dictionary p_self); - [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self, + public static partial godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self, in godot_variant p_key); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, + public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, out godot_dictionary r_dest); - [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, + public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, in godot_variant p_key); - [DllImport(GodotDllName)] - public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str); + public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str); // StringExtensions - [DllImport(GodotDllName)] - public static extern void godotsharp_string_md5_buffer(in godot_string p_self, + public static partial void godotsharp_string_md5_buffer(in godot_string p_self, out godot_packed_byte_array r_md5_buffer); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_md5_text(in godot_string p_self, out godot_string r_md5_text); + public static partial void godotsharp_string_md5_text(in godot_string p_self, out godot_string r_md5_text); - [DllImport(GodotDllName)] - public static extern int godotsharp_string_rfind(in godot_string p_self, in godot_string p_what, int p_from); + public static partial int godotsharp_string_rfind(in godot_string p_self, in godot_string p_what, int p_from); - [DllImport(GodotDllName)] - public static extern int godotsharp_string_rfindn(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); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_sha256_buffer(in godot_string p_self, + public static partial void godotsharp_string_sha256_buffer(in godot_string p_self, out godot_packed_byte_array r_sha256_buffer); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_sha256_text(in godot_string p_self, + public static partial void godotsharp_string_sha256_text(in godot_string p_self, out godot_string r_sha256_text); - [DllImport(GodotDllName)] - public static extern void godotsharp_string_simplify_path(in godot_string p_self, + public static partial void godotsharp_string_simplify_path(in godot_string p_self, out godot_string r_simplified_path); // NodePath - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_as_property_path(in godot_node_path p_self, + public static partial void godotsharp_node_path_get_as_property_path(in godot_node_path p_self, ref godot_node_path r_dest); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_concatenated_names(in godot_node_path p_self, + public static partial void godotsharp_node_path_get_concatenated_names(in godot_node_path p_self, out godot_string r_names); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_concatenated_subnames(in godot_node_path p_self, + public static partial void godotsharp_node_path_get_concatenated_subnames(in godot_node_path p_self, out godot_string r_subnames); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_name(in godot_node_path p_self, int p_idx, + public static partial void godotsharp_node_path_get_name(in godot_node_path p_self, int p_idx, out godot_string r_name); - [DllImport(GodotDllName)] - public static extern int godotsharp_node_path_get_name_count(in godot_node_path p_self); + public static partial int godotsharp_node_path_get_name_count(in godot_node_path p_self); - [DllImport(GodotDllName)] - public static extern void godotsharp_node_path_get_subname(in godot_node_path p_self, int p_idx, + public static partial void godotsharp_node_path_get_subname(in godot_node_path p_self, int p_idx, out godot_string r_subname); - [DllImport(GodotDllName)] - public static extern int godotsharp_node_path_get_subname_count(in godot_node_path p_self); + public static partial int godotsharp_node_path_get_subname_count(in godot_node_path p_self); - [DllImport(GodotDllName)] - public static extern godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self); + public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self); // GD, etc - [DllImport(GodotDllName)] - internal static extern void godotsharp_bytes2var(in godot_packed_byte_array p_bytes, + internal static partial void godotsharp_bytes2var(in godot_packed_byte_array p_bytes, godot_bool p_allow_objects, out godot_variant r_ret); - [DllImport(GodotDllName)] - internal static extern void godotsharp_convert(in godot_variant p_what, int p_type, + internal static partial void godotsharp_convert(in godot_variant p_what, int p_type, out godot_variant r_ret); - [DllImport(GodotDllName)] - internal static extern int godotsharp_hash(in godot_variant p_var); + internal static partial int godotsharp_hash(in godot_variant p_var); - [DllImport(GodotDllName)] - internal static extern IntPtr godotsharp_instance_from_id(ulong p_instance_id); + internal static partial IntPtr godotsharp_instance_from_id(ulong p_instance_id); - [DllImport(GodotDllName)] - internal static extern void godotsharp_print(in godot_string p_what); + internal static partial void godotsharp_print(in godot_string p_what); - [DllImport(GodotDllName)] - public static extern void godotsharp_print_rich(in godot_string p_what); + public static partial void godotsharp_print_rich(in godot_string p_what); - [DllImport(GodotDllName)] - internal static extern void godotsharp_printerr(in godot_string p_what); + internal static partial void godotsharp_printerr(in godot_string p_what); - [DllImport(GodotDllName)] - internal static extern void godotsharp_printraw(in godot_string p_what); + internal static partial void godotsharp_printraw(in godot_string p_what); - [DllImport(GodotDllName)] - internal static extern void godotsharp_prints(in godot_string p_what); + internal static partial void godotsharp_prints(in godot_string p_what); - [DllImport(GodotDllName)] - internal static extern void godotsharp_printt(in godot_string p_what); + internal static partial void godotsharp_printt(in godot_string p_what); - [DllImport(GodotDllName)] - internal static extern float godotsharp_randf(); + internal static partial float godotsharp_randf(); - [DllImport(GodotDllName)] - internal static extern uint godotsharp_randi(); + internal static partial uint godotsharp_randi(); - [DllImport(GodotDllName)] - internal static extern void godotsharp_randomize(); + internal static partial void godotsharp_randomize(); - [DllImport(GodotDllName)] - internal static extern double godotsharp_randf_range(double from, double to); + internal static partial double godotsharp_randf_range(double from, double to); - [DllImport(GodotDllName)] - internal static extern double godotsharp_randfn(double mean, double deviation); + internal static partial double godotsharp_randfn(double mean, double deviation); - [DllImport(GodotDllName)] - internal static extern int godotsharp_randi_range(int from, int to); + internal static partial int godotsharp_randi_range(int from, int to); - [DllImport(GodotDllName)] - internal static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); + internal static partial uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); - [DllImport(GodotDllName)] - internal static extern void godotsharp_seed(ulong seed); + internal static partial void godotsharp_seed(ulong seed); - [DllImport(GodotDllName)] - internal static extern void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref); + internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref); - [DllImport(GodotDllName)] - internal static extern void godotsharp_str(in godot_array p_what, out godot_string r_ret); + internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret); - [DllImport(GodotDllName)] - internal static extern void godotsharp_str2var(in godot_string p_str, out godot_variant r_ret); + internal static partial void godotsharp_str2var(in godot_string p_str, out godot_variant r_ret); - [DllImport(GodotDllName)] - internal static extern void godotsharp_var2bytes(in godot_variant p_what, godot_bool p_full_objects, + internal static partial void godotsharp_var2bytes(in godot_variant p_what, godot_bool p_full_objects, out godot_packed_byte_array r_bytes); - [DllImport(GodotDllName)] - internal static extern void godotsharp_var2str(in godot_variant p_var, out godot_string r_ret); + internal static partial void godotsharp_var2str(in godot_variant p_var, out godot_string r_ret); - [DllImport(GodotDllName)] - internal static extern void godotsharp_pusherror(in godot_string p_str); + internal static partial void godotsharp_pusherror(in godot_string p_str); - [DllImport(GodotDllName)] - internal static extern void godotsharp_pushwarning(in godot_string p_str); + internal static partial void godotsharp_pushwarning(in godot_string p_str); // Object - [DllImport(GodotDllName)] - public static extern void godotsharp_object_to_string(IntPtr ptr, out godot_string r_str); + public static partial void godotsharp_object_to_string(IntPtr ptr, out godot_string r_str); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 7022d96cfc..111920ecf6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -41,6 +41,9 @@ + + + -- cgit v1.2.3