diff --git a/Source/Engine/Engine/NativeInterop.Invoker.cs b/Source/Engine/Engine/NativeInterop.Invoker.cs index 381e6dac0..36d7f2e03 100644 --- a/Source/Engine/Engine/NativeInterop.Invoker.cs +++ b/Source/Engine/Engine/NativeInterop.Invoker.cs @@ -84,7 +84,7 @@ namespace FlaxEngine.Interop internal static IntPtr MarshalReturnValueType(ref Type returnValue) { - return returnValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(returnValue)) : IntPtr.Zero; + return returnValue != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(returnValue)) : IntPtr.Zero; } internal static IntPtr MarshalReturnValueArray(ref TRet returnValue) @@ -162,8 +162,8 @@ namespace FlaxEngine.Interop return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject); if (returnType == typeof(bool)) return (bool)returnObject ? boolTruePtr : boolFalsePtr; - if (returnType == typeof(Type)) - return ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As(returnObject))); + if (returnType == typeof(Type) || returnType == typeof(TypeHolder)) + return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As(returnObject))); if (returnType.IsArray && ArrayFactory.GetMarshalledType(returnType.GetElementType()) == returnType.GetElementType()) return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnObject)), GCHandleType.Weak); if (returnType.IsArray) @@ -186,8 +186,8 @@ namespace FlaxEngine.Interop return (IntPtr)(object)returnObject; if (returnType == typeof(ManagedHandle)) return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject); - if (returnType == typeof(Type)) - return returnObject != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As(returnObject))) : IntPtr.Zero; + if (returnType == typeof(Type) || returnType == typeof(TypeHolder)) + return returnObject != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As(returnObject))) : IntPtr.Zero; if (returnType.IsArray) { var elementType = returnType.GetElementType(); diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs index a74299670..58aa95a2f 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -119,13 +119,13 @@ namespace FlaxEngine.Interop [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] public static class SystemTypeMarshaller { - public static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)); + public static Type ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandleMarshaller.ConvertToManaged(unmanaged)).type : null; public static IntPtr ConvertToUnmanaged(Type managed) { if (managed == null) return IntPtr.Zero; - ManagedHandle handle = NativeInterop.GetTypeGCHandle(managed); + ManagedHandle handle = NativeInterop.GetTypeManagedHandle(managed); return ManagedHandle.ToIntPtr(handle); } diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index cfa71f97f..a2647dd10 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -19,6 +19,7 @@ namespace FlaxEngine.Interop internal struct NativeClassDefinitions { internal ManagedHandle typeHandle; + internal IntPtr nativePointer; internal IntPtr name; internal IntPtr fullname; internal IntPtr @namespace; @@ -40,6 +41,7 @@ namespace FlaxEngine.Interop internal IntPtr name; internal ManagedHandle fieldHandle; internal ManagedHandle fieldTypeHandle; + internal int fieldOffset; internal uint fieldAttributes; } @@ -139,6 +141,9 @@ namespace FlaxEngine.Interop unsafe partial class NativeInterop { + [LibraryImport("FlaxEngine", EntryPoint = "NativeInterop_CreateClass", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] + internal static partial void NativeInterop_CreateClass(ref NativeClassDefinitions managedClass, ManagedHandle assemblyHandle); + internal enum MTypes : uint { End = 0x00, @@ -205,46 +210,8 @@ namespace FlaxEngine.Interop NativeMemory.AlignedFree(ptr); } - [UnmanagedCallersOnly] - internal static void GetManagedClasses(ManagedHandle assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount) + private static Assembly GetOwningAssembly(Type type) { - 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 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, - }; - Assembly assembly = null; if (type.IsGenericType && !type.Assembly.IsCollectible) { @@ -261,14 +228,87 @@ namespace FlaxEngine.Interop } if (assembly == null) assembly = type.Assembly; + return assembly; + } - *assemblyHandle = GetAssemblyHandle(assembly); + private static NativeClassDefinitions CreateNativeClassDefinitions(Type type, out ManagedHandle assemblyHandle) + { + assemblyHandle = GetAssemblyHandle(GetOwningAssembly(type)); + return CreateNativeClassDefinitions(type); + } + + private static NativeClassDefinitions CreateNativeClassDefinitions(Type type) + { + return new NativeClassDefinitions() + { + typeHandle = RegisterType(type).handle, + name = NativeAllocStringAnsi(type.Name), + fullname = NativeAllocStringAnsi(type.GetTypeName()), + @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), + typeAttributes = (uint)type.Attributes, + }; + } + + private static NativeClassDefinitions CreateNativeClassDefinitions(Type type, ManagedHandle typeHandle, out ManagedHandle assemblyHandle) + { + assemblyHandle = GetAssemblyHandle(GetOwningAssembly(type)); + return new NativeClassDefinitions() + { + typeHandle = typeHandle, + name = NativeAllocStringAnsi(type.Name), + fullname = NativeAllocStringAnsi(type.GetTypeName()), + @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), + typeAttributes = (uint)type.Attributes, + }; + } + + [UnmanagedCallersOnly] + internal static void GetManagedClasses(ManagedHandle assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount) + { + Assembly assembly = Unsafe.As(assemblyHandle.Target); + Type[] assemblyTypes = GetAssemblyTypes(assembly); + + *managedClasses = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf()); + *managedClassCount = assemblyTypes.Length; + Span span = new Span(*managedClasses, assemblyTypes.Length); + for (int i = 0; i < assemblyTypes.Length; i++) + { + Type type = assemblyTypes[i]; + ref var managedClass = ref span[i]; + managedClass = CreateNativeClassDefinitions(type); + } + } + + [UnmanagedCallersOnly] + internal static void RegisterManagedClassNativePointers(NativeClassDefinitions** managedClasses, int managedClassCount) + { + Span span = new Span(Unsafe.Read(managedClasses).ToPointer(), managedClassCount); + foreach (ref NativeClassDefinitions managedClass in span) + { + TypeHolder typeHolder = Unsafe.As(managedClass.typeHandle.Target); + typeHolder.managedClassPointer = managedClass.nativePointer; + } + } + + [UnmanagedCallersOnly] + internal static void GetManagedClassFromType(ManagedHandle typeHandle, NativeClassDefinitions* managedClass, ManagedHandle* assemblyHandle) + { + Type type = Unsafe.As(typeHandle.Target); + *managedClass = CreateNativeClassDefinitions(type, out ManagedHandle handle); + *assemblyHandle = handle; + } + + private static void RegisterNativeClassFromType(TypeHolder typeHolder, ManagedHandle typeHandle) + { + NativeClassDefinitions managedClass = CreateNativeClassDefinitions(typeHolder.type, typeHandle, out ManagedHandle assemblyHandle); + NativeInterop_CreateClass(ref managedClass, assemblyHandle); + typeHolder.managedClassPointer = managedClass.nativePointer; } [UnmanagedCallersOnly] internal static void GetClassMethods(ManagedHandle typeHandle, NativeMethodDefinitions** classMethods, int* classMethodsCount) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); var methods = new List(); var staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); @@ -296,7 +336,7 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); var fields = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf()); @@ -318,7 +358,8 @@ namespace FlaxEngine.Interop { name = NativeAllocStringAnsi(fieldHolder.field.Name), fieldHandle = fieldHandle, - fieldTypeHandle = GetTypeGCHandle(fieldHolder.field.FieldType), + fieldTypeHandle = GetTypeManagedHandle(fieldHolder.field.FieldType), + fieldOffset = fieldHolder.fieldOffset, fieldAttributes = (uint)fieldHolder.field.Attributes, }; Unsafe.Write(IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i).ToPointer(), classField); @@ -330,7 +371,7 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static void GetClassProperties(ManagedHandle typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf()); @@ -364,7 +405,7 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); object[] attributeValues = type.GetCustomAttributes(false); ManagedHandle* arr = (ManagedHandle*)NativeAlloc(attributeValues.Length, Unsafe.SizeOf()); @@ -384,13 +425,13 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static ManagedHandle GetCustomAttribute(ManagedHandle typeHandle, ManagedHandle attributeHandle) { - Type type = Unsafe.As(typeHandle.Target); + 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); + Type attributeType = Unsafe.As(attributeHandle.Target); attrib = attributes.FirstOrDefault(x => x.GetType() == attributeType); } else @@ -413,7 +454,7 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static void GetClassInterfaces(ManagedHandle typeHandle, IntPtr* classInterfaces, int* classInterfacesCount) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); Type[] interfaces = type.GetInterfaces(); // Match mono_class_get_interfaces which doesn't return interfaces from base class @@ -465,7 +506,7 @@ namespace FlaxEngine.Interop IntPtr arr = (IntPtr)NativeAlloc(interfaces.Length, IntPtr.Size); for (int i = 0; i < interfaces.Length; i++) { - ManagedHandle handle = GetTypeGCHandle(interfaces[i]); + ManagedHandle handle = GetTypeManagedHandle(interfaces[i]); Unsafe.Write(IntPtr.Add(arr, IntPtr.Size * i).ToPointer(), handle); } *classInterfaces = arr; @@ -477,7 +518,7 @@ namespace FlaxEngine.Interop { MethodHolder methodHolder = Unsafe.As(methodHandle.Target); Type returnType = methodHolder.returnType; - return GetTypeGCHandle(returnType); + return GetTypeManagedHandle(returnType); } [UnmanagedCallersOnly] @@ -488,7 +529,7 @@ namespace FlaxEngine.Interop IntPtr arr = (IntPtr)NativeAlloc(methodHolder.parameterTypes.Length, IntPtr.Size); for (int i = 0; i < methodHolder.parameterTypes.Length; i++) { - ManagedHandle typeHandle = GetTypeGCHandle(methodHolder.parameterTypes[i]); + ManagedHandle typeHandle = GetTypeManagedHandle(methodHolder.parameterTypes[i]); Unsafe.Write(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i).ToPointer(), typeHandle); } *typeHandles = arr; @@ -509,22 +550,15 @@ namespace FlaxEngine.Interop [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); + TypeHolder typeHolder = Unsafe.As(typeHandle.Target); + object value = typeHolder.CreateObject(); return ManagedHandle.Alloc(value); } [UnmanagedCallersOnly] internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size) { - Type elementType = Unsafe.As(typeHandle.Target); + Type elementType = Unsafe.As(typeHandle.Target); Type marshalledType = ArrayFactory.GetMarshalledType(elementType); Type arrayType = ArrayFactory.GetArrayType(elementType); if (marshalledType.IsValueType) @@ -543,9 +577,9 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) { - Type elementType = Unsafe.As(elementTypeHandle.Target); + Type elementType = Unsafe.As(elementTypeHandle.Target); Type classType = ArrayFactory.GetArrayType(elementType); - return GetTypeGCHandle(classType); + return GetTypeManagedHandle(classType); } [UnmanagedCallersOnly] @@ -606,7 +640,7 @@ namespace FlaxEngine.Interop Type classType = obj.GetType(); if (classType == typeof(ManagedArray)) classType = ((ManagedArray)obj).ArrayType; - return GetTypeGCHandle(classType); + return GetTypeManagedHandle(classType); } [UnmanagedCallersOnly] @@ -647,7 +681,7 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); object value = MarshalToManaged(valuePtr, type); return ManagedHandle.Alloc(value, GCHandleType.Weak); } @@ -690,6 +724,14 @@ namespace FlaxEngine.Interop } } + [UnmanagedCallersOnly] + internal static IntPtr GetObjectClass(ManagedHandle objectHandle) + { + object obj = objectHandle.Target; + TypeHolder typeHolder = GetTypeHolder(obj.GetType()); + return typeHolder.managedClassPointer; + } + [UnmanagedCallersOnly] internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) { @@ -706,7 +748,7 @@ namespace FlaxEngine.Interop catch (Exception exception) { if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); + Unsafe.Write(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); return IntPtr.Zero; } return returnValue; @@ -721,7 +763,7 @@ namespace FlaxEngine.Interop for (int i = 0; i < numParams; i++) { - IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); + IntPtr nativePtr = Unsafe.Read((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer()); methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]); } @@ -737,7 +779,7 @@ namespace FlaxEngine.Interop realException = exception.InnerException; if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(realException, GCHandleType.Weak)); + Unsafe.Write(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak)); else throw realException; return IntPtr.Zero; @@ -749,7 +791,7 @@ namespace FlaxEngine.Interop Type parameterType = methodHolder.parameterTypes[i]; if (parameterType.IsByRef) { - IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); + IntPtr nativePtr = Unsafe.Read((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer()); MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType()); } } @@ -803,7 +845,7 @@ namespace FlaxEngine.Interop internal static int FieldGetOffset(ManagedHandle fieldHandle) { FieldHolder field = Unsafe.As(fieldHandle.Target); - return (int)Marshal.OffsetOf(field.field.DeclaringType, field.field.Name); + return field.fieldOffset; } [UnmanagedCallersOnly] @@ -811,7 +853,40 @@ namespace FlaxEngine.Interop { object fieldOwner = fieldOwnerHandle.Target; FieldHolder field = Unsafe.As(fieldHandle.Target); - field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset); + field.toNativeMarshaller(field.fieldOffset, fieldOwner, valuePtr, out int fieldSize); + } + + [UnmanagedCallersOnly] + internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) + { + object fieldOwner = fieldOwnerHandle.Target; + FieldHolder field = Unsafe.As(fieldHandle.Target); + if (fieldOwner.GetType().IsValueType) + { + ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference(field.fieldOffset, ref fieldOwner); + Unsafe.Write(valuePtr.ToPointer(), fieldRef); + } + else + { + ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(field.fieldOffset, ref fieldOwner); + Unsafe.Write(valuePtr.ToPointer(), fieldRef); + } + } + + [UnmanagedCallersOnly] + internal static void FieldGetValueReferenceWithOffset(ManagedHandle fieldOwnerHandle, int fieldOffset, IntPtr valuePtr) + { + object fieldOwner = fieldOwnerHandle.Target; + if (fieldOwner.GetType().IsValueType) + { + ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + Unsafe.Write(valuePtr.ToPointer(), fieldRef); + } + else + { + ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + Unsafe.Write(valuePtr.ToPointer(), fieldRef); + } } [UnmanagedCallersOnly] @@ -839,7 +914,15 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static ManagedHandle LoadAssemblyImage(IntPtr assemblyPathPtr, IntPtr* assemblyName, IntPtr* assemblyFullName) + internal static void GetAssemblyName(ManagedHandle assemblyHandle, IntPtr* assemblyName, IntPtr* assemblyFullName) + { + Assembly assembly = Unsafe.As(assemblyHandle.Target); + *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); + *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle LoadAssemblyImage(IntPtr assemblyPathPtr) { if (!firstAssemblyLoaded) { @@ -847,8 +930,6 @@ namespace FlaxEngine.Interop 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); } try @@ -882,8 +963,6 @@ namespace FlaxEngine.Interop // 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); } catch (Exception ex) @@ -894,15 +973,12 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static ManagedHandle GetAssemblyByName(IntPtr namePtr, IntPtr* assemblyName, IntPtr* assemblyFullName) + internal static ManagedHandle GetAssemblyByName(IntPtr namePtr) { string name = Marshal.PtrToStringAnsi(namePtr); 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); } @@ -941,9 +1017,9 @@ namespace FlaxEngine.Interop // Release all references in collectible ALC cachedDelegatesCollectible.Clear(); - foreach (var pair in typeHandleCacheCollectible) - pair.Value.Free(); - typeHandleCacheCollectible.Clear(); + foreach (var pair in managedTypesCollectible) + pair.Value.handle.Free(); + managedTypesCollectible.Clear(); foreach (var handle in methodHandlesCollectible) handle.Free(); methodHandlesCollectible.Clear(); @@ -973,7 +1049,7 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static int NativeSizeOf(ManagedHandle typeHandle) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); Type nativeType = GetInternalType(type) ?? type; if (nativeType == typeof(Version)) nativeType = typeof(NativeVersion); @@ -991,8 +1067,8 @@ namespace FlaxEngine.Interop if (typeHandle == otherTypeHandle) return 1; - Type type = Unsafe.As(typeHandle.Target); - Type otherType = Unsafe.As(otherTypeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); + Type otherType = Unsafe.As(otherTypeHandle.Target); if (type == otherType) return 1; @@ -1009,37 +1085,39 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static byte TypeIsAssignableFrom(ManagedHandle typeHandle, ManagedHandle otherTypeHandle) { - Type type = Unsafe.As(typeHandle.Target); - Type otherType = Unsafe.As(otherTypeHandle.Target); + 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); + 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); + Type type = Unsafe.As(typeHandle.Target); return (byte)(type.IsEnum ? 1 : 0); } [UnmanagedCallersOnly] - internal static ManagedHandle GetClassParent(ManagedHandle typeHandle) + internal static IntPtr GetClassParent(ManagedHandle typeHandle) { - Type type = Unsafe.As(typeHandle.Target); - return GetTypeGCHandle(type.BaseType); + TypeHolder typeHolder = Unsafe.As(typeHandle.Target); + TypeHolder baseTypeHolder = GetTypeHolder(typeHolder.type.BaseType); + return baseTypeHolder.managedClassPointer; } [UnmanagedCallersOnly] - internal static ManagedHandle GetElementClass(ManagedHandle typeHandle) + internal static IntPtr GetElementClass(ManagedHandle typeHandle) { - Type type = Unsafe.As(typeHandle.Target); - return GetTypeGCHandle(type.GetElementType()); + TypeHolder typeHolder = Unsafe.As(typeHandle.Target); + TypeHolder elementTypeHolder = GetTypeHolder(typeHolder.type.GetElementType()); + return elementTypeHolder.managedClassPointer; } [UnmanagedCallersOnly] @@ -1130,32 +1208,35 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static ManagedHandle GetTypeClass(ManagedHandle typeHandle) + internal static IntPtr 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); + TypeHolder typeHolder = Unsafe.As(typeHandle.Target); + if (typeHolder.type.IsByRef) + { + // Drop reference type (&) to get actual value type + return GetTypeHolder(typeHolder.type.GetElementType()).managedClassPointer; + } + return typeHolder.managedClassPointer; } [UnmanagedCallersOnly] internal static bool GetTypeIsPointer(ManagedHandle typeHandle) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); return type.IsPointer; } [UnmanagedCallersOnly] internal static bool GetTypeIsReference(ManagedHandle typeHandle) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); return type.IsByRef; } [UnmanagedCallersOnly] internal static uint GetTypeMTypesEnum(ManagedHandle typeHandle) { - Type type = Unsafe.As(typeHandle.Target); + Type type = Unsafe.As(typeHandle.Target); if (type.IsByRef) type = type.GetElementType(); // Drop reference type (&) to get actual value type MTypes monoType; diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 359172e09..bb29a38fd 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -38,12 +38,12 @@ namespace FlaxEngine.Interop private static List methodHandles = new(); private static ConcurrentDictionary cachedDelegates = new(); - private static Dictionary typeHandleCache = new(); + private static Dictionary managedTypes = new(new TypeComparer()); private static List fieldHandleCache = new(); #if FLAX_EDITOR private static List methodHandlesCollectible = new(); private static ConcurrentDictionary cachedDelegatesCollectible = new(); - private static Dictionary typeHandleCacheCollectible = new(); + private static Dictionary managedTypesCollectible = new(new TypeComparer()); private static List fieldHandleCacheCollectible = new(); #endif private static Dictionary classAttributesCacheCollectible = new(); @@ -119,6 +119,38 @@ namespace FlaxEngine.Interop { } + // Cache offsets to frequently accessed fields of FlaxEngine.Object + private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); + private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); + + [UnmanagedCallersOnly] + internal static void ScriptingObjectSetInternalValues(ManagedHandle objectHandle, IntPtr unmanagedPtr, IntPtr idPtr) + { + object obj = objectHandle.Target; + if (obj is not Object) + return; + + { + ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(unmanagedPtrFieldOffset, ref obj); + fieldRef = unmanagedPtr; + } + + if (idPtr != IntPtr.Zero) + { + ref Guid nativeId = ref Unsafe.AsRef(idPtr.ToPointer()); + ref Guid fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(internalIdFieldOffset, ref obj); + fieldRef = nativeId; + } + } + + [UnmanagedCallersOnly] + internal static ManagedHandle ScriptingObjectCreate(ManagedHandle typeHandle, IntPtr unmanagedPtr, IntPtr idPtr) + { + TypeHolder typeHolder = Unsafe.As(typeHandle.Target); + object obj = typeHolder.CreateScriptingObject(unmanagedPtr, idPtr); + return ManagedHandle.Alloc(obj); + } + internal static void* NativeAlloc(int byteCount) { return NativeMemory.AlignedAlloc((UIntPtr)byteCount, 16); @@ -326,19 +358,12 @@ namespace FlaxEngine.Interop return FindType(internalAssemblyQualifiedName); } - internal class ReferenceTypePlaceholder - { - } - - internal struct ValueTypePlaceholder - { - } + 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 delegate void MarshalToNativeFieldDelegate(int fieldOffset, object fieldOwner, IntPtr nativePtr, out int fieldSize); internal static ConcurrentDictionary toManagedMarshallers = new ConcurrentDictionary(1, 3); internal static ConcurrentDictionary toNativeMarshallers = new ConcurrentDictionary(1, 3); @@ -380,7 +405,7 @@ namespace FlaxEngine.Interop deleg(managedObject, nativePtr); } - internal static MarshalToNativeFieldDelegate GetToNativeFieldMarshallerDelegate(Type type) + internal static MarshalToNativeFieldDelegate GetToNativeFieldMarshallerDelegate(FieldInfo field, Type type) { static MarshalToNativeFieldDelegate Factory(Type type) { @@ -397,9 +422,46 @@ namespace FlaxEngine.Interop return toNativeFieldMarshallers.GetOrAdd(type, Factory); } - internal static void MarshalToNativeField(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset) + internal static class FieldHelper { - GetToNativeFieldMarshallerDelegate(fieldOwner.GetType())(field, fieldOwner, nativePtr, out fieldOffset); + /// + /// Returns the address of the field, relative to field owner. + /// + internal static int GetFieldOffset(FieldInfo field, Type type) + { + // Get the address of the field, source: https://stackoverflow.com/a/56512720 + int fieldOffset = Unsafe.Read((field.FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF; + if (!type.IsValueType) + fieldOffset += IntPtr.Size; + return fieldOffset; + } + + /// + /// Returns a reference to the value of the field. + /// + internal static ref TField GetReferenceTypeFieldReference(int fieldOffset, ref object fieldOwner) + { + byte* fieldPtr = (byte*)Unsafe.As(ref fieldOwner) + fieldOffset; + return ref Unsafe.AsRef(fieldPtr); + } + + /// + /// Returns a reference to the value of the field. + /// + internal static ref TField GetValueTypeFieldReference(int fieldOffset, ref T fieldOwner) //where T : struct + { + byte* fieldPtr = (byte*)Unsafe.AsPointer(ref fieldOwner) + fieldOffset; + return ref Unsafe.AsRef(fieldPtr); + } + + /// + /// Returns a reference to the value of the field. + /// + internal static ref TField GetReferenceTypeFieldReference(int fieldOffset, ref T fieldOwner) //where T : class + { + byte* fieldPtr = (byte*)Unsafe.As(ref fieldOwner) + fieldOffset; + return ref Unsafe.AsRef(fieldPtr); + } } /// @@ -408,12 +470,12 @@ namespace FlaxEngine.Interop 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 delegate void MarshalFieldTypedDelegate(int managedFieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize); + internal delegate void* GetBasePointer(ref T fieldOwner); internal static FieldInfo[] marshallableFields; + internal static int[] marshallableFieldOffsets; internal static MarshalFieldTypedDelegate[] toManagedFieldMarshallers; internal static MarshalFieldTypedDelegate[] toNativeFieldMarshallers; @@ -424,28 +486,9 @@ namespace FlaxEngine.Interop { 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(); - + // Setup field-by-field marshallers for reference types or structures containing references 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; @@ -456,6 +499,7 @@ namespace FlaxEngine.Interop { toManagedFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; toNativeFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; + marshallableFieldOffsets = new int[marshallableFields.Length]; BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; for (int i = 0; i < marshallableFields.Length; i++) { @@ -466,8 +510,16 @@ namespace FlaxEngine.Interop if (fieldType.IsPointer) { - toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointer), bindingFlags); - toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointer), bindingFlags); + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointerValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointerValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointerReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointerReferenceType), bindingFlags); + } } else if (fieldType.IsValueType) { @@ -478,8 +530,16 @@ namespace FlaxEngine.Interop } else { - toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedField), bindingFlags); - toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeField), bindingFlags); + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeFieldValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeFieldReferenceType), bindingFlags); + } } } else if (fieldType.IsArray) @@ -487,25 +547,125 @@ namespace FlaxEngine.Interop Type arrayElementType = fieldType.GetElementType(); if (arrayElementType.IsValueType) { - toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArray), bindingFlags); - toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArrayValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeFieldValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArrayReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeFieldReferenceType), bindingFlags); + } } else { - toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedFieldArray), bindingFlags); - toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedFieldArrayValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeFieldValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedFieldArrayReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeFieldReferenceType), bindingFlags); + } } } else { - toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), bindingFlags); - toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedFieldValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeFieldValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedFieldReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeFieldReferenceType), bindingFlags); + } } toManagedFieldMarshallers[i] = toManagedFieldMethod.CreateDelegate(); toNativeFieldMarshallers[i] = toNativeFieldMethod.CreateDelegate(); + marshallableFieldOffsets[i] = FieldHelper.GetFieldOffset(field, type); } } } + + // Setup marshallers for managed and native directions + MethodInfo toManagedMethod; + if (type.IsValueType) + { + string methodName; + if (type == typeof(IntPtr)) + methodName = nameof(MarshalHelperValueType.ToManagedPointer); + else if (type == typeof(ManagedHandle)) + methodName = nameof(MarshalHelperValueType.ToManagedHandle); + else if (marshallableFields != null) + methodName = nameof(MarshalHelperValueType.ToManagedWithMarshallableFields); + else + methodName = nameof(MarshalHelperValueType.ToManaged); + + toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + } + else if (type.IsArray) + { + Type elementType = type.GetElementType(); + if (elementType.IsValueType) + { + string methodName; + if (ArrayFactory.GetMarshalledType(elementType) == elementType) + methodName = nameof(MarshalHelperValueType.ToManagedArray); + else + methodName = nameof(MarshalHelperValueType.ToManagedArrayMarshalled); + toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type.GetElementType()).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + } + else + toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type.GetElementType()).GetMethod(nameof(MarshalHelperReferenceType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); + } + else + { + string methodName; + if (type == typeof(string)) + methodName = nameof(MarshalHelperReferenceType.ToManagedString); + else if (type == typeof(Type)) + methodName = nameof(MarshalHelperReferenceType.ToManagedType); + else if (type.IsClass) + methodName = nameof(MarshalHelperReferenceType.ToManagedClass); + else if (type.IsInterface) // Dictionary + methodName = nameof(MarshalHelperReferenceType.ToManagedInterface); + else + throw new NativeInteropException($"Unsupported type '{type.FullName}'"); + toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + } + toManagedTypedMarshaller = toManagedMethod.CreateDelegate(); + + MethodInfo toNativeMethod; + if (type.IsValueType) + { + if (type.IsByRef) + throw new NotImplementedException(); // Is this possible? + if (marshallableFields != null) + toNativeMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNativeWithMarshallableFields), BindingFlags.Static | BindingFlags.NonPublic); + else + toNativeMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNative), BindingFlags.Static | BindingFlags.NonPublic); + } + else + { + string methodName; + if (type == typeof(string)) + methodName = nameof(MarshalHelperReferenceType.ToNativeString); + else if (type == typeof(Type)) + methodName = nameof(MarshalHelperReferenceType.ToNativeType); + else if (type.IsPointer) + methodName = nameof(MarshalHelperReferenceType.ToNativePointer); + else if (type.IsArray) + methodName = nameof(MarshalHelperReferenceType.ToNativeArray); + else + methodName = nameof(MarshalHelperReferenceType.ToNative); + toNativeMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + } + toNativeTypedMarshaller = toNativeMethod.CreateDelegate(); } internal static object ToManagedWrapper(IntPtr nativePtr, bool byRef) @@ -523,16 +683,12 @@ namespace FlaxEngine.Interop [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static T ToManagedUnbox(IntPtr nativePtr) { - T managed = default; - if (nativePtr != IntPtr.Zero) - { - Type type = typeof(T); - if (type.IsArray) - managed = (T)MarshalToManaged(nativePtr, type); // Array might be in internal format of custom structs so unbox if need to - else - managed = (T)ManagedHandle.FromIntPtr(nativePtr).Target; - } - return managed; + T value = default; + if (nativePtr == IntPtr.Zero) + return value; + + MarshalHelper.ToManaged(ref value, nativePtr, false); + return value; } internal static Array ToManagedArray(Span ptrSpan) @@ -560,52 +716,48 @@ namespace FlaxEngine.Interop toNativeTypedMarshaller(ref managedValue, nativePtr); } - internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr nativePtr, out int fieldOffset) + internal static void ToNativeField(int fieldOffset, ref T fieldOwner, IntPtr nativePtr, out int fieldSize) { if (marshallableFields != null) { for (int i = 0; i < marshallableFields.Length; i++) { - if (marshallableFields[i] == field) + if (marshallableFieldOffsets[i] == fieldOffset) { - toNativeFieldMarshallers[i](marshallableFields[i], ref fieldOwner, nativePtr, out fieldOffset); + toNativeFieldMarshallers[i](fieldOffset, ref fieldOwner, nativePtr, out fieldSize); return; } } } - throw new NativeInteropException($"Invalid field {field.Name} to marshal for type {typeof(T).Name}"); + throw new NativeInteropException($"Invalid field with offset {fieldOffset} to marshal for type {typeof(T).Name}"); } - private static void ToManagedFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + private static void ToManagedFieldPointerValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { - ref IntPtr fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - fieldValueRef = Unsafe.Read(fieldPtr.ToPointer()); - fieldOffset = IntPtr.Size; + ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + fieldValueRef = Unsafe.Read(nativeFieldPtr.ToPointer()); + fieldSize = IntPtr.Size; } - private static void ToNativeFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + private static void ToManagedFieldPointerReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { - ref IntPtr fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - Unsafe.Write(fieldPtr.ToPointer(), fieldValueRef); - fieldOffset = IntPtr.Size; + ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + fieldValueRef = Unsafe.Read(nativeFieldPtr.ToPointer()); + fieldSize = IntPtr.Size; } - /// - /// Returns a reference to the value of the field. - /// - private static ref TField GetFieldReference(FieldInfo field, ref T fieldOwner) + private static void ToNativeFieldPointerValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { - // 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); - } + ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + Unsafe.Write(nativeFieldPtr.ToPointer(), fieldValueRef); + fieldSize = IntPtr.Size; + } + + private static void ToNativeFieldPointerReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + Unsafe.Write(nativeFieldPtr.ToPointer(), fieldValueRef); + fieldSize = IntPtr.Size; } private static IntPtr EnsureAlignment(IntPtr ptr, int alignment) @@ -636,48 +788,92 @@ namespace FlaxEngine.Interop fieldAlignment = GetTypeSize(fieldType); } - internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToManagedFieldValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { - fieldOffset = Unsafe.SizeOf(); + fieldSize = Unsafe.SizeOf(); if (fieldAlignment > 1) { - IntPtr fieldStartPtr = fieldPtr; - fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); - fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, fieldAlignment); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); } - ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - MarshalHelperValueType.ToManaged(ref fieldValueRef, fieldPtr, false); + ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, nativeFieldPtr, false); } - internal static void ToManagedFieldArray(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToManagedFieldReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { - // Follows the same marshalling semantics with reference types - fieldOffset = Unsafe.SizeOf(); - IntPtr fieldStartPtr = fieldPtr; - fieldPtr = EnsureAlignment(fieldPtr, IntPtr.Size); - fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); - - ref TField[] fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - MarshalHelperValueType.ToManagedArray(ref fieldValueRef, Unsafe.Read(fieldPtr.ToPointer()), false); - } - - internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) - { - fieldOffset = Unsafe.SizeOf(); + fieldSize = Unsafe.SizeOf(); if (fieldAlignment > 1) { - IntPtr startPtr = fieldPtr; - fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); - fieldOffset += (fieldPtr - startPtr).ToInt32(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, fieldAlignment); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); + } + + ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, nativeFieldPtr, false); + } + + internal static void ToManagedFieldArrayValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + // Follows the same marshalling semantics with reference types + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); + + ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); + } + + internal static void ToManagedFieldArrayReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + // Follows the same marshalling semantics with reference types + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); + + ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); + } + + internal static void ToNativeFieldValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + fieldSize = Unsafe.SizeOf(); + if (fieldAlignment > 1) + { + IntPtr startPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, fieldAlignment); + fieldSize += (nativeFieldPtr - startPtr).ToInt32(); } #if USE_AOT TField fieldValueRef = (TField)field.GetValue(fieldOwner); #else - ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); #endif - MarshalHelperValueType.ToNative(ref fieldValueRef, fieldPtr); + MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); + } + + internal static void ToNativeFieldReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); + if (fieldAlignment > 1) + { + IntPtr startPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, fieldAlignment); + fieldSize += (nativeFieldPtr - startPtr).ToInt32(); + } + +#if USE_AOT + TField fieldValueRef = (TField)field.GetValue(fieldOwner); +#else + ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); } } @@ -687,50 +883,83 @@ namespace FlaxEngine.Interop { } - internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToManagedField(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) { - fieldOffset = 0; + fieldSize = 0; } - internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToNativeField(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) { - fieldOffset = 0; + fieldSize = 0; } } private static class ReferenceTypeField where TField : class { - internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToManagedFieldValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { - fieldOffset = Unsafe.SizeOf(); - IntPtr fieldStartPtr = fieldPtr; - fieldPtr = EnsureAlignment(fieldPtr, IntPtr.Size); - fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - MarshalHelperReferenceType.ToManaged(ref fieldValueRef, Unsafe.Read(fieldPtr.ToPointer()), false); + ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); } - internal static void ToManagedFieldArray(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToManagedFieldReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { - fieldOffset = Unsafe.SizeOf(); - IntPtr fieldStartPtr = fieldPtr; - fieldPtr = EnsureAlignment(fieldPtr, IntPtr.Size); - fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField[] fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - MarshalHelperReferenceType.ToManagedArray(ref fieldValueRef, Unsafe.Read(fieldPtr.ToPointer()), false); + ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); } - internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + internal static void ToManagedFieldArrayValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { - fieldOffset = Unsafe.SizeOf(); - IntPtr fieldStartPtr = fieldPtr; - fieldPtr = EnsureAlignment(fieldPtr, IntPtr.Size); - fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); - MarshalHelperReferenceType.ToNative(ref fieldValueRef, fieldPtr); + ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); + } + + internal static void ToManagedFieldArrayReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); + + ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); + } + + internal static void ToNativeFieldValueType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); + + ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); + } + + internal static void ToNativeFieldReferenceType(int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); + IntPtr fieldStartPtr = nativeFieldPtr; + nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); + fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); + + ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); } } } @@ -739,80 +968,87 @@ namespace FlaxEngine.Interop { internal static void ToNativeWrapper(object managedObject, IntPtr nativePtr) { - ToNative(ref Unsafe.Unbox(managedObject), nativePtr); + MarshalHelper.ToNative(ref Unsafe.Unbox(managedObject), nativePtr); } - internal static void ToNativeFieldWrapper(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset) + internal static void ToNativeFieldWrapper(int fieldOffset, object fieldOwner, IntPtr nativePtr, out int fieldSize) { - MarshalHelper.ToNativeField(field, ref Unsafe.Unbox(fieldOwner), nativePtr, out fieldOffset); + MarshalHelper.ToNativeField(fieldOffset, ref Unsafe.Unbox(fieldOwner), nativePtr, out fieldSize); + } + + internal static void ToManagedPointer(ref IntPtr managedValue, IntPtr nativePtr, bool byRef) + { + Type type = typeof(T); + byRef |= type.IsByRef; // Is this needed? + if (type.IsByRef) + Assert.IsTrue(type.GetElementType().IsValueType); + + managedValue = byRef ? nativePtr : Unsafe.Read(nativePtr.ToPointer()); + } + + internal static void ToManagedHandle(ref ManagedHandle managedValue, IntPtr nativePtr, bool byRef) + { + managedValue = ManagedHandle.FromIntPtr(nativePtr); + } + + internal static void ToManagedWithMarshallableFields(ref T managedValue, IntPtr nativePtr, bool byRef) + { + IntPtr fieldPtr = nativePtr; + for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) + { + MarshalHelper.toManagedFieldMarshallers[i](MarshalHelper.marshallableFieldOffsets[i], ref managedValue, fieldPtr, out int fieldSize); + fieldPtr += fieldSize; + } + Assert.IsTrue((fieldPtr - nativePtr) <= Unsafe.SizeOf()); } internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) { - Type type = typeof(T); - byRef |= type.IsByRef; - if (byRef) - { - if (type.IsByRef) - type = type.GetElementType(); - Assert.IsTrue(type.IsValueType); - } - - if (type == typeof(IntPtr) && byRef) - managedValue = (T)(object)nativePtr; - else if (type == typeof(ManagedHandle)) - managedValue = (T)(object)ManagedHandle.FromIntPtr(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()); + managedValue = Unsafe.Read(nativePtr.ToPointer()); } internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef) { if (byRef) - nativePtr = Marshal.ReadIntPtr(nativePtr); + nativePtr = Unsafe.Read(nativePtr.ToPointer()); - Type elementType = typeof(T); if (nativePtr != IntPtr.Zero) { ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); - if (ArrayFactory.GetMarshalledType(elementType) == elementType) - managedValue = Unsafe.As(managedArray.ToArray()); - else if (elementType.IsValueType) - managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray)); - else - managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.ToSpan())); + managedValue = Unsafe.As(managedArray.ToArray()); } else managedValue = null; } - internal static void ToNative(ref T managedValue, IntPtr nativePtr) + internal static void ToManagedArrayMarshalled(ref T[] managedValue, IntPtr nativePtr, bool byRef) { - if (typeof(T).IsByRef) - throw new NotImplementedException(); + if (byRef) + nativePtr = Unsafe.Read(nativePtr.ToPointer()); - if (MarshalHelper.marshallableFields != null) + if (nativePtr != IntPtr.Zero) { - 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()); + ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); + managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray)); } else - Unsafe.AsRef(nativePtr.ToPointer()) = managedValue; + managedValue = null; + } + + internal static void ToNativeWithMarshallableFields(ref T managedValue, IntPtr nativePtr) + { + IntPtr fieldPtr = nativePtr; + for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) + { + MarshalHelper.toNativeFieldMarshallers[i](MarshalHelper.marshallableFieldOffsets[i], ref managedValue, nativePtr, out int fieldSize); + nativePtr += fieldSize; + } + Assert.IsTrue((nativePtr - fieldPtr) <= Unsafe.SizeOf()); + } + + internal static void ToNative(ref T managedValue, IntPtr nativePtr) + { + Unsafe.AsRef(nativePtr.ToPointer()) = managedValue; } } @@ -821,37 +1057,47 @@ namespace FlaxEngine.Interop internal static void ToNativeWrapper(object managedObject, IntPtr nativePtr) { T managedValue = Unsafe.As(managedObject); - ToNative(ref managedValue, nativePtr); + MarshalHelper.ToNative(ref managedValue, nativePtr); } - internal static void ToNativeFieldWrapper(FieldInfo field, object managedObject, IntPtr nativePtr, out int fieldOffset) + internal static void ToNativeFieldWrapper(int fieldOffset, object managedObject, IntPtr nativePtr, out int fieldSize) { T managedValue = Unsafe.As(managedObject); - MarshalHelper.ToNativeField(field, ref managedValue, nativePtr, out fieldOffset); + MarshalHelper.ToNativeField(fieldOffset, ref managedValue, nativePtr, out fieldSize); } - internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) + internal static void ToManagedString(ref string managedValue, IntPtr nativePtr, bool byRef) { - Type type = typeof(T); if (byRef) - nativePtr = Marshal.ReadIntPtr(nativePtr); + nativePtr = Unsafe.Read(nativePtr.ToPointer()); + managedValue = ManagedString.ToManaged(nativePtr); + } - if (type == typeof(string)) - managedValue = Unsafe.As(ManagedString.ToManaged(nativePtr)); - else if (nativePtr == IntPtr.Zero) - managedValue = null; - else if (type.IsClass) - managedValue = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); - else if (type.IsInterface) // Dictionary - managedValue = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); - else - throw new NotImplementedException(); + internal static void ToManagedType(ref Type managedValue, IntPtr nativePtr, bool byRef) + { + if (byRef) + nativePtr = Unsafe.Read(nativePtr.ToPointer()); + managedValue = nativePtr == IntPtr.Zero ? null : Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); + } + + internal static void ToManagedClass(ref T managedValue, IntPtr nativePtr, bool byRef) + { + if (byRef) + nativePtr = Unsafe.Read(nativePtr.ToPointer()); + managedValue = nativePtr == IntPtr.Zero ? null : Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); + } + + internal static void ToManagedInterface(ref T managedValue, IntPtr nativePtr, bool byRef) // Dictionary + { + if (byRef) + nativePtr = Unsafe.Read(nativePtr.ToPointer()); + managedValue = nativePtr == IntPtr.Zero ? null : Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); } internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef) { if (byRef) - nativePtr = Marshal.ReadIntPtr(nativePtr); + nativePtr = Unsafe.Read(nativePtr.ToPointer()); if (nativePtr != IntPtr.Zero) { @@ -862,56 +1108,64 @@ namespace FlaxEngine.Interop managedValue = null; } + + internal static void ToNativeString(ref string managedValue, IntPtr nativePtr) + { + Unsafe.Write(nativePtr.ToPointer(), ManagedString.ToNativeWeak(managedValue)); + } + + internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr) + { + Unsafe.Write(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(managedValue)) : IntPtr.Zero); + } + + internal static void ToNativePointer(ref T managedValue, IntPtr nativePtr) + { + IntPtr managedPtr; + if (Pointer.Unbox(managedValue) == null) + managedPtr = IntPtr.Zero; + else if (managedValue is FlaxEngine.Object obj) + managedPtr = FlaxEngine.Object.GetUnmanagedPtr(obj); + else + managedPtr = ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak); + Unsafe.Write(nativePtr.ToPointer(), managedPtr); + } + + internal static void ToNativeArray(ref T managedValue, IntPtr nativePtr) + { + IntPtr managedPtr; + if (managedValue == null) + managedPtr = IntPtr.Zero; + else + { + Type type = typeof(T); + var elementType = type.GetElementType(); + var arr = Unsafe.As(managedValue); + var marshalledType = ArrayFactory.GetMarshalledType(elementType); + ManagedArray managedArray; + if (marshalledType == elementType) + managedArray = ManagedArray.WrapNewArray(arr, type); + else if (elementType.IsValueType) + { + // Convert array of custom structures into internal native layout + managedArray = ManagedArray.AllocateNewArray(arr.Length, type, marshalledType); + IntPtr managedArrayPtr = managedArray.Pointer; + for (int i = 0; i < arr.Length; i++) + { + MarshalToNative(arr.GetValue(i), managedArrayPtr, elementType); + managedArrayPtr += managedArray.ElementSize; + } + } + else + managedArray = ManagedArrayToGCHandleWrappedArray(arr); + managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak); + } + Unsafe.Write(nativePtr.ToPointer(), managedPtr); + } + internal static void ToNative(ref T managedValue, IntPtr nativePtr) { - Type type = typeof(T); - - IntPtr managedPtr; - if (type == typeof(string)) - managedPtr = ManagedString.ToNativeWeak(managedValue as string); - else if (type.IsPointer) - { - if (Pointer.Unbox(managedValue) == null) - managedPtr = IntPtr.Zero; - else if (managedValue is FlaxEngine.Object flaxObj) - managedPtr = FlaxEngine.Object.GetUnmanagedPtr(flaxObj); - else - managedPtr = ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak); - } - else if (type == typeof(Type)) - managedPtr = managedValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle((Type)(object)managedValue)) : IntPtr.Zero; - else if (type.IsArray) - { - if (managedValue == null) - managedPtr = IntPtr.Zero; - else - { - var elementType = type.GetElementType(); - var arr = Unsafe.As(managedValue); - var marshalledType = ArrayFactory.GetMarshalledType(elementType); - ManagedArray managedArray; - if (marshalledType == elementType) - managedArray = ManagedArray.WrapNewArray(arr, type); - else if (elementType.IsValueType) - { - // Convert array of custom structures into internal native layout - managedArray = ManagedArray.AllocateNewArray(arr.Length, type, marshalledType); - IntPtr managedArrayPtr = managedArray.Pointer; - for (int i = 0; i < arr.Length; i++) - { - MarshalToNative(arr.GetValue(i), managedArrayPtr, elementType); - managedArrayPtr += managedArray.ElementSize; - } - } - else - managedArray = ManagedArrayToGCHandleWrappedArray(arr); - managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak); - } - } - else - managedPtr = managedValue != null ? ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak) : IntPtr.Zero; - - Unsafe.Write(nativePtr.ToPointer(), managedPtr); + Unsafe.Write(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak) : IntPtr.Zero); } } @@ -1002,14 +1256,82 @@ namespace FlaxEngine.Interop { internal FieldInfo field; internal MarshalToNativeFieldDelegate toNativeMarshaller; + internal int fieldOffset; internal FieldHolder(FieldInfo field, Type type) { this.field = field; - toNativeMarshaller = GetToNativeFieldMarshallerDelegate(type); + toNativeMarshaller = GetToNativeFieldMarshallerDelegate(field, type); + fieldOffset = FieldHelper.GetFieldOffset(field, type); } } + internal class TypeComparer : IEqualityComparer + { + public bool Equals(Type x, Type y) => x == y; + public int GetHashCode(Type obj) => obj.GetHashCode(); + } + + internal class TypeHolder + { + internal Type type; + internal Type wrappedType; + internal ConstructorInfo ctor; + internal IntPtr managedClassPointer; // MClass* + + internal TypeHolder(Type type) + { + this.type = type; + wrappedType = type; + + 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) + wrappedType = abstractWrapper; + } + + ctor = wrappedType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); + } + + internal object CreateObject() + { + return RuntimeHelpers.GetUninitializedObject(wrappedType); + } + + internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr) + { + object obj = CreateObject(); + if (obj is Object) + { + { + ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(unmanagedPtrFieldOffset, ref obj); + fieldRef = unmanagedPtr; + } + + if (idPtr != IntPtr.Zero) + { + ref Guid nativeId = ref Unsafe.AsRef(idPtr.ToPointer()); + ref Guid fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(internalIdFieldOffset, ref obj); + fieldRef = nativeId; + } + } + + if (ctor != null) + ctor.Invoke(obj, null); + else + Debug.LogException(new Exception($"Missing empty constructor in type '{wrappedType}'.")); + + return obj; + } + + public static implicit operator Type(TypeHolder holder) => holder?.type ?? null; + public bool Equals(TypeHolder other) => type == other.type; + public bool Equals(Type other) => type == other; + public override int GetHashCode() => type.GetHashCode(); + } + internal static class ArrayFactory { private delegate Array CreateArrayDelegate(long size); @@ -1173,29 +1495,77 @@ namespace FlaxEngine.Interop return types; } - /// - /// Returns a static ManagedHandle for given Type, and caches it if needed. - /// - internal static ManagedHandle GetTypeGCHandle(Type type) + internal static TypeHolder GetTypeHolder(Type type) { - if (typeHandleCache.TryGetValue(type, out ManagedHandle handle)) - return handle; + if (managedTypes.TryGetValue(type, out (TypeHolder typeHolder, ManagedHandle handle) tuple)) + return tuple.typeHolder; #if FLAX_EDITOR - if (typeHandleCacheCollectible.TryGetValue(type, out handle)) - return handle; + if (managedTypesCollectible.TryGetValue(type, out tuple)) + return tuple.typeHolder; #endif + return RegisterType(type, true).typeHolder; + } - handle = ManagedHandle.Alloc(type); + internal static (TypeHolder typeHolder, ManagedHandle handle) GetTypeHolderAndManagedHandle(Type type) + { + if (managedTypes.TryGetValue(type, out (TypeHolder typeHolder, ManagedHandle handle) tuple)) + return tuple; #if FLAX_EDITOR - if (type.IsCollectible) // check if generic parameters are also collectible? - typeHandleCacheCollectible.Add(type, handle); + if (managedTypesCollectible.TryGetValue(type, out tuple)) + return tuple; +#endif + return RegisterType(type, true); + } + + /// + /// Returns a static ManagedHandle to TypeHolder for given Type, and caches it if needed. + /// + internal static ManagedHandle GetTypeManagedHandle(Type type) + { + if (managedTypes.TryGetValue(type, out (TypeHolder typeHolder, ManagedHandle handle) tuple)) + return tuple.handle; +#if FLAX_EDITOR + if (managedTypesCollectible.TryGetValue(type, out tuple)) + return tuple.handle; +#endif + return RegisterType(type, true).handle; + } + + internal static (TypeHolder typeHolder, ManagedHandle handle) RegisterType(Type type, bool registerNativeType = false) + { + // TODO: should this strip by-ref? + + (TypeHolder typeHolder, ManagedHandle handle) tuple; + tuple.typeHolder = new TypeHolder(type); + tuple.handle = ManagedHandle.Alloc(tuple.typeHolder); +#if FLAX_EDITOR + bool isCollectible = type.IsCollectible; + if (!isCollectible && type.IsGenericType && !type.Assembly.IsCollectible) + { + // The owning assembly of a generic type with type arguments referencing + // collectible assemblies must be one of the collectible assemblies. + foreach (var genericType in type.GetGenericArguments()) + { + if (genericType.Assembly.IsCollectible) + { + isCollectible = true; + break; + } + } + } + + if (isCollectible) + managedTypesCollectible.Add(type, tuple); else #endif { - typeHandleCache.Add(type, handle); + managedTypes.Add(type, tuple); } - return handle; + if (registerNativeType) + RegisterNativeClassFromType(tuple.typeHolder, tuple.handle); + + return tuple; } internal static int GetTypeSize(Type type) @@ -1339,7 +1709,7 @@ namespace FlaxEngine.Interop // Returned exception is the last parameter IntPtr exceptionPtr = nativePtrs[parameterTypes.Length]; if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); + Unsafe.Write(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); return IntPtr.Zero; } return returnValue; @@ -1361,7 +1731,7 @@ namespace FlaxEngine.Interop if (type.IsByRef) { // References use indirection to support value returning - nativePtr = Marshal.ReadIntPtr(nativePtr); + nativePtr = Unsafe.Read(nativePtr.ToPointer()); type = elementType; } if (type.IsArray) @@ -1381,7 +1751,7 @@ namespace FlaxEngine.Interop // Returned exception is the last parameter IntPtr exceptionPtr = nativePtrs[numParams]; if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); + Unsafe.Write(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); return IntPtr.Zero; } @@ -1395,11 +1765,11 @@ namespace FlaxEngine.Interop { type = type.GetElementType(); if (managed == null) - Marshal.WriteIntPtr(nativePtr, IntPtr.Zero); + Unsafe.Write(nativePtr.ToPointer(), IntPtr.Zero); else if (type.IsArray) MarshalToNative(managed, nativePtr, type); else - Marshal.WriteIntPtr(nativePtr, ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak))); + Unsafe.Write(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak))); } } diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.h b/Source/Engine/Scripting/ManagedCLR/MAssembly.h index e0952f9f6..3436df567 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.h +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.h @@ -51,6 +51,15 @@ public: /// The assembly name. MAssembly(MDomain* domain, const StringAnsiView& name); + /// + /// Initializes a new instance of the class. + /// + /// The assembly domain. + /// The assembly name. + /// The assembly full name. + /// The managed handle of the assembly. + MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle); + /// /// Finalizes an instance of the class. /// diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index 86b906f81..d9cc6f863 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -48,6 +48,18 @@ MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name) { } +MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle) + : _domain(domain) + , _isLoaded(false) + , _isLoading(false) + , _hasCachedClasses(false) + , _reloadCount(0) + , _name(name) + , _fullname(fullname) + , _handle(handle) +{ +} + MAssembly::~MAssembly() { Unload(); diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index 7c61031c7..5a2264047 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -190,4 +190,13 @@ public: static MClass* Double; static MClass* String; }; + + /// + /// Utilities for ScriptingObject management. + /// + struct FLAXENGINE_API ScriptingObject + { + static void SetInternalValues(MObject* object, void* unmanagedPtr, const Guid* id); + static MObject* CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id); + }; }; diff --git a/Source/Engine/Scripting/ManagedCLR/MField.h b/Source/Engine/Scripting/ManagedCLR/MField.h index 796de27f7..41545656d 100644 --- a/Source/Engine/Scripting/ManagedCLR/MField.h +++ b/Source/Engine/Scripting/ManagedCLR/MField.h @@ -19,6 +19,7 @@ protected: #elif USE_NETCORE void* _handle; void* _type; + int32 _fieldOffset; #endif MClass* _parentClass; @@ -35,7 +36,7 @@ public: #if USE_MONO explicit MField(MonoClassField* monoField, const char* name, MClass* parentClass); #elif USE_NETCORE - MField(MClass* parentClass, void* handle, const char* name, void* type, MFieldAttributes attributes); + MField(MClass* parentClass, void* handle, const char* name, void* type, int fieldOffset, MFieldAttributes attributes); #endif public: @@ -102,6 +103,16 @@ public: /// The return value of undefined type. void GetValue(MObject* instance, void* result) const; + /// + /// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null. + /// + /// + /// Value will be a pointer. + /// + /// The object of given type to get value from. + /// The return value of undefined type. + void GetValueReference(MObject* instance, void* result) const; + /// /// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null. If returned value is a value type it will be boxed. /// diff --git a/Source/Engine/Scripting/Object.cs b/Source/Engine/Scripting/Object.cs index 170367b7e..c64532c2b 100644 --- a/Source/Engine/Scripting/Object.cs +++ b/Source/Engine/Scripting/Object.cs @@ -48,7 +48,7 @@ namespace FlaxEngine // Construct missing native object if managed objects gets created in managed world if (__unmanagedPtr == IntPtr.Zero) { - Internal_ManagedInstanceCreated(this); + Internal_ManagedInstanceCreated(this, FlaxEngine.Interop.NativeInterop.GetTypeHolder(GetType()).managedClassPointer); if (__unmanagedPtr == IntPtr.Zero) throw new Exception($"Failed to create native instance for object of type {GetType().FullName} (assembly: {GetType().Assembly.FullName})."); } @@ -320,7 +320,7 @@ namespace FlaxEngine internal static partial Object Internal_Create2(string typeName); [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] - internal static partial void Internal_ManagedInstanceCreated(Object managedInstance); + internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr theKlass); [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 685e0ec91..9bc4fc89d 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -13,6 +13,7 @@ #include "Engine/Platform/Platform.h" #include "Engine/Platform/File.h" #include "Engine/Platform/FileSystem.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MClass.h" @@ -214,6 +215,7 @@ void* GetCustomAttribute(const MClass* klass, const MClass* attributeClass); struct NativeClassDefinitions { void* typeHandle; + MClass* nativePointer; const char* name; const char* fullname; const char* namespace_; @@ -233,6 +235,7 @@ struct NativeFieldDefinitions const char* name; void* fieldHandle; void* fieldType; + int fieldOffset; MFieldAttributes fieldAttributes; }; @@ -341,8 +344,8 @@ void MCore::Object::Init(MObject* obj) MClass* MCore::Object::GetClass(MObject* obj) { ASSERT(obj); - MType* typeHandle = GetObjectType(obj); - return GetOrCreateClass(typeHandle); + static void* GetObjectClassPtr = GetStaticMethodPointer(TEXT("GetObjectClass")); + return (MClass*)CallStaticMethod(GetObjectClassPtr, obj); } MString* MCore::Object::ToString(MObject* obj) @@ -574,8 +577,7 @@ MObject* MCore::Exception::GetNotSupported(const char* msg) MClass* MCore::Type::GetClass(MType* type) { static void* GetTypeClassPtr = GetStaticMethodPointer(TEXT("GetTypeClass")); - type = (MType*)CallStaticMethod(GetTypeClassPtr, type); - return GetOrCreateClass(type); + return CallStaticMethod(GetTypeClassPtr, type); } MType* MCore::Type::GetElementType(MType* type) @@ -640,10 +642,16 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const MClass* klass = New(this, managedClass.typeHandle, managedClass.name, managedClass.fullname, managedClass.namespace_, managedClass.typeAttributes); _classes.Add(klass->GetFullName(), klass); + managedClass.nativePointer = klass; + MCore::GC::FreeMemory((void*)managedClasses[i].name); MCore::GC::FreeMemory((void*)managedClasses[i].fullname); MCore::GC::FreeMemory((void*)managedClasses[i].namespace_); } + + static void* RegisterManagedClassNativePointersPtr = GetStaticMethodPointer(TEXT("RegisterManagedClassNativePointers")); + CallStaticMethod(RegisterManagedClassNativePointersPtr, &managedClasses, classCount); + MCore::GC::FreeMemory(managedClasses); const auto endTime = DateTime::NowUTC(); @@ -658,6 +666,39 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const return _classes; } +void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullname) +{ + static void* GetAssemblyNamePtr = GetStaticMethodPointer(TEXT("GetAssemblyName")); + const char* name_; + const char* fullname_; + CallStaticMethod(GetAssemblyNamePtr, assemblyHandle, &name_, &fullname_); + name = name_; + fullname = fullname_; + MCore::GC::FreeMemory((void*)name_); + MCore::GC::FreeMemory((void*)fullname_); +} + +DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* managedClass, void* assemblyHandle) +{ + MAssembly* assembly = GetAssembly(assemblyHandle); + if (assembly == nullptr) + { + StringAnsi assemblyName; + StringAnsi assemblyFullName; + GetAssemblyName(assemblyHandle, assemblyName, assemblyFullName); + + assembly = New(nullptr, assemblyName, assemblyFullName, assemblyHandle); + CachedAssemblyHandles.Add(assemblyHandle, assembly); + } + + MClass* klass = New(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes); + if (assembly != nullptr) + { + const_cast(assembly->GetClasses()).Add(klass->GetFullName(), klass); + } + managedClass->nativePointer = klass; +} + bool MAssembly::LoadCorlib() { if (IsLoaded()) @@ -677,14 +718,9 @@ bool MAssembly::LoadCorlib() // Load { - const char* name; - const char* fullname; static void* GetAssemblyByNamePtr = GetStaticMethodPointer(TEXT("GetAssemblyByName")); - _handle = CallStaticMethod(GetAssemblyByNamePtr, "System.Private.CoreLib", &name, &fullname); - _name = name; - _fullname = fullname; - MCore::GC::FreeMemory((void*)name); - MCore::GC::FreeMemory((void*)fullname); + _handle = CallStaticMethod(GetAssemblyByNamePtr, "System.Private.CoreLib"); + GetAssemblyName(_handle, _name, _fullname); } if (_handle == nullptr) { @@ -703,19 +739,14 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa { // TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+) // Open .Net assembly - const char* name = nullptr; - const char* fullname = nullptr; static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage")); - _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPath.Get(), &name, &fullname); - _name = StringAnsi(name); - _fullname = StringAnsi(fullname); - MCore::GC::FreeMemory((void*)name); - MCore::GC::FreeMemory((void*)fullname); + _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPath.Get()); if (_handle == nullptr) { Log::CLRInnerException(TEXT(".NET assembly image is invalid at ") + assemblyPath); return true; } + GetAssemblyName(_handle, _name, _fullname); CachedAssemblyHandles.Add(_handle, this); // Provide new path of hot-reloaded native library path for managed DllImport @@ -845,8 +876,7 @@ MType* MClass::GetType() const MClass* MClass::GetBaseClass() const { static void* GetClassParentPtr = GetStaticMethodPointer(TEXT("GetClassParent")); - MType* parentTypeHandle = CallStaticMethod(GetClassParentPtr, _handle); - return GetOrCreateClass(parentTypeHandle); + return CallStaticMethod(GetClassParentPtr, _handle); } bool MClass::IsSubClassOf(const MClass* klass, bool checkInterfaces) const @@ -881,8 +911,7 @@ uint32 MClass::GetInstanceSize() const MClass* MClass::GetElementClass() const { static void* GetElementClassPtr = GetStaticMethodPointer(TEXT("GetElementClass")); - MType* elementTypeHandle = CallStaticMethod(GetElementClassPtr, _handle); - return GetOrCreateClass(elementTypeHandle); + return CallStaticMethod(GetElementClassPtr, _handle); } MMethod* MClass::GetMethod(const char* name, int32 numParams) const @@ -941,7 +970,7 @@ const Array& MClass::GetFields() const for (int32 i = 0; i < numFields; i++) { NativeFieldDefinitions& definition = fields[i]; - MField* field = New(const_cast(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldAttributes); + MField* field = New(const_cast(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldOffset, definition.fieldAttributes); _fields.Add(field); MCore::GC::FreeMemory((void*)definition.name); } @@ -1022,7 +1051,7 @@ bool MClass::HasAttribute(const MClass* monoClass) const bool MClass::HasAttribute() const { - return GetCustomAttribute(this, nullptr) != nullptr; + return !GetAttributes().IsEmpty(); } MObject* MClass::GetAttribute(const MClass* monoClass) const @@ -1132,11 +1161,12 @@ MException::~MException() Delete(InnerException); } -MField::MField(MClass* parentClass, void* handle, const char* name, void* type, MFieldAttributes attributes) +MField::MField(MClass* parentClass, void* handle, const char* name, void* type, int fieldOffset, MFieldAttributes attributes) : _handle(handle) , _type(type) , _parentClass(parentClass) , _name(name) + , _fieldOffset(fieldOffset) , _hasCachedAttributes(false) { switch (attributes & MFieldAttributes::FieldAccessMask) @@ -1172,8 +1202,7 @@ MType* MField::GetType() const int32 MField::GetOffset() const { - static void* FieldGetOffsetPtr = GetStaticMethodPointer(TEXT("FieldGetOffset")); - return CallStaticMethod(FieldGetOffsetPtr, _handle); + return _fieldOffset; } void MField::GetValue(MObject* instance, void* result) const @@ -1182,6 +1211,12 @@ void MField::GetValue(MObject* instance, void* result) const CallStaticMethod(FieldGetValuePtr, instance, _handle, result); } +void MField::GetValueReference(MObject* instance, void* result) const +{ + static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReferenceWithOffset")); + CallStaticMethod(FieldGetValueReferencePtr, instance, _fieldOffset, result); +} + MObject* MField::GetValueBoxed(MObject* instance) const { static void* FieldGetValueBoxedPtr = GetStaticMethodPointer(TEXT("FieldGetValueBoxed")); @@ -1514,8 +1549,7 @@ void* GetCustomAttribute(const MClass* klass, const MClass* attributeClass) const Array& attributes = klass->GetAttributes(); for (MObject* attr : attributes) { - MType* typeHandle = GetObjectType(attr); - MClass* attrClass = GetOrCreateClass(typeHandle); + MClass* attrClass = MCore::Object::GetClass(attr); if (attrClass == attributeClass) return attr; } @@ -1703,6 +1737,18 @@ void* GetStaticMethodPointer(const String& methodName) return fun; } +void MCore::ScriptingObject::SetInternalValues(MObject* object, void* unmanagedPtr, const Guid* id) +{ + static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectSetInternalValues")); + CallStaticMethod(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id); +} + +MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id) +{ + static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectCreate")); + return CallStaticMethod(ScriptingObjectSetInternalValuesPtr, klass->_handle, unmanagedPtr, id); +} + #elif DOTNET_HOST_MONO #ifdef USE_MONO_AOT_MODULE diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index da21e90d0..8d186c661 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -2127,6 +2127,15 @@ const Array& MProperty::GetAttributes() const return _attributes; } +void MCore::ScriptingObject::SetInternalValues(MObject* object, void* unmanagedPtr, const Guid* id) +{ +} + +MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id) +{ + return nullptr; +} + #endif #if USE_MONO && PLATFORM_WIN32 && !USE_MONO_DYNAMIC_LIB diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index 39d85ad84..073b7ca19 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -565,4 +565,13 @@ const Array& MProperty::GetAttributes() const return _attributes; } +void MCore::ScriptingObject::SetInternalValues(MObject* object, void* unmanagedPtr, const Guid* id) +{ +} + +MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id) +{ + return nullptr; +} + #endif diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 12144385d..ddd0eb07d 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -180,10 +180,15 @@ ScriptingObject* ScriptingObject::ToNative(MObject* obj) #if USE_CSHARP if (obj) { +#if USE_MONO // TODO: cache the field offset from object and read directly from object pointer const auto ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr); CHECK_RETURN(ptrField, nullptr); ptrField->GetValue(obj, &ptr); +#else + static const MField* ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr); + ptrField->GetValueReference(obj, &ptr); +#endif } #endif return ptr; @@ -274,12 +279,7 @@ bool ScriptingObject::CreateManaged() if (const auto monoClass = GetClass()) { // Reset managed to unmanaged pointer - const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr); - if (monoUnmanagedPtrField) - { - void* param = nullptr; - monoUnmanagedPtrField->SetValue(managedInstance, ¶m); - } + SetInternalValues(monoClass, managedInstance, nullptr, nullptr); } MCore::GCHandle::Free(handle); return true; @@ -295,6 +295,32 @@ bool ScriptingObject::CreateManaged() #if USE_CSHARP +void ScriptingObject::SetInternalValues(MClass* monoClass, MObject* managedInstance, void* unmanagedPtr, const Guid* id) +{ +#if USE_MONO + // Set handle to unmanaged object + const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr); + if (monoUnmanagedPtrField) + { + const void* param = unmanagedPtr; + monoUnmanagedPtrField->SetValue(managedInstance, ¶m); + } + + if (id != nullptr) + { + // Set object id + const MField* monoIdField = monoClass->GetField(ScriptingObject_id); + if (monoIdField) + { + monoIdField->SetValue(managedInstance, (void*)id); + } + } + +#else + MCore::ScriptingObject::SetInternalValues(managedInstance, unmanagedPtr, id); +#endif +} + MObject* ScriptingObject::CreateManagedInternal() { // Get class @@ -308,6 +334,7 @@ MObject* ScriptingObject::CreateManagedInternal() // Ensure to have managed domain attached (this can be called from custom native thread, eg. content loader) MCore::Thread::Attach(); +#if USE_MONO // Allocate managed instance MObject* managedInstance = MCore::Object::New(monoClass); if (managedInstance == nullptr) @@ -315,23 +342,17 @@ MObject* ScriptingObject::CreateManagedInternal() LOG(Warning, "Failed to create new instance of the object of type {0}", String(monoClass->GetFullName())); } - // Set handle to unmanaged object - const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr); - if (monoUnmanagedPtrField) - { - const void* value = this; - monoUnmanagedPtrField->SetValue(managedInstance, &value); - } - - // Set object id - const MField* monoIdField = monoClass->GetField(ScriptingObject_id); - if (monoIdField) - { - monoIdField->SetValue(managedInstance, (void*)&_id); - } + SetInternalValues(monoClass, managedInstance, this, &_id); // Initialize managed instance (calls constructor) MCore::Object::Init(managedInstance); +#else + MObject* managedInstance = MCore::ScriptingObject::CreateScriptingObject(monoClass, this, &_id); + if (managedInstance == nullptr) + { + LOG(Warning, "Failed to create new instance of the object of type {0}", String(monoClass->GetFullName())); + } +#endif return managedInstance; } @@ -349,12 +370,7 @@ void ScriptingObject::DestroyManaged() { if (const auto monoClass = GetClass()) { - const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr); - if (monoUnmanagedPtrField) - { - void* param = nullptr; - monoUnmanagedPtrField->SetValue(managedInstance, ¶m); - } + SetInternalValues(monoClass, managedInstance, nullptr, nullptr); } } @@ -478,12 +494,7 @@ bool ManagedScriptingObject::CreateManaged() if (const auto monoClass = GetClass()) { // Reset managed to unmanaged pointer - const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr); - if (monoUnmanagedPtrField) - { - void* param = nullptr; - monoUnmanagedPtrField->SetValue(managedInstance, ¶m); - } + SetInternalValues(monoClass, managedInstance, nullptr, nullptr); } MCore::GCHandle::Free(handle); return true; @@ -605,10 +616,8 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create2(MString* typeNameObj) return managedInstance; } -DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* managedInstance) +DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* managedInstance, MClass* typeClass) { - MClass* typeClass = MCore::Object::GetClass(managedInstance); - // Get the assembly with that class auto module = ManagedBinaryModule::FindModule(typeClass); if (module == nullptr) @@ -646,21 +655,8 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* manage MClass* monoClass = obj->GetClass(); - // Set handle to unmanaged object - const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr); - if (monoUnmanagedPtrField) - { - const void* value = obj; - monoUnmanagedPtrField->SetValue(managedInstance, &value); - } - - // Set object id - const MField* monoIdField = monoClass->GetField(ScriptingObject_id); - if (monoIdField) - { - const Guid id = obj->GetID(); - monoIdField->SetValue(managedInstance, (void*)&id); - } + const Guid id = obj->GetID(); + ScriptingObject::SetInternalValues(monoClass, managedInstance, obj, &id); // Register object if (!obj->IsRegistered()) diff --git a/Source/Engine/Scripting/ScriptingObject.h b/Source/Engine/Scripting/ScriptingObject.h index ad72a31c3..0dfa80450 100644 --- a/Source/Engine/Scripting/ScriptingObject.h +++ b/Source/Engine/Scripting/ScriptingObject.h @@ -206,6 +206,13 @@ public: /// void UnregisterObject(); +#if USE_CSHARP + /// + /// Sets the internal values in managed object. + /// + static void SetInternalValues(MClass* monoClass, MObject* managedInstance, void* unmanagedPtr, const Guid* id); +#endif + protected: #if USE_CSHARP ///