Add better support for Arrays in Variant for C# Scripting

This commit is contained in:
Wojciech Figat
2021-11-16 20:46:04 +01:00
parent 4722b58a40
commit 13deb4a54f
4 changed files with 132 additions and 20 deletions

View File

@@ -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<object, object>))
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<object, object>)value).Count);
foreach (var e in (Dictionary<object, object>)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;

View File

@@ -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<Variant, HeapAllocation>),
const Variant Variant::Zero(0.0f);
const Variant Variant::One(1.0f);
const Variant Variant::Null(static_cast<void*>(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>& Variant::AsArray()
{
return *reinterpret_cast<Array<Variant, HeapAllocation>*>(AsData);
}
const Array<Variant>& Variant::AsArray() const
{
return *reinterpret_cast<const Array<Variant, HeapAllocation>*>(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)
{

View File

@@ -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<Variant, HeapAllocation>& AsArray();
const Array<Variant, HeapAllocation>& AsArray() const;
public:

View File

@@ -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<const Array<Variant, HeapAllocation>*>(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: