diff --git a/Source/Engine/Engine/NativeInterop.Invoker.cs b/Source/Engine/Engine/NativeInterop.Invoker.cs index 2fc54597c..3b5db40e9 100644 --- a/Source/Engine/Engine/NativeInterop.Invoker.cs +++ b/Source/Engine/Engine/NativeInterop.Invoker.cs @@ -31,7 +31,7 @@ namespace FlaxEngine.Interop if (typeof(TRet) == typeof(bool)) return (bool)(object)returnValue ? boolTruePtr : boolFalsePtr; if (typeof(TRet) == typeof(Type)) - return ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As(returnValue))); + return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As(returnValue))); if (typeof(TRet).IsArray) { var elementType = typeof(TRet).GetElementType(); @@ -52,8 +52,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) @@ -72,7 +72,7 @@ namespace FlaxEngine.Interop if (typeof(TRet) == typeof(ManagedHandle)) return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnValue); if (typeof(TRet) == typeof(Type)) - return ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As(returnValue))); + return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As(returnValue))); if (typeof(TRet).IsArray) { var elementType = typeof(TRet).GetElementType(); @@ -108,8 +108,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 2e3bc9183..ebfe4e61d 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -117,13 +117,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 f6239dcf4..4c3cab9c5 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.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.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 = elementType.MakeArrayType(); 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 = elementType.MakeArrayType(); - return GetTypeGCHandle(classType); + return GetTypeManagedHandle(classType); } [UnmanagedCallersOnly] @@ -593,7 +627,7 @@ namespace FlaxEngine.Interop Type classType = obj.GetType(); if (classType == typeof(ManagedArray)) classType = ((ManagedArray)obj).ArrayType; - return GetTypeGCHandle(classType); + return GetTypeManagedHandle(classType); } [UnmanagedCallersOnly] @@ -634,7 +668,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); } @@ -679,6 +713,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) { @@ -792,7 +834,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] @@ -861,7 +903,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) { @@ -869,8 +919,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); } @@ -903,21 +951,16 @@ 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); } [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); } @@ -956,9 +999,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(); @@ -988,7 +1031,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); @@ -1006,8 +1049,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; @@ -1024,37 +1067,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] @@ -1145,32 +1190,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 739329ed8..6c0388d53 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -36,12 +36,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(); @@ -117,6 +117,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); @@ -1036,7 +1068,7 @@ namespace FlaxEngine.Interop internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr) { - Unsafe.Write(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(managedValue)) : IntPtr.Zero); + Unsafe.Write(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(managedValue)) : IntPtr.Zero); } internal static void ToNativePointer(ref T managedValue, IntPtr nativePtr) @@ -1186,6 +1218,72 @@ namespace FlaxEngine.Interop } } + 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); @@ -1302,29 +1400,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) 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 e1de3c207..2f4a94bde 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -189,4 +189,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 0180c0935..8af120eaa 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})."); } @@ -308,7 +308,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 36f1f16af..92cff843d 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) @@ -568,8 +571,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) @@ -634,10 +636,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(); @@ -652,6 +660,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()) @@ -671,14 +712,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) { @@ -698,19 +734,14 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa // TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+) // Open .Net assembly const StringAnsi assemblyPathAnsi = assemblyPath.ToStringAnsi(); - const char* name; - const char* fullname; static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage")); - _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPathAnsi.Get(), &name, &fullname); - _name = name; - _fullname = fullname; - MCore::GC::FreeMemory((void*)name); - MCore::GC::FreeMemory((void*)fullname); + _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPathAnsi.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 @@ -833,8 +864,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 @@ -869,8 +899,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 @@ -930,7 +959,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); @@ -1013,7 +1042,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 @@ -1123,11 +1152,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) @@ -1163,8 +1193,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 @@ -1173,6 +1202,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")); @@ -1505,8 +1540,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; } @@ -1661,6 +1695,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 0a60db42f..d0e273bf7 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -2122,6 +2122,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 ef9c118cf..df47c7683 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -560,4 +560,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 9ece58f2a..f676b5962 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 ///