// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "MUtils.h" #include "MClass.h" #include "MCore.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/DataContainer.h" #include "Engine/Core/Types/Version.h" #include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/Rectangle.h" #include "Engine/Core/Math/Color.h" #include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Vector4.h" #include "Engine/Core/Math/Quaternion.h" #include "Engine/Core/Math/Matrix.h" #include "Engine/Core/Math/Transform.h" #include "Engine/Core/Math/Ray.h" #include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/Internal/StdTypesContainer.h" #include "Engine/Scripting/Internal/ManagedDictionary.h" #include "Engine/Utilities/StringConverter.h" #include "Engine/Content/Asset.h" #if USE_CSHARP namespace { // typeName in format System.Collections.Generic.Dictionary`2[KeyType,ValueType] void GetDictionaryKeyValueTypes(const StringAnsiView& typeName, MClass*& keyClass, MClass*& valueClass) { const int32 keyStart = typeName.Find('['); const int32 keyEnd = typeName.Find(','); const int32 valueEnd = typeName.Find(']'); const StringAnsiView keyTypename(*typeName + keyStart + 1, keyEnd - keyStart - 1); const StringAnsiView valueTypename(*typeName + keyEnd + 1, valueEnd - keyEnd - 1); keyClass = Scripting::FindClass(keyTypename); valueClass = Scripting::FindClass(valueTypename); } } StringView MUtils::ToString(MString* str) { if (str == nullptr) return StringView::Empty; return MCore::String::GetChars(str); } StringAnsi MUtils::ToStringAnsi(MString* str) { if (str == nullptr) return StringAnsi::Empty; return StringAnsi(MCore::String::GetChars(str)); } void MUtils::ToString(MString* str, String& result) { if (str) { const StringView chars = MCore::String::GetChars(str); result.Set(chars.Get(), chars.Length()); } else result.Clear(); } void MUtils::ToString(MString* str, StringView& result) { if (str) result = MCore::String::GetChars(str); else result = StringView(); } void MUtils::ToString(MString* str, Variant& result) { result.SetString(str ? MCore::String::GetChars(str) : StringView::Empty); } void MUtils::ToString(MString* str, StringAnsi& result) { if (str) { const StringView chars = MCore::String::GetChars(str); result.Set(chars.Get(), chars.Length()); } else result.Clear(); } MString* MUtils::ToString(const char* str) { if (str == nullptr || *str == 0) return MCore::String::GetEmpty(); return MCore::String::New(str, StringUtils::Length(str)); } MString* MUtils::ToString(const StringAnsi& str) { const int32 len = str.Length(); if (len <= 0) return MCore::String::GetEmpty(); return MCore::String::New(str.Get(), len); } MString* MUtils::ToString(const String& str) { const int32 len = str.Length(); if (len <= 0) return MCore::String::GetEmpty(); return MCore::String::New(str.Get(), len); } MString* MUtils::ToString(const String& str, MDomain* domain) { const int32 len = str.Length(); if (len <= 0) return MCore::String::GetEmpty(domain); return MCore::String::New(str.Get(), len, domain); } MString* MUtils::ToString(const StringAnsiView& str) { const int32 len = str.Length(); if (len <= 0) return MCore::String::GetEmpty(); return MCore::String::New(str.Get(), str.Length()); } MString* MUtils::ToString(const StringView& str) { const int32 len = str.Length(); if (len <= 0) return MCore::String::GetEmpty(); return MCore::String::New(str.Get(), len); } MString* MUtils::ToString(const StringView& str, MDomain* domain) { const int32 len = str.Length(); if (len <= 0) return MCore::String::GetEmpty(domain); return MCore::String::New(str.Get(), len, domain); } ScriptingTypeHandle MUtils::UnboxScriptingTypeHandle(MTypeObject* value) { MClass* klass = GetClass(value); if (!klass) return ScriptingTypeHandle(); const StringAnsi& typeName = klass->GetFullName(); const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName); if (!typeHandle) LOG(Warning, "Unknown scripting type {}", String(typeName)); return typeHandle; } MTypeObject* MUtils::BoxScriptingTypeHandle(const ScriptingTypeHandle& value) { MTypeObject* result = nullptr; if (value) { MType* mType = value.GetType().ManagedClass->GetType(); result = INTERNAL_TYPE_GET_OBJECT(mType); } return result; } VariantType MUtils::UnboxVariantType(MType* type) { if (!type) return VariantType(VariantType::Null); const auto& stdTypes = *StdTypesContainer::Instance(); MClass* klass = MCore::Type::GetClass(type); MTypes types = MCore::Type::GetType(type); // Fast type detection for in-built types switch (types) { case MTypes::Void: return VariantType(VariantType::Void); case MTypes::Boolean: return VariantType(VariantType::Bool); case MTypes::I1: case MTypes::I2: return VariantType(VariantType::Int16); case MTypes::U1: case MTypes::U2: return VariantType(VariantType::Uint16); case MTypes::I4: case MTypes::Char: return VariantType(VariantType::Int); case MTypes::U4: return VariantType(VariantType::Uint); case MTypes::I8: return VariantType(VariantType::Int64); case MTypes::U8: return VariantType(VariantType::Uint64); case MTypes::R4: return VariantType(VariantType::Float); case MTypes::R8: return VariantType(VariantType::Double); case MTypes::String: return VariantType(VariantType::String); case MTypes::Ptr: return VariantType(VariantType::Pointer); case MTypes::ValueType: if (klass == stdTypes.GuidClass) return VariantType(VariantType::Guid); if (klass == stdTypes.Vector2Class) return VariantType(VariantType::Vector2); if (klass == stdTypes.Vector3Class) return VariantType(VariantType::Vector3); if (klass == stdTypes.Vector4Class) return VariantType(VariantType::Vector4); if (klass == Int2::TypeInitializer.GetClass()) return VariantType(VariantType::Int2); if (klass == Int3::TypeInitializer.GetClass()) return VariantType(VariantType::Int3); if (klass == Int4::TypeInitializer.GetClass()) return VariantType(VariantType::Int4); if (klass == Float2::TypeInitializer.GetClass()) return VariantType(VariantType::Float2); if (klass == Float3::TypeInitializer.GetClass()) return VariantType(VariantType::Float3); if (klass == Float4::TypeInitializer.GetClass()) return VariantType(VariantType::Float4); if (klass == Double2::TypeInitializer.GetClass()) return VariantType(VariantType::Double2); if (klass == Double3::TypeInitializer.GetClass()) return VariantType(VariantType::Double3); if (klass == Double4::TypeInitializer.GetClass()) return VariantType(VariantType::Double4); if (klass == stdTypes.ColorClass) return VariantType(VariantType::Color); if (klass == stdTypes.BoundingBoxClass) return VariantType(VariantType::BoundingBox); if (klass == stdTypes.QuaternionClass) return VariantType(VariantType::Quaternion); if (klass == stdTypes.TransformClass) return VariantType(VariantType::Transform); if (klass == stdTypes.BoundingSphereClass) return VariantType(VariantType::BoundingSphere); if (klass == stdTypes.RectangleClass) return VariantType(VariantType::Rectangle); if (klass == stdTypes.MatrixClass) return VariantType(VariantType::Matrix); break; case MTypes::Object: return VariantType(VariantType::ManagedObject); case MTypes::SzArray: if (klass == MCore::Array::GetClass(MCore::TypeCache::Byte)) return VariantType(VariantType::Blob); break; } // Get actual typename for full type info if (!klass) return VariantType(VariantType::Null); const StringAnsiView fullname = klass->GetFullName(); switch (types) { case MTypes::SzArray: case MTypes::Array: return VariantType(VariantType::Array, fullname); case MTypes::Enum: return VariantType(VariantType::Enum, fullname); case MTypes::ValueType: return VariantType(VariantType::Structure, fullname); } if (klass == stdTypes.TypeClass) return VariantType(VariantType::Typename); if (klass->IsSubClassOf(Asset::GetStaticClass())) { if (klass == Asset::GetStaticClass()) return VariantType(VariantType::Asset); return VariantType(VariantType::Asset, fullname); } if (klass->IsSubClassOf(ScriptingObject::GetStaticClass())) { if (klass == ScriptingObject::GetStaticClass()) return VariantType(VariantType::Object); return VariantType(VariantType::Object, fullname); } // TODO: support any dictionary unboxing LOG(Error, "Invalid managed type to unbox {0}", String(fullname)); return VariantType(); } MTypeObject* MUtils::BoxVariantType(const VariantType& value) { if (value.Type == VariantType::Null) return nullptr; MClass* klass = GetClass(value); if (!klass) { LOG(Error, "Invalid native type to box {0}", value); return nullptr; } MType* mType = klass->GetType(); return INTERNAL_TYPE_GET_OBJECT(mType); } Variant MUtils::UnboxVariant(MObject* value) { if (value == nullptr) return Variant::Null; const auto& stdTypes = *StdTypesContainer::Instance(); MClass* klass = MCore::Object::GetClass(value); MType* mType = klass->GetType(); const MTypes mTypes = MCore::Type::GetType(mType); void* unboxed = MCore::Object::Unbox(value); // Fast type detection for in-built types switch (mTypes) { case MTypes::Void: return Variant(VariantType(VariantType::Void)); case MTypes::Boolean: return *static_cast(unboxed); case MTypes::I1: return *static_cast(unboxed); case MTypes::U1: return *static_cast(unboxed); case MTypes::I2: return *static_cast(unboxed); case MTypes::U2: return *static_cast(unboxed); case MTypes::Char: return *static_cast(unboxed); case MTypes::I4: return *static_cast(unboxed); case MTypes::U4: return *static_cast(unboxed); case MTypes::I8: return *static_cast(unboxed); case MTypes::U8: return *static_cast(unboxed); case MTypes::R4: return *static_cast(unboxed); case MTypes::R8: return *static_cast(unboxed); case MTypes::String: return Variant(MUtils::ToString((MString*)value)); case MTypes::Ptr: return *static_cast(unboxed); case MTypes::ValueType: if (klass == stdTypes.GuidClass) return Variant(*static_cast(unboxed)); if (klass == stdTypes.Vector2Class) return *static_cast(unboxed); if (klass == stdTypes.Vector3Class) return *static_cast(unboxed); if (klass == stdTypes.Vector4Class) return *static_cast(unboxed); if (klass == Int2::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Int3::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Int4::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Float2::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Float3::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Float4::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Double2::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Double3::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == Double4::TypeInitializer.GetClass()) return *static_cast(unboxed); if (klass == stdTypes.ColorClass) return *static_cast(unboxed); if (klass == stdTypes.BoundingBoxClass) return Variant(*static_cast(unboxed)); if (klass == stdTypes.QuaternionClass) return *static_cast(unboxed); if (klass == stdTypes.TransformClass) return Variant(*static_cast(unboxed)); if (klass == stdTypes.BoundingSphereClass) return *static_cast(unboxed); if (klass == stdTypes.RectangleClass) return *static_cast(unboxed); if (klass == stdTypes.MatrixClass) return Variant(*reinterpret_cast(unboxed)); break; case MTypes::SzArray: case MTypes::Array: { void* ptr = MCore::Array::GetAddress((MArray*)value); MClass* elementClass = klass->GetElementClass(); if (elementClass == MCore::TypeCache::Byte) { Variant v; v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value)); return v; } const StringAnsiView fullname = klass->GetFullName(); Variant v; v.SetType(MoveTemp(VariantType(VariantType::Array, fullname))); auto& array = v.AsArray(); array.Resize(MCore::Array::GetLength((MArray*)value)); const StringAnsiView elementTypename(*fullname, fullname.Length() - 2); const int32 elementSize = elementClass->GetInstanceSize(); if (elementClass->IsEnum()) { // Array of Enums for (int32 i = 0; i < array.Count(); i++) { array[i].SetType(VariantType(VariantType::Enum, elementTypename)); Platform::MemoryCopy(&array[i].AsUint64, (byte*)ptr + elementSize * i, elementSize); } } else if (elementClass->IsValueType()) { // Array of Structures VariantType elementType = UnboxVariantType(elementClass->GetType()); switch (elementType.Type) { case VariantType::Bool: case VariantType::Int: case VariantType::Uint: case VariantType::Int64: case VariantType::Uint64: case VariantType::Float: case VariantType::Double: case VariantType::Float2: case VariantType::Float3: case VariantType::Float4: case VariantType::Color: case VariantType::Guid: case VariantType::Quaternion: case VariantType::Rectangle: case VariantType::Int2: case VariantType::Int3: case VariantType::Int4: case VariantType::Int16: case VariantType::Uint16: case VariantType::Double2: case VariantType::Double3: #if !USE_LARGE_WORLDS case VariantType::BoundingSphere: case VariantType::BoundingBox: case VariantType::Ray: #endif // Optimized unboxing of raw data type for (int32 i = 0; i < array.Count(); i++) { auto& a = array[i]; a.SetType(elementType); Platform::MemoryCopy(&a.AsData, (byte*)ptr + elementSize * i, elementSize); } break; case VariantType::Transform: case VariantType::Matrix: case VariantType::Double4: #if USE_LARGE_WORLDS case VariantType::BoundingSphere: case VariantType::BoundingBox: case VariantType::Ray: #endif // Optimized unboxing of raw data type for (int32 i = 0; i < array.Count(); i++) { auto& a = array[i]; a.SetType(elementType); Platform::MemoryCopy(a.AsBlob.Data, (byte*)ptr + elementSize * i, elementSize); } break; case VariantType::Structure: { const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(elementType.TypeName); if (typeHandle) { // Unbox array of structures const ScriptingType& type = typeHandle.GetType(); ASSERT(type.Type == ScriptingTypes::Structure); // TODO: optimize this for large arrays to prevent multiple AllocStructure calls in Variant::SetType by using computed struct type for (int32 i = 0; i < array.Count(); i++) { auto& a = array[i]; a.SetType(elementType); void* managed = (byte*)ptr + elementSize * i; // TODO: optimize structures unboxing to not require MObject* but raw managed value data to prevent additional boxing here MObject* boxed = MCore::Object::New(elementClass); Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize); type.Struct.Unbox(a.AsBlob.Data, boxed); } break; } LOG(Error, "Invalid type to unbox {0}", v.Type); break; } default: LOG(Error, "Invalid type to unbox {0}", v.Type); break; } } else { // Array of Objects for (int32 i = 0; i < array.Count(); i++) array[i] = UnboxVariant(((MObject**)ptr)[i]); } return v; } case MTypes::GenericInst: { if (klass->GetName() == "Dictionary`2" && klass->GetNamespace() == "System.Collections.Generic") { // Dictionary ManagedDictionary managed(value); MArray* managedKeys = managed.GetKeys(); int32 length = managedKeys ? MCore::Array::GetLength(managedKeys) : 0; Dictionary native; native.EnsureCapacity(length); MObject** managedKeysPtr = MCore::Array::GetAddress(managedKeys); for (int32 i = 0; i < length; i++) { MObject* keyManaged = managedKeysPtr[i]; MObject* valueManaged = managed.GetValue(keyManaged); native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged)); } Variant v(MoveTemp(native)); v.Type.SetTypeName(klass->GetFullName()); return v; } break; } } if (klass->IsSubClassOf(Asset::GetStaticClass())) return static_cast(ScriptingObject::ToNative(value)); if (klass->IsSubClassOf(ScriptingObject::GetStaticClass())) return ScriptingObject::ToNative(value); if (klass->IsEnum()) { const StringAnsiView fullname = klass->GetFullName(); Variant v; v.Type = MoveTemp(VariantType(VariantType::Enum, fullname)); // TODO: what about 64-bit enum? use enum size with memcpy v.AsUint64 = *static_cast(MCore::Object::Unbox(value)); return v; } if (klass->IsValueType()) { const StringAnsiView fullname = klass->GetFullName(); const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(fullname); if (typeHandle) { const ScriptingType& type = typeHandle.GetType(); Variant v; v.Type = MoveTemp(VariantType(VariantType::Structure, fullname)); v.AsBlob.Data = Allocator::Allocate(type.Size); v.AsBlob.Length = type.Size; type.Struct.Ctor(v.AsBlob.Data); type.Struct.Unbox(v.AsBlob.Data, value); return v; } return Variant(value); } return Variant(value); } MObject* MUtils::BoxVariant(const Variant& value) { const auto& stdTypes = *StdTypesContainer::Instance(); switch (value.Type.Type) { case VariantType::Null: case VariantType::Void: return nullptr; case VariantType::Bool: return MCore::Object::Box((void*)&value.AsBool, MCore::TypeCache::Boolean); case VariantType::Int16: return MCore::Object::Box((void*)&value.AsInt16, MCore::TypeCache::Int16); case VariantType::Uint16: return MCore::Object::Box((void*)&value.AsUint16, MCore::TypeCache::UInt16); case VariantType::Int: return MCore::Object::Box((void*)&value.AsInt, MCore::TypeCache::Int32); case VariantType::Uint: return MCore::Object::Box((void*)&value.AsUint, MCore::TypeCache::UInt32); case VariantType::Int64: return MCore::Object::Box((void*)&value.AsInt64, MCore::TypeCache::Int64); case VariantType::Uint64: return MCore::Object::Box((void*)&value.AsUint64, MCore::TypeCache::UInt64); case VariantType::Float: return MCore::Object::Box((void*)&value.AsFloat, MCore::TypeCache::Single); case VariantType::Double: return MCore::Object::Box((void*)&value.AsDouble, MCore::TypeCache::Double); case VariantType::Float2: return MCore::Object::Box((void*)&value.AsData, Float2::TypeInitializer.GetClass()); case VariantType::Float3: return MCore::Object::Box((void*)&value.AsData, Float3::TypeInitializer.GetClass()); case VariantType::Float4: return MCore::Object::Box((void*)&value.AsData, Float4::TypeInitializer.GetClass()); case VariantType::Double2: return MCore::Object::Box((void*)&value.AsData, Double2::TypeInitializer.GetClass()); case VariantType::Double3: return MCore::Object::Box((void*)&value.AsData, Double3::TypeInitializer.GetClass()); case VariantType::Double4: return MCore::Object::Box((void*)&value.AsData, Double4::TypeInitializer.GetClass()); case VariantType::Color: return MCore::Object::Box((void*)&value.AsData, stdTypes.ColorClass); case VariantType::Guid: return MCore::Object::Box((void*)&value.AsData, stdTypes.GuidClass); case VariantType::String: #if USE_NETCORE return (MObject*)MUtils::ToString((StringView)value); #else return (MObject*)MUtils::ToString((StringView)value); #endif case VariantType::Quaternion: return MCore::Object::Box((void*)&value.AsData, stdTypes.QuaternionClass); case VariantType::BoundingSphere: return MCore::Object::Box((void*)&value.AsBoundingSphere(), stdTypes.BoundingSphereClass); case VariantType::Rectangle: return MCore::Object::Box((void*)&value.AsData, stdTypes.RectangleClass); case VariantType::Pointer: return MCore::Object::Box((void*)&value.AsPointer, MCore::TypeCache::IntPtr); case VariantType::Ray: return MCore::Object::Box((void*)&value.AsRay(), stdTypes.RayClass); case VariantType::BoundingBox: return MCore::Object::Box((void*)&value.AsBoundingBox(), stdTypes.BoundingBoxClass); case VariantType::Transform: return MCore::Object::Box(value.AsBlob.Data, stdTypes.TransformClass); case VariantType::Matrix: return MCore::Object::Box(value.AsBlob.Data, stdTypes.MatrixClass); case VariantType::Blob: return (MObject*)ToArray(Span((const byte*)value.AsBlob.Data, value.AsBlob.Length)); case VariantType::Object: return value.AsObject ? value.AsObject->GetOrCreateManagedInstance() : nullptr; case VariantType::Asset: return value.AsAsset ? value.AsAsset->GetOrCreateManagedInstance() : nullptr; case VariantType::Array: { MArray* managed; const auto& array = value.AsArray(); if (value.Type.TypeName) { const StringAnsiView elementTypename(value.Type.TypeName, StringUtils::Length(value.Type.TypeName) - 2); const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(elementTypename); MClass* elementClass; if (typeHandle && typeHandle.GetType().ManagedClass) elementClass = typeHandle.GetType().ManagedClass; else elementClass = Scripting::FindClass(elementTypename); if (!elementClass) { LOG(Error, "Invalid type to box {0}", value.Type); return nullptr; } const int32 elementSize = elementClass->GetInstanceSize(); managed = MCore::Array::New(elementClass, array.Count()); if (elementClass->IsEnum()) { // Array of Enums byte* managedPtr = (byte*)MCore::Array::GetAddress(managed); for (int32 i = 0; i < array.Count(); i++) { auto data = (uint64)array[i]; Platform::MemoryCopy(managedPtr + elementSize * i, &data, elementSize); } } else if (elementClass->IsValueType()) { // Array of Structures const VariantType elementType = UnboxVariantType(elementClass->GetType()); byte* managedPtr = (byte*)MCore::Array::GetAddress(managed); switch (elementType.Type) { case VariantType::Bool: case VariantType::Int: case VariantType::Uint: case VariantType::Int64: case VariantType::Uint64: case VariantType::Float: case VariantType::Double: case VariantType::Float2: case VariantType::Float3: case VariantType::Float4: case VariantType::Color: case VariantType::Guid: case VariantType::Quaternion: case VariantType::Rectangle: case VariantType::Int2: case VariantType::Int3: case VariantType::Int4: case VariantType::Int16: case VariantType::Uint16: case VariantType::Double2: case VariantType::Double3: #if !USE_LARGE_WORLDS case VariantType::BoundingSphere: case VariantType::BoundingBox: case VariantType::Ray: #endif // Optimized boxing of raw data type for (int32 i = 0; i < array.Count(); i++) Platform::MemoryCopy(managedPtr + elementSize * i, &array[i].AsData, elementSize); break; case VariantType::Transform: case VariantType::Matrix: case VariantType::Double4: #if USE_LARGE_WORLDS case VariantType::BoundingSphere: case VariantType::BoundingBox: case VariantType::Ray: #endif // Optimized boxing of raw data type for (int32 i = 0; i < array.Count(); i++) Platform::MemoryCopy(managedPtr + elementSize * i, array[i].AsBlob.Data, elementSize); break; case VariantType::Structure: if (typeHandle) { const ScriptingType& type = typeHandle.GetType(); ASSERT(type.Type == ScriptingTypes::Structure); for (int32 i = 0; i < array.Count(); i++) { // TODO: optimize structures boxing to not return MObject* but use raw managed object to prevent additional boxing here MObject* boxed = type.Struct.Box(array[i].AsBlob.Data); Platform::MemoryCopy(managedPtr + elementSize * i, MCore::Object::Unbox(boxed), elementSize); } break; } LOG(Error, "Invalid type to box {0}", value.Type); break; default: LOG(Error, "Invalid type to box {0}", value.Type); break; } } else { // Array of Objects for (int32 i = 0; i < array.Count(); i++) MCore::GC::WriteArrayRef(managed, BoxVariant(array[i]), i); } } else { // object[] managed = MCore::Array::New(MCore::TypeCache::Object, array.Count()); for (int32 i = 0; i < array.Count(); i++) MCore::GC::WriteArrayRef(managed, BoxVariant(array[i]), i); } return (MObject*)managed; } case VariantType::Dictionary: { // Get dictionary key and value types MClass *keyClass, *valueClass; GetDictionaryKeyValueTypes(value.Type.GetTypeName(), keyClass, valueClass); if (!keyClass || !valueClass) { LOG(Error, "Invalid type to box {0}", value.Type); return nullptr; } // Allocate managed dictionary ManagedDictionary managed = ManagedDictionary::New(keyClass->GetType(), valueClass->GetType()); if (!managed.Instance) return nullptr; // Add native keys and values const auto& dictionary = *value.AsDictionary; for (const auto& e : dictionary) { managed.Add(BoxVariant(e.Key), BoxVariant(e.Value)); } return managed.Instance; } case VariantType::Structure: { if (value.AsBlob.Data == nullptr) return nullptr; const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(value.Type.TypeName)); if (typeHandle) { const ScriptingType& type = typeHandle.GetType(); return type.Struct.Box(value.AsBlob.Data); } LOG(Error, "Invalid type to box {0}", value.Type); return nullptr; } case VariantType::Enum: { const auto klass = Scripting::FindClass(StringAnsiView(value.Type.TypeName)); if (klass) return MCore::Object::Box((void*)&value.AsUint64, klass); LOG(Error, "Invalid type to box {0}", value.Type); return nullptr; } case VariantType::ManagedObject: #if USE_NETCORE return value.AsUint64 ? MCore::GCHandle::GetTarget(value.AsUint64) : nullptr; #else return value.AsUint ? MCore::GCHandle::GetTarget(value.AsUint) : nullptr; #endif case VariantType::Typename: { const auto klass = Scripting::FindClass((StringAnsiView)value); if (klass) return (MObject*)GetType(klass); LOG(Error, "Invalid type to box {0}", value); return nullptr; } default: LOG(Error, "Invalid type to box {0}", value.Type); return nullptr; } } const StringAnsi& MUtils::GetClassFullname(MObject* obj) { if (obj) { MClass* mClass = MCore::Object::GetClass(obj); return mClass->GetFullName(); } return StringAnsi::Empty; } MClass* MUtils::GetClass(MTypeObject* type) { if (type == nullptr) return nullptr; MType* mType = INTERNAL_TYPE_OBJECT_GET(type); return MCore::Type::GetClass(mType); } MClass* MUtils::GetClass(const VariantType& value) { auto mclass = Scripting::FindClass(StringAnsiView(value.TypeName)); if (mclass) return mclass; const auto& stdTypes = *StdTypesContainer::Instance(); switch (value.Type) { case VariantType::Void: return MCore::TypeCache::Void; case VariantType::Bool: return MCore::TypeCache::Boolean; case VariantType::Int16: return MCore::TypeCache::Int16; case VariantType::Uint16: return MCore::TypeCache::UInt16; case VariantType::Int: return MCore::TypeCache::Int32; case VariantType::Uint: return MCore::TypeCache::UInt32; case VariantType::Int64: return MCore::TypeCache::Int64; case VariantType::Uint64: return MCore::TypeCache::UInt64; case VariantType::Float: return MCore::TypeCache::Single; case VariantType::Double: return MCore::TypeCache::Double; case VariantType::Pointer: return MCore::TypeCache::IntPtr; case VariantType::String: return MCore::TypeCache::String; case VariantType::Object: return ScriptingObject::GetStaticClass(); case VariantType::Asset: return Asset::GetStaticClass(); case VariantType::Blob: return MCore::Array::GetClass(MCore::TypeCache::Byte); case VariantType::Float2: return Double2::TypeInitializer.GetClass(); case VariantType::Float3: return Float3::TypeInitializer.GetClass(); case VariantType::Float4: return Float4::TypeInitializer.GetClass(); case VariantType::Double2: return Double2::TypeInitializer.GetClass(); case VariantType::Double3: return Double3::TypeInitializer.GetClass(); case VariantType::Double4: return Double4::TypeInitializer.GetClass(); case VariantType::Color: return stdTypes.ColorClass; case VariantType::Guid: return stdTypes.GuidClass; case VariantType::Typename: return stdTypes.TypeClass; case VariantType::BoundingBox: return stdTypes.BoundingBoxClass; case VariantType::BoundingSphere: return stdTypes.BoundingSphereClass; case VariantType::Quaternion: return stdTypes.QuaternionClass; case VariantType::Transform: return stdTypes.TransformClass; case VariantType::Rectangle: return stdTypes.RectangleClass; case VariantType::Ray: return stdTypes.RayClass; case VariantType::Matrix: return stdTypes.MatrixClass; case VariantType::Array: if (value.TypeName) { const StringAnsiView elementTypename(value.TypeName, StringUtils::Length(value.TypeName) - 2); mclass = Scripting::FindClass(elementTypename); if (mclass) return MCore::Array::GetClass(mclass); } return MCore::Array::GetClass(MCore::TypeCache::Object); case VariantType::Dictionary: { MClass *keyClass, *valueClass; GetDictionaryKeyValueTypes(value.GetTypeName(), keyClass, valueClass); if (!keyClass || !valueClass) { LOG(Error, "Invalid type to box {0}", value.ToString()); return nullptr; } return GetClass(ManagedDictionary::GetClass(keyClass->GetType(), valueClass->GetType())); } case VariantType::ManagedObject: return MCore::TypeCache::Object; default: ; } return nullptr; } MClass* MUtils::GetClass(const Variant& value) { const auto& stdTypes = *StdTypesContainer::Instance(); switch (value.Type.Type) { case VariantType::Void: return MCore::TypeCache::Void; case VariantType::Bool: return MCore::TypeCache::Boolean; case VariantType::Int16: return MCore::TypeCache::Int16; case VariantType::Uint16: return MCore::TypeCache::UInt16; case VariantType::Int: return MCore::TypeCache::Int32; case VariantType::Uint: return MCore::TypeCache::UInt32; case VariantType::Int64: return MCore::TypeCache::Int64; case VariantType::Uint64: return MCore::TypeCache::UInt64; case VariantType::Float: return MCore::TypeCache::Single; case VariantType::Double: return MCore::TypeCache::Double; case VariantType::Pointer: return MCore::TypeCache::IntPtr; case VariantType::String: return MCore::TypeCache::String; case VariantType::Blob: return MCore::Array::GetClass(MCore::TypeCache::Byte); case VariantType::Float2: return Float2::TypeInitializer.GetClass(); case VariantType::Float3: return Float3::TypeInitializer.GetClass(); case VariantType::Float4: return Float4::TypeInitializer.GetClass(); case VariantType::Double2: return Double2::TypeInitializer.GetClass(); case VariantType::Double3: return Double3::TypeInitializer.GetClass(); case VariantType::Double4: return Double4::TypeInitializer.GetClass(); case VariantType::Color: return stdTypes.ColorClass; case VariantType::Guid: return stdTypes.GuidClass; case VariantType::Typename: return stdTypes.TypeClass; case VariantType::BoundingBox: return stdTypes.BoundingBoxClass; case VariantType::BoundingSphere: return stdTypes.BoundingSphereClass; case VariantType::Quaternion: return stdTypes.QuaternionClass; case VariantType::Transform: return stdTypes.TransformClass; case VariantType::Rectangle: return stdTypes.RectangleClass; case VariantType::Ray: return stdTypes.RayClass; case VariantType::Matrix: return stdTypes.MatrixClass; case VariantType::Array: case VariantType::Dictionary: break; case VariantType::Object: return value.AsObject ? value.AsObject->GetClass() : nullptr; case VariantType::Asset: return value.AsAsset ? value.AsAsset->GetClass() : nullptr; case VariantType::Structure: case VariantType::Enum: return Scripting::FindClass(StringAnsiView(value.Type.TypeName)); case VariantType::ManagedObject: { MObject* obj = (MObject*)value; if (obj) return MCore::Object::GetClass(obj); } default: ; } return GetClass(value.Type); } MTypeObject* MUtils::GetType(MObject* object) { if (!object) return nullptr; MClass* klass = MCore::Object::GetClass(object); return GetType(klass); } MTypeObject* MUtils::GetType(MClass* klass) { if (!klass) return nullptr; MType* type = klass->GetType(); return INTERNAL_TYPE_GET_OBJECT(type); } BytesContainer MUtils::LinkArray(MArray* arrayObj) { BytesContainer result; const int32 length = arrayObj ? MCore::Array::GetLength(arrayObj) : 0; if (length != 0) { result.Link((byte*)MCore::Array::GetAddress(arrayObj), length); } return result; } void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed) { // Convert Variant into matching managed type and return pointer to data for the method invocation MTypes mType = MCore::Type::GetType(type); switch (mType) { case MTypes::Boolean: if (value.Type.Type != VariantType::Bool) value = (bool)value; return &value.AsBool; case MTypes::Char: case MTypes::I1: case MTypes::I2: if (value.Type.Type != VariantType::Int16) value = (int16)value; return &value.AsInt16; case MTypes::I4: if (value.Type.Type != VariantType::Int) value = (int32)value; return &value.AsInt; case MTypes::U1: case MTypes::U2: if (value.Type.Type != VariantType::Uint16) value = (uint16)value; return &value.AsUint16; case MTypes::U4: if (value.Type.Type != VariantType::Uint) value = (uint32)value; return &value.AsUint; case MTypes::I8: if (value.Type.Type != VariantType::Int64) value = (int64)value; return &value.AsInt64; case MTypes::U8: if (value.Type.Type != VariantType::Uint64) value = (uint64)value; return &value.AsUint64; case MTypes::R4: if (value.Type.Type != VariantType::Float) value = (float)value; return &value.AsFloat; case MTypes::R8: if (value.Type.Type != VariantType::Double) value = (double)value; return &value.AsDouble; case MTypes::String: return MUtils::ToString((StringView)value); case MTypes::ValueType: { MClass* klass = MCore::Type::GetClass(type); if (klass->IsEnum()) { if (value.Type.Type != VariantType::Enum) { value.SetType(VariantType(VariantType::Enum, klass)); value.AsUint64 = 0; } return &value.AsUint64; } const auto stdTypes = StdTypesContainer::Instance(); #define CASE_IN_BUILD_TYPE(type, access) \ if (klass == stdTypes->type##Class) \ { \ if (value.Type.Type != VariantType::type) \ value = Variant((type)value); \ return value.access; \ } CASE_IN_BUILD_TYPE(Color, AsData); CASE_IN_BUILD_TYPE(Quaternion, AsData); CASE_IN_BUILD_TYPE(Guid, AsData); CASE_IN_BUILD_TYPE(Rectangle, AsData); CASE_IN_BUILD_TYPE(Matrix, AsBlob.Data); CASE_IN_BUILD_TYPE(Transform, AsBlob.Data); #undef CASE_IN_BUILD_TYPE #define CASE_IN_BUILD_TYPE(type, access) \ if (klass == stdTypes->type##Class) \ { \ if (value.Type.Type != VariantType::type) \ value = Variant((type)value); \ return (void*)&value.access(); \ } CASE_IN_BUILD_TYPE(Vector2, AsVector2); CASE_IN_BUILD_TYPE(Vector3, AsVector3); CASE_IN_BUILD_TYPE(Vector4, AsVector4); CASE_IN_BUILD_TYPE(BoundingSphere, AsBoundingSphere); CASE_IN_BUILD_TYPE(BoundingBox, AsBoundingBox); CASE_IN_BUILD_TYPE(Ray, AsRay); #undef CASE_IN_BUILD_TYPE #define CASE_IN_BUILD_TYPE(type, access) \ if (klass == type::TypeInitializer.GetClass()) \ { \ if (value.Type.Type != VariantType::type) \ value = Variant((type)value); \ return value.access; \ } CASE_IN_BUILD_TYPE(Float2, AsData); CASE_IN_BUILD_TYPE(Float3, AsData); CASE_IN_BUILD_TYPE(Float4, AsData); CASE_IN_BUILD_TYPE(Double2, AsData); CASE_IN_BUILD_TYPE(Double3, AsData); CASE_IN_BUILD_TYPE(Double4, AsBlob.Data); #undef CASE_IN_BUILD_TYPE if (klass->IsValueType()) { if (value.Type.Type == VariantType::Structure) { const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(value.Type.TypeName)); if (typeHandle && value.AsBlob.Data) { auto& valueType = typeHandle.GetType(); if (valueType.ManagedClass == MCore::Type::GetClass(type)) { return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data)); } LOG(Error, "Cannot marshal argument of type {0} as {1}", String(valueType.Fullname), MCore::Type::ToString(type)); } } else { const StringAnsiView fullname = klass->GetFullName(); const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(fullname); if (typeHandle) { auto& valueType = typeHandle.GetType(); value.SetType(VariantType(VariantType::Structure, fullname)); return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data)); } } } } break; case MTypes::Enum: { if (value.Type.Type != VariantType::Enum) return nullptr; return &value.AsUint64; } case MTypes::Class: { if (value.Type.Type == VariantType::Null) return nullptr; MObject* object = BoxVariant(value); if (object && !MCore::Object::GetClass(object)->IsSubClassOf(MCore::Type::GetClass(type))) object = nullptr; return object; } case MTypes::Object: return BoxVariant(value); case MTypes::SzArray: case MTypes::Array: { if (value.Type.Type != VariantType::Array) return nullptr; MObject* object = BoxVariant(value); auto typeStr = MCore::Type::ToString(type); if (object && !MCore::Object::GetClass(object)->IsSubClassOf(MCore::Type::GetClass(type))) object = nullptr; return object; } case MTypes::GenericInst: { if (value.Type.Type == VariantType::Null) return nullptr; MObject* object = BoxVariant(value); if (object && !MCore::Object::GetClass(object)->IsSubClassOf(MCore::Type::GetClass(type))) object = nullptr; return object; } case MTypes::Ptr: switch (value.Type.Type) { case VariantType::Pointer: return &value.AsPointer; case VariantType::Object: return &value.AsObject; case VariantType::Asset: return &value.AsAsset; case VariantType::Structure: case VariantType::Blob: return &value.AsBlob.Data; default: return nullptr; } default: break; } failed = true; return nullptr; } MObject* MUtils::ToManaged(const Version& value) { #if USE_NETCORE auto scriptingClass = Scripting::GetStaticClass(); CHECK_RETURN(scriptingClass, nullptr); auto versionToManaged = scriptingClass->GetMethod("VersionToManaged", 4); CHECK_RETURN(versionToManaged, nullptr); int32 major = value.Major(); int32 minor = value.Minor(); int32 build = value.Build(); int32 revision = value.Revision(); void* params[4]; params[0] = &major; params[1] = &minor; params[2] = &build; params[3] = &revision; auto obj = versionToManaged->Invoke(nullptr, params, nullptr); #else auto obj = MCore::Object::New(Scripting::FindClass("System.Version")); Platform::MemoryCopy(MCore::Object::Unbox(obj), &value, sizeof(Version)); #endif return obj; } Version MUtils::ToNative(MObject* value) { Version result; if (value) #if USE_NETCORE { auto scriptingClass = Scripting::GetStaticClass(); CHECK_RETURN(scriptingClass, result); auto versionToNative = scriptingClass->GetMethod("VersionToNative", 5); CHECK_RETURN(versionToNative, result); void* params[5]; params[0] = value; params[1] = (byte*)&result; params[2] = (byte*)&result + sizeof(int32); params[3] = (byte*)&result + sizeof(int32) * 2; params[4] = (byte*)&result + sizeof(int32) * 3; versionToNative->Invoke(nullptr, params, nullptr); return result; } #else return *(Version*)MCore::Object::Unbox(value); #endif return result; } #endif