diff --git a/Source/Editor/Content/Import/TextureImportEntry.cs b/Source/Editor/Content/Import/TextureImportEntry.cs
index 3e8006f73..c08ef6e8f 100644
--- a/Source/Editor/Content/Import/TextureImportEntry.cs
+++ b/Source/Editor/Content/Import/TextureImportEntry.cs
@@ -391,8 +391,8 @@ namespace FlaxEditor.Content.Import
MaxSize = managed.MaxSize,
TextureGroup = managed.TextureGroup,
Size = managed.Size,
- SpriteAreas = managed.SpriteAreas?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteAreas))) : IntPtr.Zero,
- SpriteNames = managed.SpriteNames?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteNames))) : IntPtr.Zero,
+ SpriteAreas = managed.SpriteAreas?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.SpriteAreas)) : IntPtr.Zero,
+ SpriteNames = managed.SpriteNames?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.SpriteNames)) : IntPtr.Zero,
};
}
internal static void Free(InternalOptionsNative unmanaged)
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index 8a5ffcf9c..5bc40a03f 100644
--- a/Source/Engine/Engine/NativeInterop.cs
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -157,39 +157,49 @@ namespace FlaxEngine
///
public unsafe class ManagedArray
{
- private ManagedHandle pinnedArrayHandle;
- private IntPtr unmanagedData;
- private Type elementType;
- private int elementSize;
- private int length;
+ private ManagedHandle _pinnedArrayHandle;
+ private IntPtr _unmanagedData;
+ private Type _arrayType;
+ private Type _elementType;
+ private int _elementSize;
+ private int _length;
- public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr);
+ public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType());
+
+ public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType);
///
/// Returns an instance of ManagedArray from shared pool.
///
- ///
- /// The resources must be released by calling FreePooled() instead of Free()-method.
- ///
+ /// The resources must be released by calling FreePooled() instead of Free()-method.
public static ManagedArray WrapPooledArray(Array arr)
{
ManagedArray managedArray = ManagedArrayPool.Get();
- managedArray.WrapArray(arr);
+ managedArray.WrapArray(arr, arr.GetType());
return managedArray;
}
- internal static ManagedArray AllocateNewArray(int length, Type elementType)
- => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, elementType);
-
- internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type elementType)
- => new ManagedArray(ptr, length, elementType);
-
///
/// Returns an instance of ManagedArray from shared pool.
///
- ///
- /// The resources must be released by calling FreePooled() instead of Free()-method.
- ///
+ /// The resources must be released by calling FreePooled() instead of Free()-method.
+ public static ManagedArray WrapPooledArray(Array arr, Type arrayType)
+ {
+ ManagedArray managedArray = ManagedArrayPool.Get();
+ managedArray.WrapArray(arr, arrayType);
+ return managedArray;
+ }
+
+ internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
+ => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, arrayType, elementType);
+
+ internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType)
+ => new ManagedArray(ptr, length, arrayType, elementType);
+
+ ///
+ /// Returns an instance of ManagedArray from shared pool.
+ ///
+ /// The resources must be released by calling FreePooled() instead of Free()-method.
public static ManagedArray AllocatePooledArray(T* ptr, int length) where T : unmanaged
{
ManagedArray managedArray = ManagedArrayPool.Get();
@@ -200,67 +210,67 @@ namespace FlaxEngine
///
/// Returns an instance of ManagedArray from shared pool.
///
- ///
- /// The resources must be released by calling FreePooled() instead of Free()-method.
- ///
+ /// The resources must be released by calling FreePooled() instead of Free()-method.
public static ManagedArray AllocatePooledArray(int length) where T : unmanaged
{
ManagedArray managedArray = ManagedArrayPool.Get();
- managedArray.Allocate((IntPtr)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length, typeof(T));
+ managedArray.Allocate((T*)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length);
return managedArray;
}
- public ManagedArray(Array arr) => WrapArray(arr);
+ public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType);
- internal void WrapArray(Array arr)
+ internal void WrapArray(Array arr, Type arrayType)
{
- pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned);
- unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0);
- length = arr.Length;
- elementType = arr.GetType().GetElementType();
- elementSize = Marshal.SizeOf(elementType);
+ _pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned);
+ _unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0);
+ _length = arr.Length;
+ _arrayType = arrayType;
+ _elementType = arr.GetType().GetElementType();
+ _elementSize = Marshal.SizeOf(_elementType);
}
internal void Allocate(T* ptr, int length) where T : unmanaged
{
- unmanagedData = new IntPtr(ptr);
- this.length = length;
- elementType = typeof(T);
- elementSize = Unsafe.SizeOf();
- }
-
- internal void Allocate(IntPtr ptr, int length, Type elementType)
- {
- unmanagedData = ptr;
- this.length = length;
- this.elementType = elementType;
- elementSize = Marshal.SizeOf(elementType);
+ _unmanagedData = new IntPtr(ptr);
+ _length = length;
+ _arrayType = typeof(T).MakeArrayType();
+ _elementType = typeof(T);
+ _elementSize = Unsafe.SizeOf();
}
private ManagedArray()
{
}
- private ManagedArray(IntPtr ptr, int length, Type elementType) => Allocate(ptr, length, elementType);
+ private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType)
+ {
+ Assert.IsTrue(arrayType.IsArray);
+ _unmanagedData = ptr;
+ _length = length;
+ _arrayType = arrayType;
+ _elementType = elementType;
+ _elementSize = Marshal.SizeOf(elementType);
+ }
~ManagedArray()
{
- if (unmanagedData != IntPtr.Zero)
+ if (_unmanagedData != IntPtr.Zero)
Free();
}
public void Free()
{
GC.SuppressFinalize(this);
- if (pinnedArrayHandle.IsAllocated)
+ if (_pinnedArrayHandle.IsAllocated)
{
- pinnedArrayHandle.Free();
- unmanagedData = IntPtr.Zero;
+ _pinnedArrayHandle.Free();
+ _unmanagedData = IntPtr.Zero;
}
- if (unmanagedData != IntPtr.Zero)
+ if (_unmanagedData != IntPtr.Zero)
{
- NativeInterop.NativeFree(unmanagedData.ToPointer());
- unmanagedData = IntPtr.Zero;
+ NativeInterop.NativeFree(_unmanagedData.ToPointer());
+ _unmanagedData = IntPtr.Zero;
}
}
@@ -270,17 +280,21 @@ namespace FlaxEngine
ManagedArrayPool.Put(this);
}
- internal IntPtr Pointer => unmanagedData;
+ internal IntPtr Pointer => _unmanagedData;
- internal int Length => length;
+ internal int Length => _length;
- internal int ElementSize => elementSize;
+ internal int ElementSize => _elementSize;
- public Span ToSpan() where T : struct => new Span(unmanagedData.ToPointer(), length);
+ internal Type ElementType => _elementType;
- public T[] ToArray() where T : struct => new Span(unmanagedData.ToPointer(), length).ToArray();
+ internal Type ArrayType => _arrayType;
- public Array ToArray() => ArrayCast.ToArray(new Span(unmanagedData.ToPointer(), length * elementSize), elementType);
+ public Span ToSpan() where T : struct => new Span(_unmanagedData.ToPointer(), _length);
+
+ public T[] ToArray() where T : struct => new Span(_unmanagedData.ToPointer(), _length).ToArray();
+
+ public Array ToArray() => ArrayCast.ToArray(new Span(_unmanagedData.ToPointer(), _length * _elementSize), _elementType);
///
/// Creates an Array of the specified type from span of bytes.
@@ -1251,6 +1265,8 @@ namespace FlaxEngine
internal static IntPtr[] ManagedArrayToGCHandleArray(Array array)
{
+ if (array.Length == 0)
+ return Array.Empty();
IntPtr[] pointerArray = new IntPtr[array.Length];
for (int i = 0; i < pointerArray.Length; i++)
{
@@ -1261,6 +1277,12 @@ namespace FlaxEngine
return pointerArray;
}
+ internal static ManagedArray ManagedArrayToGCHandleWrappedArray(Array array)
+ {
+ IntPtr[] pointerArray = ManagedArrayToGCHandleArray(array);
+ return ManagedArray.WrapNewArray(pointerArray, array.GetType());
+ }
+
internal static T[] NativeArrayToManagedArray(Span nativeSpan, Func toManagedFunc)
{
T[] managedArray = new T[nativeSpan.Length];
@@ -1877,11 +1899,11 @@ namespace FlaxEngine
var marshalledType = ArrayFactory.GetMarshalledType(elementType);
ManagedArray managedArray;
if (marshalledType == elementType)
- managedArray = ManagedArray.WrapNewArray(arr);
+ managedArray = ManagedArray.WrapNewArray(arr, type);
else if (elementType.IsValueType)
{
// Convert array of custom structures into internal native layout
- managedArray = ManagedArray.AllocateNewArray(arr.Length, marshalledType);
+ managedArray = ManagedArray.AllocateNewArray(arr.Length, type, marshalledType);
IntPtr managedArrayPtr = managedArray.Pointer;
for (int i = 0; i < arr.Length; i++)
{
@@ -1890,7 +1912,7 @@ namespace FlaxEngine
}
}
else
- managedArray = ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(arr));
+ managedArray = ManagedArrayToGCHandleWrappedArray(arr);
managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
}
}
@@ -2325,21 +2347,30 @@ namespace FlaxEngine
[UnmanagedCallersOnly]
internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size)
{
- Type type = Unsafe.As(typeHandle.Target);
- Type marshalledType = ArrayFactory.GetMarshalledType(type);
+ Type elementType = Unsafe.As(typeHandle.Target);
+ Type marshalledType = ArrayFactory.GetMarshalledType(elementType);
+ Type arrayType = elementType.MakeArrayType();
if (marshalledType.IsValueType)
{
- ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, marshalledType);
+ ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType);
return ManagedHandle.Alloc(managedArray);
}
else
{
- Array arr = ArrayFactory.CreateArray(type, size);
- ManagedArray managedArray = ManagedArray.WrapNewArray(arr);
+ Array arr = ArrayFactory.CreateArray(elementType, size);
+ ManagedArray managedArray = ManagedArray.WrapNewArray(arr, arrayType);
return ManagedHandle.Alloc(managedArray);
}
}
+ [UnmanagedCallersOnly]
+ internal static unsafe ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
+ {
+ Type elementType = Unsafe.As(elementTypeHandle.Target);
+ Type classType = elementType.MakeArrayType();
+ return GetTypeGCHandle(classType);
+ }
+
[UnmanagedCallersOnly]
internal static unsafe IntPtr GetArrayPointer(ManagedHandle arrayHandle)
{
@@ -2392,8 +2423,10 @@ namespace FlaxEngine
[UnmanagedCallersOnly]
internal static ManagedHandle GetObjectType(ManagedHandle handle)
{
- var obj = handle.Target;
+ object obj = handle.Target;
Type classType = obj.GetType();
+ if (classType == typeof(ManagedArray))
+ classType = ((ManagedArray)obj).ArrayType;
return GetTypeGCHandle(classType);
}
@@ -2688,15 +2721,17 @@ namespace FlaxEngine
}
[UnmanagedCallersOnly]
- internal static unsafe int NativeSizeOf(ManagedHandle typeHandle, uint* align)
+ internal static unsafe int NativeSizeOf(ManagedHandle typeHandle)
{
Type type = Unsafe.As(typeHandle.Target);
Type nativeType = GetInternalType(type) ?? type;
if (nativeType == typeof(Version))
nativeType = typeof(VersionNative);
-
- int size = Marshal.SizeOf(nativeType);
- *align = (uint)size; // Is this correct?
+ int size;
+ if (nativeType.IsClass)
+ size = sizeof(IntPtr);
+ else
+ size = Marshal.SizeOf(nativeType);
return size;
}
@@ -2927,6 +2962,7 @@ namespace FlaxEngine
break;
}
case Type _ when type.IsArray:
+ case Type _ when type == typeof(ManagedArray):
monoType = MTypes.Array;
break;
case Type _ when type.IsValueType && !type.IsEnum && !type.IsPrimitive:
diff --git a/Source/Engine/Engine/NativeInterop_Invoker.cs b/Source/Engine/Engine/NativeInterop_Invoker.cs
index b9b793cd5..e7f4f5be2 100644
--- a/Source/Engine/Engine/NativeInterop_Invoker.cs
+++ b/Source/Engine/Engine/NativeInterop_Invoker.cs
@@ -88,7 +88,7 @@ namespace FlaxEngine
var elementType = typeof(TRet).GetElementType();
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnValue)), GCHandleType.Weak);
- return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnValue))), GCHandleType.Weak);
+ return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnValue)), GCHandleType.Weak);
}
return ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak);
}
@@ -108,7 +108,7 @@ namespace FlaxEngine
if (returnType.IsArray && ArrayFactory.GetMarshalledType(returnType.GetElementType()) == returnType.GetElementType())
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnObject)), GCHandleType.Weak);
if (returnType.IsArray)
- return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak);
+ return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnObject)), GCHandleType.Weak);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak);
}
@@ -129,7 +129,7 @@ namespace FlaxEngine
var elementType = typeof(TRet).GetElementType();
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnValue)), GCHandleType.Weak);
- return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnValue))), GCHandleType.Weak);
+ return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnValue)), GCHandleType.Weak);
}
// Match Mono bindings and pass value as pointer to prevent boxing it
if (typeof(TRet) == typeof(System.Boolean))
@@ -166,7 +166,7 @@ namespace FlaxEngine
var elementType = returnType.GetElementType();
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnObject)), GCHandleType.Weak);
- return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak);
+ return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnObject)), GCHandleType.Weak);
}
// Match Mono bindings and pass value as pointer to prevent boxing it
if (returnType == typeof(System.Boolean))
diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp
index 1238d2548..3232605f8 100644
--- a/Source/Engine/Scripting/BinaryModule.cpp
+++ b/Source/Engine/Scripting/BinaryModule.cpp
@@ -1200,9 +1200,10 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
{
// Box instance into C# object
MObject* instanceObject = MUtils::BoxVariant(instance);
+ const MClass* instanceObjectClass = MCore::Object::GetClass(instanceObject);
// Validate instance
- if (!instanceObject || !MCore::Object::GetClass(instanceObject)->IsSubClassOf(mMethod->GetParentClass(), withInterfaces))
+ if (!instanceObject || !instanceObjectClass->IsSubClassOf(mMethod->GetParentClass(), withInterfaces))
{
if (!instanceObject)
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount);
@@ -1212,7 +1213,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
}
// For value-types instance is the actual boxed object data, not te object itself
- mInstance = MCore::Object::GetClass(instanceObject)->IsValueType() ? MCore::Object::Unbox(instanceObject) : instanceObject;
+ mInstance = instanceObjectClass->IsValueType() ? MCore::Object::Unbox(instanceObject) : instanceObject;
}
// Marshal parameters
diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
index 188135611..1022ab5a1 100644
--- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
+++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
@@ -389,7 +389,8 @@ Variant MUtils::UnboxVariant(MObject* value)
case MTypes::Array:
{
void* ptr = MCore::Array::GetAddress((MArray*)value);
- if (klass->GetElementClass() == MCore::TypeCache::Byte)
+ MClass* elementClass = klass->GetElementClass();
+ if (elementClass == MCore::TypeCache::Byte)
{
Variant v;
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
@@ -401,7 +402,6 @@ Variant MUtils::UnboxVariant(MObject* value)
auto& array = v.AsArray();
array.Resize(MCore::Array::GetLength((MArray*)value));
const StringAnsiView elementTypename(*fullname, fullname.Length() - 2);
- MClass* elementClass = klass->GetElementClass();
const int32 elementSize = elementClass->GetInstanceSize();
if (elementClass->IsEnum())
{
diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp
index 271b69137..d50865988 100644
--- a/Source/Engine/Scripting/Runtime/DotNet.cpp
+++ b/Source/Engine/Scripting/Runtime/DotNet.cpp
@@ -374,8 +374,9 @@ MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
MClass* MCore::Array::GetClass(MClass* elementKlass)
{
- MISSING_CODE("TODO: MCore::Array::GetClass"); // TODO: MCore::Object::GetClass
- return nullptr;
+ static void* GetArrayLengthPtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType"));
+ void* classHandle = CallStaticMethod(GetArrayLengthPtr, elementKlass->_handle);
+ return GetOrCreateClass((void*)classHandle);
}
int32 MCore::Array::GetLength(const MArray* obj)
@@ -806,9 +807,8 @@ uint32 MClass::GetInstanceSize() const
{
if (_size != 0)
return _size;
- uint32 align;
static void* NativeSizeOfPtr = GetStaticMethodPointer(TEXT("NativeSizeOf"));
- _size = CallStaticMethod(NativeSizeOfPtr, _handle, &align);
+ _size = CallStaticMethod(NativeSizeOfPtr, _handle);
return _size;
}
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
index 6b9f3d410..85264cb09 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
@@ -1541,7 +1541,7 @@ namespace Flax.Build.Bindings
{
// Array elements passed as GCHandles
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>((ManagedArray)ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
- toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.{fieldInfo.Name})), GCHandleType.Weak) : IntPtr.Zero");
+ toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).ToSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).ToSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}");
}