// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "MTypes.h" #include "MClass.h" #include "MCore.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/DataContainer.h" #include "Engine/Core/Types/Variant.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Scripting/ScriptingObject.h" #if USE_CSHARP struct Version; class CultureInfo; template class BitArray; namespace MUtils { extern FLAXENGINE_API StringView ToString(MString* str); extern FLAXENGINE_API StringAnsi ToStringAnsi(MString* str); extern FLAXENGINE_API void ToString(MString* str, String& result); extern FLAXENGINE_API void ToString(MString* str, StringView& result); extern FLAXENGINE_API void ToString(MString* str, Variant& result); extern FLAXENGINE_API void ToString(MString* str, StringAnsi& result); extern FLAXENGINE_API MString* ToString(const char* str); extern FLAXENGINE_API MString* ToString(const StringAnsi& str); extern FLAXENGINE_API MString* ToString(const String& str); extern FLAXENGINE_API MString* ToString(const String& str, MDomain* domain); extern FLAXENGINE_API MString* ToString(const StringAnsiView& str); extern FLAXENGINE_API MString* ToString(const StringView& str); extern FLAXENGINE_API MString* ToString(const StringView& str, MDomain* domain); extern FLAXENGINE_API ScriptingTypeHandle UnboxScriptingTypeHandle(MTypeObject* value); extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value); extern FLAXENGINE_API VariantType UnboxVariantType(MType* type); extern FLAXENGINE_API MTypeObject* BoxVariantType(const VariantType& value); extern FLAXENGINE_API Variant UnboxVariant(MObject* value); extern FLAXENGINE_API MObject* BoxVariant(const Variant& value); } // Converter for data of type T between managed and unmanaged world template struct MConverter { MObject* Box(const T& data, const MClass* klass); void Unbox(T& result, MObject* data); void ToManagedArray(MArray* result, const Span& data); void ToNativeArray(Span& result, const MArray* data); }; // Converter for POD types (that can use raw memory copy). template struct MConverter, TNot::Type>>>::Value>::Type> { MObject* Box(const T& data, const MClass* klass) { return MCore::Object::Box((void*)&data, klass); } void Unbox(T& result, MObject* data) { CHECK(data); Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T)); } void ToManagedArray(MArray* result, const Span& data) { Platform::MemoryCopy(MCore::Array::GetAddress(result), data.Get(), data.Length() * sizeof(T)); } void ToNativeArray(Span& result, const MArray* data) { Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(T)); } }; // Converter for String. template<> struct MConverter { MObject* Box(const String& data, const MClass* klass) { #if USE_NETCORE MString* str = MUtils::ToString(data); return MCore::Object::Box(str, klass); #else return (MObject*)MUtils::ToString(data); #endif } void Unbox(String& result, MObject* data) { #if USE_NETCORE MString* str = (MString*)MCore::Object::Unbox(data); result = MUtils::ToString(str); #else result = MUtils::ToString((MString*)data); #endif } void ToManagedArray(MArray* result, const Span& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = (MObject*)MUtils::ToString(data.Get()[i]); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span& result, const MArray* data) { MString** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) MUtils::ToString(dataPtr[i], result[i]); } }; // Converter for StringAnsi. template<> struct MConverter { MObject* Box(const StringAnsi& data, const MClass* klass) { return (MObject*)MUtils::ToString(data); } void Unbox(StringAnsi& result, MObject* data) { result = MUtils::ToStringAnsi((MString*)data); } void ToManagedArray(MArray* result, const Span& data) { if (data.Length() == 0) return; auto* objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = (MObject*)MUtils::ToString(data.Get()[i]); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span& result, const MArray* data) { MString** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) MUtils::ToString(dataPtr[i], result[i]); } }; // Converter for StringView. template<> struct MConverter { MObject* Box(const StringView& data, const MClass* klass) { return (MObject*)MUtils::ToString(data); } void Unbox(StringView& result, MObject* data) { result = MUtils::ToString((MString*)data); } void ToManagedArray(MArray* result, const Span& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = (MObject*)MUtils::ToString(data.Get()[i]); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span& result, const MArray* data) { MString** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) MUtils::ToString(dataPtr[i], result[i]); } }; // Converter for Variant. template<> struct MConverter { MObject* Box(const Variant& data, const MClass* klass) { return MUtils::BoxVariant(data); } void Unbox(Variant& result, MObject* data) { result = MUtils::UnboxVariant(data); } void ToManagedArray(MArray* result, const Span& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = MUtils::BoxVariant(data[i]); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span& result, const MArray* data) { MObject** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) result[i] = MUtils::UnboxVariant(dataPtr[i]); } }; // Converter for Scripting Objects (collection of pointers). template struct MConverter::Value>::Type> { MObject* Box(T* data, const MClass* klass) { return data ? data->GetOrCreateManagedInstance() : nullptr; } void Unbox(T*& result, MObject* data) { result = (T*)ScriptingObject::ToNative(data); } void ToManagedArray(MArray* result, const Span& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = data.Get()[i] ? data.Get()[i]->GetOrCreateManagedInstance() : nullptr; MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span& result, const MArray* data) { MObject** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) result[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); } }; // Converter for Scripting Objects (collection of values). template struct MConverter::Value>::Type> { MObject* Box(const T& data, const MClass* klass) { return data.GetOrCreateManagedInstance(); } void ToManagedArray(MArray* result, const Span& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = data.Get()[i].GetOrCreateManagedInstance(); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } }; // Converter for ScriptingObject References. template class ScriptingObjectReference; template struct MConverter> { MObject* Box(const ScriptingObjectReference& data, const MClass* klass) { return data.GetManagedInstance(); } void Unbox(ScriptingObjectReference& result, MObject* data) { result = (T*)ScriptingObject::ToNative(data); } void ToManagedArray(MArray* result, const Span>& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = data[i].GetManagedInstance(); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span>& result, const MArray* data) { MObject** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) result[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); } }; // Converter for Asset References. template class AssetReference; template struct MConverter> { MObject* Box(const AssetReference& data, const MClass* klass) { return data.GetManagedInstance(); } void Unbox(AssetReference& result, MObject* data) { result = (T*)ScriptingObject::ToNative(data); } void ToManagedArray(MArray* result, const Span>& data) { if (data.Length() == 0) return; MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); for (int32 i = 0; i < data.Length(); i++) objects[i] = data[i].GetManagedInstance(); MCore::GC::WriteArrayRef(result, Span(objects, data.Length())); Allocator::Free(objects); } void ToNativeArray(Span>& result, const MArray* data) { MObject** dataPtr = MCore::Array::GetAddress(data); for (int32 i = 0; i < result.Length(); i++) result[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); } }; // Converter for Array. template struct MConverter> { MObject* Box(const Array& data, const MClass* klass) { if (!klass) return nullptr; MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count()); MConverter converter; converter.ToManagedArray(result, Span(data.Get(), data.Count())); return (MObject*)result; } void Unbox(Array& result, MObject* data) { MArray* array = MCore::Array::Unbox(data); const int32 length = array ? MCore::Array::GetLength(array) : 0; result.Resize(length); MConverter converter; Span resultSpan(result.Get(), length); converter.ToNativeArray(resultSpan, array); } }; namespace MUtils { // Outputs the full typename for the type of the specified object. extern FLAXENGINE_API const StringAnsi& GetClassFullname(MObject* obj); // Returns the class of the provided object. extern FLAXENGINE_API MClass* GetClass(MObject* object); // Returns the class of the provided type. extern FLAXENGINE_API MClass* GetClass(MTypeObject* type); // Returns the class of the provided VariantType value. extern FLAXENGINE_API MClass* GetClass(const VariantType& value); // Returns the class of the provided Variant value. extern FLAXENGINE_API MClass* GetClass(const Variant& value); // Returns the type of the provided object. extern FLAXENGINE_API MTypeObject* GetType(MObject* object); // Returns the type of the provided class. extern FLAXENGINE_API MTypeObject* GetType(MClass* klass); /// /// Boxes the native value into the managed object. /// /// The value. /// The value type class. template MObject* Box(const T& value, const MClass* valueClass) { MConverter converter; return converter.Box(value, valueClass); } /// /// Unboxes MObject to the native value of the given type. /// template T Unbox(MObject* object) { MConverter converter; T result; converter.Unbox(result, object); return result; } /// /// Links managed array data to the unmanaged BytesContainer. /// /// The array object. /// The result data container with linked array data bytes (not copied). extern FLAXENGINE_API BytesContainer LinkArray(MArray* arrayObj); /// /// Allocates new managed array of data and copies contents from given native array. /// /// The array object. /// The array values type class. /// The output array. template MArray* ToArray(const Span& data, const MClass* valueClass) { if (!valueClass) return nullptr; MArray* result = MCore::Array::New(valueClass, data.Length()); MConverter converter; converter.ToManagedArray(result, data); return result; } /// /// Allocates new managed array of data and copies contents from given native array. /// /// The array object. /// The array values type class. /// The output array. template FORCE_INLINE MArray* ToArray(const Array& data, const MClass* valueClass) { return MUtils::ToArray(Span(data.Get(), data.Count()), valueClass); } /// /// Converts the managed array into native array container object. /// /// The managed array object. /// The output array. template Array ToArray(MArray* arrayObj) { Array result; const int32 length = arrayObj ? MCore::Array::GetLength(arrayObj) : 0; result.Resize(length); MConverter converter; Span resultSpan(result.Get(), length); converter.ToNativeArray(resultSpan, arrayObj); return result; } /// /// Converts the managed array into native Span. /// /// The managed array object. /// The output array pointer and size. template Span ToSpan(MArray* arrayObj) { T* ptr = (T*)MCore::Array::GetAddress(arrayObj); const int32 length = arrayObj ? MCore::Array::GetLength(arrayObj) : 0; return Span(ptr, length); } /// /// Converts the native array into native Span. /// /// The native array object. /// The output array pointer and size. template FORCE_INLINE Span ToSpan(const Array& data) { return Span(data.Get(), data.Count()); } /// /// Links managed array data to the unmanaged DataContainer (must use simple type like struct or float/int/bool). /// /// The array object. /// The result data (linked not copied). template void ToArray(MArray* arrayObj, DataContainer& result) { const int32 length = arrayObj ? MCore::Array::GetLength(arrayObj) : 0; if (length == 0) { result.Release(); return; } T* bytesRaw = (T*)MCore::Array::GetAddress(arrayObj); result.Link(bytesRaw, length); } /// /// Allocates new managed bytes array and copies data from the given unmanaged data container. /// /// The input data. /// The output array. FORCE_INLINE MArray* ToArray(const Span& data) { return ToArray(data, MCore::TypeCache::Byte); } /// /// Allocates new managed bytes array and copies data from the given unmanaged data container. /// /// The input data. /// The output array. FORCE_INLINE MArray* ToArray(Array& data) { return ToArray(Span(data.Get(), data.Count()), MCore::TypeCache::Byte); } /// /// Allocates new managed strings array and copies data from the given unmanaged data container. /// /// The input data. /// The output array. FORCE_INLINE MArray* ToArray(const Span& data) { return ToArray(data, MCore::TypeCache::String); } /// /// Allocates new managed strings array and copies data from the given unmanaged data container. /// /// The input data. /// The output array. FORCE_INLINE MArray* ToArray(const Array& data) { return ToArray(Span(data.Get(), data.Count()), MCore::TypeCache::String); } #if USE_NETCORE /// /// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data. /// /// The input data. /// The output array. FORCE_INLINE bool* ToBoolArray(const Array& data) { // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer bool* arr = (bool*)MCore::GC::AllocateMemory(data.Count() * sizeof(bool), true); memcpy(arr, data.Get(), data.Count() * sizeof(bool)); return arr; } /// /// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data. /// /// The input data. /// The output array. template FORCE_INLINE bool* ToBoolArray(const BitArray& data) { // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer bool* arr = (bool*)MCore::GC::AllocateMemory(data.Count() * sizeof(bool), true); for (int i = 0; i < data.Count(); i++) arr[i] = data[i]; return arr; } #else FORCE_INLINE bool* ToBoolArray(const Array& data) { return nullptr; } template FORCE_INLINE bool* ToBoolArray(const BitArray& data) { return nullptr; } #endif extern void* VariantToManagedArgPtr(Variant& value, MType* type, bool& failed); extern MObject* ToManaged(const Version& value); extern Version ToNative(MObject* value); }; #endif