Optimize ScriptingObject managed initialization

This commit is contained in:
2023-08-04 21:39:17 +03:00
parent d4d404ac0b
commit 53b1d0dd85
14 changed files with 507 additions and 205 deletions

View File

@@ -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<Type>(returnValue)));
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(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<Type>(returnObject)));
if (returnType == typeof(Type) || returnType == typeof(TypeHolder))
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnObject)));
if (returnType.IsArray && ArrayFactory.GetMarshalledType(returnType.GetElementType()) == returnType.GetElementType())
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(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<Type>(returnValue)));
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(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<Type>(returnObject))) : IntPtr.Zero;
if (returnType == typeof(Type) || returnType == typeof(TypeHolder))
return returnObject != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnObject))) : IntPtr.Zero;
if (returnType.IsArray)
{
var elementType = returnType.GetElementType();

View File

@@ -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<Type>(ManagedHandleMarshaller.ConvertToManaged(unmanaged));
public static Type ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As<FlaxEngine.Interop.NativeInterop.TypeHolder>(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);
}

View File

@@ -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<Assembly>(assemblyHandle.Target);
var assemblyTypes = GetAssemblyTypes(assembly);
NativeClassDefinitions* arr = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf<NativeClassDefinitions>());
for (int i = 0; i < assemblyTypes.Length; i++)
{
var type = assemblyTypes[i];
IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf<NativeClassDefinitions>() * 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<Type>(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<Assembly>(assemblyHandle.Target);
Type[] assemblyTypes = GetAssemblyTypes(assembly);
*managedClasses = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf<NativeClassDefinitions>());
*managedClassCount = assemblyTypes.Length;
Span<NativeClassDefinitions> span = new Span<NativeClassDefinitions>(*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<NativeClassDefinitions> span = new Span<NativeClassDefinitions>(Unsafe.Read<IntPtr>(managedClasses).ToPointer(), managedClassCount);
foreach (ref NativeClassDefinitions managedClass in span)
{
TypeHolder typeHolder = Unsafe.As<TypeHolder>(managedClass.typeHandle.Target);
typeHolder.managedClassPointer = managedClass.nativePointer;
}
}
[UnmanagedCallersOnly]
internal static void GetManagedClassFromType(ManagedHandle typeHandle, NativeClassDefinitions* managedClass, ManagedHandle* assemblyHandle)
{
Type type = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
var methods = new List<MethodInfo>();
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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf<NativeFieldDefinitions>());
@@ -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<NativeFieldDefinitions>() * 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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf<NativePropertyDefinitions>());
@@ -364,7 +405,7 @@ namespace FlaxEngine.Interop
[UnmanagedCallersOnly]
internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount)
{
Type type = Unsafe.As<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
object[] attributeValues = type.GetCustomAttributes(false);
ManagedHandle* arr = (ManagedHandle*)NativeAlloc(attributeValues.Length, Unsafe.SizeOf<ManagedHandle>());
@@ -384,13 +425,13 @@ namespace FlaxEngine.Interop
[UnmanagedCallersOnly]
internal static ManagedHandle GetCustomAttribute(ManagedHandle typeHandle, ManagedHandle attributeHandle)
{
Type type = Unsafe.As<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
var attributes = type.GetCustomAttributes(false);
object attrib;
if (attributeHandle.IsAllocated)
{
// Check for certain attribute type
Type attributeType = Unsafe.As<Type>(attributeHandle.Target);
Type attributeType = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(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<ManagedHandle>(IntPtr.Add(arr, IntPtr.Size * i).ToPointer(), handle);
}
*classInterfaces = arr;
@@ -477,7 +518,7 @@ namespace FlaxEngine.Interop
{
MethodHolder methodHolder = Unsafe.As<MethodHolder>(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<ManagedHandle>(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<Type>(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<TypeHolder>(typeHandle.Target);
object value = typeHolder.CreateObject();
return ManagedHandle.Alloc(value);
}
[UnmanagedCallersOnly]
internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size)
{
Type elementType = Unsafe.As<Type>(typeHandle.Target);
Type elementType = Unsafe.As<TypeHolder>(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<Type>(elementTypeHandle.Target);
Type elementType = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(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<FieldHolder>(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<Assembly>(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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
Type otherType = Unsafe.As<Type>(otherTypeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
Type otherType = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
Type otherType = Unsafe.As<Type>(otherTypeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
Type otherType = Unsafe.As<TypeHolder>(otherTypeHandle.Target);
return (byte)(type.IsAssignableFrom(otherType) ? 1 : 0);
}
[UnmanagedCallersOnly]
internal static byte TypeIsValueType(ManagedHandle typeHandle)
{
Type type = Unsafe.As<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
return (byte)(type.IsValueType ? 1 : 0);
}
[UnmanagedCallersOnly]
internal static byte TypeIsEnum(ManagedHandle typeHandle)
{
Type type = Unsafe.As<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
return GetTypeGCHandle(type.BaseType);
TypeHolder typeHolder = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
return GetTypeGCHandle(type.GetElementType());
TypeHolder typeHolder = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
if (type.IsByRef)
type = type.GetElementType(); // Drop reference type (&) to get actual value type
return GetTypeGCHandle(type);
TypeHolder typeHolder = Unsafe.As<TypeHolder>(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<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
return type.IsPointer;
}
[UnmanagedCallersOnly]
internal static bool GetTypeIsReference(ManagedHandle typeHandle)
{
Type type = Unsafe.As<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
return type.IsByRef;
}
[UnmanagedCallersOnly]
internal static uint GetTypeMTypesEnum(ManagedHandle typeHandle)
{
Type type = Unsafe.As<Type>(typeHandle.Target);
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
if (type.IsByRef)
type = type.GetElementType(); // Drop reference type (&) to get actual value type
MTypes monoType;

View File

@@ -36,12 +36,12 @@ namespace FlaxEngine.Interop
private static List<ManagedHandle> methodHandles = new();
private static ConcurrentDictionary<IntPtr, Delegate> cachedDelegates = new();
private static Dictionary<Type, ManagedHandle> typeHandleCache = new();
private static Dictionary<Type, (TypeHolder typeHolder, ManagedHandle handle)> managedTypes = new(new TypeComparer());
private static List<ManagedHandle> fieldHandleCache = new();
#if FLAX_EDITOR
private static List<ManagedHandle> methodHandlesCollectible = new();
private static ConcurrentDictionary<IntPtr, Delegate> cachedDelegatesCollectible = new();
private static Dictionary<Type, ManagedHandle> typeHandleCacheCollectible = new();
private static Dictionary<Type, (TypeHolder typeHolder, ManagedHandle handle)> managedTypesCollectible = new(new TypeComparer());
private static List<ManagedHandle> fieldHandleCacheCollectible = new();
#endif
private static Dictionary<object, ManagedHandle> 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<int>((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read<int>((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<IntPtr>(unmanagedPtrFieldOffset, ref obj);
fieldRef = unmanagedPtr;
}
if (idPtr != IntPtr.Zero)
{
ref Guid nativeId = ref Unsafe.AsRef<Guid>(idPtr.ToPointer());
ref Guid fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<Guid>(internalIdFieldOffset, ref obj);
fieldRef = nativeId;
}
}
[UnmanagedCallersOnly]
internal static ManagedHandle ScriptingObjectCreate(ManagedHandle typeHandle, IntPtr unmanagedPtr, IntPtr idPtr)
{
TypeHolder typeHolder = Unsafe.As<TypeHolder>(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<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(managedValue)) : IntPtr.Zero);
Unsafe.Write<IntPtr>(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<Type>
{
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<IntPtr>(unmanagedPtrFieldOffset, ref obj);
fieldRef = unmanagedPtr;
}
if (idPtr != IntPtr.Zero)
{
ref Guid nativeId = ref Unsafe.AsRef<Guid>(idPtr.ToPointer());
ref Guid fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<Guid>(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;
}
/// <summary>
/// Returns a static ManagedHandle for given Type, and caches it if needed.
/// </summary>
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);
}
/// <summary>
/// Returns a static ManagedHandle to TypeHolder for given Type, and caches it if needed.
/// </summary>
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)

View File

@@ -51,6 +51,15 @@ public:
/// <param name="name">The assembly name.</param>
MAssembly(MDomain* domain, const StringAnsiView& name);
/// <summary>
/// Initializes a new instance of the <see cref="MAssembly"/> class.
/// </summary>
/// <param name="domain">The assembly domain.</param>
/// <param name="name">The assembly name.</param>
/// <param name="fullname">The assembly full name.</param>
/// <param name="handle">The managed handle of the assembly.</param>
MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle);
/// <summary>
/// Finalizes an instance of the <see cref="MAssembly"/> class.
/// </summary>

View File

@@ -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();

View File

@@ -189,4 +189,13 @@ public:
static MClass* Double;
static MClass* String;
};
/// <summary>
/// Utilities for ScriptingObject management.
/// </summary>
struct FLAXENGINE_API ScriptingObject
{
static void SetInternalValues(MObject* object, void* unmanagedPtr, const Guid* id);
static MObject* CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id);
};
};

View File

@@ -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:
/// <param name="result">The return value of undefined type.</param>
void GetValue(MObject* instance, void* result) const;
/// <summary>
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null.
/// </summary>
/// <remarks>
/// Value will be a pointer.
/// </remarks>
/// <param name="instance">The object of given type to get value from.</param>
/// <param name="result">The return value of undefined type.</param>
void GetValueReference(MObject* instance, void* result) const;
/// <summary>
/// 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.
/// </summary>

View File

@@ -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);

View File

@@ -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<MClass*, void*>(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<void*, void*>(GetTypeClassPtr, type);
return GetOrCreateClass(type);
return CallStaticMethod<MClass*, void*>(GetTypeClassPtr, type);
}
MType* MCore::Type::GetElementType(MType* type)
@@ -634,10 +636,16 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
MClass* klass = New<MClass>(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<void, NativeClassDefinitions**, int>(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<void, void*, const char**, const char**>(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<MAssembly>(nullptr, assemblyName, assemblyFullName, assemblyHandle);
CachedAssemblyHandles.Add(assemblyHandle, assembly);
}
MClass* klass = New<MClass>(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes);
if (assembly != nullptr)
{
const_cast<MAssembly::ClassesDictionary&>(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<void*, const char*, const char**, const char**>(GetAssemblyByNamePtr, "System.Private.CoreLib", &name, &fullname);
_name = name;
_fullname = fullname;
MCore::GC::FreeMemory((void*)name);
MCore::GC::FreeMemory((void*)fullname);
_handle = CallStaticMethod<void*, const char*>(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<void*, const char*, const char**, const char**>(LoadAssemblyImagePtr, assemblyPathAnsi.Get(), &name, &fullname);
_name = name;
_fullname = fullname;
MCore::GC::FreeMemory((void*)name);
MCore::GC::FreeMemory((void*)fullname);
_handle = CallStaticMethod<void*, const char*>(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<MType*, void*>(GetClassParentPtr, _handle);
return GetOrCreateClass(parentTypeHandle);
return CallStaticMethod<MClass*, void*>(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<MType*, void*>(GetElementClassPtr, _handle);
return GetOrCreateClass(elementTypeHandle);
return CallStaticMethod<MClass*, void*>(GetElementClassPtr, _handle);
}
MMethod* MClass::GetMethod(const char* name, int32 numParams) const
@@ -930,7 +959,7 @@ const Array<MField*>& MClass::GetFields() const
for (int32 i = 0; i < numFields; i++)
{
NativeFieldDefinitions& definition = fields[i];
MField* field = New<MField>(const_cast<MClass*>(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldAttributes);
MField* field = New<MField>(const_cast<MClass*>(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<int32, void*>(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<void, void*, void*, void*>(FieldGetValuePtr, instance, _handle, result);
}
void MField::GetValueReference(MObject* instance, void* result) const
{
static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReferenceWithOffset"));
CallStaticMethod<void, void*, int, void*>(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<MObject*>& 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<void, MObject*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id);
}
MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id)
{
static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectCreate"));
return CallStaticMethod<MObject*, void*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, klass->_handle, unmanagedPtr, id);
}
#elif DOTNET_HOST_MONO
#ifdef USE_MONO_AOT_MODULE

View File

@@ -2122,6 +2122,15 @@ const Array<MObject*>& 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

View File

@@ -560,4 +560,13 @@ const Array<MObject*>& 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

View File

@@ -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, &param);
}
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, &param);
}
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, &param);
}
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, &param);
}
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())

View File

@@ -206,6 +206,13 @@ public:
/// </summary>
void UnregisterObject();
#if USE_CSHARP
/// <summary>
/// Sets the internal values in managed object.
/// </summary>
static void SetInternalValues(MClass* monoClass, MObject* managedInstance, void* unmanagedPtr, const Guid* id);
#endif
protected:
#if USE_CSHARP
/// <summary>