diff --git a/Source/Engine/Engine/NativeInterop_Invoker.cs b/Source/Engine/Engine/NativeInterop.Invoker.cs similarity index 99% rename from Source/Engine/Engine/NativeInterop_Invoker.cs rename to Source/Engine/Engine/NativeInterop.Invoker.cs index 456000340..4ee9e80a1 100644 --- a/Source/Engine/Engine/NativeInterop_Invoker.cs +++ b/Source/Engine/Engine/NativeInterop.Invoker.cs @@ -11,7 +11,7 @@ using System.Diagnostics; namespace FlaxEngine { - internal unsafe static partial class NativeInterop + unsafe partial class NativeInterop { /// /// Helper class for invoking managed methods from delegates. diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs new file mode 100644 index 000000000..77c3587a5 --- /dev/null +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -0,0 +1,489 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if USE_NETCORE +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Threading; +using FlaxEngine.Assertions; +using FlaxEngine.Utilities; + +#pragma warning disable 1591 + +namespace FlaxEngine +{ + /// + /// Wrapper for managed arrays which are passed to unmanaged code. + /// + public unsafe class ManagedArray + { + private ManagedHandle _pinnedArrayHandle; + private IntPtr _unmanagedData; + private Type _arrayType; + private Type _elementType; + private int _elementSize; + private int _length; + + public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType()); + + public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType); + + /// + /// Returns an instance of ManagedArray from shared pool. + /// + /// The resources must be released by calling FreePooled() instead of Free()-method. + public static ManagedArray WrapPooledArray(Array arr) + { + ManagedArray managedArray = ManagedArrayPool.Get(); + managedArray.WrapArray(arr, arr.GetType()); + return managedArray; + } + + /// + /// Returns an instance of ManagedArray from shared pool. + /// + /// The resources must be released by calling FreePooled() instead of Free()-method. + public static ManagedArray WrapPooledArray(Array arr, Type arrayType) + { + ManagedArray managedArray = ManagedArrayPool.Get(); + managedArray.WrapArray(arr, arrayType); + return managedArray; + } + + internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType) + => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, arrayType, elementType); + + internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType) + => new ManagedArray(ptr, length, arrayType, elementType); + + /// + /// Returns an instance of ManagedArray from shared pool. + /// + /// The resources must be released by calling FreePooled() instead of Free()-method. + public static ManagedArray AllocatePooledArray(T* ptr, int length) where T : unmanaged + { + ManagedArray managedArray = ManagedArrayPool.Get(); + managedArray.Allocate(ptr, length); + return managedArray; + } + + /// + /// Returns an instance of ManagedArray from shared pool. + /// + /// The resources must be released by calling FreePooled() instead of Free()-method. + public static ManagedArray AllocatePooledArray(int length) where T : unmanaged + { + ManagedArray managedArray = ManagedArrayPool.Get(); + managedArray.Allocate((T*)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length); + return managedArray; + } + + public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType); + + internal void WrapArray(Array arr, Type arrayType) + { + _pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); + _unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); + _length = arr.Length; + _arrayType = arrayType; + _elementType = arr.GetType().GetElementType(); + _elementSize = Marshal.SizeOf(_elementType); + } + + internal void Allocate(T* ptr, int length) where T : unmanaged + { + _unmanagedData = new IntPtr(ptr); + _length = length; + _arrayType = typeof(T).MakeArrayType(); + _elementType = typeof(T); + _elementSize = Unsafe.SizeOf(); + } + + private ManagedArray() + { + } + + private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType) + { + Assert.IsTrue(arrayType.IsArray); + _unmanagedData = ptr; + _length = length; + _arrayType = arrayType; + _elementType = elementType; + _elementSize = Marshal.SizeOf(elementType); + } + + ~ManagedArray() + { + if (_unmanagedData != IntPtr.Zero) + Free(); + } + + public void Free() + { + GC.SuppressFinalize(this); + if (_pinnedArrayHandle.IsAllocated) + { + _pinnedArrayHandle.Free(); + _unmanagedData = IntPtr.Zero; + } + if (_unmanagedData != IntPtr.Zero) + { + NativeInterop.NativeFree(_unmanagedData.ToPointer()); + _unmanagedData = IntPtr.Zero; + } + } + + public void FreePooled() + { + Free(); + ManagedArrayPool.Put(this); + } + + internal IntPtr Pointer => _unmanagedData; + + internal int Length => _length; + + internal int ElementSize => _elementSize; + + internal Type ElementType => _elementType; + + internal Type ArrayType => _arrayType; + + public Span ToSpan() where T : struct => new Span(_unmanagedData.ToPointer(), _length); + + public T[] ToArray() where T : struct => new Span(_unmanagedData.ToPointer(), _length).ToArray(); + + public Array ToArray() => ArrayCast.ToArray(new Span(_unmanagedData.ToPointer(), _length * _elementSize), _elementType); + + /// + /// Creates an Array of the specified type from span of bytes. + /// + private static class ArrayCast + { + delegate Array GetDelegate(Span span); + + [ThreadStatic] + private static Dictionary delegates; + + internal static Array ToArray(Span span, Type type) + { + if (delegates == null) + delegates = new(); + if (!delegates.TryGetValue(type, out var deleg)) + { + deleg = typeof(ArrayCastInternal<>).MakeGenericType(type).GetMethod(nameof(ArrayCastInternal.ToArray), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); + delegates.Add(type, deleg); + } + return deleg(span); + } + + private static class ArrayCastInternal where T : struct + { + internal static Array ToArray(Span span) + { + return MemoryMarshal.Cast(span).ToArray(); + } + } + } + + /// + /// Provides a pool of pre-allocated ManagedArray that can be re-used. + /// + private static class ManagedArrayPool + { + [ThreadStatic] + private static List> pool; + + internal static ManagedArray Get() + { + if (pool == null) + pool = new List>(); + for (int i = 0; i < pool.Count; i++) + { + if (!pool[i].Item1) + { + var tuple = pool[i]; + tuple.Item1 = true; + pool[i] = tuple; + return tuple.Item2; + } + } + + var newTuple = (true, new ManagedArray()); + pool.Add(newTuple); + return newTuple.Item2; + } + + internal static void Put(ManagedArray obj) + { + for (int i = 0; i < pool.Count; i++) + { + if (pool[i].Item2 == obj) + { + var tuple = pool[i]; + tuple.Item1 = false; + pool[i] = tuple; + return; + } + } + + throw new Exception("Tried to free non-pooled ManagedArray as pooled ManagedArray"); + } + } + } + + internal static class ManagedString + { + internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty); + + [System.Diagnostics.DebuggerStepThrough] + internal static unsafe IntPtr ToNative(string str) + { + if (str == null) + return IntPtr.Zero; + else if (str == string.Empty) + return ManagedHandle.ToIntPtr(EmptyStringHandle); + Assert.IsTrue(str.Length > 0); + return ManagedHandle.ToIntPtr(str); + } + + [System.Diagnostics.DebuggerStepThrough] + internal static unsafe IntPtr ToNativeWeak(string str) + { + if (str == null) + return IntPtr.Zero; + else if (str == string.Empty) + return ManagedHandle.ToIntPtr(EmptyStringHandle); + Assert.IsTrue(str.Length > 0); + return ManagedHandle.ToIntPtr(str, GCHandleType.Weak); + } + + [System.Diagnostics.DebuggerStepThrough] + internal static string ToManaged(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + return null; + return Unsafe.As(ManagedHandle.FromIntPtr(ptr).Target); + } + + [System.Diagnostics.DebuggerStepThrough] + internal static void Free(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + return; + ManagedHandle handle = ManagedHandle.FromIntPtr(ptr); + if (handle == EmptyStringHandle) + return; + handle.Free(); + } + } + + /// + /// Handle to managed objects which can be stored in native code. + /// + public struct ManagedHandle + { + private IntPtr handle; + + private ManagedHandle(IntPtr handle) + { + this.handle = handle; + } + + private ManagedHandle(object value, GCHandleType type) + { + handle = ManagedHandlePool.AllocateHandle(value, type); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type); + + public void Free() + { + if (handle != IntPtr.Zero) + { + ManagedHandlePool.FreeHandle(handle); + handle = IntPtr.Zero; + } + + ManagedHandlePool.TryCollectWeakHandles(); + } + + public object Target + { + get => ManagedHandlePool.GetObject(handle); + set => ManagedHandlePool.SetObject(handle, value); + } + + public bool IsAllocated => handle != IntPtr.Zero; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ManagedHandle FromIntPtr(IntPtr value) => new ManagedHandle(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ToIntPtr(object value, GCHandleType type) => ManagedHandlePool.AllocateHandle(value, type); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ToIntPtr(ManagedHandle value) => value.handle; + + public override int GetHashCode() => handle.GetHashCode(); + + public override bool Equals(object o) => o is ManagedHandle other && Equals(other); + + public bool Equals(ManagedHandle other) => handle == other.handle; + + public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle; + + public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle; + + private static class ManagedHandlePool + { + private static ulong normalHandleAccumulator = 0; + private static ulong pinnedHandleAccumulator = 0; + private static ulong weakHandleAccumulator = 0; + + private static object poolLock = new object(); + private static Dictionary persistentPool = new Dictionary(); + private static Dictionary pinnedPool = new Dictionary(); + + private static Dictionary weakPool1 = new Dictionary(); + private static Dictionary weakPool2 = new Dictionary(); + private static Dictionary weakPool = weakPool1; + private static Dictionary weakPoolOther = weakPool2; + + private static int nextCollection = GC.CollectionCount(0) + 1; + + /// + /// Tries to free all references to old weak handles so GC can collect them. + /// + internal static void TryCollectWeakHandles() + { + if (GC.CollectionCount(0) < nextCollection) + return; + + lock (poolLock) + { + nextCollection = GC.CollectionCount(0) + 1; + + var swap = weakPoolOther; + weakPoolOther = weakPool; + weakPool = swap; + + weakPool.Clear(); + } + } + + private static IntPtr NewHandle(GCHandleType type) + { + IntPtr handle; + if (type == GCHandleType.Normal) + handle = (IntPtr)Interlocked.Increment(ref normalHandleAccumulator); + else if (type == GCHandleType.Pinned) + handle = (IntPtr)Interlocked.Increment(ref pinnedHandleAccumulator); + else //if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) + handle = (IntPtr)Interlocked.Increment(ref weakHandleAccumulator); + + // Two bits reserved for the type + handle |= (IntPtr)(((ulong)type << 62) & 0xC000000000000000); + return handle; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static GCHandleType GetHandleType(IntPtr handle) + { + return (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62); + } + + internal static IntPtr AllocateHandle(object value, GCHandleType type) + { + IntPtr handle = NewHandle(type); + lock (poolLock) + { + if (type == GCHandleType.Normal) + persistentPool.Add(handle, value); + else if (type == GCHandleType.Pinned) + pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned)); + else if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) + weakPool.Add(handle, value); + } + + return handle; + } + + internal static object GetObject(IntPtr handle) + { + object value; + GCHandleType type = GetHandleType(handle); + lock (poolLock) + { + if (type == GCHandleType.Normal && persistentPool.TryGetValue(handle, out value)) + return value; + else if (type == GCHandleType.Pinned && pinnedPool.TryGetValue(handle, out GCHandle gchandle)) + return gchandle.Target; + else if (weakPool.TryGetValue(handle, out value)) + return value; + else if (weakPoolOther.TryGetValue(handle, out value)) + return value; + } + + throw new Exception("Invalid ManagedHandle"); + } + + internal static void SetObject(IntPtr handle, object value) + { + GCHandleType type = GetHandleType(handle); + lock (poolLock) + { + if (type == GCHandleType.Normal && persistentPool.ContainsKey(handle)) + persistentPool[handle] = value; + else if (type == GCHandleType.Pinned && pinnedPool.TryGetValue(handle, out GCHandle gchandle)) + gchandle.Target = value; + else if (weakPool.ContainsKey(handle)) + weakPool[handle] = value; + else if (weakPoolOther.ContainsKey(handle)) + weakPoolOther[handle] = value; + else + throw new Exception("Invalid ManagedHandle"); + } + } + + internal static void FreeHandle(IntPtr handle) + { + GCHandleType type = GetHandleType(handle); + lock (poolLock) + { + if (type == GCHandleType.Normal && persistentPool.Remove(handle)) + return; + else if (type == GCHandleType.Pinned && pinnedPool.Remove(handle, out GCHandle gchandle)) + { + gchandle.Free(); + return; + } + else if (weakPool.Remove(handle)) + return; + else if (weakPoolOther.Remove(handle)) + return; + } + + throw new Exception("Invalid ManagedHandle"); + } + } + } +} + +#endif diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs new file mode 100644 index 000000000..f6434c325 --- /dev/null +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -0,0 +1,519 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if USE_NETCORE +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices.Marshalling; + +#pragma warning disable 1591 + +namespace FlaxEngine +{ + + [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedHandleMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(ManagedHandleMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(object), MarshalMode.ElementOut, typeof(ManagedHandleMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedRef, typeof(ManagedHandleMarshaller.Bidirectional))] + [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedRef, typeof(ManagedHandleMarshaller.Bidirectional))] + [CustomMarshaller(typeof(object), MarshalMode.ElementRef, typeof(ManagedHandleMarshaller))] + public static class ManagedHandleMarshaller + { + public static class NativeToManaged + { + public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target; + + public static void Free(IntPtr unmanaged) + { + // This is a permanent handle, do not release it + } + } + + public static class ManagedToNative + { + public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; + + public static void Free(IntPtr unmanaged) + { + if (unmanaged == IntPtr.Zero) + return; + ManagedHandle.FromIntPtr(unmanaged).Free(); + } + } + + public struct Bidirectional + { + object managed; + IntPtr unmanaged; + + public void FromManaged(object managed) + { + this.managed = managed; + } + + public IntPtr ToUnmanaged() + { + unmanaged = ManagedHandleMarshaller.ToNative(managed); + return unmanaged; + } + + public void FromUnmanaged(IntPtr unmanaged) + { + this.unmanaged = unmanaged; + } + + public object ToManaged() + { + managed = ManagedHandleMarshaller.ToManaged(unmanaged); + unmanaged = IntPtr.Zero; + return managed; + } + + public void Free() + { + // FIXME, might be a permanent handle or a temporary one + throw new NotImplementedException(); + /*if (unmanaged == IntPtr.Zero) + return; + unmanaged.Free();*/ + } + } + + public static object ConvertToManaged(IntPtr unmanaged) => ToManaged(unmanaged); + public static IntPtr ConvertToUnmanaged(object managed) => ToNative(managed); + public static object ToManaged(IntPtr managed) => managed != IntPtr.Zero ? ManagedHandle.FromIntPtr(managed).Target : null; + public static IntPtr ToNative(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; + + public static void Free(IntPtr unmanaged) + { + if (unmanaged == IntPtr.Zero) + return; + ManagedHandle.FromIntPtr(unmanaged).Free(); + } + } + + [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] + public static class SystemTypeMarshaller + { + public static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); + + public static IntPtr ConvertToUnmanaged(Type managed) + { + if (managed == null) + return IntPtr.Zero; + ManagedHandle handle = NativeInterop.GetTypeGCHandle(managed); + return ManagedHandle.ToIntPtr(handle); + } + + public static void Free(IntPtr unmanaged) + { + // Cached handle, do not release + } + } + + [CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))] + public static class ExceptionMarshaller + { + public static Exception ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); + public static IntPtr ConvertToUnmanaged(Exception managed) => ManagedHandleMarshaller.ConvertToUnmanaged(managed); + public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged); + } + + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))] + public static class ObjectMarshaller + { + public static class NativeToManaged + { + public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target) : null; + } + + public static class ManagedToNative + { + public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; + } + } + + [CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))] + public static class CultureInfoMarshaller + { + public static CultureInfo ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); + public static IntPtr ConvertToUnmanaged(CultureInfo managed) => ManagedHandleMarshaller.ConvertToUnmanaged(managed); + public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged); + } + + [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedIn, typeof(SystemArrayMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedOut, typeof(SystemArrayMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedIn, typeof(SystemArrayMarshaller.NativeToManaged))] + public static unsafe class SystemArrayMarshaller + { + public struct ManagedToNative + { + ManagedArray managedArray; + ManagedHandle handle; + + public void FromManaged(Array managed) + { + if (managed != null) + managedArray = ManagedArray.WrapPooledArray(managed); + } + + public IntPtr ToUnmanaged() + { + if (managedArray == null) + return IntPtr.Zero; + handle = ManagedHandle.Alloc(managedArray); + return ManagedHandle.ToIntPtr(handle); + } + + public void Free() + { + if (managedArray == null) + return; + managedArray.FreePooled(); + handle.Free(); + } + } + + public struct NativeToManaged + { + ManagedHandle handle; + ManagedArray managedArray; + + public void FromUnmanaged(IntPtr unmanaged) + { + if (unmanaged == IntPtr.Zero) + return; + handle = ManagedHandle.FromIntPtr(unmanaged); + } + + public Array ToManaged() + { + if (!handle.IsAllocated) + return null; + managedArray = Unsafe.As(handle.Target); + return managedArray.ToArray(); + } + + public void Free() + { + if (!handle.IsAllocated) + return; + managedArray.Free(); + handle.Free(); + } + } + } + + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedOut, typeof(DictionaryMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedOut, typeof(DictionaryMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedIn, typeof(DictionaryMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementOut, typeof(DictionaryMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedRef, typeof(DictionaryMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedRef, typeof(DictionaryMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementRef, typeof(DictionaryMarshaller<,>))] + public static unsafe class DictionaryMarshaller + { + public static class NativeToManaged + { + public static Dictionary ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller.ToManaged(unmanaged); + public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged); + } + + public static class ManagedToNative + { + public static IntPtr ConvertToUnmanaged(Dictionary managed) => DictionaryMarshaller.ToNative(managed); + public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged); + } + + public struct Bidirectional + { + Dictionary managed; + IntPtr unmanaged; + + public void FromManaged(Dictionary managed) => this.managed = managed; + + public IntPtr ToUnmanaged() + { + unmanaged = DictionaryMarshaller.ToNative(managed); + return unmanaged; + } + + public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; + + public Dictionary ToManaged() + { + managed = DictionaryMarshaller.ToManaged(unmanaged); + unmanaged = IntPtr.Zero; + return managed; + } + + public void Free() => DictionaryMarshaller.Free(unmanaged); + } + + public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(ManagedHandle.FromIntPtr(unmanaged).Target) : null; + public static IntPtr ConvertToUnmanaged(Dictionary managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; + + public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(ManagedHandle.FromIntPtr(unmanaged).Target) : null; + public static IntPtr ToNative(Dictionary managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; + + public static void Free(IntPtr unmanaged) + { + if (unmanaged != IntPtr.Zero) + ManagedHandle.FromIntPtr(unmanaged).Free(); + } + } + + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(ArrayMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(ArrayMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(ArrayMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(ArrayMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(ArrayMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(ArrayMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(ArrayMarshaller<,>))] + [ContiguousCollectionMarshaller] + public static unsafe class ArrayMarshaller where TUnmanagedElement : unmanaged + { + public static class NativeToManaged + { + public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) + { + if (unmanaged is null) + return null; + return new T[numElements]; + } + + public static Span GetManagedValuesDestination(T[]? managed) => managed; + + public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) + { + if (unmanaged == null) + return ReadOnlySpan.Empty; + ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.ToSpan(); + } + + public static void Free(TUnmanagedElement* unmanaged) + { + if (unmanaged == null) + return; + ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); + (Unsafe.As(handle.Target)).Free(); + handle.Free(); + } + + public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) + { + if (unmanaged == null) + return Span.Empty; + ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.ToSpan(); + } + } + + public static class ManagedToNative + { + public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) + { + if (managed is null) + { + numElements = 0; + return null; + } + numElements = managed.Length; + ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length); + var ptr = ManagedHandle.ToIntPtr(managedArray); + return (TUnmanagedElement*)ptr; + } + + public static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; + + public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) + { + if (unmanaged == null) + return Span.Empty; + ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.ToSpan(); + } + + public static void Free(TUnmanagedElement* unmanaged) + { + if (unmanaged == null) + return; + ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); + (Unsafe.As(handle.Target)).FreePooled(); + handle.Free(); + } + } + + public struct Bidirectional + { + T[] managedArray; + ManagedArray unmanagedArray; + ManagedHandle handle; + + public void FromManaged(T[]? managed) + { + if (managed == null) + return; + managedArray = managed; + unmanagedArray = ManagedArray.AllocatePooledArray(managed.Length); + handle = ManagedHandle.Alloc(unmanagedArray); + } + + public ReadOnlySpan GetManagedValuesSource() => managedArray; + + public Span GetUnmanagedValuesDestination() + { + if (unmanagedArray == null) + return Span.Empty; + return unmanagedArray.ToSpan(); + } + + public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle); + + public void FromUnmanaged(TUnmanagedElement* unmanaged) + { + ManagedArray arr = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + if (managedArray == null || managedArray.Length != arr.Length) + managedArray = new T[arr.Length]; + } + + public ReadOnlySpan GetUnmanagedValuesSource(int numElements) + { + if (unmanagedArray == null) + return ReadOnlySpan.Empty; + return unmanagedArray.ToSpan(); + } + + public Span GetManagedValuesDestination(int numElements) => managedArray; + + public T[] ToManaged() => managedArray; + + public void Free() + { + unmanagedArray.FreePooled(); + handle.Free(); + } + } + + public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) + { + if (managed is null) + { + numElements = 0; + return null; + } + numElements = managed.Length; + ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length); + IntPtr handle = ManagedHandle.ToIntPtr(managedArray); + return (TUnmanagedElement*)handle; + } + + public static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; + + public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) + { + if (unmanaged == null) + return Span.Empty; + ManagedArray unmanagedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return unmanagedArray.ToSpan(); + } + + public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements]; + + public static Span GetManagedValuesDestination(T[]? managed) => managed; + + public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) + { + if (unmanaged == null) + return ReadOnlySpan.Empty; + ManagedArray array = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return array.ToSpan(); + } + + public static void Free(TUnmanagedElement* unmanaged) + { + if (unmanaged == null) + return; + ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); + Unsafe.As(handle.Target).FreePooled(); + handle.Free(); + } + } + + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshaller))] + public static class StringMarshaller + { + public static class NativeToManaged + { + public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); + public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); + } + + public static class ManagedToNative + { + public static unsafe IntPtr ConvertToUnmanaged(string managed) + { + return managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed); + } + + public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); + } + + public struct Bidirectional + { + string managed; + IntPtr unmanaged; + + public void FromManaged(string managed) => this.managed = managed; + + public IntPtr ToUnmanaged() + { + unmanaged = ManagedString.ToNative(managed); + return unmanaged; + } + + public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; + + public string ToManaged() + { + managed = ManagedString.ToManaged(unmanaged); + unmanaged = IntPtr.Zero; + return managed; + } + + public void Free() => ManagedString.Free(unmanaged); + } + + public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); + public static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed); + public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); + + public static string ToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); + public static IntPtr ToNative(string managed) => ManagedString.ToNative(managed); + } +} + +#endif diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs new file mode 100644 index 000000000..1e9e1e1da --- /dev/null +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -0,0 +1,1076 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if USE_NETCORE +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using FlaxEngine.Utilities; + +#pragma warning disable 1591 + +namespace FlaxEngine +{ + [StructLayout(LayoutKind.Sequential)] + internal struct NativeClassDefinitions + { + internal ManagedHandle typeHandle; + internal IntPtr name; + internal IntPtr fullname; + internal IntPtr @namespace; + internal uint typeAttributes; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativeMethodDefinitions + { + internal IntPtr name; + internal int numParameters; + internal ManagedHandle typeHandle; + internal uint methodAttributes; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativeFieldDefinitions + { + internal IntPtr name; + internal ManagedHandle fieldHandle; + internal ManagedHandle fieldTypeHandle; + internal uint fieldAttributes; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativePropertyDefinitions + { + internal IntPtr name; + internal ManagedHandle getterHandle; + internal ManagedHandle setterHandle; + internal uint getterAttributes; + internal uint setterAttributes; + } + + [StructLayout(LayoutKind.Explicit)] + public struct NativeVariant + { + [StructLayout(LayoutKind.Sequential)] + internal struct NativeVariantType + { + internal VariantUtils.VariantType types; + internal IntPtr TypeName; // char* + } + + [FieldOffset(0)] + NativeVariantType Type; + + [FieldOffset(8)] + byte AsBool; + + [FieldOffset(8)] + short AsInt16; + + [FieldOffset(8)] + ushort AsUint16; + + [FieldOffset(8)] + int AsInt; + + [FieldOffset(8)] + uint AsUint; + + [FieldOffset(8)] + long AsInt64; + + [FieldOffset(8)] + ulong AsUint64; + + [FieldOffset(8)] + float AsFloat; + + [FieldOffset(8)] + double AsDouble; + + [FieldOffset(8)] + IntPtr AsPointer; + + [FieldOffset(8)] + int AsData0; + + [FieldOffset(12)] + int AsData1; + + [FieldOffset(16)] + int AsData2; + + [FieldOffset(20)] + int AsData3; + + [FieldOffset(24)] + int AsData4; + + [FieldOffset(28)] + int AsData5; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativeVersion + { + internal int _Major; + internal int _Minor; + internal int _Build; + internal int _Revision; + + internal NativeVersion(Version ver) + { + _Major = ver.Major; + _Minor = ver.Minor; + _Build = ver.Build; + _Revision = ver.Revision; + } + + internal Version GetVersion() + { + return new Version(_Major, _Minor, _Build, _Revision); + } + } + + unsafe partial class NativeInterop + { + internal enum MTypes : uint + { + End = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + I1 = 0x04, + U1 = 0x05, + I2 = 0x06, + U2 = 0x07, + I4 = 0x08, + U4 = 0x09, + I8 = 0x0a, + U8 = 0x0b, + R4 = 0x0c, + R8 = 0x0d, + String = 0x0e, + Ptr = 0x0f, + ByRef = 0x10, + ValueType = 0x11, + Class = 0x12, + Var = 0x13, + Array = 0x14, + GenericInst = 0x15, + TypeByRef = 0x16, + I = 0x18, + U = 0x19, + Fnptr = 0x1b, + Object = 0x1c, + SzArray = 0x1d, + MVar = 0x1e, + CmodReqd = 0x1f, + CmodOpt = 0x20, + Internal = 0x21, + Modifier = 0x40, + Sentinel = 0x41, + Pinned = 0x45, + Enum = 0x55, + }; + + [UnmanagedCallersOnly] + internal static void RegisterNativeLibrary(IntPtr moduleName_, IntPtr modulePath_) + { + string moduleName = Marshal.PtrToStringAnsi(moduleName_); + string modulePath = Marshal.PtrToStringAnsi(modulePath_); + nativeLibraryPaths[moduleName] = modulePath; + } + + [UnmanagedCallersOnly] + internal static void* AllocMemory(int size, bool coTaskMem) + { + if (coTaskMem) + return Marshal.AllocCoTaskMem(size).ToPointer(); + else + return NativeMemory.AlignedAlloc((UIntPtr)size, 16); + } + + [UnmanagedCallersOnly] + internal static void FreeMemory(void* ptr, bool coTaskMem) + { + if (coTaskMem) + Marshal.FreeCoTaskMem(new IntPtr(ptr)); + else + NativeMemory.AlignedFree(ptr); + } + + [UnmanagedCallersOnly] + internal static unsafe void GetManagedClasses(ManagedHandle assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount) + { + Assembly assembly = Unsafe.As(assemblyHandle.Target); + var assemblyTypes = GetAssemblyTypes(assembly); + + NativeClassDefinitions* arr = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf()); + + for (int i = 0; i < assemblyTypes.Length; i++) + { + var type = assemblyTypes[i]; + IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); + var managedClass = new NativeClassDefinitions + { + typeHandle = GetTypeGCHandle(type), + name = NativeAllocStringAnsi(type.Name), + fullname = NativeAllocStringAnsi(type.GetTypeName()), + @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), + typeAttributes = (uint)type.Attributes, + }; + Unsafe.Write(ptr.ToPointer(), managedClass); + } + + *managedClasses = arr; + *managedClassCount = assemblyTypes.Length; + } + + [UnmanagedCallersOnly] + internal static unsafe void GetManagedClassFromType(ManagedHandle typeHandle, NativeClassDefinitions* managedClass, ManagedHandle* assemblyHandle) + { + Type type = Unsafe.As(typeHandle.Target); + *managedClass = new NativeClassDefinitions + { + typeHandle = GetTypeGCHandle(type), + name = NativeAllocStringAnsi(type.Name), + fullname = NativeAllocStringAnsi(type.GetTypeName()), + @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), + typeAttributes = (uint)type.Attributes, + }; + *assemblyHandle = GetAssemblyHandle(type.Assembly); + } + + [UnmanagedCallersOnly] + internal static void GetClassMethods(ManagedHandle typeHandle, NativeMethodDefinitions** classMethods, int* classMethodsCount) + { + Type type = Unsafe.As(typeHandle.Target); + + var methods = new List(); + var staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + var instanceMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + methods.AddRange(staticMethods); + methods.AddRange(instanceMethods); + + var arr = (NativeMethodDefinitions*)NativeAlloc(methods.Count, Unsafe.SizeOf()); + for (int i = 0; i < methods.Count; i++) + { + IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); + var classMethod = new NativeMethodDefinitions + { + name = NativeAllocStringAnsi(methods[i].Name), + numParameters = methods[i].GetParameters().Length, + methodAttributes = (uint)methods[i].Attributes, + }; + classMethod.typeHandle = GetMethodGCHandle(methods[i]); + Unsafe.Write(ptr.ToPointer(), classMethod); + } + *classMethods = arr; + *classMethodsCount = methods.Count; + } + + [UnmanagedCallersOnly] + internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount) + { + Type type = Unsafe.As(typeHandle.Target); + var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf()); + for (int i = 0; i < fields.Length; i++) + { + FieldHolder fieldHolder = new FieldHolder(fields[i], type); + + ManagedHandle fieldHandle = ManagedHandle.Alloc(fieldHolder); + if (type.IsCollectible) + fieldHandleCacheCollectible.Add(fieldHandle); + else + fieldHandleCache.Add(fieldHandle); + + NativeFieldDefinitions classField = new NativeFieldDefinitions() + { + name = NativeAllocStringAnsi(fieldHolder.field.Name), + fieldHandle = fieldHandle, + fieldTypeHandle = GetTypeGCHandle(fieldHolder.field.FieldType), + fieldAttributes = (uint)fieldHolder.field.Attributes, + }; + Unsafe.Write(IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i).ToPointer(), classField); + } + *classFields = arr; + *classFieldsCount = fields.Length; + } + + [UnmanagedCallersOnly] + internal static void GetClassProperties(ManagedHandle typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount) + { + Type type = Unsafe.As(typeHandle.Target); + var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf()); + for (int i = 0; i < properties.Length; i++) + { + IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); + + var getterMethod = properties[i].GetGetMethod(true); + var setterMethod = properties[i].GetSetMethod(true); + + var classProperty = new NativePropertyDefinitions + { + name = NativeAllocStringAnsi(properties[i].Name), + }; + if (getterMethod != null) + { + classProperty.getterHandle = GetMethodGCHandle(getterMethod); + classProperty.getterAttributes = (uint)getterMethod.Attributes; + } + if (setterMethod != null) + { + classProperty.setterHandle = GetMethodGCHandle(setterMethod); + classProperty.setterAttributes = (uint)setterMethod.Attributes; + } + Unsafe.Write(ptr.ToPointer(), classProperty); + } + *classProperties = arr; + *classPropertiesCount = properties.Length; + } + + [UnmanagedCallersOnly] + internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount) + { + Type type = Unsafe.As(typeHandle.Target); + object[] attributeValues = type.GetCustomAttributes(false); + + ManagedHandle* arr = (ManagedHandle*)NativeAlloc(attributeValues.Length, Unsafe.SizeOf()); + for (int i = 0; i < attributeValues.Length; i++) + { + if (!classAttributesCacheCollectible.TryGetValue(attributeValues[i], out ManagedHandle attributeHandle)) + { + attributeHandle = ManagedHandle.Alloc(attributeValues[i]); + classAttributesCacheCollectible.Add(attributeValues[i], attributeHandle); + } + arr[i] = attributeHandle; + } + *classAttributes = arr; + *classAttributesCount = attributeValues.Length; + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetCustomAttribute(ManagedHandle typeHandle, ManagedHandle attributeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + var attributes = type.GetCustomAttributes(false); + object attrib; + if (attributeHandle.IsAllocated) + { + // Check for certain attribute type + Type attributeType = Unsafe.As(attributeHandle.Target); + attrib = attributes.FirstOrDefault(x => x.GetType() == attributeType); + } + else + { + // Check if has any attribute + attrib = attributes.FirstOrDefault(); + } + if (attrib != null) + return ManagedHandle.Alloc(attrib, GCHandleType.Weak); + return new ManagedHandle(); + } + + [UnmanagedCallersOnly] + internal static void GetClassInterfaces(ManagedHandle typeHandle, IntPtr* classInterfaces, int* classInterfacesCount) + { + Type type = Unsafe.As(typeHandle.Target); + Type[] interfaces = type.GetInterfaces(); + + // Match mono_class_get_interfaces which doesn't return interfaces from base class + var baseTypeInterfaces = type.BaseType?.GetInterfaces(); + if (baseTypeInterfaces != null) + { + if (baseTypeInterfaces.Length == interfaces.Length) + { + // Both base and this type have the same amount of interfaces so assume that this one has none unique + interfaces = Array.Empty(); + } + else if (baseTypeInterfaces.Length != 0) + { + // Count unique interface types + int uniqueCount = interfaces.Length; + for (int i = 0; i < interfaces.Length; i++) + { + for (int j = 0; j < baseTypeInterfaces.Length; j++) + { + if (interfaces[i] == baseTypeInterfaces[j]) + { + uniqueCount--; + break; + } + } + } + + // Get only unique interface types + var unique = new Type[uniqueCount]; + uniqueCount = 0; + for (int i = 0; i < interfaces.Length; i++) + { + bool isUnique = true; + for (int j = 0; j < baseTypeInterfaces.Length; j++) + { + if (interfaces[i] == baseTypeInterfaces[j]) + { + isUnique = false; + break; + } + } + if (isUnique) + unique[uniqueCount++] = interfaces[i]; + } + interfaces = unique; + } + } + + IntPtr arr = (IntPtr)NativeAlloc(interfaces.Length, IntPtr.Size); + for (int i = 0; i < interfaces.Length; i++) + { + ManagedHandle handle = GetTypeGCHandle(interfaces[i]); + Unsafe.Write(IntPtr.Add(arr, IntPtr.Size * i).ToPointer(), handle); + } + *classInterfaces = arr; + *classInterfacesCount = interfaces.Length; + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetMethodReturnType(ManagedHandle methodHandle) + { + MethodHolder methodHolder = Unsafe.As(methodHandle.Target); + Type returnType = methodHolder.returnType; + + return GetTypeGCHandle(returnType); + } + + [UnmanagedCallersOnly] + internal static void GetMethodParameterTypes(ManagedHandle methodHandle, IntPtr* typeHandles) + { + MethodHolder methodHolder = Unsafe.As(methodHandle.Target); + Type returnType = methodHolder.returnType; + IntPtr arr = (IntPtr)NativeAlloc(methodHolder.parameterTypes.Length, IntPtr.Size); + for (int i = 0; i < methodHolder.parameterTypes.Length; i++) + { + ManagedHandle typeHandle = GetTypeGCHandle(methodHolder.parameterTypes[i]); + Unsafe.Write(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i).ToPointer(), typeHandle); + } + *typeHandles = arr; + } + + /// + /// Returns pointer to the string's internal structure, containing the buffer and length of the string. + /// + [UnmanagedCallersOnly] + internal static IntPtr GetStringPointer(ManagedHandle stringHandle) + { + string str = Unsafe.As(stringHandle.Target); + IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2); + if (ptr < 0x1024) + throw new Exception("null string ptr"); + return ptr; + } + + [UnmanagedCallersOnly] + internal static ManagedHandle NewObject(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + if (type.IsAbstract) + { + // Dotnet doesn't allow to instantiate abstract type thus allow to use generated mock class usage (eg. for Script or GPUResource) for generated abstract types + var abstractWrapper = type.GetNestedType("AbstractWrapper", BindingFlags.NonPublic); + if (abstractWrapper != null) + type = abstractWrapper; + } + object value = RuntimeHelpers.GetUninitializedObject(type); + return ManagedHandle.Alloc(value); + } + + + [UnmanagedCallersOnly] + internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size) + { + Type elementType = Unsafe.As(typeHandle.Target); + Type marshalledType = ArrayFactory.GetMarshalledType(elementType); + Type arrayType = elementType.MakeArrayType(); + if (marshalledType.IsValueType) + { + ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType); + return ManagedHandle.Alloc(managedArray); + } + else + { + Array arr = ArrayFactory.CreateArray(elementType, size); + ManagedArray managedArray = ManagedArray.WrapNewArray(arr, arrayType); + return ManagedHandle.Alloc(managedArray); + } + } + + [UnmanagedCallersOnly] + internal static unsafe ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) + { + Type elementType = Unsafe.As(elementTypeHandle.Target); + Type classType = elementType.MakeArrayType(); + return GetTypeGCHandle(classType); + } + + [UnmanagedCallersOnly] + internal static unsafe IntPtr GetArrayPointer(ManagedHandle arrayHandle) + { + if (!arrayHandle.IsAllocated) + return IntPtr.Zero; + ManagedArray managedArray = Unsafe.As(arrayHandle.Target); + if (managedArray.Length == 0) + return IntPtr.Zero; + return managedArray.Pointer; + } + + [UnmanagedCallersOnly] + internal static int GetArrayLength(ManagedHandle arrayHandle) + { + if (!arrayHandle.IsAllocated) + return 0; + ManagedArray managedArray = Unsafe.As(arrayHandle.Target); + return managedArray.Length; + } + + [UnmanagedCallersOnly] + internal static IntPtr GetStringEmpty() + { + return ManagedHandle.ToIntPtr(ManagedString.EmptyStringHandle); + } + + [UnmanagedCallersOnly] + internal static IntPtr NewStringUTF16(char* text, int length) + { + return ManagedString.ToNativeWeak(new string(new ReadOnlySpan(text, length))); + } + + [UnmanagedCallersOnly] + internal static IntPtr NewStringLength(sbyte* text, int length) + { + return ManagedString.ToNativeWeak(new string(text, 0, length)); + } + + /// + /// Creates a managed copy of the value, and stores it in a boxed reference. + /// + [UnmanagedCallersOnly] + internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr) + { + Type type = Unsafe.As(typeHandle.Target); + object value = MarshalToManaged(valuePtr, type); + return ManagedHandle.Alloc(value, GCHandleType.Weak); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetObjectType(ManagedHandle handle) + { + object obj = handle.Target; + Type classType = obj.GetType(); + if (classType == typeof(ManagedArray)) + classType = ((ManagedArray)obj).ArrayType; + return GetTypeGCHandle(classType); + } + + /// + /// Returns the address of the boxed value type. + /// + [UnmanagedCallersOnly] + internal static unsafe IntPtr UnboxValue(ManagedHandle handle) + { + object value = handle.Target; + Type type = value.GetType(); + if (!type.IsValueType) + return ManagedHandle.ToIntPtr(handle); + + // HACK: Get the address of a non-pinned value + return ValueTypeUnboxer.GetPointer(value); + } + + [UnmanagedCallersOnly] + internal static void RaiseException(ManagedHandle exceptionHandle) + { + Exception exception = Unsafe.As(exceptionHandle.Target); + throw exception; + } + + [UnmanagedCallersOnly] + internal static void ObjectInit(ManagedHandle objectHandle) + { + object obj = objectHandle.Target; + Type type = obj.GetType(); + ConstructorInfo ctor = type.GetMember(".ctor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).First() as ConstructorInfo; + ctor.Invoke(obj, null); + } + + [UnmanagedCallersOnly] + internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) + { + MethodHolder methodHolder = Unsafe.As(methodHandle.Target); + if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext)) + { + // Fast path, invoke the method with minimal allocations + IntPtr returnValue; + try + { + returnValue = methodDelegate(methodDelegateContext, instanceHandle, paramPtr); + } + catch (Exception exception) + { + if (exceptionPtr != IntPtr.Zero) + Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); + return IntPtr.Zero; + } + return returnValue; + } + else + { + // Slow path, method parameters needs to be stored in heap + object returnObject; + int numParams = methodHolder.parameterTypes.Length; + object[] methodParameters = new object[numParams]; + + for (int i = 0; i < numParams; i++) + { + IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); + methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]); + } + + try + { + returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters); + } + catch (Exception exception) + { + // The internal exception thrown in MethodInfo.Invoke is caught here + Exception realException = exception; + if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker") + realException = exception.InnerException; + + if (exceptionPtr != IntPtr.Zero) + Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(realException, GCHandleType.Weak)); + else + throw realException; + return IntPtr.Zero; + } + + // Marshal reference parameters back to original unmanaged references + for (int i = 0; i < numParams; i++) + { + Type parameterType = methodHolder.parameterTypes[i]; + if (parameterType.IsByRef) + { + IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); + MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType()); + } + } + + // Return value + return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject); + } + } + + [UnmanagedCallersOnly] + internal static IntPtr GetMethodUnmanagedFunctionPointer(ManagedHandle methodHandle) + { + MethodHolder methodHolder = Unsafe.As(methodHandle.Target); + + // Wrap the method call, this is needed to get the object instance from ManagedHandle and to pass the exception back to native side + ThunkContext context = new ThunkContext((MethodInfo)methodHolder.method); + Delegate methodDelegate = typeof(ThunkContext).GetMethod(nameof(ThunkContext.InvokeThunk)).CreateDelegate(context); + IntPtr functionPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); + + // Keep a reference to the delegate to prevent it from being garbage collected + if (methodHolder.method.IsCollectible) + cachedDelegatesCollectible[functionPtr] = methodDelegate; + else + cachedDelegates[functionPtr] = methodDelegate; + + return functionPtr; + } + + [UnmanagedCallersOnly] + internal static void FieldSetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) + { + object fieldOwner = fieldOwnerHandle.Target; + FieldHolder field = Unsafe.As(fieldHandle.Target); + object value = null; + if (field.field.FieldType.IsValueType) + value = Marshal.PtrToStructure(valuePtr, field.field.FieldType); + else if (valuePtr != IntPtr.Zero) + value = ManagedHandle.FromIntPtr(valuePtr).Target; + field.field.SetValue(fieldOwner, value); + } + + [UnmanagedCallersOnly] + internal static void FieldGetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) + { + object fieldOwner = fieldOwnerHandle.Target; + FieldHolder field = Unsafe.As(fieldHandle.Target); + field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset); + } + + [UnmanagedCallersOnly] + internal static void SetArrayValueReference(ManagedHandle arrayHandle, IntPtr valueHandle, int index) + { + ManagedArray managedArray = Unsafe.As(arrayHandle.Target); + managedArray.ToSpan()[index] = valueHandle; + } + + [UnmanagedCallersOnly] + internal static ManagedHandle LoadAssemblyImage(IntPtr data, int len, IntPtr assemblyPath_, IntPtr* assemblyName, IntPtr* assemblyFullName) + { + if (!firstAssemblyLoaded) + { + // This assembly was already loaded when initializing the host context + firstAssemblyLoaded = true; + + Assembly flaxEngineAssembly = AssemblyLoadContext.Default.Assemblies.First(x => x.GetName().Name == "FlaxEngine.CSharp"); + *assemblyName = NativeAllocStringAnsi(flaxEngineAssembly.GetName().Name); + *assemblyFullName = NativeAllocStringAnsi(flaxEngineAssembly.FullName); + return GetAssemblyHandle(flaxEngineAssembly); + } + + string assemblyPath = Marshal.PtrToStringAnsi(assemblyPath_); + + byte[] raw = new byte[len]; + Marshal.Copy(data, raw, 0, len); + + using MemoryStream stream = new MemoryStream(raw); + Assembly assembly; +#if !BUILD_RELEASE + var pdbPath = Path.ChangeExtension(assemblyPath, "pdb"); + if (File.Exists(pdbPath)) + { + using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open); + assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream); + } + else +#endif + { + assembly = scriptingAssemblyLoadContext.LoadFromStream(stream); + } + if (assembly == null) + return new ManagedHandle(); + NativeLibrary.SetDllImportResolver(assembly, NativeLibraryImportResolver); + + // Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822 + AssemblyLocations.Add(assembly.FullName, assemblyPath); + + *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); + *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); + return GetAssemblyHandle(assembly); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetAssemblyByName(IntPtr name_, IntPtr* assemblyName, IntPtr* assemblyFullName) + { + string name = Marshal.PtrToStringAnsi(name_); + Assembly assembly = Utils.GetAssemblies().FirstOrDefault(x => x.GetName().Name == name); + if (assembly == null) + return new ManagedHandle(); + + *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); + *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); + return GetAssemblyHandle(assembly); + } + + [UnmanagedCallersOnly] + internal static IntPtr GetRuntimeInformation() + { + return NativeAllocStringAnsi(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); + } + + [UnmanagedCallersOnly] + internal static void CloseAssembly(ManagedHandle assemblyHandle) + { + Assembly assembly = Unsafe.As(assemblyHandle.Target); + assemblyHandle.Free(); + assemblyHandles.Remove(assembly); + + AssemblyLocations.Remove(assembly.FullName); + + // Clear all caches which might hold references to closing assembly + cachedDelegatesCollectible.Clear(); + typeCache.Clear(); + + // Release all GCHandles in collectible ALC + foreach (var pair in typeHandleCacheCollectible) + pair.Value.Free(); + typeHandleCacheCollectible.Clear(); + + foreach (var handle in methodHandlesCollectible) + handle.Free(); + methodHandlesCollectible.Clear(); + + foreach (var handle in fieldHandleCacheCollectible) + handle.Free(); + fieldHandleCacheCollectible.Clear(); + + foreach (var pair in classAttributesCacheCollectible) + pair.Value.Free(); + classAttributesCacheCollectible.Clear(); + + // Unload native library handles associated for this assembly + string nativeLibraryName = assemblyOwnedNativeLibraries.GetValueOrDefault(assembly); + if (nativeLibraryName != null && loadedNativeLibraries.TryGetValue(nativeLibraryName, out IntPtr nativeLibrary)) + { + NativeLibrary.Free(nativeLibrary); + loadedNativeLibraries.Remove(nativeLibraryName); + } + if (nativeLibraryName != null) + nativeLibraryPaths.Remove(nativeLibraryName); + + // Unload the ALC + bool unloading = true; + scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; }; + scriptingAssemblyLoadContext.Unload(); + + while (unloading) + System.Threading.Thread.Sleep(1); + + // TODO: benchmark collectible setting performance, maybe enable it only in editor builds? + scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", true); + DelegateHelpers.InitMethods(); + } + + [UnmanagedCallersOnly] + internal static unsafe int NativeSizeOf(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + Type nativeType = GetInternalType(type) ?? type; + if (nativeType == typeof(Version)) + nativeType = typeof(NativeVersion); + int size; + if (nativeType.IsClass) + size = sizeof(IntPtr); + else + size = Marshal.SizeOf(nativeType); + return size; + } + + [UnmanagedCallersOnly] + internal static byte TypeIsSubclassOf(ManagedHandle typeHandle, ManagedHandle otherTypeHandle, byte checkInterfaces) + { + if (typeHandle == otherTypeHandle) + return 1; + + Type type = Unsafe.As(typeHandle.Target); + Type otherType = Unsafe.As(otherTypeHandle.Target); + + if (type == otherType) + return 1; + + if (checkInterfaces != 0 && otherType.IsInterface) + { + if (type.GetInterfaces().Contains(otherType)) + return 1; + } + + return type.IsSubclassOf(otherType) ? (byte)1 : (byte)0; + } + + [UnmanagedCallersOnly] + internal static byte TypeIsAssignableFrom(ManagedHandle typeHandle, ManagedHandle otherTypeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + Type otherType = Unsafe.As(otherTypeHandle.Target); + return (byte)(type.IsAssignableFrom(otherType) ? 1 : 0); + } + + [UnmanagedCallersOnly] + internal static byte TypeIsValueType(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return (byte)(type.IsValueType ? 1 : 0); + } + + [UnmanagedCallersOnly] + internal static byte TypeIsEnum(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return (byte)(type.IsEnum ? 1 : 0); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetClassParent(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return GetTypeGCHandle(type.BaseType); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetElementClass(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return GetTypeGCHandle(type.GetElementType()); + } + + [UnmanagedCallersOnly] + internal static byte GetMethodParameterIsOut(ManagedHandle methodHandle, int parameterNum) + { + MethodHolder methodHolder = Unsafe.As(methodHandle.Target); + ParameterInfo parameterInfo = methodHolder.method.GetParameters()[parameterNum]; + return (byte)(parameterInfo.IsOut ? 1 : 0); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetNullReferenceException() + { + var exception = new NullReferenceException(); + return ManagedHandle.Alloc(exception, GCHandleType.Weak); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetNotSupportedException() + { + var exception = new NotSupportedException(); + return ManagedHandle.Alloc(exception, GCHandleType.Weak); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetArgumentNullException() + { + var exception = new ArgumentNullException(); + return ManagedHandle.Alloc(exception, GCHandleType.Weak); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetArgumentException() + { + var exception = new ArgumentException(); + return ManagedHandle.Alloc(exception, GCHandleType.Weak); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetArgumentOutOfRangeException() + { + var exception = new ArgumentOutOfRangeException(); + return ManagedHandle.Alloc(exception, GCHandleType.Weak); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle NewGCHandle(ManagedHandle valueHandle, byte pinned) + { + object value = valueHandle.Target; + ManagedHandle handle = ManagedHandle.Alloc(value, pinned != 0 ? GCHandleType.Pinned : GCHandleType.Normal); + return handle; + } + + [UnmanagedCallersOnly] + internal static ManagedHandle NewGCHandleWeak(ManagedHandle valueHandle, byte trackResurrection) + { + object value = valueHandle.Target; + ManagedHandle handle = ManagedHandle.Alloc(value, trackResurrection != 0 ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); + return handle; + } + + [UnmanagedCallersOnly] + internal static void FreeGCHandle(ManagedHandle valueHandle) + { + valueHandle.Free(); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetTypeClass(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + if (type.IsByRef) + type = type.GetElementType(); // Drop reference type (&) to get actual value type + return GetTypeGCHandle(type); + } + + [UnmanagedCallersOnly] + internal static bool GetTypeIsPointer(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return type.IsPointer; + } + + [UnmanagedCallersOnly] + internal static bool GetTypeIsReference(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return type.IsByRef; + } + + [UnmanagedCallersOnly] + internal static uint GetTypeMTypesEnum(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + if (type.IsByRef) + type = type.GetElementType(); // Drop reference type (&) to get actual value type + MTypes monoType; + switch (type) + { + case Type _ when type == typeof(void): + monoType = MTypes.Void; + break; + case Type _ when type == typeof(bool): + monoType = MTypes.Boolean; + break; + case Type _ when type == typeof(sbyte): + case Type _ when type == typeof(short): + monoType = MTypes.I2; + break; + case Type _ when type == typeof(byte): + case Type _ when type == typeof(ushort): + monoType = MTypes.U2; + break; + case Type _ when type == typeof(int): + monoType = MTypes.I4; + break; + case Type _ when type == typeof(uint): + monoType = MTypes.U4; + break; + case Type _ when type == typeof(long): + monoType = MTypes.I8; + break; + case Type _ when type == typeof(ulong): + monoType = MTypes.U8; + break; + case Type _ when type == typeof(float): + monoType = MTypes.R4; + break; + case Type _ when type == typeof(double): + monoType = MTypes.R8; + break; + case Type _ when type == typeof(string): + monoType = MTypes.String; + break; + case Type _ when type == typeof(IntPtr): + monoType = MTypes.Ptr; + break; + case Type _ when type.IsEnum: + monoType = MTypes.Enum; + break; + case Type _ when type.IsArray: + case Type _ when type == typeof(ManagedArray): + monoType = MTypes.Array; + break; + case Type _ when type.IsValueType && !type.IsEnum && !type.IsPrimitive: + monoType = MTypes.ValueType; + break; + case Type _ when type.IsGenericType: + monoType = MTypes.GenericInst; + break; + case Type _ when type.IsClass: + monoType = MTypes.Object; + break; + default: throw new Exception($"Unsupported type '{type.FullName}'"); + } + return (uint)monoType; + } + } +} + +#endif diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 1ab25c3e9..b0686f2d1 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -10,1128 +10,13 @@ using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; -using System.IO; using System.Runtime.CompilerServices; using FlaxEngine.Assertions; -using FlaxEngine.Utilities; -using System.Runtime.InteropServices.Marshalling; -using System.Buffers; using System.Collections.Concurrent; using System.Text; -using System.Threading; - -#pragma warning disable 1591 namespace FlaxEngine { - #region Native structures - - [StructLayout(LayoutKind.Sequential)] - internal struct NativeClassDefinitions - { - internal ManagedHandle typeHandle; - internal IntPtr name; - internal IntPtr fullname; - internal IntPtr @namespace; - internal uint typeAttributes; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct NativeMethodDefinitions - { - internal IntPtr name; - internal int numParameters; - internal ManagedHandle typeHandle; - internal uint methodAttributes; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct NativeFieldDefinitions - { - internal IntPtr name; - internal ManagedHandle fieldHandle; - internal ManagedHandle fieldTypeHandle; - internal uint fieldAttributes; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct NativePropertyDefinitions - { - internal IntPtr name; - internal ManagedHandle getterHandle; - internal ManagedHandle setterHandle; - internal uint getterAttributes; - internal uint setterAttributes; - } - - [StructLayout(LayoutKind.Explicit)] - public struct VariantNative - { - [StructLayout(LayoutKind.Sequential)] - internal struct VariantNativeType - { - internal VariantUtils.VariantType types; - internal IntPtr TypeName; // char* - } - - [FieldOffset(0)] - VariantNativeType Type; - - [FieldOffset(8)] - byte AsBool; - - [FieldOffset(8)] - short AsInt16; - - [FieldOffset(8)] - ushort AsUint16; - - [FieldOffset(8)] - int AsInt; - - [FieldOffset(8)] - uint AsUint; - - [FieldOffset(8)] - long AsInt64; - - [FieldOffset(8)] - ulong AsUint64; - - [FieldOffset(8)] - float AsFloat; - - [FieldOffset(8)] - double AsDouble; - - [FieldOffset(8)] - IntPtr AsPointer; - - [FieldOffset(8)] - int AsData0; - - [FieldOffset(12)] - int AsData1; - - [FieldOffset(16)] - int AsData2; - - [FieldOffset(20)] - int AsData3; - - [FieldOffset(24)] - int AsData4; - - [FieldOffset(28)] - int AsData5; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct VersionNative - { - internal int _Major; - internal int _Minor; - internal int _Build; - internal int _Revision; - - internal VersionNative(Version ver) - { - _Major = ver.Major; - _Minor = ver.Minor; - _Build = ver.Build; - _Revision = ver.Revision; - } - - internal Version GetVersion() - { - return new Version(_Major, _Minor, _Build, _Revision); - } - } - - #endregion - - #region Wrappers - - /// - /// Wrapper for managed arrays which are passed to unmanaged code. - /// - public unsafe class ManagedArray - { - private ManagedHandle _pinnedArrayHandle; - private IntPtr _unmanagedData; - private Type _arrayType; - private Type _elementType; - private int _elementSize; - private int _length; - - public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType()); - - public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType); - - /// - /// Returns an instance of ManagedArray from shared pool. - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray WrapPooledArray(Array arr) - { - ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.WrapArray(arr, arr.GetType()); - return managedArray; - } - - /// - /// Returns an instance of ManagedArray from shared pool. - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray WrapPooledArray(Array arr, Type arrayType) - { - ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.WrapArray(arr, arrayType); - return managedArray; - } - - internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType) - => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, arrayType, elementType); - - internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType) - => new ManagedArray(ptr, length, arrayType, elementType); - - /// - /// Returns an instance of ManagedArray from shared pool. - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray AllocatePooledArray(T* ptr, int length) where T : unmanaged - { - ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.Allocate(ptr, length); - return managedArray; - } - - /// - /// Returns an instance of ManagedArray from shared pool. - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray AllocatePooledArray(int length) where T : unmanaged - { - ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.Allocate((T*)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length); - return managedArray; - } - - public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType); - - internal void WrapArray(Array arr, Type arrayType) - { - _pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); - _unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); - _length = arr.Length; - _arrayType = arrayType; - _elementType = arr.GetType().GetElementType(); - _elementSize = Marshal.SizeOf(_elementType); - } - - internal void Allocate(T* ptr, int length) where T : unmanaged - { - _unmanagedData = new IntPtr(ptr); - _length = length; - _arrayType = typeof(T).MakeArrayType(); - _elementType = typeof(T); - _elementSize = Unsafe.SizeOf(); - } - - private ManagedArray() - { - } - - private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType) - { - Assert.IsTrue(arrayType.IsArray); - _unmanagedData = ptr; - _length = length; - _arrayType = arrayType; - _elementType = elementType; - _elementSize = Marshal.SizeOf(elementType); - } - - ~ManagedArray() - { - if (_unmanagedData != IntPtr.Zero) - Free(); - } - - public void Free() - { - GC.SuppressFinalize(this); - if (_pinnedArrayHandle.IsAllocated) - { - _pinnedArrayHandle.Free(); - _unmanagedData = IntPtr.Zero; - } - if (_unmanagedData != IntPtr.Zero) - { - NativeInterop.NativeFree(_unmanagedData.ToPointer()); - _unmanagedData = IntPtr.Zero; - } - } - - public void FreePooled() - { - Free(); - ManagedArrayPool.Put(this); - } - - internal IntPtr Pointer => _unmanagedData; - - internal int Length => _length; - - internal int ElementSize => _elementSize; - - internal Type ElementType => _elementType; - - internal Type ArrayType => _arrayType; - - public Span ToSpan() where T : struct => new Span(_unmanagedData.ToPointer(), _length); - - public T[] ToArray() where T : struct => new Span(_unmanagedData.ToPointer(), _length).ToArray(); - - public Array ToArray() => ArrayCast.ToArray(new Span(_unmanagedData.ToPointer(), _length * _elementSize), _elementType); - - /// - /// Creates an Array of the specified type from span of bytes. - /// - private static class ArrayCast - { - delegate Array GetDelegate(Span span); - - [ThreadStatic] - private static Dictionary delegates; - - internal static Array ToArray(Span span, Type type) - { - if (delegates == null) - delegates = new(); - if (!delegates.TryGetValue(type, out var deleg)) - { - deleg = typeof(ArrayCastInternal<>).MakeGenericType(type).GetMethod(nameof(ArrayCastInternal.ToArray), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); - delegates.Add(type, deleg); - } - return deleg(span); - } - - private static class ArrayCastInternal where T : struct - { - internal static Array ToArray(Span span) - { - return MemoryMarshal.Cast(span).ToArray(); - } - } - } - - /// - /// Provides a pool of pre-allocated ManagedArray that can be re-used. - /// - private static class ManagedArrayPool - { - [ThreadStatic] - private static List> pool; - - internal static ManagedArray Get() - { - if (pool == null) - pool = new List>(); - for (int i = 0; i < pool.Count; i++) - { - if (!pool[i].Item1) - { - var tuple = pool[i]; - tuple.Item1 = true; - pool[i] = tuple; - return tuple.Item2; - } - } - - var newTuple = (true, new ManagedArray()); - pool.Add(newTuple); - return newTuple.Item2; - } - - internal static void Put(ManagedArray obj) - { - for (int i = 0; i < pool.Count; i++) - { - if (pool[i].Item2 == obj) - { - var tuple = pool[i]; - tuple.Item1 = false; - pool[i] = tuple; - return; - } - } - - throw new Exception("Tried to free non-pooled ManagedArray as pooled ManagedArray"); - } - } - } - - internal static class ManagedString - { - internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty); - - [System.Diagnostics.DebuggerStepThrough] - internal static unsafe IntPtr ToNative(string str) - { - if (str == null) - return IntPtr.Zero; - else if (str == string.Empty) - return ManagedHandle.ToIntPtr(EmptyStringHandle); - Assert.IsTrue(str.Length > 0); - return ManagedHandle.ToIntPtr(str); - } - - [System.Diagnostics.DebuggerStepThrough] - internal static unsafe IntPtr ToNativeWeak(string str) - { - if (str == null) - return IntPtr.Zero; - else if (str == string.Empty) - return ManagedHandle.ToIntPtr(EmptyStringHandle); - Assert.IsTrue(str.Length > 0); - return ManagedHandle.ToIntPtr(str, GCHandleType.Weak); - } - - [System.Diagnostics.DebuggerStepThrough] - internal static string ToManaged(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - return null; - return Unsafe.As(ManagedHandle.FromIntPtr(ptr).Target); - } - - [System.Diagnostics.DebuggerStepThrough] - internal static void Free(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - return; - ManagedHandle handle = ManagedHandle.FromIntPtr(ptr); - if (handle == EmptyStringHandle) - return; - handle.Free(); - } - } - - /// - /// Handle to managed objects which can be stored in native code. - /// - public struct ManagedHandle - { - private IntPtr handle; - - private ManagedHandle(IntPtr handle) - { - this.handle = handle; - } - - private ManagedHandle(object value, GCHandleType type) - { - handle = ManagedHandlePool.AllocateHandle(value, type); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type); - - public void Free() - { - if (handle != IntPtr.Zero) - { - ManagedHandlePool.FreeHandle(handle); - handle = IntPtr.Zero; - } - - ManagedHandlePool.TryCollectWeakHandles(); - } - - public object Target - { - get => ManagedHandlePool.GetObject(handle); - set => ManagedHandlePool.SetObject(handle, value); - } - - public bool IsAllocated => handle != IntPtr.Zero; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ManagedHandle FromIntPtr(IntPtr value) => new ManagedHandle(value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr ToIntPtr(object value, GCHandleType type) => ManagedHandlePool.AllocateHandle(value, type); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr ToIntPtr(ManagedHandle value) => value.handle; - - public override int GetHashCode() => handle.GetHashCode(); - - public override bool Equals(object o) => o is ManagedHandle other && Equals(other); - - public bool Equals(ManagedHandle other) => handle == other.handle; - - public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle; - - public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle; - - private static class ManagedHandlePool - { - private static ulong normalHandleAccumulator = 0; - private static ulong pinnedHandleAccumulator = 0; - private static ulong weakHandleAccumulator = 0; - - private static object poolLock = new object(); - private static Dictionary persistentPool = new Dictionary(); - private static Dictionary pinnedPool = new Dictionary(); - - private static Dictionary weakPool1 = new Dictionary(); - private static Dictionary weakPool2 = new Dictionary(); - private static Dictionary weakPool = weakPool1; - private static Dictionary weakPoolOther = weakPool2; - - private static int nextCollection = GC.CollectionCount(0) + 1; - - /// - /// Tries to free all references to old weak handles so GC can collect them. - /// - internal static void TryCollectWeakHandles() - { - if (GC.CollectionCount(0) < nextCollection) - return; - - lock (poolLock) - { - nextCollection = GC.CollectionCount(0) + 1; - - var swap = weakPoolOther; - weakPoolOther = weakPool; - weakPool = swap; - - weakPool.Clear(); - } - } - - private static IntPtr NewHandle(GCHandleType type) - { - IntPtr handle; - if (type == GCHandleType.Normal) - handle = (IntPtr)Interlocked.Increment(ref normalHandleAccumulator); - else if (type == GCHandleType.Pinned) - handle = (IntPtr)Interlocked.Increment(ref pinnedHandleAccumulator); - else //if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) - handle = (IntPtr)Interlocked.Increment(ref weakHandleAccumulator); - - // Two bits reserved for the type - handle |= (IntPtr)(((ulong)type << 62) & 0xC000000000000000); - return handle; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static GCHandleType GetHandleType(IntPtr handle) - { - return (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62); - } - - internal static IntPtr AllocateHandle(object value, GCHandleType type) - { - IntPtr handle = NewHandle(type); - lock (poolLock) - { - if (type == GCHandleType.Normal) - persistentPool.Add(handle, value); - else if (type == GCHandleType.Pinned) - pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned)); - else if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) - weakPool.Add(handle, value); - } - - return handle; - } - - internal static object GetObject(IntPtr handle) - { - object value; - GCHandleType type = GetHandleType(handle); - lock (poolLock) - { - if (type == GCHandleType.Normal && persistentPool.TryGetValue(handle, out value)) - return value; - else if (type == GCHandleType.Pinned && pinnedPool.TryGetValue(handle, out GCHandle gchandle)) - return gchandle.Target; - else if (weakPool.TryGetValue(handle, out value)) - return value; - else if (weakPoolOther.TryGetValue(handle, out value)) - return value; - } - - throw new Exception("Invalid ManagedHandle"); - } - - internal static void SetObject(IntPtr handle, object value) - { - GCHandleType type = GetHandleType(handle); - lock (poolLock) - { - if (type == GCHandleType.Normal && persistentPool.ContainsKey(handle)) - persistentPool[handle] = value; - else if (type == GCHandleType.Pinned && pinnedPool.TryGetValue(handle, out GCHandle gchandle)) - gchandle.Target = value; - else if (weakPool.ContainsKey(handle)) - weakPool[handle] = value; - else if (weakPoolOther.ContainsKey(handle)) - weakPoolOther[handle] = value; - else - throw new Exception("Invalid ManagedHandle"); - } - } - - internal static void FreeHandle(IntPtr handle) - { - GCHandleType type = GetHandleType(handle); - lock (poolLock) - { - if (type == GCHandleType.Normal && persistentPool.Remove(handle)) - return; - else if (type == GCHandleType.Pinned && pinnedPool.Remove(handle, out GCHandle gchandle)) - { - gchandle.Free(); - return; - } - else if (weakPool.Remove(handle)) - return; - else if (weakPoolOther.Remove(handle)) - return; - } - - throw new Exception("Invalid ManagedHandle"); - } - } - } - - #endregion - - #region Marshallers - - [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedHandleMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(ManagedHandleMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(object), MarshalMode.ElementOut, typeof(ManagedHandleMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedRef, typeof(ManagedHandleMarshaller.Bidirectional))] - [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedRef, typeof(ManagedHandleMarshaller.Bidirectional))] - [CustomMarshaller(typeof(object), MarshalMode.ElementRef, typeof(ManagedHandleMarshaller))] - public static class ManagedHandleMarshaller - { - public static class NativeToManaged - { - public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target; - - public static void Free(IntPtr unmanaged) - { - // This is a permanent handle, do not release it - } - } - - public static class ManagedToNative - { - public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; - - public static void Free(IntPtr unmanaged) - { - if (unmanaged == IntPtr.Zero) - return; - ManagedHandle.FromIntPtr(unmanaged).Free(); - } - } - - public struct Bidirectional - { - object managed; - IntPtr unmanaged; - - public void FromManaged(object managed) - { - this.managed = managed; - } - - public IntPtr ToUnmanaged() - { - unmanaged = ManagedHandleMarshaller.ToNative(managed); - return unmanaged; - } - - public void FromUnmanaged(IntPtr unmanaged) - { - this.unmanaged = unmanaged; - } - - public object ToManaged() - { - managed = ManagedHandleMarshaller.ToManaged(unmanaged); - unmanaged = IntPtr.Zero; - return managed; - } - - public void Free() - { - // FIXME, might be a permanent handle or a temporary one - throw new NotImplementedException(); - /*if (unmanaged == IntPtr.Zero) - return; - unmanaged.Free();*/ - } - } - - public static object ConvertToManaged(IntPtr unmanaged) => ToManaged(unmanaged); - public static IntPtr ConvertToUnmanaged(object managed) => ToNative(managed); - public static object ToManaged(IntPtr managed) => managed != IntPtr.Zero ? ManagedHandle.FromIntPtr(managed).Target : null; - public static IntPtr ToNative(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; - - public static void Free(IntPtr unmanaged) - { - if (unmanaged == IntPtr.Zero) - return; - ManagedHandle.FromIntPtr(unmanaged).Free(); - } - } - - [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] - public static class SystemTypeMarshaller - { - public static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); - - public static IntPtr ConvertToUnmanaged(Type managed) - { - if (managed == null) - return IntPtr.Zero; - ManagedHandle handle = NativeInterop.GetTypeGCHandle(managed); - return ManagedHandle.ToIntPtr(handle); - } - - public static void Free(IntPtr unmanaged) - { - // Cached handle, do not release - } - } - - [CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))] - public static class ExceptionMarshaller - { - public static Exception ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); - public static IntPtr ConvertToUnmanaged(Exception managed) => ManagedHandleMarshaller.ConvertToUnmanaged(managed); - public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged); - } - - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))] - public static class ObjectMarshaller - { - public static class NativeToManaged - { - public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target) : null; - } - - public static class ManagedToNative - { - public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; - } - } - - [CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))] - public static class CultureInfoMarshaller - { - public static CultureInfo ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); - public static IntPtr ConvertToUnmanaged(CultureInfo managed) => ManagedHandleMarshaller.ConvertToUnmanaged(managed); - public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged); - } - - [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedIn, typeof(SystemArrayMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedOut, typeof(SystemArrayMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedIn, typeof(SystemArrayMarshaller.NativeToManaged))] - public static unsafe class SystemArrayMarshaller - { - public struct ManagedToNative - { - ManagedArray managedArray; - ManagedHandle handle; - - public void FromManaged(Array managed) - { - if (managed != null) - managedArray = ManagedArray.WrapPooledArray(managed); - } - - public IntPtr ToUnmanaged() - { - if (managedArray == null) - return IntPtr.Zero; - handle = ManagedHandle.Alloc(managedArray); - return ManagedHandle.ToIntPtr(handle); - } - - public void Free() - { - if (managedArray == null) - return; - managedArray.FreePooled(); - handle.Free(); - } - } - - public struct NativeToManaged - { - ManagedHandle handle; - ManagedArray managedArray; - - public void FromUnmanaged(IntPtr unmanaged) - { - if (unmanaged == IntPtr.Zero) - return; - handle = ManagedHandle.FromIntPtr(unmanaged); - } - - public Array ToManaged() - { - if (!handle.IsAllocated) - return null; - managedArray = Unsafe.As(handle.Target); - return managedArray.ToArray(); - } - - public void Free() - { - if (!handle.IsAllocated) - return; - managedArray.Free(); - handle.Free(); - } - } - } - - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedOut, typeof(DictionaryMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedOut, typeof(DictionaryMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedIn, typeof(DictionaryMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementOut, typeof(DictionaryMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedRef, typeof(DictionaryMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedRef, typeof(DictionaryMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementRef, typeof(DictionaryMarshaller<,>))] - public static unsafe class DictionaryMarshaller - { - public static class NativeToManaged - { - public static Dictionary ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller.ToManaged(unmanaged); - public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged); - } - - public static class ManagedToNative - { - public static IntPtr ConvertToUnmanaged(Dictionary managed) => DictionaryMarshaller.ToNative(managed); - public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged); - } - - public struct Bidirectional - { - Dictionary managed; - IntPtr unmanaged; - - public void FromManaged(Dictionary managed) => this.managed = managed; - - public IntPtr ToUnmanaged() - { - unmanaged = DictionaryMarshaller.ToNative(managed); - return unmanaged; - } - - public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; - - public Dictionary ToManaged() - { - managed = DictionaryMarshaller.ToManaged(unmanaged); - unmanaged = IntPtr.Zero; - return managed; - } - - public void Free() => DictionaryMarshaller.Free(unmanaged); - } - - public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(ManagedHandle.FromIntPtr(unmanaged).Target) : null; - public static IntPtr ConvertToUnmanaged(Dictionary managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; - - public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(ManagedHandle.FromIntPtr(unmanaged).Target) : null; - public static IntPtr ToNative(Dictionary managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; - - public static void Free(IntPtr unmanaged) - { - if (unmanaged != IntPtr.Zero) - ManagedHandle.FromIntPtr(unmanaged).Free(); - } - } - - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(ArrayMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(ArrayMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(ArrayMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(ArrayMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(ArrayMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(ArrayMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(ArrayMarshaller<,>))] - [ContiguousCollectionMarshaller] - public static unsafe class ArrayMarshaller where TUnmanagedElement : unmanaged - { - public static class NativeToManaged - { - public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged is null) - return null; - return new T[numElements]; - } - - public static Span GetManagedValuesDestination(T[]? managed) => managed; - - public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged == null) - return ReadOnlySpan.Empty; - ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.ToSpan(); - } - - public static void Free(TUnmanagedElement* unmanaged) - { - if (unmanaged == null) - return; - ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); - (Unsafe.As(handle.Target)).Free(); - handle.Free(); - } - - public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged == null) - return Span.Empty; - ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.ToSpan(); - } - } - - public static class ManagedToNative - { - public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) - { - if (managed is null) - { - numElements = 0; - return null; - } - numElements = managed.Length; - ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length); - var ptr = ManagedHandle.ToIntPtr(managedArray); - return (TUnmanagedElement*)ptr; - } - - public static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; - - public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged == null) - return Span.Empty; - ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.ToSpan(); - } - - public static void Free(TUnmanagedElement* unmanaged) - { - if (unmanaged == null) - return; - ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); - (Unsafe.As(handle.Target)).FreePooled(); - handle.Free(); - } - } - - public struct Bidirectional - { - T[] managedArray; - ManagedArray unmanagedArray; - ManagedHandle handle; - - public void FromManaged(T[]? managed) - { - if (managed == null) - return; - managedArray = managed; - unmanagedArray = ManagedArray.AllocatePooledArray(managed.Length); - handle = ManagedHandle.Alloc(unmanagedArray); - } - - public ReadOnlySpan GetManagedValuesSource() => managedArray; - - public Span GetUnmanagedValuesDestination() - { - if (unmanagedArray == null) - return Span.Empty; - return unmanagedArray.ToSpan(); - } - - public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle); - - public void FromUnmanaged(TUnmanagedElement* unmanaged) - { - ManagedArray arr = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - if (managedArray == null || managedArray.Length != arr.Length) - managedArray = new T[arr.Length]; - } - - public ReadOnlySpan GetUnmanagedValuesSource(int numElements) - { - if (unmanagedArray == null) - return ReadOnlySpan.Empty; - return unmanagedArray.ToSpan(); - } - - public Span GetManagedValuesDestination(int numElements) => managedArray; - - public T[] ToManaged() => managedArray; - - public void Free() - { - unmanagedArray.FreePooled(); - handle.Free(); - } - } - - public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) - { - if (managed is null) - { - numElements = 0; - return null; - } - numElements = managed.Length; - ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length); - IntPtr handle = ManagedHandle.ToIntPtr(managedArray); - return (TUnmanagedElement*)handle; - } - - public static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; - - public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged == null) - return Span.Empty; - ManagedArray unmanagedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return unmanagedArray.ToSpan(); - } - - public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements]; - - public static Span GetManagedValuesDestination(T[]? managed) => managed; - - public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged == null) - return ReadOnlySpan.Empty; - ManagedArray array = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return array.ToSpan(); - } - - public static void Free(TUnmanagedElement* unmanaged) - { - if (unmanaged == null) - return; - ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); - Unsafe.As(handle.Target).FreePooled(); - handle.Free(); - } - } - - [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringMarshaller.Bidirectional))] - [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringMarshaller.Bidirectional))] - [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshaller))] - public static class StringMarshaller - { - public static class NativeToManaged - { - public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); - } - - public static class ManagedToNative - { - public static unsafe IntPtr ConvertToUnmanaged(string managed) - { - return managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed); - } - - public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); - } - - public struct Bidirectional - { - string managed; - IntPtr unmanaged; - - public void FromManaged(string managed) => this.managed = managed; - - public IntPtr ToUnmanaged() - { - unmanaged = ManagedString.ToNative(managed); - return unmanaged; - } - - public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; - - public string ToManaged() - { - managed = ManagedString.ToManaged(unmanaged); - unmanaged = IntPtr.Zero; - return managed; - } - - public void Free() => ManagedString.Free(unmanaged); - } - - public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed); - public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); - - public static string ToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static IntPtr ToNative(string managed) => ManagedString.ToNative(managed); - } - - #endregion - /// /// Provides a Mono-like API for native code to access managed runtime. /// @@ -1199,32 +84,6 @@ namespace FlaxEngine { } - [UnmanagedCallersOnly] - internal static void RegisterNativeLibrary(IntPtr moduleName_, IntPtr modulePath_) - { - string moduleName = Marshal.PtrToStringAnsi(moduleName_); - string modulePath = Marshal.PtrToStringAnsi(modulePath_); - nativeLibraryPaths[moduleName] = modulePath; - } - - [UnmanagedCallersOnly] - internal static void* AllocMemory(int size, bool coTaskMem) - { - if (coTaskMem) - return Marshal.AllocCoTaskMem(size).ToPointer(); - else - return NativeMemory.AlignedAlloc((UIntPtr)size, 16); - } - - [UnmanagedCallersOnly] - internal static void FreeMemory(void* ptr, bool coTaskMem) - { - if (coTaskMem) - Marshal.FreeCoTaskMem(new IntPtr(ptr)); - else - NativeMemory.AlignedFree(ptr); - } - internal static void* NativeAlloc(int byteCount) { return NativeMemory.AlignedAlloc((UIntPtr)byteCount, 16); @@ -1998,76 +857,6 @@ namespace FlaxEngine return handle; } - [UnmanagedCallersOnly] - internal static unsafe void GetManagedClasses(ManagedHandle assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount) - { - Assembly assembly = Unsafe.As(assemblyHandle.Target); - var assemblyTypes = GetAssemblyTypes(assembly); - - NativeClassDefinitions* arr = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf()); - - for (int i = 0; i < assemblyTypes.Length; i++) - { - var type = assemblyTypes[i]; - IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); - var managedClass = new NativeClassDefinitions - { - typeHandle = GetTypeGCHandle(type), - name = NativeAllocStringAnsi(type.Name), - fullname = NativeAllocStringAnsi(type.GetTypeName()), - @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), - typeAttributes = (uint)type.Attributes, - }; - Unsafe.Write(ptr.ToPointer(), managedClass); - } - - *managedClasses = arr; - *managedClassCount = assemblyTypes.Length; - } - - [UnmanagedCallersOnly] - internal static unsafe void GetManagedClassFromType(ManagedHandle typeHandle, NativeClassDefinitions* managedClass, ManagedHandle* assemblyHandle) - { - Type type = Unsafe.As(typeHandle.Target); - *managedClass = new NativeClassDefinitions - { - typeHandle = GetTypeGCHandle(type), - name = NativeAllocStringAnsi(type.Name), - fullname = NativeAllocStringAnsi(type.GetTypeName()), - @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), - typeAttributes = (uint)type.Attributes, - }; - *assemblyHandle = GetAssemblyHandle(type.Assembly); - } - - [UnmanagedCallersOnly] - internal static void GetClassMethods(ManagedHandle typeHandle, NativeMethodDefinitions** classMethods, int* classMethodsCount) - { - Type type = Unsafe.As(typeHandle.Target); - - var methods = new List(); - var staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); - var instanceMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); - methods.AddRange(staticMethods); - methods.AddRange(instanceMethods); - - var arr = (NativeMethodDefinitions*)NativeAlloc(methods.Count, Unsafe.SizeOf()); - for (int i = 0; i < methods.Count; i++) - { - IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); - var classMethod = new NativeMethodDefinitions - { - name = NativeAllocStringAnsi(methods[i].Name), - numParameters = methods[i].GetParameters().Length, - methodAttributes = (uint)methods[i].Attributes, - }; - classMethod.typeHandle = GetMethodGCHandle(methods[i]); - Unsafe.Write(ptr.ToPointer(), classMethod); - } - *classMethods = arr; - *classMethodsCount = methods.Count; - } - internal class FieldHolder { internal FieldInfo field; @@ -2080,225 +869,6 @@ namespace FlaxEngine } } - [UnmanagedCallersOnly] - internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount) - { - Type type = Unsafe.As(typeHandle.Target); - var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf()); - for (int i = 0; i < fields.Length; i++) - { - FieldHolder fieldHolder = new FieldHolder(fields[i], type); - - ManagedHandle fieldHandle = ManagedHandle.Alloc(fieldHolder); - if (type.IsCollectible) - fieldHandleCacheCollectible.Add(fieldHandle); - else - fieldHandleCache.Add(fieldHandle); - - NativeFieldDefinitions classField = new NativeFieldDefinitions() - { - name = NativeAllocStringAnsi(fieldHolder.field.Name), - fieldHandle = fieldHandle, - fieldTypeHandle = GetTypeGCHandle(fieldHolder.field.FieldType), - fieldAttributes = (uint)fieldHolder.field.Attributes, - }; - Unsafe.Write(IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i).ToPointer(), classField); - } - *classFields = arr; - *classFieldsCount = fields.Length; - } - - [UnmanagedCallersOnly] - internal static void GetClassProperties(ManagedHandle typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount) - { - Type type = Unsafe.As(typeHandle.Target); - var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf()); - for (int i = 0; i < properties.Length; i++) - { - IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); - - var getterMethod = properties[i].GetGetMethod(true); - var setterMethod = properties[i].GetSetMethod(true); - - var classProperty = new NativePropertyDefinitions - { - name = NativeAllocStringAnsi(properties[i].Name), - }; - if (getterMethod != null) - { - classProperty.getterHandle = GetMethodGCHandle(getterMethod); - classProperty.getterAttributes = (uint)getterMethod.Attributes; - } - if (setterMethod != null) - { - classProperty.setterHandle = GetMethodGCHandle(setterMethod); - classProperty.setterAttributes = (uint)setterMethod.Attributes; - } - Unsafe.Write(ptr.ToPointer(), classProperty); - } - *classProperties = arr; - *classPropertiesCount = properties.Length; - } - - [UnmanagedCallersOnly] - internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount) - { - Type type = Unsafe.As(typeHandle.Target); - object[] attributeValues = type.GetCustomAttributes(false); - - ManagedHandle* arr = (ManagedHandle*)NativeAlloc(attributeValues.Length, Unsafe.SizeOf()); - for (int i = 0; i < attributeValues.Length; i++) - { - if (!classAttributesCacheCollectible.TryGetValue(attributeValues[i], out ManagedHandle attributeHandle)) - { - attributeHandle = ManagedHandle.Alloc(attributeValues[i]); - classAttributesCacheCollectible.Add(attributeValues[i], attributeHandle); - } - arr[i] = attributeHandle; - } - *classAttributes = arr; - *classAttributesCount = attributeValues.Length; - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetCustomAttribute(ManagedHandle typeHandle, ManagedHandle attributeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - var attributes = type.GetCustomAttributes(false); - object attrib; - if (attributeHandle.IsAllocated) - { - // Check for certain attribute type - Type attributeType = Unsafe.As(attributeHandle.Target); - attrib = attributes.FirstOrDefault(x => x.GetType() == attributeType); - } - else - { - // Check if has any attribute - attrib = attributes.FirstOrDefault(); - } - if (attrib != null) - return ManagedHandle.Alloc(attrib, GCHandleType.Weak); - return new ManagedHandle(); - } - - [UnmanagedCallersOnly] - internal static void GetClassInterfaces(ManagedHandle typeHandle, IntPtr* classInterfaces, int* classInterfacesCount) - { - Type type = Unsafe.As(typeHandle.Target); - Type[] interfaces = type.GetInterfaces(); - - // Match mono_class_get_interfaces which doesn't return interfaces from base class - var baseTypeInterfaces = type.BaseType?.GetInterfaces(); - if (baseTypeInterfaces != null) - { - if (baseTypeInterfaces.Length == interfaces.Length) - { - // Both base and this type have the same amount of interfaces so assume that this one has none unique - interfaces = Array.Empty(); - } - else if (baseTypeInterfaces.Length != 0) - { - // Count unique interface types - int uniqueCount = interfaces.Length; - for (int i = 0; i < interfaces.Length; i++) - { - for (int j = 0; j < baseTypeInterfaces.Length; j++) - { - if (interfaces[i] == baseTypeInterfaces[j]) - { - uniqueCount--; - break; - } - } - } - - // Get only unique interface types - var unique = new Type[uniqueCount]; - uniqueCount = 0; - for (int i = 0; i < interfaces.Length; i++) - { - bool isUnique = true; - for (int j = 0; j < baseTypeInterfaces.Length; j++) - { - if (interfaces[i] == baseTypeInterfaces[j]) - { - isUnique = false; - break; - } - } - if (isUnique) - unique[uniqueCount++] = interfaces[i]; - } - interfaces = unique; - } - } - - IntPtr arr = (IntPtr)NativeAlloc(interfaces.Length, IntPtr.Size); - for (int i = 0; i < interfaces.Length; i++) - { - ManagedHandle handle = GetTypeGCHandle(interfaces[i]); - Unsafe.Write(IntPtr.Add(arr, IntPtr.Size * i).ToPointer(), handle); - } - *classInterfaces = arr; - *classInterfacesCount = interfaces.Length; - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetMethodReturnType(ManagedHandle methodHandle) - { - MethodHolder methodHolder = Unsafe.As(methodHandle.Target); - Type returnType = methodHolder.returnType; - - return GetTypeGCHandle(returnType); - } - - [UnmanagedCallersOnly] - internal static void GetMethodParameterTypes(ManagedHandle methodHandle, IntPtr* typeHandles) - { - MethodHolder methodHolder = Unsafe.As(methodHandle.Target); - Type returnType = methodHolder.returnType; - IntPtr arr = (IntPtr)NativeAlloc(methodHolder.parameterTypes.Length, IntPtr.Size); - for (int i = 0; i < methodHolder.parameterTypes.Length; i++) - { - ManagedHandle typeHandle = GetTypeGCHandle(methodHolder.parameterTypes[i]); - Unsafe.Write(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i).ToPointer(), typeHandle); - } - *typeHandles = arr; - } - - /// - /// Returns pointer to the string's internal structure, containing the buffer and length of the string. - /// - [UnmanagedCallersOnly] - internal static IntPtr GetStringPointer(ManagedHandle stringHandle) - { - string str = Unsafe.As(stringHandle.Target); - IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2); - if (ptr < 0x1024) - throw new Exception("null string ptr"); - return ptr; - } - - [UnmanagedCallersOnly] - internal static ManagedHandle NewObject(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - if (type.IsAbstract) - { - // Dotnet doesn't allow to instantiate abstract type thus allow to use generated mock class usage (eg. for Script or GPUResource) for generated abstract types - var abstractWrapper = type.GetNestedType("AbstractWrapper", BindingFlags.NonPublic); - if (abstractWrapper != null) - type = abstractWrapper; - } - object value = RuntimeHelpers.GetUninitializedObject(type); - return ManagedHandle.Alloc(value); - } - internal static class ArrayFactory { private delegate Array CreateArrayDelegate(long size); @@ -2344,92 +914,6 @@ namespace FlaxEngine } } - [UnmanagedCallersOnly] - internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size) - { - Type elementType = Unsafe.As(typeHandle.Target); - Type marshalledType = ArrayFactory.GetMarshalledType(elementType); - Type arrayType = elementType.MakeArrayType(); - if (marshalledType.IsValueType) - { - ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType); - return ManagedHandle.Alloc(managedArray); - } - else - { - Array arr = ArrayFactory.CreateArray(elementType, size); - ManagedArray managedArray = ManagedArray.WrapNewArray(arr, arrayType); - return ManagedHandle.Alloc(managedArray); - } - } - - [UnmanagedCallersOnly] - internal static unsafe ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) - { - Type elementType = Unsafe.As(elementTypeHandle.Target); - Type classType = elementType.MakeArrayType(); - return GetTypeGCHandle(classType); - } - - [UnmanagedCallersOnly] - internal static unsafe IntPtr GetArrayPointer(ManagedHandle arrayHandle) - { - if (!arrayHandle.IsAllocated) - return IntPtr.Zero; - ManagedArray managedArray = Unsafe.As(arrayHandle.Target); - if (managedArray.Length == 0) - return IntPtr.Zero; - return managedArray.Pointer; - } - - [UnmanagedCallersOnly] - internal static int GetArrayLength(ManagedHandle arrayHandle) - { - if (!arrayHandle.IsAllocated) - return 0; - ManagedArray managedArray = Unsafe.As(arrayHandle.Target); - return managedArray.Length; - } - - [UnmanagedCallersOnly] - internal static IntPtr GetStringEmpty() - { - return ManagedHandle.ToIntPtr(ManagedString.EmptyStringHandle); - } - - [UnmanagedCallersOnly] - internal static IntPtr NewStringUTF16(char* text, int length) - { - return ManagedString.ToNativeWeak(new string(new ReadOnlySpan(text, length))); - } - - [UnmanagedCallersOnly] - internal static IntPtr NewStringLength(sbyte* text, int length) - { - return ManagedString.ToNativeWeak(new string(text, 0, length)); - } - - /// - /// Creates a managed copy of the value, and stores it in a boxed reference. - /// - [UnmanagedCallersOnly] - internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr) - { - Type type = Unsafe.As(typeHandle.Target); - object value = MarshalToManaged(valuePtr, type); - return ManagedHandle.Alloc(value, GCHandleType.Weak); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetObjectType(ManagedHandle handle) - { - object obj = handle.Target; - Type classType = obj.GetType(); - if (classType == typeof(ManagedArray)) - classType = ((ManagedArray)obj).ArrayType; - return GetTypeGCHandle(classType); - } - internal static class ValueTypeUnboxer { private static ConcurrentDictionary unboxers = new ConcurrentDictionary(1, 3); @@ -2456,538 +940,8 @@ namespace FlaxEngine } } - /// - /// Returns the address of the boxed value type. - /// - [UnmanagedCallersOnly] - internal static unsafe IntPtr UnboxValue(ManagedHandle handle) - { - object value = handle.Target; - Type type = value.GetType(); - if (!type.IsValueType) - return ManagedHandle.ToIntPtr(handle); - - // HACK: Get the address of a non-pinned value - return ValueTypeUnboxer.GetPointer(value); - } - - [UnmanagedCallersOnly] - internal static void RaiseException(ManagedHandle exceptionHandle) - { - Exception exception = Unsafe.As(exceptionHandle.Target); - throw exception; - } - - [UnmanagedCallersOnly] - internal static void ObjectInit(ManagedHandle objectHandle) - { - object obj = objectHandle.Target; - Type type = obj.GetType(); - ConstructorInfo ctor = type.GetMember(".ctor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).First() as ConstructorInfo; - ctor.Invoke(obj, null); - } - - [UnmanagedCallersOnly] - internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) - { - MethodHolder methodHolder = Unsafe.As(methodHandle.Target); - if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext)) - { - // Fast path, invoke the method with minimal allocations - IntPtr returnValue; - try - { - returnValue = methodDelegate(methodDelegateContext, instanceHandle, paramPtr); - } - catch (Exception exception) - { - if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); - return IntPtr.Zero; - } - return returnValue; - } - else - { - // Slow path, method parameters needs to be stored in heap - object returnObject; - int numParams = methodHolder.parameterTypes.Length; - object[] methodParameters = new object[numParams]; - - for (int i = 0; i < numParams; i++) - { - IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); - methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]); - } - - try - { - returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters); - } - catch (Exception exception) - { - // The internal exception thrown in MethodInfo.Invoke is caught here - Exception realException = exception; - if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker") - realException = exception.InnerException; - - if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(realException, GCHandleType.Weak)); - else - throw realException; - return IntPtr.Zero; - } - - // Marshal reference parameters back to original unmanaged references - for (int i = 0; i < numParams; i++) - { - Type parameterType = methodHolder.parameterTypes[i]; - if (parameterType.IsByRef) - { - IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); - MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType()); - } - } - - // Return value - return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject); - } - } - private delegate IntPtr InvokeThunkDelegate(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7); - [UnmanagedCallersOnly] - internal static IntPtr GetMethodUnmanagedFunctionPointer(ManagedHandle methodHandle) - { - MethodHolder methodHolder = Unsafe.As(methodHandle.Target); - - // Wrap the method call, this is needed to get the object instance from ManagedHandle and to pass the exception back to native side - ThunkContext context = new ThunkContext((MethodInfo)methodHolder.method); - Delegate methodDelegate = typeof(ThunkContext).GetMethod(nameof(ThunkContext.InvokeThunk)).CreateDelegate(context); - IntPtr functionPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); - - // Keep a reference to the delegate to prevent it from being garbage collected - if (methodHolder.method.IsCollectible) - cachedDelegatesCollectible[functionPtr] = methodDelegate; - else - cachedDelegates[functionPtr] = methodDelegate; - - return functionPtr; - } - - [UnmanagedCallersOnly] - internal static void FieldSetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) - { - object fieldOwner = fieldOwnerHandle.Target; - FieldHolder field = Unsafe.As(fieldHandle.Target); - object value = null; - if (field.field.FieldType.IsValueType) - value = Marshal.PtrToStructure(valuePtr, field.field.FieldType); - else if (valuePtr != IntPtr.Zero) - value = ManagedHandle.FromIntPtr(valuePtr).Target; - field.field.SetValue(fieldOwner, value); - } - - [UnmanagedCallersOnly] - internal static void FieldGetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) - { - object fieldOwner = fieldOwnerHandle.Target; - FieldHolder field = Unsafe.As(fieldHandle.Target); - field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset); - } - - [UnmanagedCallersOnly] - internal static void SetArrayValueReference(ManagedHandle arrayHandle, IntPtr valueHandle, int index) - { - ManagedArray managedArray = Unsafe.As(arrayHandle.Target); - managedArray.ToSpan()[index] = valueHandle; - } - - [UnmanagedCallersOnly] - internal static ManagedHandle LoadAssemblyImage(IntPtr data, int len, IntPtr assemblyPath_, IntPtr* assemblyName, IntPtr* assemblyFullName) - { - if (!firstAssemblyLoaded) - { - // This assembly was already loaded when initializing the host context - firstAssemblyLoaded = true; - - Assembly flaxEngineAssembly = AssemblyLoadContext.Default.Assemblies.First(x => x.GetName().Name == "FlaxEngine.CSharp"); - *assemblyName = NativeAllocStringAnsi(flaxEngineAssembly.GetName().Name); - *assemblyFullName = NativeAllocStringAnsi(flaxEngineAssembly.FullName); - return GetAssemblyHandle(flaxEngineAssembly); - } - - string assemblyPath = Marshal.PtrToStringAnsi(assemblyPath_); - - byte[] raw = new byte[len]; - Marshal.Copy(data, raw, 0, len); - - using MemoryStream stream = new MemoryStream(raw); - Assembly assembly; -#if !BUILD_RELEASE - var pdbPath = Path.ChangeExtension(assemblyPath, "pdb"); - if (File.Exists(pdbPath)) - { - using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open); - assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream); - } - else -#endif - { - assembly = scriptingAssemblyLoadContext.LoadFromStream(stream); - } - if (assembly == null) - return new ManagedHandle(); - NativeLibrary.SetDllImportResolver(assembly, NativeLibraryImportResolver); - - // Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822 - AssemblyLocations.Add(assembly.FullName, assemblyPath); - - *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); - *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); - return GetAssemblyHandle(assembly); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetAssemblyByName(IntPtr name_, IntPtr* assemblyName, IntPtr* assemblyFullName) - { - string name = Marshal.PtrToStringAnsi(name_); - Assembly assembly = Utils.GetAssemblies().FirstOrDefault(x => x.GetName().Name == name); - if (assembly == null) - return new ManagedHandle(); - - *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); - *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); - return GetAssemblyHandle(assembly); - } - - [UnmanagedCallersOnly] - internal static IntPtr GetRuntimeInformation() - { - return NativeAllocStringAnsi(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); - } - - [UnmanagedCallersOnly] - internal static void CloseAssembly(ManagedHandle assemblyHandle) - { - Assembly assembly = Unsafe.As(assemblyHandle.Target); - assemblyHandle.Free(); - assemblyHandles.Remove(assembly); - - AssemblyLocations.Remove(assembly.FullName); - - // Clear all caches which might hold references to closing assembly - cachedDelegatesCollectible.Clear(); - typeCache.Clear(); - - // Release all GCHandles in collectible ALC - foreach (var pair in typeHandleCacheCollectible) - pair.Value.Free(); - typeHandleCacheCollectible.Clear(); - - foreach (var handle in methodHandlesCollectible) - handle.Free(); - methodHandlesCollectible.Clear(); - - foreach (var handle in fieldHandleCacheCollectible) - handle.Free(); - fieldHandleCacheCollectible.Clear(); - - foreach (var pair in classAttributesCacheCollectible) - pair.Value.Free(); - classAttributesCacheCollectible.Clear(); - - // Unload native library handles associated for this assembly - string nativeLibraryName = assemblyOwnedNativeLibraries.GetValueOrDefault(assembly); - if (nativeLibraryName != null && loadedNativeLibraries.TryGetValue(nativeLibraryName, out IntPtr nativeLibrary)) - { - NativeLibrary.Free(nativeLibrary); - loadedNativeLibraries.Remove(nativeLibraryName); - } - if (nativeLibraryName != null) - nativeLibraryPaths.Remove(nativeLibraryName); - - // Unload the ALC - bool unloading = true; - scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; }; - scriptingAssemblyLoadContext.Unload(); - - while (unloading) - System.Threading.Thread.Sleep(1); - - // TODO: benchmark collectible setting performance, maybe enable it only in editor builds? - scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", true); - DelegateHelpers.InitMethods(); - } - - [UnmanagedCallersOnly] - internal static unsafe int NativeSizeOf(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - Type nativeType = GetInternalType(type) ?? type; - if (nativeType == typeof(Version)) - nativeType = typeof(VersionNative); - int size; - if (nativeType.IsClass) - size = sizeof(IntPtr); - else - size = Marshal.SizeOf(nativeType); - return size; - } - - [UnmanagedCallersOnly] - internal static byte TypeIsSubclassOf(ManagedHandle typeHandle, ManagedHandle otherTypeHandle, byte checkInterfaces) - { - if (typeHandle == otherTypeHandle) - return 1; - - Type type = Unsafe.As(typeHandle.Target); - Type otherType = Unsafe.As(otherTypeHandle.Target); - - if (type == otherType) - return 1; - - if (checkInterfaces != 0 && otherType.IsInterface) - { - if (type.GetInterfaces().Contains(otherType)) - return 1; - } - - return type.IsSubclassOf(otherType) ? (byte)1 : (byte)0; - } - - [UnmanagedCallersOnly] - internal static byte TypeIsAssignableFrom(ManagedHandle typeHandle, ManagedHandle otherTypeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - Type otherType = Unsafe.As(otherTypeHandle.Target); - return (byte)(type.IsAssignableFrom(otherType) ? 1 : 0); - } - - [UnmanagedCallersOnly] - internal static byte TypeIsValueType(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - return (byte)(type.IsValueType ? 1 : 0); - } - - [UnmanagedCallersOnly] - internal static byte TypeIsEnum(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - return (byte)(type.IsEnum ? 1 : 0); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetClassParent(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - return GetTypeGCHandle(type.BaseType); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetElementClass(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - return GetTypeGCHandle(type.GetElementType()); - } - - [UnmanagedCallersOnly] - internal static byte GetMethodParameterIsOut(ManagedHandle methodHandle, int parameterNum) - { - MethodHolder methodHolder = Unsafe.As(methodHandle.Target); - ParameterInfo parameterInfo = methodHolder.method.GetParameters()[parameterNum]; - return (byte)(parameterInfo.IsOut ? 1 : 0); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetNullReferenceException() - { - var exception = new NullReferenceException(); - return ManagedHandle.Alloc(exception, GCHandleType.Weak); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetNotSupportedException() - { - var exception = new NotSupportedException(); - return ManagedHandle.Alloc(exception, GCHandleType.Weak); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetArgumentNullException() - { - var exception = new ArgumentNullException(); - return ManagedHandle.Alloc(exception, GCHandleType.Weak); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetArgumentException() - { - var exception = new ArgumentException(); - return ManagedHandle.Alloc(exception, GCHandleType.Weak); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetArgumentOutOfRangeException() - { - var exception = new ArgumentOutOfRangeException(); - return ManagedHandle.Alloc(exception, GCHandleType.Weak); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle NewGCHandle(ManagedHandle valueHandle, byte pinned) - { - object value = valueHandle.Target; - ManagedHandle handle = ManagedHandle.Alloc(value, pinned != 0 ? GCHandleType.Pinned : GCHandleType.Normal); - return handle; - } - - [UnmanagedCallersOnly] - internal static ManagedHandle NewGCHandleWeak(ManagedHandle valueHandle, byte trackResurrection) - { - object value = valueHandle.Target; - ManagedHandle handle = ManagedHandle.Alloc(value, trackResurrection != 0 ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); - return handle; - } - - [UnmanagedCallersOnly] - internal static void FreeGCHandle(ManagedHandle valueHandle) - { - valueHandle.Free(); - } - - [UnmanagedCallersOnly] - internal static ManagedHandle GetTypeClass(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - if (type.IsByRef) - type = type.GetElementType(); // Drop reference type (&) to get actual value type - return GetTypeGCHandle(type); - } - - [UnmanagedCallersOnly] - internal static bool GetTypeIsPointer(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - return type.IsPointer; - } - - [UnmanagedCallersOnly] - internal static bool GetTypeIsReference(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - return type.IsByRef; - } - - internal enum MTypes : uint - { - End = 0x00, - Void = 0x01, - Boolean = 0x02, - Char = 0x03, - I1 = 0x04, - U1 = 0x05, - I2 = 0x06, - U2 = 0x07, - I4 = 0x08, - U4 = 0x09, - I8 = 0x0a, - U8 = 0x0b, - R4 = 0x0c, - R8 = 0x0d, - String = 0x0e, - Ptr = 0x0f, - ByRef = 0x10, - ValueType = 0x11, - Class = 0x12, - Var = 0x13, - Array = 0x14, - GenericInst = 0x15, - TypeByRef = 0x16, - I = 0x18, - U = 0x19, - Fnptr = 0x1b, - Object = 0x1c, - SzArray = 0x1d, - MVar = 0x1e, - CmodReqd = 0x1f, - CmodOpt = 0x20, - Internal = 0x21, - Modifier = 0x40, - Sentinel = 0x41, - Pinned = 0x45, - Enum = 0x55, - }; - - [UnmanagedCallersOnly] - internal static uint GetTypeMTypesEnum(ManagedHandle typeHandle) - { - Type type = Unsafe.As(typeHandle.Target); - if (type.IsByRef) - type = type.GetElementType(); // Drop reference type (&) to get actual value type - MTypes monoType; - switch (type) - { - case Type _ when type == typeof(void): - monoType = MTypes.Void; - break; - case Type _ when type == typeof(bool): - monoType = MTypes.Boolean; - break; - case Type _ when type == typeof(sbyte): - case Type _ when type == typeof(short): - monoType = MTypes.I2; - break; - case Type _ when type == typeof(byte): - case Type _ when type == typeof(ushort): - monoType = MTypes.U2; - break; - case Type _ when type == typeof(int): - monoType = MTypes.I4; - break; - case Type _ when type == typeof(uint): - monoType = MTypes.U4; - break; - case Type _ when type == typeof(long): - monoType = MTypes.I8; - break; - case Type _ when type == typeof(ulong): - monoType = MTypes.U8; - break; - case Type _ when type == typeof(float): - monoType = MTypes.R4; - break; - case Type _ when type == typeof(double): - monoType = MTypes.R8; - break; - case Type _ when type == typeof(string): - monoType = MTypes.String; - break; - case Type _ when type == typeof(IntPtr): - monoType = MTypes.Ptr; - break; - case Type _ when type.IsEnum: - monoType = MTypes.Enum; - break; - case Type _ when type.IsArray: - case Type _ when type == typeof(ManagedArray): - monoType = MTypes.Array; - break; - case Type _ when type.IsValueType && !type.IsEnum && !type.IsPrimitive: - monoType = MTypes.ValueType; - break; - case Type _ when type.IsGenericType: - monoType = MTypes.GenericInst; - break; - case Type _ when type.IsClass: - monoType = MTypes.Object; - break; - default: throw new Exception($"Unsupported type '{type.FullName}'"); - } - return (uint)monoType; - } - /// /// Returns all types that that owned by this assembly. /// @@ -3041,16 +995,16 @@ namespace FlaxEngine internal static void InitMethods() { MakeNewCustomDelegateFunc = - typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") - .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate>(); + typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") + .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate>(); // Load System.Linq.Expressions assembly to collectible ALC. // The dynamic assembly where delegates are stored is cached in the DelegateHelpers class, so we should // use the DelegateHelpers in collectible ALC to make sure the delegates are also stored in the same ALC. Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(typeof(Expression).Assembly.Location); MakeNewCustomDelegateFuncCollectible = - assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") - .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate>(); + assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") + .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate>(); // Create dummy delegates to force the dynamic Snippets assembly to be loaded in correcet ALCs MakeNewCustomDelegateFunc(new[] { typeof(void) }); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 85264cb09..edfdbf3b3 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1451,7 +1451,7 @@ namespace Flax.Build.Bindings else if (type == "bool") type = "byte"; else if (type == "object") - type = "VariantNative"; + type = "NativeVariant"; else if (internalType) { internalTypeMarshaler = type + "Marshaller"; @@ -1468,8 +1468,8 @@ namespace Flax.Build.Bindings if (fieldInfo.NoArray && fieldInfo.Type.IsArray) continue; - if (type == "VariantNative") - continue; // FIXME + if (type == "NativeVariant") + continue; // TODO: FIXME if (useSeparator) {