#if USE_NETCORE using System; using System.Collections.Generic; using System.Globalization; using System.Linq; 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 FlaxEngine.Visject; using System.Diagnostics; using System.Collections; using System.Buffers; using System.Collections.Concurrent; [assembly: DisableRuntimeMarshalling] namespace FlaxEngine { #region Native structures [StructLayout(LayoutKind.Sequential)] internal struct NativeClassDefinitions { internal IntPtr 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 IntPtr typeHandle; internal uint methodAttributes; } [StructLayout(LayoutKind.Sequential)] internal struct NativeFieldDefinitions { internal IntPtr name; internal IntPtr fieldHandle; internal IntPtr fieldTypeHandle; internal uint fieldAttributes; } [StructLayout(LayoutKind.Sequential)] internal struct NativePropertyDefinitions { internal IntPtr name; internal IntPtr getterHandle; internal IntPtr setterHandle; internal uint getterFlags; internal uint setterFlags; } [StructLayout(LayoutKind.Sequential)] internal struct NativeAttributeDefinitions { internal IntPtr name; internal IntPtr attributeHandle; internal IntPtr attributeTypeHandle; } [StructLayout(LayoutKind.Explicit)] internal 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. /// internal unsafe class ManagedArray { private GCHandle pinnedArrayHandle; private IntPtr unmanagedData; private int elementSize; private int length; internal static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr); /// /// Returns an instance of ManagedArray from shared pool. /// /// /// The resources must be released by calling FreePooled() instead of Free()-method. /// internal static ManagedArray WrapPooledArray(Array arr) { ManagedArray managedArray = ManagedArrayPool.Get(); managedArray.WrapArray(arr); return managedArray; } internal static ManagedArray AllocateNewArray(T* ptr, int length) where T : unmanaged => new ManagedArray(ptr, length, Unsafe.SizeOf()); internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, int elementSize) => new ManagedArray(ptr.ToPointer(), length, elementSize); /// /// Returns an instance of ManagedArray from shared pool. /// /// /// The resources must be released by calling FreePooled() instead of Free()-method. /// internal static ManagedArray AllocatePooledArray(T* ptr, int length) where T : unmanaged { ManagedArray managedArray = ManagedArrayPool.Get(); managedArray.Allocate(ptr, length); return managedArray; } internal ManagedArray(Array arr) => WrapArray(arr); internal void WrapArray(Array arr) { pinnedArrayHandle = GCHandle.Alloc(arr, GCHandleType.Pinned); unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); length = arr.Length; elementSize = Marshal.SizeOf(arr.GetType().GetElementType()); } internal void Allocate(T* ptr, int length) where T : unmanaged { unmanagedData = new IntPtr(ptr); this.length = length; elementSize = Unsafe.SizeOf(); } internal void Allocate(IntPtr ptr, int length, int elementSize) { unmanagedData = ptr; this.length = length; this.elementSize = elementSize; } private ManagedArray() { } private ManagedArray(void* ptr, int length, int elementSize) => Allocate(new IntPtr(ptr), length, elementSize); ~ManagedArray() { if (unmanagedData != IntPtr.Zero) Free(); } internal void Free() { GC.SuppressFinalize(this); if (pinnedArrayHandle.IsAllocated) { pinnedArrayHandle.Free(); unmanagedData = IntPtr.Zero; } if (unmanagedData != IntPtr.Zero) { Marshal.FreeHGlobal(unmanagedData); unmanagedData = IntPtr.Zero; } } internal void FreePooled() { Free(); ManagedArrayPool.Put(this); } internal IntPtr GetPointer => unmanagedData; internal int Length => length; internal int ElementSize => elementSize; internal Span GetSpan() where T : struct => new Span(unmanagedData.ToPointer(), length); internal T[] GetArray() where T : struct => new Span(unmanagedData.ToPointer(), length).ToArray(); /// /// Provides a pool of pre-allocated ManagedArray that can be re-used. /// private static class ManagedArrayPool { private static List> pool = new List>(); internal static ManagedArray Get() { 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 GCHandle EmptyStringHandle = GCHandle.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 GCHandle.ToIntPtr(EmptyStringHandle); Assert.IsTrue(str.Length > 0); return GCHandle.ToIntPtr(GCHandle.Alloc(str, GCHandleType.Weak)); } [System.Diagnostics.DebuggerStepThrough] internal static string ToManaged(IntPtr ptr) { if (ptr == IntPtr.Zero) return null; return Unsafe.As(GCHandle.FromIntPtr(ptr).Target); } [System.Diagnostics.DebuggerStepThrough] internal static void Free(IntPtr ptr) { if (ptr == IntPtr.Zero) return; GCHandle handle = GCHandle.FromIntPtr(ptr); if (handle == EmptyStringHandle) return; handle.Free(); } } #endregion #region Marshallers [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(GCHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(GCHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(GCHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(GCHandleMarshaller.NativeToManaged))] [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(GCHandleMarshaller.NativeToManaged))] [CustomMarshaller(typeof(object), MarshalMode.ElementOut, typeof(GCHandleMarshaller.NativeToManaged))] [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedRef, typeof(GCHandleMarshaller.Bidirectional))] [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedRef, typeof(GCHandleMarshaller.Bidirectional))] [CustomMarshaller(typeof(object), MarshalMode.ElementRef, typeof(GCHandleMarshaller))] internal static class GCHandleMarshaller { public static class NativeToManaged { public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : GCHandle.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 ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; public static void Free(IntPtr unmanaged) { if (unmanaged == IntPtr.Zero) return; GCHandle.FromIntPtr(unmanaged).Free(); } } public struct Bidirectional { object managed; IntPtr unmanaged; public void FromManaged(object managed) { this.managed = managed; } public IntPtr ToUnmanaged() { unmanaged = GCHandleMarshaller.ToNative(managed); return unmanaged; } public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; } public object ToManaged() { managed = GCHandleMarshaller.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; GCHandle.FromIntPtr(unmanaged).Free();*/ } } internal static object ConvertToManaged(IntPtr unmanaged) => ToManaged(unmanaged); internal static IntPtr ConvertToUnmanaged(object managed) => ToNative(managed); internal static object ToManaged(IntPtr managed) => managed != IntPtr.Zero ? GCHandle.FromIntPtr(managed).Target : null; internal static IntPtr ToNative(object managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; internal static void Free(IntPtr unmanaged) { if (unmanaged == IntPtr.Zero) return; GCHandle.FromIntPtr(unmanaged).Free(); } } [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] internal static class SystemTypeMarshaller { internal static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged)); internal static IntPtr ConvertToUnmanaged(Type managed) { if (managed == null) return IntPtr.Zero; GCHandle handle = NativeInterop.GetOrAddTypeGCHandle(managed); return GCHandle.ToIntPtr(handle); } internal static void Free(IntPtr unmanaged) { // Cached handle, do not release } } [CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))] internal static class ExceptionMarshaller { internal static Exception ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged)); internal static IntPtr ConvertToUnmanaged(Exception managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.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))] internal static class ObjectMarshaller { public static class NativeToManaged { public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(unmanaged).Target) : null; } public static class ManagedToNative { public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; } } [CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))] internal static class CultureInfoMarshaller { internal static CultureInfo ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged)); internal static IntPtr ConvertToUnmanaged(CultureInfo managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged); } [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedIn, typeof(SystemArrayMarshaller.ManagedToNative))] [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))] internal static class SystemArrayMarshaller { public struct ManagedToNative { ManagedArray managedArray; GCHandle handle; public void FromManaged(Array managed) { if (managed != null) managedArray = ManagedArray.WrapPooledArray(managed); } public IntPtr ToUnmanaged() { if (managedArray == null) return IntPtr.Zero; handle = GCHandle.Alloc(managedArray); return GCHandle.ToIntPtr(handle); } public void Free() { if (managedArray != null) { managedArray.FreePooled(); 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<,>))] internal 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>(GCHandle.FromIntPtr(unmanaged).Target) : null; public static IntPtr ConvertToUnmanaged(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(GCHandle.FromIntPtr(unmanaged).Target) : null; public static IntPtr ToNative(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; public static void Free(IntPtr unmanaged) { if (unmanaged != IntPtr.Zero) GCHandle.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] internal static unsafe class ArrayMarshaller where TUnmanagedElement : unmanaged { public static class NativeToManaged { internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged is null) return null; return new T[numElements]; } internal static Span GetManagedValuesDestination(T[]? managed) => managed; internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return ReadOnlySpan.Empty; ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); return managedArray.GetSpan(); } internal static void Free(TUnmanagedElement* unmanaged) { if (unmanaged == null) return; GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged)); (Unsafe.As(handle.Target)).Free(); handle.Free(); } internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return Span.Empty; ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); return managedArray.GetSpan(); } } public static class ManagedToNative { internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) { if (managed is null) { numElements = 0; return null; } numElements = managed.Length; ManagedArray managedArray = ManagedArray.AllocatePooledArray((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length); var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray)); return (TUnmanagedElement*)ptr; } internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return Span.Empty; ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); return managedArray.GetSpan(); } internal static void Free(TUnmanagedElement* unmanaged) { if (unmanaged == null) return; GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged)); (Unsafe.As(handle.Target)).FreePooled(); handle.Free(); } } public struct Bidirectional { T[] managedArray; ManagedArray unmanagedArray; GCHandle handle; public void FromManaged(T[]? managed) { if (managed == null) return; managedArray = managed; unmanagedArray = ManagedArray.AllocatePooledArray((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length); handle = GCHandle.Alloc(unmanagedArray); } public ReadOnlySpan GetManagedValuesSource() => managedArray; public Span GetUnmanagedValuesDestination() { if (unmanagedArray == null) return Span.Empty; return unmanagedArray.GetSpan(); } public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)GCHandle.ToIntPtr(handle); public void FromUnmanaged(TUnmanagedElement* unmanaged) { ManagedArray arr = Unsafe.As(GCHandle.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.GetSpan(); } public Span GetManagedValuesDestination(int numElements) => managedArray; public T[] ToManaged() => managedArray; public void Free() { unmanagedArray.FreePooled(); handle.Free(); } } internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) { if (managed is null) { numElements = 0; return null; } numElements = managed.Length; ManagedArray managedArray = ManagedArray.AllocatePooledArray((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length); IntPtr handle = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray)); return (TUnmanagedElement*)handle; } internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return Span.Empty; ManagedArray unmanagedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); return unmanagedArray.GetSpan(); } internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements]; internal static Span GetManagedValuesDestination(T[]? managed) => managed; internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return ReadOnlySpan.Empty; ManagedArray array = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); return array.GetSpan(); } internal static void Free(TUnmanagedElement* unmanaged) { if (unmanaged == null) return; GCHandle handle = GCHandle.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))] internal 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) { if (managed == null) return IntPtr.Zero; return GCHandle.ToIntPtr(GCHandle.Alloc(managed, GCHandleType.Weak)); } 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); } internal static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); internal static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed); internal static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); internal static string ToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); internal static IntPtr ToNative(string managed) => ManagedString.ToNative(managed); } #endregion /// /// Provides a Mono-like API for native code to access managed runtime. /// internal unsafe static partial class NativeInterop { /// /// Enables first-chance exception handling in invoked methods while debugger is attached. /// public static bool CatchExceptions = true; internal static Dictionary AssemblyLocations = new Dictionary(); private static bool firstAssemblyLoaded = false; private static Dictionary typeCache = new Dictionary(); private static IntPtr boolTruePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)1, GCHandleType.Pinned)); private static IntPtr boolFalsePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)0, GCHandleType.Pinned)); private static List methodHandles = new(); private static List methodHandlesCollectible = new(); private static ConcurrentDictionary cachedDelegates = new ConcurrentDictionary(); private static ConcurrentDictionary cachedDelegatesCollectible = new ConcurrentDictionary(); private static Dictionary typeHandleCache = new Dictionary(); private static Dictionary typeHandleCacheCollectible = new Dictionary(); private static List fieldHandleCache = new(); private static List fieldHandleCacheCollectible = new(); private static Dictionary classAttributesCacheCollectible = new(); private static Dictionary assemblyHandles = new Dictionary(); private static string hostExecutable; private static IntPtr hostExecutableHandle = IntPtr.Zero; private static AssemblyLoadContext scriptingAssemblyLoadContext; [System.Diagnostics.DebuggerStepThrough] private static IntPtr InternalDllResolver(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath) { if (libraryName == "FlaxEngine") { if (hostExecutableHandle == IntPtr.Zero) hostExecutableHandle = NativeLibrary.Load(hostExecutable, assembly, dllImportSearchPath); return hostExecutableHandle; } return IntPtr.Zero; } [UnmanagedCallersOnly] internal static unsafe void Init(IntPtr hostExecutableName) { hostExecutable = Marshal.PtrToStringUni(hostExecutableName); NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), InternalDllResolver); // Change default culture to match with Mono runtime default culture CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; // TODO: benchmark collectible setting performance, maybe enable it only in editor builds? scriptingAssemblyLoadContext = new AssemblyLoadContext(null, true); DelegateHelpers.Init(); } [UnmanagedCallersOnly] internal static unsafe void Exit() { } internal static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) { Span span = ptrArray.GetSpan(); T[] managedArray = new T[ptrArray.Length]; for (int i = 0; i < managedArray.Length; i++) managedArray[i] = span[i] != IntPtr.Zero ? (T)GCHandle.FromIntPtr(span[i]).Target : default; return managedArray; } internal static IntPtr[] ManagedArrayToGCHandleArray(Array array) { IntPtr[] pointerArray = new IntPtr[array.Length]; for (int i = 0; i < pointerArray.Length; i++) { var obj = array.GetValue(i); if (obj != null) pointerArray.SetValue(GCHandle.ToIntPtr(GCHandle.Alloc(obj)), i); } return pointerArray; } internal static T[] NativeArrayToManagedArray(Span nativeSpan, Func toManagedFunc) { T[] managedArray = new T[nativeSpan.Length]; for (int i = 0; i < nativeSpan.Length; i++) managedArray[i] = toManagedFunc(nativeSpan[i]); return managedArray; } private static Type FindType(string typeName) { if (typeCache.TryGetValue(typeName, out Type type)) return type; type = Type.GetType(typeName, ResolveAssemblyByName, null); if (type == null) { foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) { type = assembly.GetType(typeName); if (type != null) break; } } if (type == null) { string oldTypeName = typeName; typeName = typeName.Substring(0, typeName.IndexOf(',')); type = Type.GetType(typeName, ResolveAssemblyByName, null); if (type == null) { foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) { type = assembly.GetType(typeName); if (type != null) break; } } typeName = oldTypeName; } typeCache.Add(typeName, type); return type; } private static Assembly ResolveAssemblyByName(AssemblyName assemblyName) { foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies) if (assembly.GetName() == assemblyName) return assembly; return null; } internal static bool IsBlittable(Type type) { if (type.IsPrimitive || type.IsEnum) return true; if (type.IsArray && IsBlittable(type.GetElementType())) return true; if (type.IsPointer && type.HasElementType && type.GetElementType().IsPrimitive) return true; if (type.IsClass) return false; if (type.IsValueType) { var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { if (!IsBlittable(field.FieldType)) return false; } } return true; } /// /// Returns blittable internal type for given type. /// internal static Type GetInternalType(Type type) { string[] splits = type.AssemblyQualifiedName.Split(','); string @namespace = string.Join('.', splits[0].Split('.').SkipLast(1)); string className = @namespace.Length > 0 ? splits[0].Substring(@namespace.Length + 1) : splits[0]; string parentClassName = ""; if (className.Contains('+')) { parentClassName = className.Substring(0, className.LastIndexOf('+') + 1); className = className.Substring(parentClassName.Length); } string marshallerName = className + "Marshaller"; string internalAssemblyQualifiedName = $"{@namespace}.{parentClassName}{marshallerName}+{className}Internal,{String.Join(',', splits.Skip(1))}"; return FindType(internalAssemblyQualifiedName); } internal class ReferenceTypePlaceholder { } internal struct ValueTypePlaceholder { } internal delegate object MarshalToManagedDelegate(IntPtr nativePtr, bool byRef); internal delegate void MarshalToNativeDelegate(object managedObject, IntPtr nativePtr); internal delegate void MarshalToNativeFieldDelegate(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset); internal static ConcurrentDictionary toManagedMarshallers = new ConcurrentDictionary(1, 3); internal static ConcurrentDictionary toNativeMarshallers = new ConcurrentDictionary(1, 3); internal static ConcurrentDictionary toNativeFieldMarshallers = new ConcurrentDictionary(1, 3); internal static object MarshalToManaged(IntPtr nativePtr, Type type) { static MarshalToManagedDelegate Factory(Type type) { Type marshalType = type; if (marshalType.IsByRef) marshalType = marshalType.GetElementType(); else if (marshalType.IsPointer) marshalType = typeof(IntPtr); MethodInfo method = typeof(MarshalHelper<>).MakeGenericType(marshalType).GetMethod(nameof(MarshalHelper.ToManagedWrapper), BindingFlags.Static | BindingFlags.NonPublic); return method.CreateDelegate(); } if (toManagedMarshallers.TryGetValue(type, out var deleg)) return deleg(nativePtr, type.IsByRef); return toManagedMarshallers.GetOrAdd(type, Factory)(nativePtr, type.IsByRef); } internal static void MarshalToNative(object managedObject, IntPtr nativePtr, Type type) { static MarshalToNativeDelegate Factory(Type type) { MethodInfo method; if (type.IsValueType) method = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNativeWrapper), BindingFlags.Static | BindingFlags.NonPublic); else method = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToNativeWrapper), BindingFlags.Static | BindingFlags.NonPublic); return method.CreateDelegate(); } if (toNativeMarshallers.TryGetValue(type, out var deleg)) deleg(managedObject, nativePtr); else toNativeMarshallers.GetOrAdd(type, Factory)(managedObject, nativePtr); } internal static MarshalToNativeFieldDelegate GetToNativeFieldMarshallerDelegate(Type type) { static MarshalToNativeFieldDelegate Factory(Type type) { MethodInfo method; if (type.IsValueType) method = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNativeFieldWrapper), BindingFlags.Static | BindingFlags.NonPublic); else method = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToNativeFieldWrapper), BindingFlags.Static | BindingFlags.NonPublic); return method.CreateDelegate(); } if (toNativeFieldMarshallers.TryGetValue(type, out var deleg)) return deleg; return toNativeFieldMarshallers.GetOrAdd(type, Factory); } internal static void MarshalToNativeField(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset) { GetToNativeFieldMarshallerDelegate(fieldOwner.GetType())(field, fieldOwner, nativePtr, out fieldOffset); } /// /// Helper class for managing stored marshaller delegates for each type. /// internal static class MarshalHelper { private delegate void MarshalToNativeTypedDelegate(ref T managedValue, IntPtr nativePtr); private delegate void MarshalToManagedTypedDelegate(ref T managedValue, IntPtr nativePtr, bool byRef); internal delegate void MarshalFieldTypedDelegate(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset); internal static FieldInfo[] marshallableFields; internal static MarshalFieldTypedDelegate[] toManagedFieldMarshallers; internal static MarshalFieldTypedDelegate[] toNativeFieldMarshallers; private static MarshalToNativeTypedDelegate toNativeTypedMarshaller; private static MarshalToManagedTypedDelegate toManagedTypedMarshaller; static MarshalHelper() { Type type = typeof(T); // Setup marshallers for managed and native directions MethodInfo toManagedMethod; if (type.IsValueType) toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToManaged), BindingFlags.Static | BindingFlags.NonPublic); else if (type.IsArray && type.GetElementType().IsValueType) toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type.GetElementType()).GetMethod(nameof(MarshalHelperValueType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); else if (type.IsArray && !type.GetElementType().IsValueType) toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type.GetElementType()).GetMethod(nameof(MarshalHelperReferenceType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); else toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToManaged), BindingFlags.Static | BindingFlags.NonPublic); toManagedTypedMarshaller = toManagedMethod.CreateDelegate(); MethodInfo toNativeMethod; if (type.IsValueType) toNativeMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNative), BindingFlags.Static | BindingFlags.NonPublic); else toNativeMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToNative), BindingFlags.Static | BindingFlags.NonPublic); toNativeTypedMarshaller = toNativeMethod.CreateDelegate(); if (!type.IsPrimitive && !type.IsPointer && type != typeof(bool)) { // Setup field-by-field marshallers for reference types or structures containing references marshallableFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (type.IsValueType && !marshallableFields.Any(x => (x.FieldType.IsClass && !x.FieldType.IsPointer) || x.FieldType.Name == "Boolean")) marshallableFields = null; else if (!type.IsValueType && !marshallableFields.Any()) marshallableFields = null; if (marshallableFields != null) { toManagedFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; toNativeFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; for (int i = 0; i < marshallableFields.Length; i++) { FieldInfo field = marshallableFields[i]; MethodInfo toManagedFieldMethod; MethodInfo toNativeFieldMethod; if (field.FieldType.IsPointer) { toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointer), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointer), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); } else if (field.FieldType.IsValueType) { toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); } else if (field.FieldType.IsArray) { Type arrayElementType = field.FieldType.GetElementType(); if (arrayElementType.IsValueType) { toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArray), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); } else { toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); } } else { toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); } toManagedFieldMarshallers[i] = toManagedFieldMethod.CreateDelegate(); toNativeFieldMarshallers[i] = toNativeFieldMethod.CreateDelegate(); } } } } internal static object ToManagedWrapper(IntPtr nativePtr, bool byRef) { T managed = default; toManagedTypedMarshaller(ref managed, nativePtr, byRef); return managed; } internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) { toManagedTypedMarshaller(ref managedValue, nativePtr, byRef); } internal static Array ToManagedArray(Span ptrSpan) { T[] arr = new T[ptrSpan.Length]; for (int i = 0; i < arr.Length; i++) toManagedTypedMarshaller(ref arr[i], ptrSpan[i], false); return arr; } internal static void ToNative(ref T managedValue, IntPtr nativePtr) { toNativeTypedMarshaller(ref managedValue, nativePtr); } internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr nativePtr, out int fieldOffset) { if (marshallableFields != null) { for (int i = 0; i < marshallableFields.Length; i++) { if (marshallableFields[i] == field) { toNativeFieldMarshallers[i](marshallableFields[i], ref fieldOwner, nativePtr, out fieldOffset); return; } } } throw new Exception($"Invalid field {field.Name} to marshal for type {typeof(T).Name}"); } private static void ToManagedFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { ref IntPtr fieldValueRef = ref GetFieldReference(field, ref fieldOwner); fieldValueRef = Unsafe.Read(fieldPtr.ToPointer()); fieldOffset = IntPtr.Size; } private static void ToNativeFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { ref IntPtr fieldValueRef = ref GetFieldReference(field, ref fieldOwner); Unsafe.Write(fieldPtr.ToPointer(), fieldValueRef); fieldOffset = IntPtr.Size; } /// /// Returns a reference to the value of the field. /// private static ref TField GetFieldReference(FieldInfo field, ref T fieldOwner) { // Get the address of the field, source: https://stackoverflow.com/a/56512720 if (typeof(T).IsValueType) { byte* fieldPtr = (byte*)Unsafe.AsPointer(ref fieldOwner) + (Marshal.ReadInt32(field.FieldHandle.Value + 4 + IntPtr.Size) & 0xFFFFFF); return ref Unsafe.AsRef(fieldPtr); } else { byte* fieldPtr = (byte*)Unsafe.As(ref fieldOwner) + IntPtr.Size + (Marshal.ReadInt32(field.FieldHandle.Value + 4 + IntPtr.Size) & 0xFFFFFF); return ref Unsafe.AsRef(fieldPtr); } } private static IntPtr EnsureAlignment(IntPtr ptr, int alignment) { if (ptr % alignment != 0) ptr = IntPtr.Add(ptr, alignment - (int)(ptr % alignment)); return ptr; } private static class ValueTypeField where TField : struct { private static int fieldAlignment; static ValueTypeField() { Type fieldType = typeof(TField); if (fieldType.IsEnum) fieldType = fieldType.GetEnumUnderlyingType(); else if (fieldType == typeof(bool)) fieldType = typeof(byte); if (fieldType.IsValueType && !fieldType.IsEnum && !fieldType.IsPrimitive) // Is it a structure? { } else if (fieldType.IsClass || fieldType.IsPointer) fieldAlignment = IntPtr.Size; else fieldAlignment = Marshal.SizeOf(fieldType); } internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { fieldOffset = Unsafe.SizeOf(); if (fieldAlignment > 1) { IntPtr fieldStartPtr = fieldPtr; fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); } ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); MarshalHelperValueType.ToManaged(ref fieldValueRef, fieldPtr, false); } internal static void ToManagedFieldArray(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { fieldOffset = Unsafe.SizeOf(); if (fieldAlignment > 1) { IntPtr fieldStartPtr = fieldPtr; fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); } ref TField[] fieldValueRef = ref GetFieldReference(field, ref fieldOwner); MarshalHelperValueType.ToManagedArray(ref fieldValueRef, fieldPtr, false); } internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { fieldOffset = Unsafe.SizeOf(); if (fieldAlignment > 1) { IntPtr startPtr = fieldPtr; fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); fieldOffset += (fieldPtr - startPtr).ToInt32(); } ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); MarshalHelperValueType.ToNative(ref fieldValueRef, fieldPtr); } } private static class ReferenceTypeField where TField : class { internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); MarshalHelperReferenceType.ToManaged(ref fieldValueRef, Marshal.ReadIntPtr(fieldPtr), false); fieldOffset = IntPtr.Size; } internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); MarshalHelperReferenceType.ToNative(ref fieldValueRef, fieldPtr); fieldOffset = IntPtr.Size; } } } internal static class MarshalHelperValueType where T : struct { internal static void ToNativeWrapper(object managedObject, IntPtr nativePtr) { ToNative(ref Unsafe.Unbox(managedObject), nativePtr); } internal static void ToNativeFieldWrapper(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset) { MarshalHelper.ToNativeField(field, ref Unsafe.Unbox(fieldOwner), nativePtr, out fieldOffset); } internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) { Type type = typeof(T); if (type.IsByRef || byRef) { if (type.IsByRef) type = type.GetElementType(); Assert.IsTrue(type.IsValueType); } if (type == typeof(IntPtr)) managedValue = (T)(object)nativePtr; else if (MarshalHelper.marshallableFields != null) { IntPtr fieldPtr = nativePtr; for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) { MarshalHelper.toManagedFieldMarshallers[i](MarshalHelper.marshallableFields[i], ref managedValue, fieldPtr, out int fieldOffset); fieldPtr += fieldOffset; } Assert.IsTrue((fieldPtr - nativePtr) == Unsafe.SizeOf()); } else managedValue = Unsafe.Read(nativePtr.ToPointer()); } internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef) { Assert.IsTrue(!byRef); Type elementType = typeof(T); if (nativePtr != IntPtr.Zero) { ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target); if (ArrayFactory.GetMarshalledType(elementType) == elementType) managedValue = Unsafe.As(managedArray.GetArray()); else managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.GetSpan())); } else managedValue = null; } internal static void ToNative(ref T managedValue, IntPtr nativePtr) { if (typeof(T).IsByRef) throw new NotImplementedException(); if (MarshalHelper.marshallableFields != null) { IntPtr fieldPtr = nativePtr; for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) { MarshalHelper.toNativeFieldMarshallers[i](MarshalHelper.marshallableFields[i], ref managedValue, nativePtr, out int fieldOffset); nativePtr += fieldOffset; } Assert.IsTrue((nativePtr - fieldPtr) == Unsafe.SizeOf()); } else Unsafe.AsRef(nativePtr.ToPointer()) = managedValue; } } internal static class MarshalHelperReferenceType where T : class { internal static void ToNativeWrapper(object managedObject, IntPtr nativePtr) { T managedValue = Unsafe.As(managedObject); ToNative(ref managedValue, nativePtr); } internal static void ToNativeFieldWrapper(FieldInfo field, object managedObject, IntPtr nativePtr, out int fieldOffset) { T managedValue = Unsafe.As(managedObject); MarshalHelper.ToNativeField(field, ref managedValue, nativePtr, out fieldOffset); } internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) { Type type = typeof(T); if (byRef) nativePtr = Marshal.ReadIntPtr(nativePtr); if (type == typeof(string)) managedValue = Unsafe.As(ManagedString.ToManaged(nativePtr)); else if (type.IsClass) managedValue = nativePtr != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target) : null; else if (type.IsInterface) // Dictionary managedValue = nativePtr != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target) : null; else throw new NotImplementedException(); } internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef) { Assert.IsTrue(!byRef); Type elementType = typeof(T); if (nativePtr != IntPtr.Zero) { ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target); managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.GetSpan())); } else managedValue = null; } internal static void ToNative(ref T managedValue, IntPtr nativePtr) { Type type = typeof(T); IntPtr managedPtr; if (type == typeof(string)) managedPtr = ManagedString.ToNative(managedValue as string); else if (type.IsPointer) { if (Pointer.Unbox(managedValue) == null) managedPtr = IntPtr.Zero; else if (managedValue is FlaxEngine.Object flaxObj) managedPtr = flaxObj.__unmanagedPtr; else managedPtr = GCHandle.ToIntPtr(GCHandle.Alloc(managedValue, GCHandleType.Weak)); } else if (type == typeof(Type)) managedPtr = managedValue != null ? GCHandle.ToIntPtr(GetOrAddTypeGCHandle((Type)(object)managedValue)) : IntPtr.Zero; else managedPtr = managedValue != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managedValue, GCHandleType.Weak)) : IntPtr.Zero; Unsafe.Write(nativePtr.ToPointer(), managedPtr); } } internal class MethodHolder { internal Type[] parameterTypes; internal MethodInfo method; private Invoker.MarshalAndInvokeDelegate invokeDelegate; private object delegInvoke; internal MethodHolder(MethodInfo method) { this.method = method; parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); } internal bool TryGetDelegate(out Invoker.MarshalAndInvokeDelegate outDeleg, out object outDelegInvoke) { if (invokeDelegate == null) { List methodTypes = new List(); if (!method.IsStatic) methodTypes.Add(method.DeclaringType); if (method.ReturnType != typeof(void)) methodTypes.Add(method.ReturnType); methodTypes.AddRange(parameterTypes); List genericParamTypes = new List(); foreach (var type in methodTypes) { if (type.IsByRef) genericParamTypes.Add(type.GetElementType()); else if (type.IsPointer) genericParamTypes.Add(typeof(IntPtr)); else genericParamTypes.Add(type); } string typeName = $"FlaxEngine.NativeInterop+Invoker+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; Type invokerType = genericParamTypes.Count == 0 ? Type.GetType(typeName) : Type.GetType(typeName).MakeGenericType(genericParamTypes.ToArray()); invokeDelegate = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.MarshalAndInvoke), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); delegInvoke = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.CreateDelegate), BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { method }); } outDeleg = invokeDelegate; outDelegInvoke = delegInvoke; return outDeleg != null; } } internal static GCHandle GetMethodGCHandle(MethodInfo method) { MethodHolder methodHolder = new MethodHolder(method); GCHandle handle = GCHandle.Alloc(methodHolder); if (methodHolder.parameterTypes.Any(x => x.IsCollectible) || method.IsCollectible) methodHandlesCollectible.Add(handle); else methodHandles.Add(handle); return handle; } internal static GCHandle GetAssemblyHandle(Assembly assembly) { if (!assemblyHandles.TryGetValue(assembly, out GCHandle handle)) { handle = GCHandle.Alloc(assembly); assemblyHandles.Add(assembly, handle); } return handle; } [UnmanagedCallersOnly] internal static unsafe void GetManagedClasses(IntPtr assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount) { Assembly assembly = Unsafe.As(GCHandle.FromIntPtr(assemblyHandle).Target); var assemblyTypes = GetAssemblyTypes(assembly); NativeClassDefinitions* arr = (NativeClassDefinitions*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * assemblyTypes.Length).ToPointer(); for (int i = 0; i < assemblyTypes.Length; i++) { var type = assemblyTypes[i]; IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); bool isStatic = type.IsAbstract && type.IsSealed; bool isInterface = type.IsInterface; bool isAbstract = type.IsAbstract; GCHandle typeHandle = GetOrAddTypeGCHandle(type); NativeClassDefinitions managedClass = new NativeClassDefinitions() { typeHandle = GCHandle.ToIntPtr(typeHandle), name = Marshal.StringToCoTaskMemAnsi(type.Name), fullname = Marshal.StringToCoTaskMemAnsi(type.FullName), @namespace = Marshal.StringToCoTaskMemAnsi(type.Namespace ?? ""), typeAttributes = (uint)type.Attributes, }; Unsafe.Write(ptr.ToPointer(), managedClass); } *managedClasses = arr; *managedClassCount = assemblyTypes.Length; } [UnmanagedCallersOnly] internal static unsafe void GetManagedClassFromType(IntPtr typeHandle, NativeClassDefinitions* managedClass, IntPtr* assemblyHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); GCHandle classTypeHandle = GetOrAddTypeGCHandle(type); *managedClass = new NativeClassDefinitions() { typeHandle = GCHandle.ToIntPtr(classTypeHandle), name = Marshal.StringToCoTaskMemAnsi(type.Name), fullname = Marshal.StringToCoTaskMemAnsi(type.FullName), @namespace = Marshal.StringToCoTaskMemAnsi(type.Namespace ?? ""), typeAttributes = (uint)type.Attributes, }; *assemblyHandle = GCHandle.ToIntPtr(GetAssemblyHandle(type.Assembly)); } [UnmanagedCallersOnly] internal static void GetClassMethods(IntPtr typeHandle, NativeMethodDefinitions** classMethods, int* classMethodsCount) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); List 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); foreach (MethodInfo method in staticMethods) methods.Add(method); foreach (MethodInfo method in instanceMethods) methods.Add(method); NativeMethodDefinitions* arr = (NativeMethodDefinitions*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * methods.Count).ToPointer(); for (int i = 0; i < methods.Count; i++) { IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); NativeMethodDefinitions classMethod = new NativeMethodDefinitions() { name = Marshal.StringToCoTaskMemAnsi(methods[i].Name), numParameters = methods[i].GetParameters().Length, methodAttributes = (uint)methods[i].Attributes, }; classMethod.typeHandle = GCHandle.ToIntPtr(GetMethodGCHandle(methods[i])); Unsafe.Write(ptr.ToPointer(), classMethod); } *classMethods = arr; *classMethodsCount = methods.Count; } internal class FieldHolder { internal FieldInfo field; internal MarshalToNativeFieldDelegate toNativeMarshaller; internal FieldHolder(FieldInfo field, Type type) { this.field = field; toNativeMarshaller = GetToNativeFieldMarshallerDelegate(type); } } [UnmanagedCallersOnly] internal static void GetClassFields(IntPtr typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); NativeFieldDefinitions* arr = (NativeFieldDefinitions*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * fields.Length).ToPointer(); for (int i = 0; i < fields.Length; i++) { IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); FieldHolder fieldHolder = new FieldHolder(fields[i], type); GCHandle fieldHandle = GCHandle.Alloc(fieldHolder); if (type.IsCollectible) fieldHandleCacheCollectible.Add(fieldHandle); else fieldHandleCache.Add(fieldHandle); NativeFieldDefinitions classField = new NativeFieldDefinitions() { name = Marshal.StringToCoTaskMemAnsi(fieldHolder.field.Name), fieldHandle = GCHandle.ToIntPtr(fieldHandle), fieldTypeHandle = GCHandle.ToIntPtr(GetOrAddTypeGCHandle(fieldHolder.field.FieldType)), fieldAttributes = (uint)fieldHolder.field.Attributes, }; Unsafe.Write(ptr.ToPointer(), classField); } *classFields = arr; *classFieldsCount = fields.Length; } [UnmanagedCallersOnly] internal static void GetClassProperties(IntPtr typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); NativePropertyDefinitions* arr = (NativePropertyDefinitions*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * properties.Length).ToPointer(); 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); NativePropertyDefinitions classProperty = new NativePropertyDefinitions() { name = Marshal.StringToCoTaskMemAnsi(properties[i].Name), }; if (getterMethod != null) { var getterHandle = GetMethodGCHandle(getterMethod); classProperty.getterHandle = GCHandle.ToIntPtr(getterHandle); } if (setterMethod != null) { var setterHandle = GetMethodGCHandle(setterMethod); classProperty.setterHandle = GCHandle.ToIntPtr(setterHandle); } Unsafe.Write(ptr.ToPointer(), classProperty); } *classProperties = arr; *classPropertiesCount = properties.Length; } [UnmanagedCallersOnly] internal static void GetClassAttributes(IntPtr typeHandle, NativeAttributeDefinitions** classAttributes, int* classAttributesCount) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); object[] attributeValues = type.GetCustomAttributes(false); Type[] attributeTypes = type.GetCustomAttributes(false).Select(x => x.GetType()).ToArray(); NativeAttributeDefinitions* arr = (NativeAttributeDefinitions*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * attributeTypes.Length).ToPointer(); for (int i = 0; i < attributeTypes.Length; i++) { IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); if (!classAttributesCacheCollectible.TryGetValue(attributeValues[i], out GCHandle attributeHandle)) { attributeHandle = GCHandle.Alloc(attributeValues[i]); classAttributesCacheCollectible.Add(attributeValues[i], attributeHandle); } GCHandle attributeTypeHandle = GetOrAddTypeGCHandle(attributeTypes[i]); NativeAttributeDefinitions classAttribute = new NativeAttributeDefinitions() { name = Marshal.StringToCoTaskMemAnsi(attributeTypes[i].Name), attributeTypeHandle = GCHandle.ToIntPtr(attributeTypeHandle), attributeHandle = GCHandle.ToIntPtr(attributeHandle), }; Unsafe.Write(ptr.ToPointer(), classAttribute); } *classAttributes = arr; *classAttributesCount = attributeTypes.Length; } [UnmanagedCallersOnly] internal static IntPtr GetCustomAttribute(IntPtr typeHandle, IntPtr attribHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); Type attribType = Unsafe.As(GCHandle.FromIntPtr(attribHandle).Target); object attrib = type.GetCustomAttributes(false).FirstOrDefault(x => x.GetType() == attribType); if (attrib != null) return GCHandle.ToIntPtr(GCHandle.Alloc(attrib, GCHandleType.Weak)); return IntPtr.Zero; } [UnmanagedCallersOnly] internal static void GetClassInterfaces(IntPtr typeHandle, IntPtr* classInterfaces, int* classInterfacesCount) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); Type[] interfaces = type.GetInterfaces(); // mono doesn't seem to return any interfaces, or returns only "immediate" interfaces? // FIXME? //foreach (Type interfaceType in type.BaseType.GetInterfaces()) // interfaces.Remove(interfaceType.FullName); IntPtr arr = Marshal.AllocCoTaskMem(IntPtr.Size * interfaces.Length); for (int i = 0; i < interfaces.Length; i++) { IntPtr ptr = IntPtr.Add(arr, IntPtr.Size * i); GCHandle handle = GetTypeGCHandle(interfaces[i]); Marshal.WriteIntPtr(ptr, GCHandle.ToIntPtr(handle)); } *classInterfaces = arr; *classInterfacesCount = interfaces.Length; } [UnmanagedCallersOnly] internal static IntPtr GetMethodReturnType(IntPtr methodHandle) { MethodHolder methodHolder = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); Type returnType = methodHolder.method.ReturnType; return GCHandle.ToIntPtr(GetTypeGCHandle(returnType)); } [UnmanagedCallersOnly] internal static void GetMethodParameterTypes(IntPtr methodHandle, IntPtr* typeHandles) { MethodHolder methodHolder = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); Type returnType = methodHolder.method.ReturnType; IntPtr arr = Marshal.AllocCoTaskMem(IntPtr.Size * methodHolder.parameterTypes.Length); for (int i = 0; i < methodHolder.parameterTypes.Length; i++) { GCHandle typeHandle = GetOrAddTypeGCHandle(methodHolder.parameterTypes[i]); Marshal.WriteIntPtr(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i), GCHandle.ToIntPtr(typeHandle)); } *typeHandles = arr; } /// /// Returns pointer to the string's internal structure, containing the buffer and length of the string. /// [UnmanagedCallersOnly] internal static IntPtr GetStringPointer(IntPtr stringHandle) { string str = Unsafe.As(GCHandle.FromIntPtr(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 IntPtr NewObject(IntPtr typeHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); if (type == typeof(Script)) { // FIXME: Script is an abstract type which can not be instantiated type = typeof(VisjectScript); } object value = RuntimeHelpers.GetUninitializedObject(type); return GCHandle.ToIntPtr(GCHandle.Alloc(value)); } internal static class ArrayFactory { private delegate Array CreateArrayDelegate(long size); private static ConcurrentDictionary marshalledTypes = new ConcurrentDictionary(1, 3); private static ConcurrentDictionary createArrayDelegates = new ConcurrentDictionary(1, 3); internal static Type GetMarshalledType(Type elementType) { static Type Factory(Type type) { Type marshalType; if (IsBlittable(type)) marshalType = type; else marshalType = GetInternalType(type) ?? typeof(IntPtr); return marshalType; } if (marshalledTypes.TryGetValue(elementType, out var marshalledType)) return marshalledType; return marshalledTypes.GetOrAdd(elementType, Factory); } internal static Array CreateArray(Type type, long size) { static CreateArrayDelegate Factory(Type type) { Type marshalledType = GetMarshalledType(type); MethodInfo method = typeof(Internal<>).MakeGenericType(marshalledType).GetMethod(nameof(Internal.CreateArrayDelegate), BindingFlags.Static | BindingFlags.NonPublic); return method.CreateDelegate(); } if (createArrayDelegates.TryGetValue(type, out var deleg)) return deleg(size); return createArrayDelegates.GetOrAdd(type, Factory)(size); } private static class Internal { internal static Array CreateArrayDelegate(long size) => new T[size]; } } [UnmanagedCallersOnly] internal static IntPtr NewArray(IntPtr typeHandle, long size) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); Type marshalledType = ArrayFactory.GetMarshalledType(type); if (marshalledType.IsValueType) { ManagedArray managedArray = ManagedArray.AllocateNewArray(Marshal.AllocHGlobal(Marshal.SizeOf(marshalledType) * (int)size), (int)size, Marshal.SizeOf(marshalledType)); return GCHandle.ToIntPtr(GCHandle.Alloc(managedArray/*, GCHandleType.Weak*/)); } else { Array arr = ArrayFactory.CreateArray(type, size); ManagedArray managedArray = ManagedArray.WrapNewArray(arr); return GCHandle.ToIntPtr(GCHandle.Alloc(managedArray/*, GCHandleType.Weak*/)); } } [UnmanagedCallersOnly] internal static unsafe IntPtr GetArrayPointerToElement(IntPtr arrayHandle, int size, int index) { ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(arrayHandle).Target); if (managedArray.Length == 0) return IntPtr.Zero; Assert.IsTrue(index >= 0 && index < managedArray.Length); return IntPtr.Add(managedArray.GetPointer, size * index); } [UnmanagedCallersOnly] internal static int GetArrayLength(IntPtr arrayHandle) { ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(arrayHandle).Target); return managedArray.Length; } [UnmanagedCallersOnly] internal static IntPtr GetStringEmpty() { return ManagedString.ToNative(string.Empty); } [UnmanagedCallersOnly] internal static IntPtr NewStringUTF16(char* text, int length) { return ManagedString.ToNative(new string(new ReadOnlySpan(text, length))); } [UnmanagedCallersOnly] internal static IntPtr NewString(sbyte* text) { return ManagedString.ToNative(new string(text)); } [UnmanagedCallersOnly] internal static IntPtr NewStringLength(sbyte* text, int length) { return ManagedString.ToNative(new string(text, 0, length)); } /// /// Creates a managed copy of the value, and stores it in a boxed reference. /// [UnmanagedCallersOnly] internal static IntPtr BoxValue(IntPtr typeHandle, IntPtr valuePtr) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); object value = MarshalToManaged(valuePtr, type); return GCHandle.ToIntPtr(GCHandle.Alloc(value, GCHandleType.Weak)); } [UnmanagedCallersOnly] internal static IntPtr GetObjectType(IntPtr handle) { var obj = GCHandle.FromIntPtr(handle).Target; Type classType = obj.GetType(); return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(classType)); } internal static class ValueTypeUnboxer { private static ConcurrentDictionary unboxers = new ConcurrentDictionary(1, 3); private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); private delegate IntPtr UnboxerDelegate(object value); private static UnboxerDelegate UnboxerDelegateFactory(Type type) { return unboxerMethod.MakeGenericMethod(type).CreateDelegate(); } internal static IntPtr GetPointer(object value) { Type type = value.GetType(); if (unboxers.TryGetValue(type, out var deleg)) return deleg(value); return unboxers.GetOrAdd(type, UnboxerDelegateFactory)(value); } private static unsafe IntPtr UnboxPointer(object value) where T : struct { return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox(value))); } } /// /// Returns the address of the boxed value type. /// [UnmanagedCallersOnly] internal unsafe static IntPtr UnboxValue(IntPtr handlePtr) { GCHandle handle = GCHandle.FromIntPtr(handlePtr); object value = handle.Target; Type type = value.GetType(); if (!type.IsValueType) return handlePtr; // HACK: Get the address of a non-pinned value return ValueTypeUnboxer.GetPointer(value); } [UnmanagedCallersOnly] internal static void RaiseException(IntPtr exceptionHandle) { Exception exception = Unsafe.As(GCHandle.FromIntPtr(exceptionHandle).Target); throw exception; } [UnmanagedCallersOnly] internal static void ObjectInit(IntPtr objectHandle) { object obj = GCHandle.FromIntPtr(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(IntPtr instancePtr, IntPtr methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) { MethodHolder methodHolder = Unsafe.As(GCHandle.FromIntPtr(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, instancePtr, paramPtr); } catch (Exception exception) { if (Debugger.IsAttached && CatchExceptions) throw; Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(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; IntPtr* nativePtrs = stackalloc IntPtr[numParams]; object[] methodParameters = new object[numParams]; for (int i = 0; i < numParams; i++) { nativePtrs[i] = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); methodParameters[i] = MarshalToManaged(nativePtrs[i], methodHolder.parameterTypes[i]); } try { returnObject = methodHolder.method.Invoke(instancePtr != IntPtr.Zero ? GCHandle.FromIntPtr(instancePtr).Target : null, methodParameters); } catch (Exception exception) { if (Debugger.IsAttached && CatchExceptions) throw; // 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, GCHandle.ToIntPtr(GCHandle.Alloc(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) MarshalToNative(methodParameters[i], nativePtrs[i], parameterType.GetElementType()); } if (returnObject is not null) { if (methodHolder.method.ReturnType == typeof(string)) return ManagedString.ToNative(Unsafe.As(returnObject)); else if (methodHolder.method.ReturnType == typeof(IntPtr)) return (IntPtr)returnObject; else if (methodHolder.method.ReturnType == typeof(bool)) return (bool)returnObject ? boolTruePtr : boolFalsePtr; else if (methodHolder.method.ReturnType == typeof(Type)) return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(Unsafe.As(returnObject))); else if (methodHolder.method.ReturnType == typeof(object[])) return GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak)); else return GCHandle.ToIntPtr(GCHandle.Alloc(returnObject, GCHandleType.Weak)); } return IntPtr.Zero; } } [UnmanagedCallersOnly] internal static IntPtr GetMethodUnmanagedFunctionPointer(IntPtr methodHandle) { MethodHolder methodHolder = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); // Wrap the method call, this is needed to get the object instance from GCHandle and to pass the exception back to native side MethodInfo invokeThunk = typeof(ThunkContext).GetMethod(nameof(ThunkContext.InvokeThunk)); Type delegateType = DelegateHelpers.MakeNewCustomDelegate(invokeThunk.GetParameters().Select(x => x.ParameterType).Append(invokeThunk.ReturnType).ToArray()); ThunkContext context = new ThunkContext(methodHolder.method); Delegate methodDelegate = invokeThunk.CreateDelegate(delegateType, 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(IntPtr fieldOwnerHandle, IntPtr fieldHandle, IntPtr valuePtr) { object fieldOwner = GCHandle.FromIntPtr(fieldOwnerHandle).Target; FieldHolder field = Unsafe.As(GCHandle.FromIntPtr(fieldHandle).Target); field.field.SetValue(fieldOwner, Marshal.PtrToStructure(valuePtr, field.field.FieldType)); } [UnmanagedCallersOnly] internal static void FieldGetValue(IntPtr fieldOwnerHandle, IntPtr fieldHandle, IntPtr valuePtr) { object fieldOwner = GCHandle.FromIntPtr(fieldOwnerHandle).Target; FieldHolder field = Unsafe.As(GCHandle.FromIntPtr(fieldHandle).Target); field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset); } [UnmanagedCallersOnly] internal static void SetArrayValueReference(IntPtr arrayHandle, IntPtr elementPtr, IntPtr valueHandle) { ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(arrayHandle).Target); int index = (int)(elementPtr.ToInt64() - managedArray.GetPointer.ToInt64()) / managedArray.ElementSize; managedArray.GetSpan()[index] = valueHandle; } [UnmanagedCallersOnly] internal static IntPtr LoadAssemblyFromPath(IntPtr assemblyPath_, IntPtr* assemblyName, IntPtr* assemblyFullName) { if (!firstAssemblyLoaded) { // This assembly was already loaded when initializing the host context firstAssemblyLoaded = true; Assembly flaxEngineAssembly = AppDomain.CurrentDomain.GetAssemblies().First(x => x.GetName().Name == "FlaxEngine.CSharp"); *assemblyName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.GetName().Name); *assemblyFullName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.FullName); return GCHandle.ToIntPtr(GetAssemblyHandle(flaxEngineAssembly)); } string assemblyPath = Marshal.PtrToStringAnsi(assemblyPath_); Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath); *assemblyName = Marshal.StringToCoTaskMemAnsi(assembly.GetName().Name); *assemblyFullName = Marshal.StringToCoTaskMemAnsi(assembly.FullName); return GCHandle.ToIntPtr(GetAssemblyHandle(assembly)); } [UnmanagedCallersOnly] internal static IntPtr 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 = AppDomain.CurrentDomain.GetAssemblies().First(x => x.GetName().Name == "FlaxEngine.CSharp"); *assemblyName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.GetName().Name); *assemblyFullName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.FullName); return GCHandle.ToIntPtr(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 = scriptingAssemblyLoadContext.LoadFromStream(stream); // Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822 AssemblyLocations.Add(assembly.FullName, assemblyPath); *assemblyName = Marshal.StringToCoTaskMemAnsi(assembly.GetName().Name); *assemblyFullName = Marshal.StringToCoTaskMemAnsi(assembly.FullName); return GCHandle.ToIntPtr(GetAssemblyHandle(assembly)); } [UnmanagedCallersOnly] internal static IntPtr GetAssemblyByName(IntPtr name_, IntPtr* assemblyName, IntPtr* assemblyFullName) { string name = Marshal.PtrToStringAnsi(name_); Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name == name); if (assembly == null) assembly = scriptingAssemblyLoadContext.Assemblies.FirstOrDefault(x => x.GetName().Name == name); *assemblyName = Marshal.StringToCoTaskMemAnsi(assembly.GetName().Name); *assemblyFullName = Marshal.StringToCoTaskMemAnsi(assembly.FullName); return GCHandle.ToIntPtr(GetAssemblyHandle(assembly)); } [UnmanagedCallersOnly] internal static IntPtr GetRuntimeInformation() { return Marshal.StringToCoTaskMemAnsi(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); } [UnmanagedCallersOnly] internal static void CloseAssembly(IntPtr assemblyHandle) { Assembly assembly = Unsafe.As(GCHandle.FromIntPtr(assemblyHandle).Target); GCHandle.FromIntPtr(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 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(null, true); } [UnmanagedCallersOnly] internal static IntPtr GetAssemblyObject(IntPtr assemblyName_) { string assemblyName = Marshal.PtrToStringAnsi(assemblyName_); Assembly assembly = null; assembly = scriptingAssemblyLoadContext.Assemblies.FirstOrDefault(x => x.FullName == assemblyName); if (assembly == null) assembly = System.AppDomain.CurrentDomain.GetAssemblies().First(x => x.FullName == assemblyName); return GCHandle.ToIntPtr(GCHandle.Alloc(assembly)); } [UnmanagedCallersOnly] internal static unsafe int NativeSizeOf(IntPtr typeHandle, uint* align) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); Type nativeType = GetInternalType(type) ?? type; if (nativeType == typeof(Version)) nativeType = typeof(VersionNative); int size = Marshal.SizeOf(nativeType); *align = (uint)size; // Is this correct? return size; } [UnmanagedCallersOnly] internal static byte TypeIsSubclassOf(IntPtr typeHandle, IntPtr othertypeHandle, byte checkInterfaces) { if (typeHandle == othertypeHandle) return 1; Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); Type otherType = Unsafe.As(GCHandle.FromIntPtr(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 TypeIsValueType(IntPtr typeHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); return (byte)(type.IsValueType ? 1 : 0); } [UnmanagedCallersOnly] internal static byte TypeIsEnum(IntPtr typeHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); return (byte)(type.IsEnum ? 1 : 0); } [UnmanagedCallersOnly] internal static IntPtr GetClassParent(IntPtr typeHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); return GCHandle.ToIntPtr(GetTypeGCHandle(type.BaseType)); } [UnmanagedCallersOnly] internal static byte GetMethodParameterIsOut(IntPtr methodHandle, int parameterNum) { MethodHolder methodHolder = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); ParameterInfo parameterInfo = methodHolder.method.GetParameters()[parameterNum]; return (byte)(parameterInfo.IsOut ? 1 : 0); } [UnmanagedCallersOnly] internal static IntPtr GetNullReferenceException() { var exception = new NullReferenceException(); return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak)); } [UnmanagedCallersOnly] internal static IntPtr GetNotSupportedException() { var exception = new NotSupportedException(); return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak)); } [UnmanagedCallersOnly] internal static IntPtr GetArgumentNullException() { var exception = new ArgumentNullException(); return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak)); } [UnmanagedCallersOnly] internal static IntPtr GetArgumentException() { var exception = new ArgumentException(); return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak)); } [UnmanagedCallersOnly] internal static IntPtr GetArgumentOutOfRangeException() { var exception = new ArgumentOutOfRangeException(); return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak)); } [UnmanagedCallersOnly] internal static IntPtr NewGCHandle(IntPtr ptr, byte pinned) { object value = GCHandle.FromIntPtr(ptr).Target; GCHandle handle = GCHandle.Alloc(value, pinned != 0 ? GCHandleType.Pinned : GCHandleType.Normal); return GCHandle.ToIntPtr(handle); } [UnmanagedCallersOnly] internal static IntPtr NewGCHandleWeakref(IntPtr ptr, byte track_resurrection) { object value = GCHandle.FromIntPtr(ptr).Target; GCHandle handle = GCHandle.Alloc(value, track_resurrection != 0 ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); return GCHandle.ToIntPtr(handle); } [UnmanagedCallersOnly] internal static void FreeGCHandle(IntPtr ptr) { GCHandle handle = GCHandle.FromIntPtr(ptr); handle.Free(); } internal enum MonoType { MONO_TYPE_END = 0x00, MONO_TYPE_VOID = 0x01, MONO_TYPE_BOOLEAN = 0x02, MONO_TYPE_CHAR = 0x03, MONO_TYPE_I1 = 0x04, MONO_TYPE_U1 = 0x05, MONO_TYPE_I2 = 0x06, MONO_TYPE_U2 = 0x07, MONO_TYPE_I4 = 0x08, MONO_TYPE_U4 = 0x09, MONO_TYPE_I8 = 0x0a, MONO_TYPE_U8 = 0x0b, MONO_TYPE_R4 = 0x0c, MONO_TYPE_R8 = 0x0d, MONO_TYPE_STRING = 0x0e, MONO_TYPE_PTR = 0x0f, MONO_TYPE_BYREF = 0x10, MONO_TYPE_VALUETYPE = 0x11, MONO_TYPE_CLASS = 0x12, MONO_TYPE_VAR = 0x13, MONO_TYPE_ARRAY = 0x14, MONO_TYPE_GENERICINST = 0x15, MONO_TYPE_TYPEDBYREF = 0x16, MONO_TYPE_I = 0x18, MONO_TYPE_U = 0x19, MONO_TYPE_FNPTR = 0x1b, MONO_TYPE_OBJECT = 0x1c, MONO_TYPE_SZARRAY = 0x1d, MONO_TYPE_MVAR = 0x1e, MONO_TYPE_CMOD_REQD = 0x1f, MONO_TYPE_CMOD_OPT = 0x20, MONO_TYPE_INTERNAL = 0x21, MONO_TYPE_MODIFIER = 0x40, MONO_TYPE_SENTINEL = 0x41, MONO_TYPE_PINNED = 0x45, MONO_TYPE_ENUM = 0x55, }; [UnmanagedCallersOnly] internal static int GetTypeMonoTypeEnum(IntPtr typeHandle) { Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); MonoType monoType; switch (type) { case Type _ when type == typeof(bool): monoType = MonoType.MONO_TYPE_BOOLEAN; break; case Type _ when type == typeof(sbyte): case Type _ when type == typeof(short): monoType = MonoType.MONO_TYPE_I2; break; case Type _ when type == typeof(byte): case Type _ when type == typeof(ushort): monoType = MonoType.MONO_TYPE_U2; break; case Type _ when type == typeof(int): monoType = MonoType.MONO_TYPE_I4; break; case Type _ when type == typeof(uint): monoType = MonoType.MONO_TYPE_U4; break; case Type _ when type == typeof(long): monoType = MonoType.MONO_TYPE_I8; break; case Type _ when type == typeof(ulong): monoType = MonoType.MONO_TYPE_U8; break; case Type _ when type == typeof(float): monoType = MonoType.MONO_TYPE_R4; break; case Type _ when type == typeof(double): monoType = MonoType.MONO_TYPE_R8; break; case Type _ when type == typeof(string): monoType = MonoType.MONO_TYPE_STRING; break; case Type _ when type == typeof(IntPtr): monoType = MonoType.MONO_TYPE_PTR; break; case Type _ when type.IsEnum: { var elementType = type.GetEnumUnderlyingType(); if (elementType == typeof(sbyte) || elementType == typeof(short)) monoType = MonoType.MONO_TYPE_I2; else if (elementType == typeof(byte) || elementType == typeof(ushort)) monoType = MonoType.MONO_TYPE_U2; else if (elementType == typeof(int)) monoType = MonoType.MONO_TYPE_I4; else if (elementType == typeof(uint)) monoType = MonoType.MONO_TYPE_U4; else if (elementType == typeof(long)) monoType = MonoType.MONO_TYPE_I8; else if (elementType == typeof(ulong)) monoType = MonoType.MONO_TYPE_U8; else throw new Exception($"GetTypeMonoTypeEnum: Unsupported type '{type.FullName}'"); break; } case Type _ when type.IsValueType && !type.IsEnum && !type.IsPrimitive: monoType = MonoType.MONO_TYPE_VALUETYPE; break; case Type _ when type.IsClass: monoType = MonoType.MONO_TYPE_OBJECT; break; case Type _ when type.IsGenericType: monoType = MonoType.MONO_TYPE_GENERICINST; break; default: throw new Exception($"GetTypeMonoTypeEnum: Unsupported type '{type.FullName}'"); } return (int)monoType; } /// /// Returns all types that that owned by this assembly. /// private static Type[] GetAssemblyTypes(Assembly assembly) { var referencedAssemblies = assembly.GetReferencedAssemblies(); var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); List referencedTypes = new List(); foreach (var assemblyName in referencedAssemblies) { var asm = allAssemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name); if (asm == null) continue; referencedTypes.AddRange(asm.DefinedTypes.Select(x => x.FullName).ToArray()); } // TODO: use MetadataReader to read types without loading any of the referenced assemblies? // https://makolyte.com/csharp-get-a-list-of-types-defined-in-an-assembly-without-loading-it/ // We need private types of this assembly too, DefinedTypes contains a lot of types from other assemblies... var types = referencedTypes.Any() ? assembly.DefinedTypes.Where(x => !referencedTypes.Contains(x.FullName)).ToArray() : assembly.DefinedTypes.ToArray(); Assert.IsTrue(AppDomain.CurrentDomain.GetAssemblies().Where(x => x.GetName().Name == "FlaxEngine.CSharp").Count() == 1); return types; } /// /// Returns a static GCHandle for given Type, and caches it if needed. /// internal static GCHandle GetOrAddTypeGCHandle(Type type) { if (typeHandleCache.TryGetValue(type, out GCHandle handle)) return handle; if (typeHandleCacheCollectible.TryGetValue(type, out handle)) return handle; handle = GCHandle.Alloc(type); if (type.IsCollectible) // check if generic parameters are also collectible? typeHandleCacheCollectible.Add(type, handle); else typeHandleCache.Add(type, handle); return handle; } /// /// Returns a static GCHandle for given Type. /// internal static GCHandle GetTypeGCHandle(Type type) { if (typeHandleCache.TryGetValue(type, out GCHandle handle)) return handle; if (typeHandleCacheCollectible.TryGetValue(type, out handle)) return handle; throw new Exception($"GCHandle not found for type '{type.FullName}'"); } private static class DelegateHelpers { private static readonly Func MakeNewCustomDelegateFunc = typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static) .CreateDelegate>(); internal static void Init() { // Ensures the MakeNewCustomDelegate is put in the collectible ALC? using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection(); MakeNewCustomDelegateFunc(new[] { typeof(void) }); } internal static Type MakeNewCustomDelegate(Type[] parameters) { if (parameters.Any(x => scriptingAssemblyLoadContext.Assemblies.Contains(x.Module.Assembly))) { // Ensure the new delegate is placed in the collectible ALC //using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection(); return MakeNewCustomDelegateFunc(parameters); } return MakeNewCustomDelegateFunc(parameters); } } /// /// Wrapper class for invoking function pointers from unmanaged code. /// internal class ThunkContext { internal MethodInfo method; internal Type[] parameterTypes; internal Invoker.InvokeThunkDelegate methodDelegate; internal object methodDelegateContext; internal ThunkContext(MethodInfo method) { this.method = method; parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); List methodTypes = new List(); if (!method.IsStatic) methodTypes.Add(method.DeclaringType); if (method.ReturnType != typeof(void)) methodTypes.Add(method.ReturnType); methodTypes.AddRange(parameterTypes); List genericParamTypes = new List(); foreach (var type in methodTypes) { if (type.IsByRef) genericParamTypes.Add(type.GetElementType()); else if (type.IsPointer) genericParamTypes.Add(typeof(IntPtr)); else genericParamTypes.Add(type); } string typeName = $"FlaxEngine.NativeInterop+Invoker+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; Type invokerType = genericParamTypes.Count == 0 ? Type.GetType(typeName) : Type.GetType(typeName).MakeGenericType(genericParamTypes.ToArray()); methodDelegate = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.InvokeThunk), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); methodDelegateContext = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.CreateInvokerDelegate), BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { method }); if (methodDelegate != null) Assert.IsTrue(methodDelegateContext != null); } public IntPtr InvokeThunk(IntPtr instancePtr, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7) { IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 }; if (methodDelegate != null) { IntPtr returnValue; try { returnValue = methodDelegate(methodDelegateContext, instancePtr, nativePtrs); } catch (Exception exception) { if (Debugger.IsAttached && CatchExceptions) throw; // Returned exception is the last parameter IntPtr exceptionPtr = nativePtrs[parameterTypes.Length]; Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak))); return IntPtr.Zero; } return returnValue; } else { // The parameters are wrapped in GCHandles object returnObject; int numParams = parameterTypes.Length; object[] methodParameters = new object[numParams]; for (int i = 0; i < numParams; i++) methodParameters[i] = nativePtrs[i] == IntPtr.Zero ? null : GCHandle.FromIntPtr(nativePtrs[i]).Target; try { returnObject = method.Invoke(instancePtr != IntPtr.Zero ? GCHandle.FromIntPtr(instancePtr).Target : null, methodParameters); } catch (Exception exception) { if (Debugger.IsAttached && CatchExceptions) throw; // Returned exception is the last parameter IntPtr exceptionPtr = nativePtrs[numParams]; Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak))); return IntPtr.Zero; } if (returnObject is not null) { if (method.ReturnType == typeof(string)) return ManagedString.ToNative(Unsafe.As(returnObject)); else if (method.ReturnType == typeof(IntPtr)) return (IntPtr)returnObject; else if (method.ReturnType == typeof(bool)) return (bool)returnObject ? boolTruePtr : boolFalsePtr; else if (method.ReturnType == typeof(Type)) return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(Unsafe.As(returnObject))); else if (method.ReturnType == typeof(object[])) return GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak)); else return GCHandle.ToIntPtr(GCHandle.Alloc(returnObject, GCHandleType.Weak)); } return IntPtr.Zero; } } } } } #endif