diff --git a/Source/Editor/Utilities/VariantUtils.cs b/Source/Editor/Utilities/VariantUtils.cs index de430829e..7e92bb849 100644 --- a/Source/Editor/Utilities/VariantUtils.cs +++ b/Source/Editor/Utilities/VariantUtils.cs @@ -132,7 +132,7 @@ namespace FlaxEditor.Utilities variantType = VariantType.Structure; else if (type == typeof(byte[])) variantType = VariantType.Blob; - else if (type == typeof(object[])) + else if (type.IsArray) variantType = VariantType.Array; else if (type == typeof(Dictionary)) variantType = VariantType.Dictionary; @@ -210,6 +210,15 @@ namespace FlaxEditor.Utilities stream.Write(int.MaxValue); stream.WriteStrAnsi(type.FullName, 77); break; + case VariantType.Array: + if (type != typeof(object[])) + { + stream.Write(int.MaxValue); + stream.WriteStrAnsi(type.FullName, 77); + } + else + stream.Write(0); + break; default: stream.Write(0); break; @@ -443,10 +452,14 @@ namespace FlaxEditor.Utilities case VariantType.Matrix: return stream.ReadMatrix(); case VariantType.Array: { + if (type == null) + type = typeof(object[]); + else if (!type.IsArray) + throw new Exception("Invalid arry type for the Variant array " + typeName); var count = stream.ReadInt32(); - var result = new object[count]; + var result = Array.CreateInstance(type.GetElementType(), count); for (int i = 0; i < count; i++) - result[i] = stream.ReadVariant(); + result.SetValue(stream.ReadVariant(), i); return result; } case VariantType.Dictionary: @@ -630,10 +643,13 @@ namespace FlaxEditor.Utilities stream.Write((Matrix)value); break; case VariantType.Array: - stream.Write(((object[])value).Length); - foreach (var e in (object[])value) + { + var array = (Array)value; + stream.Write(array.Length); + foreach (var e in array) stream.WriteVariant(e); break; + } case VariantType.Dictionary: stream.Write(((Dictionary)value).Count); foreach (var e in (Dictionary)value) @@ -679,6 +695,10 @@ namespace FlaxEditor.Utilities case VariantType.ManagedObject: withoutTypeName = false; break; + case VariantType.Array: + if (value != typeof(object[])) + withoutTypeName = false; + break; } if (withoutTypeName) { @@ -1077,7 +1097,7 @@ namespace FlaxEditor.Utilities case VariantType.Array: { stream.WriteStartArray(); - foreach (var e in (object[])value) + foreach (var e in (Array)value) stream.WriteVariant(e); stream.WriteEndArray(); break; diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 419be7a35..a2fee9f70 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -229,6 +229,8 @@ const char* VariantType::GetTypeName() const return "FlaxEngine.Matrix"; case Typename: return "System.Type"; + case Array: + return "System.Object[]"; default: return ""; } @@ -373,7 +375,7 @@ static_assert(sizeof(Variant::AsData) >= sizeof(Array), const Variant Variant::Zero(0.0f); const Variant Variant::One(1.0f); -const Variant Variant::Null(static_cast(nullptr)); +const Variant Variant::Null(nullptr); const Variant Variant::False(false); const Variant Variant::True(true); @@ -440,6 +442,11 @@ Variant::Variant(Variant&& other) noexcept } } +Variant::Variant(decltype(nullptr)) + : Type(VariantType::Null) +{ +} + Variant::Variant(bool v) : Type(VariantType::Bool) { @@ -1997,6 +2004,16 @@ const Quaternion& Variant::AsQuaternion() const return *(const Quaternion*)AsData; } +Array& Variant::AsArray() +{ + return *reinterpret_cast*>(AsData); +} + +const Array& Variant::AsArray() const +{ + return *reinterpret_cast*>(AsData); +} + void Variant::SetType(const VariantType& type) { if (Type == type) @@ -2192,6 +2209,13 @@ void Variant::SetType(VariantType&& type) void Variant::SetString(const StringView& str) { SetType(VariantType(VariantType::String)); + if (str.Length() <= 0) + { + Allocator::Free(AsBlob.Data); + AsBlob.Data = nullptr; + AsBlob.Length = 0; + return; + } const int32 length = str.Length() * sizeof(Char) + 2; if (AsBlob.Length != length) { @@ -2206,6 +2230,13 @@ void Variant::SetString(const StringView& str) void Variant::SetString(const StringAnsiView& str) { SetType(VariantType(VariantType::String)); + if (str.Length() <= 0) + { + Allocator::Free(AsBlob.Data); + AsBlob.Data = nullptr; + AsBlob.Length = 0; + return; + } const int32 length = str.Length() * sizeof(Char) + 2; if (AsBlob.Length != length) { @@ -2220,6 +2251,13 @@ void Variant::SetString(const StringAnsiView& str) void Variant::SetTypename(const StringView& typeName) { SetType(VariantType(VariantType::Typename)); + if (typeName.Length() <= 0) + { + Allocator::Free(AsBlob.Data); + AsBlob.Data = nullptr; + AsBlob.Length = 0; + return; + } const int32 length = typeName.Length() + 1; if (AsBlob.Length != length) { @@ -2234,6 +2272,13 @@ void Variant::SetTypename(const StringView& typeName) void Variant::SetTypename(const StringAnsiView& typeName) { SetType(VariantType(VariantType::Typename)); + if (typeName.Length() <= 0) + { + Allocator::Free(AsBlob.Data); + AsBlob.Data = nullptr; + AsBlob.Length = 0; + return; + } const int32 length = typeName.Length() + 1; if (AsBlob.Length != length) { diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index df8c4aeea..a7700df9d 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -189,6 +189,7 @@ public: Variant(const Variant& other); Variant(Variant&& other) noexcept; + explicit Variant(decltype(nullptr)); Variant(bool v); Variant(int16 v); Variant(uint16 v); @@ -299,6 +300,8 @@ public: const Int4& AsInt4() const; const Color& AsColor() const; const Quaternion& AsQuaternion() const; + Array& AsArray(); + const Array& AsArray() const; public: diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index fcf15815d..fa44abdcf 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -151,6 +151,7 @@ VariantType MUtils::UnboxVariantType(MonoType* monoType) const auto& stdTypes = *StdTypesContainer::Instance(); const auto klass = mono_type_get_class(monoType); + // TODO: optimize this with switch(monoType->type) maybe? if (klass == mono_get_void_class() || monoType->type == MONO_TYPE_VOID) return VariantType(VariantType::Void); if (klass == mono_get_boolean_class() || monoType->type == MONO_TYPE_BOOLEAN) @@ -232,7 +233,12 @@ VariantType MUtils::UnboxVariantType(MonoType* monoType) } if (klass == mono_array_class_get(mono_get_byte_class(), 1)) return VariantType(VariantType::Blob); - // TODO: support any array unboxing + if (monoType->type == MONO_TYPE_SZARRAY || monoType->type == MONO_TYPE_ARRAY) + { + MString fullname; + GetClassFullname(klass, fullname); + return VariantType(VariantType::Array, fullname); + } // TODO: support any dictionary unboxing // TODO: support any structure unboxing @@ -350,7 +356,32 @@ Variant MUtils::UnboxVariant(MonoObject* value) v.SetBlob(mono_array_addr((MonoArray*)value, byte, 0), (int32)mono_array_length((MonoArray*)value)); return v; } - // TODO: support any array unboxing + MonoType* monoType = mono_class_get_type(klass); + if (monoType->type == MONO_TYPE_SZARRAY || monoType->type == MONO_TYPE_ARRAY) + { + MString fullname; + GetClassFullname(klass, fullname); + Variant v; + v.SetType(MoveTemp(VariantType(VariantType::Array, fullname))); + auto& array = v.AsArray(); + array.Resize((int32)mono_array_length((MonoArray*)value)); + if (mono_class_is_valuetype(monoType->data.array->eklass)) + { + const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(*fullname, fullname.Length() - 2)); + if (typeHandle) + { + // TODO: Unboxing value-type Variant array + MISSING_CODE("Unboxing value-type Variant array"); + } + LOG(Error, "Invalid type to unbox {0}", v.Type); + } + else + { + for (int32 i = 0; i < array.Count(); i++) + array[i] = UnboxVariant(mono_array_get((MonoArray*)value, MonoObject*, i)); + } + return v; + } // TODO: support any dictionary unboxing return Variant(value); @@ -418,24 +449,36 @@ MonoObject* MUtils::BoxVariant(const Variant& value) return value.AsAsset ? value.AsAsset->GetOrCreateManagedInstance() : nullptr; case VariantType::Array: { - const auto* array = reinterpret_cast*>(value.AsData); + MonoArray* managed = nullptr; + const auto& array = value.AsArray(); if (value.Type.TypeName) { - const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(value.Type.TypeName)); + const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(value.Type.TypeName, StringUtils::Length(value.Type.TypeName) - 2)); if (typeHandle) { - // TODO: Boxing typed Variant array - MISSING_CODE("Boxing typed Variant array"); + const ScriptingType& type = typeHandle.GetType(); + if (type.Type == ScriptingTypes::Script || type.Type == ScriptingTypes::Class || (type.ManagedClass && !mono_class_is_valuetype(type.ManagedClass->GetNative()))) + { + managed = mono_array_new(mono_domain_get(), type.ManagedClass->GetNative(), array.Count()); + for (int32 i = 0; i < array.Count(); i++) + mono_array_setref(managed, i, BoxVariant(array[i])); + } + else + { + // TODO: Boxing value-type Variant array + MISSING_CODE("Boxing value-type Variant array"); + } } - LOG(Error, "Invalid type to box {0}", value.Type); - return nullptr; + else + LOG(Error, "Invalid type to box {0}", value.Type); } - MonoObject* managed = mono_object_new(mono_domain_get(), mono_array_class_get(mono_get_object_class(), 1)); - for (int32 i = 0; i < array->Count(); i++) + else { - mono_array_setref((MonoArray*)managed, i, BoxVariant(array->At(i))); + managed = mono_array_new(mono_domain_get(), mono_get_object_class(), array.Count()); + for (int32 i = 0; i < array.Count(); i++) + mono_array_setref(managed, i, BoxVariant(array[i])); } - return managed; + return (MonoObject*)managed; } // TODO: VariantType::Dictionary case VariantType::Structure: @@ -658,7 +701,8 @@ MonoClass* MUtils::GetClass(const Variant& value) case VariantType::Matrix: return stdTypes.MatrixClass->GetNative(); case VariantType::Array: - return mono_array_class_get(mono_get_object_class(), 1); + case VariantType::Dictionary: + return GetClass(value.Type); case VariantType::Object: return value.AsObject ? value.AsObject->GetClass()->GetNative() : nullptr; case VariantType::Asset: