1311 lines
47 KiB
C++
1311 lines
47 KiB
C++
// Copyright (c) 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 StringAnsiView 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, bool releaseHandle)
|
|
{
|
|
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<bool*>(unboxed);
|
|
case MTypes::I1:
|
|
return *static_cast<int8*>(unboxed);
|
|
case MTypes::U1:
|
|
return *static_cast<uint8*>(unboxed);
|
|
case MTypes::I2:
|
|
return *static_cast<int16*>(unboxed);
|
|
case MTypes::U2:
|
|
return *static_cast<uint16*>(unboxed);
|
|
case MTypes::Char:
|
|
return *static_cast<Char*>(unboxed);
|
|
case MTypes::I4:
|
|
return *static_cast<int32*>(unboxed);
|
|
case MTypes::U4:
|
|
return *static_cast<uint32*>(unboxed);
|
|
case MTypes::I8:
|
|
return *static_cast<int64*>(unboxed);
|
|
case MTypes::U8:
|
|
return *static_cast<uint64*>(unboxed);
|
|
case MTypes::R4:
|
|
return *static_cast<float*>(unboxed);
|
|
case MTypes::R8:
|
|
return *static_cast<double*>(unboxed);
|
|
case MTypes::String:
|
|
{
|
|
if (releaseHandle)
|
|
MUtils::FreeManaged<String>(value);
|
|
return Variant(MUtils::ToString((MString*)value));
|
|
}
|
|
case MTypes::Ptr:
|
|
return *static_cast<void**>(unboxed);
|
|
case MTypes::ValueType:
|
|
if (klass == stdTypes.GuidClass)
|
|
return Variant(*static_cast<Guid*>(unboxed));
|
|
if (klass == stdTypes.Vector2Class)
|
|
return *static_cast<Vector2*>(unboxed);
|
|
if (klass == stdTypes.Vector3Class)
|
|
return *static_cast<Vector3*>(unboxed);
|
|
if (klass == stdTypes.Vector4Class)
|
|
return *static_cast<Vector4*>(unboxed);
|
|
if (klass == Int2::TypeInitializer.GetClass())
|
|
return *static_cast<Int2*>(unboxed);
|
|
if (klass == Int3::TypeInitializer.GetClass())
|
|
return *static_cast<Int3*>(unboxed);
|
|
if (klass == Int4::TypeInitializer.GetClass())
|
|
return *static_cast<Int4*>(unboxed);
|
|
if (klass == Float2::TypeInitializer.GetClass())
|
|
return *static_cast<Float2*>(unboxed);
|
|
if (klass == Float3::TypeInitializer.GetClass())
|
|
return *static_cast<Float3*>(unboxed);
|
|
if (klass == Float4::TypeInitializer.GetClass())
|
|
return *static_cast<Float4*>(unboxed);
|
|
if (klass == Double2::TypeInitializer.GetClass())
|
|
return *static_cast<Double2*>(unboxed);
|
|
if (klass == Double3::TypeInitializer.GetClass())
|
|
return *static_cast<Double3*>(unboxed);
|
|
if (klass == Double4::TypeInitializer.GetClass())
|
|
return *static_cast<Double4*>(unboxed);
|
|
if (klass == stdTypes.ColorClass)
|
|
return *static_cast<Color*>(unboxed);
|
|
if (klass == stdTypes.BoundingBoxClass)
|
|
return Variant(*static_cast<BoundingBox*>(unboxed));
|
|
if (klass == stdTypes.QuaternionClass)
|
|
return *static_cast<Quaternion*>(unboxed);
|
|
if (klass == stdTypes.TransformClass)
|
|
return Variant(*static_cast<Transform*>(unboxed));
|
|
if (klass == stdTypes.BoundingSphereClass)
|
|
return *static_cast<BoundingSphere*>(unboxed);
|
|
if (klass == stdTypes.RectangleClass)
|
|
return *static_cast<Rectangle*>(unboxed);
|
|
if (klass == stdTypes.MatrixClass)
|
|
return Variant(*reinterpret_cast<Matrix*>(unboxed));
|
|
break;
|
|
case MTypes::SzArray:
|
|
case MTypes::Array:
|
|
{
|
|
void* ptr = MCore::Array::GetAddress((MArray*)value);
|
|
const MClass* arrayClass = klass == stdTypes.ManagedArrayClass ? MCore::Array::GetArrayClass((MArray*)value) : klass;
|
|
const MClass* elementClass = arrayClass->GetElementClass();
|
|
if (elementClass == MCore::TypeCache::Byte)
|
|
{
|
|
Variant v;
|
|
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
|
|
if (releaseHandle)
|
|
MUtils::FreeManaged<Array<byte>>(value);
|
|
return v;
|
|
}
|
|
const StringAnsiView fullname = arrayClass->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++)
|
|
{
|
|
auto& a = array.Get()[i];
|
|
a.SetType(VariantType(VariantType::Enum, elementTypename));
|
|
Platform::MemoryCopy(&a.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], releaseHandle);
|
|
|
|
if (releaseHandle)
|
|
MUtils::FreeManaged<Array<byte>>(value);
|
|
}
|
|
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<Variant, Variant> native;
|
|
native.EnsureCapacity(length);
|
|
MObject** managedKeysPtr = MCore::Array::GetAddress<MObject*>(managedKeys);
|
|
for (int32 i = 0; i < length; i++)
|
|
{
|
|
MObject* keyManaged = managedKeysPtr[i];
|
|
MObject* valueManaged = managed.GetValue(keyManaged);
|
|
native.Add(UnboxVariant(keyManaged, releaseHandle), UnboxVariant(valueManaged, releaseHandle));
|
|
}
|
|
Variant v(MoveTemp(native));
|
|
v.Type.SetTypeName(klass->GetFullName());
|
|
if (releaseHandle)
|
|
MCore::GCHandle::Free(*(MGCHandle*)&value);
|
|
//MUtils::FreeManaged<Dictionary<byte, byte>>(value);
|
|
return v;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (klass->IsSubClassOf(Asset::GetStaticClass()))
|
|
return static_cast<Asset*>(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<uint32*>(MCore::Object::Unbox(value));
|
|
if (releaseHandle)
|
|
MUtils::FreeManaged<byte>(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;
|
|
}
|
|
}
|
|
|
|
auto variant = Variant(value);
|
|
if (releaseHandle)
|
|
MUtils::FreeManaged<byte>(value);
|
|
return variant;
|
|
}
|
|
|
|
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<byte>((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;
|
|
}
|
|
}
|
|
|
|
StringAnsiView MUtils::GetClassFullname(MObject* obj)
|
|
{
|
|
if (obj)
|
|
{
|
|
MClass* mClass = MCore::Object::GetClass(obj);
|
|
return mClass->GetFullName();
|
|
}
|
|
return StringAnsiView::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 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:
|
|
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
|