Add better support for Arrays in Variant for C# Scripting
This commit is contained in:
@@ -151,99 +151,102 @@ 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)
|
||||
// Fast type detection for in-built types
|
||||
switch (monoType->type)
|
||||
{
|
||||
case MONO_TYPE_VOID:
|
||||
return VariantType(VariantType::Void);
|
||||
if (klass == mono_get_boolean_class() || monoType->type == MONO_TYPE_BOOLEAN)
|
||||
case MONO_TYPE_BOOLEAN:
|
||||
return VariantType(VariantType::Bool);
|
||||
if (klass == mono_get_byte_class() || monoType->type == MONO_TYPE_U1)
|
||||
case MONO_TYPE_I1:
|
||||
return VariantType(VariantType::Int16);
|
||||
if (klass == mono_get_sbyte_class() || monoType->type == MONO_TYPE_I1)
|
||||
case MONO_TYPE_U1:
|
||||
return VariantType(VariantType::Int16);
|
||||
if (klass == mono_get_int16_class() || monoType->type == MONO_TYPE_I2)
|
||||
case MONO_TYPE_I2:
|
||||
return VariantType(VariantType::Int16);
|
||||
if (klass == mono_get_uint16_class() || monoType->type == MONO_TYPE_U2)
|
||||
case MONO_TYPE_U2:
|
||||
return VariantType(VariantType::Uint16);
|
||||
if (klass == mono_get_int32_class() || monoType->type == MONO_TYPE_I4)
|
||||
case MONO_TYPE_I4:
|
||||
case MONO_TYPE_CHAR:
|
||||
return VariantType(VariantType::Int);
|
||||
if (klass == mono_get_uint32_class() || monoType->type == MONO_TYPE_U4)
|
||||
case MONO_TYPE_U4:
|
||||
return VariantType(VariantType::Uint);
|
||||
if (klass == mono_get_int64_class() || monoType->type == MONO_TYPE_I8)
|
||||
case MONO_TYPE_I8:
|
||||
return VariantType(VariantType::Int64);
|
||||
if (klass == mono_get_uint64_class() || monoType->type == MONO_TYPE_U8)
|
||||
case MONO_TYPE_U8:
|
||||
return VariantType(VariantType::Uint64);
|
||||
if (klass == mono_get_char_class() || monoType->type == MONO_TYPE_CHAR)
|
||||
return VariantType(VariantType::Int);
|
||||
if (klass == mono_get_single_class() || monoType->type == MONO_TYPE_R4)
|
||||
case MONO_TYPE_R4:
|
||||
return VariantType(VariantType::Float);
|
||||
if (klass == mono_get_double_class() || monoType->type == MONO_TYPE_R8)
|
||||
case MONO_TYPE_R8:
|
||||
return VariantType(VariantType::Double);
|
||||
if (klass == mono_get_string_class() || monoType->type == MONO_TYPE_STRING)
|
||||
case MONO_TYPE_STRING:
|
||||
return VariantType(VariantType::String);
|
||||
if (klass == mono_get_intptr_class() || klass == mono_get_uintptr_class() || monoType->type == MONO_TYPE_PTR)
|
||||
case MONO_TYPE_PTR:
|
||||
return VariantType(VariantType::Pointer);
|
||||
if (monoType->type == MONO_TYPE_OBJECT)
|
||||
case MONO_TYPE_VALUETYPE:
|
||||
if (klass == stdTypes.GuidClass->GetNative())
|
||||
return VariantType(VariantType::Guid);
|
||||
if (klass == stdTypes.TypeClass->GetNative())
|
||||
return VariantType(VariantType::Typename);
|
||||
if (klass == stdTypes.Vector2Class->GetNative())
|
||||
return VariantType(VariantType::Vector2);
|
||||
if (klass == stdTypes.Vector3Class->GetNative())
|
||||
return VariantType(VariantType::Vector3);
|
||||
if (klass == stdTypes.Vector4Class->GetNative())
|
||||
return VariantType(VariantType::Vector4);
|
||||
if (klass == stdTypes.ColorClass->GetNative())
|
||||
return VariantType(VariantType::Color);
|
||||
if (klass == stdTypes.BoundingBoxClass->GetNative())
|
||||
return VariantType(VariantType::BoundingBox);
|
||||
if (klass == stdTypes.QuaternionClass->GetNative())
|
||||
return VariantType(VariantType::Quaternion);
|
||||
if (klass == stdTypes.TransformClass->GetNative())
|
||||
return VariantType(VariantType::Transform);
|
||||
if (klass == stdTypes.BoundingSphereClass->GetNative())
|
||||
return VariantType(VariantType::BoundingSphere);
|
||||
if (klass == stdTypes.RectangleClass->GetNative())
|
||||
return VariantType(VariantType::Rectangle);
|
||||
if (klass == stdTypes.MatrixClass->GetNative())
|
||||
return VariantType(VariantType::Matrix);
|
||||
break;
|
||||
case MONO_TYPE_OBJECT:
|
||||
return VariantType(VariantType::ManagedObject);
|
||||
if (klass == stdTypes.GuidClass->GetNative())
|
||||
return VariantType(VariantType::Guid);
|
||||
if (klass == stdTypes.TypeClass->GetNative())
|
||||
return VariantType(VariantType::Typename);
|
||||
if (klass == stdTypes.Vector2Class->GetNative())
|
||||
return VariantType(VariantType::Vector2);
|
||||
if (klass == stdTypes.Vector3Class->GetNative())
|
||||
return VariantType(VariantType::Vector3);
|
||||
if (klass == stdTypes.Vector4Class->GetNative())
|
||||
return VariantType(VariantType::Vector4);
|
||||
if (klass == stdTypes.ColorClass->GetNative())
|
||||
return VariantType(VariantType::Color);
|
||||
if (klass == stdTypes.BoundingBoxClass->GetNative())
|
||||
return VariantType(VariantType::BoundingBox);
|
||||
if (klass == stdTypes.QuaternionClass->GetNative())
|
||||
return VariantType(VariantType::Quaternion);
|
||||
if (klass == stdTypes.TransformClass->GetNative())
|
||||
return VariantType(VariantType::Transform);
|
||||
if (klass == stdTypes.BoundingSphereClass->GetNative())
|
||||
return VariantType(VariantType::BoundingSphere);
|
||||
if (klass == stdTypes.RectangleClass->GetNative())
|
||||
return VariantType(VariantType::Rectangle);
|
||||
if (klass == stdTypes.MatrixClass->GetNative())
|
||||
return VariantType(VariantType::Matrix);
|
||||
CHECK_RETURN(klass, VariantType(VariantType::Null));
|
||||
case MONO_TYPE_SZARRAY:
|
||||
if (klass == mono_array_class_get(mono_get_byte_class(), 1))
|
||||
return VariantType(VariantType::Blob);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get actual typename for full type info
|
||||
if (!klass)
|
||||
return VariantType(VariantType::Null);
|
||||
MString fullname;
|
||||
GetClassFullname(klass, fullname);
|
||||
switch (monoType->type)
|
||||
{
|
||||
case MONO_TYPE_SZARRAY:
|
||||
case MONO_TYPE_ARRAY:
|
||||
return VariantType(VariantType::Array, fullname);
|
||||
case MONO_TYPE_ENUM:
|
||||
return VariantType(VariantType::Enum, fullname);
|
||||
case MONO_TYPE_VALUETYPE:
|
||||
return VariantType(VariantType::Structure, fullname);
|
||||
}
|
||||
if (mono_class_is_subclass_of(klass, Asset::GetStaticClass()->GetNative(), false) != 0)
|
||||
{
|
||||
if (klass == Asset::GetStaticClass()->GetNative())
|
||||
return VariantType(VariantType::Asset);
|
||||
StringAnsi fullname;
|
||||
GetClassFullname(klass, fullname);
|
||||
return VariantType(VariantType::Asset, fullname);
|
||||
}
|
||||
if (mono_class_is_subclass_of(klass, ScriptingObject::GetStaticClass()->GetNative(), false) != 0)
|
||||
{
|
||||
if (klass == ScriptingObject::GetStaticClass()->GetNative())
|
||||
return VariantType(VariantType::Object);
|
||||
StringAnsi fullname;
|
||||
GetClassFullname(klass, fullname);
|
||||
return VariantType(VariantType::Object, fullname);
|
||||
}
|
||||
if (mono_class_is_enum(klass))
|
||||
{
|
||||
MString fullname;
|
||||
GetClassFullname(klass, fullname);
|
||||
return VariantType(VariantType::Enum, fullname);
|
||||
}
|
||||
if (klass == mono_array_class_get(mono_get_byte_class(), 1))
|
||||
return VariantType(VariantType::Blob);
|
||||
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
|
||||
|
||||
// TODO: unbox other types as generic ManagedObject type
|
||||
LOG(Error, "Invalid managed type to unbox {0}", String(GetClassFullname(klass)));
|
||||
LOG(Error, "Invalid managed type to unbox {0}", String(fullname));
|
||||
return VariantType();
|
||||
}
|
||||
|
||||
@@ -329,6 +332,7 @@ Variant MUtils::UnboxVariant(MonoObject* value)
|
||||
GetClassFullname(klass, fullname);
|
||||
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*>(mono_object_unbox(value));
|
||||
return v;
|
||||
}
|
||||
@@ -365,18 +369,97 @@ Variant MUtils::UnboxVariant(MonoObject* value)
|
||||
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 StringAnsiView elementTypename(*fullname, fullname.Length() - 2);
|
||||
MonoClass* elementClass = monoType->data.array->eklass;
|
||||
uint32_t elementAlign;
|
||||
const int32 elementSize = mono_class_value_size(elementClass, &elementAlign);
|
||||
if (mono_class_is_enum(elementClass))
|
||||
{
|
||||
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(*fullname, fullname.Length() - 2));
|
||||
if (typeHandle)
|
||||
// Array of Enums
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
{
|
||||
// TODO: Unboxing value-type Variant array
|
||||
MISSING_CODE("Unboxing value-type Variant array");
|
||||
array[i].SetType(VariantType(VariantType::Enum, elementTypename));
|
||||
Platform::MemoryCopy(&array[i].AsUint64, mono_array_addr_with_size((MonoArray*)value, elementSize, i), elementSize);
|
||||
}
|
||||
}
|
||||
else if (mono_class_is_valuetype(elementClass))
|
||||
{
|
||||
// Array of Structures
|
||||
VariantType elementType = UnboxVariantType(mono_class_get_type(elementClass));
|
||||
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::Vector2:
|
||||
case VariantType::Vector3:
|
||||
case VariantType::Vector4:
|
||||
case VariantType::Color:
|
||||
case VariantType::Guid:
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::Quaternion:
|
||||
case VariantType::Rectangle:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Int16:
|
||||
case VariantType::Uint16:
|
||||
// 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, mono_array_addr_with_size((MonoArray*)value, elementSize, i), elementSize);
|
||||
}
|
||||
break;
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Transform:
|
||||
case VariantType::Ray:
|
||||
case VariantType::Matrix:
|
||||
// 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, mono_array_addr_with_size((MonoArray*)value, 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 = mono_array_addr_with_size((MonoArray*)value, elementSize, i);
|
||||
// TODO: optimize structures unboxing to not require MonoObject* but raw managed value data to prevent additional boxing here
|
||||
MonoObject* boxed = mono_object_new(mono_domain_get(), elementClass);
|
||||
Platform::MemoryCopy(mono_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;
|
||||
}
|
||||
LOG(Error, "Invalid type to unbox {0}", v.Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Array of Objects
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
array[i] = UnboxVariant(mono_array_get((MonoArray*)value, MonoObject*, i));
|
||||
}
|
||||
@@ -449,31 +532,102 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
|
||||
return value.AsAsset ? value.AsAsset->GetOrCreateManagedInstance() : nullptr;
|
||||
case VariantType::Array:
|
||||
{
|
||||
MonoArray* managed = nullptr;
|
||||
MonoArray* managed;
|
||||
const auto& array = value.AsArray();
|
||||
if (value.Type.TypeName)
|
||||
{
|
||||
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(value.Type.TypeName, StringUtils::Length(value.Type.TypeName) - 2));
|
||||
if (typeHandle)
|
||||
const StringAnsiView elementTypename(value.Type.TypeName, StringUtils::Length(value.Type.TypeName) - 2);
|
||||
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(elementTypename);
|
||||
MonoClass* elementClass;
|
||||
if (typeHandle && typeHandle.GetType().ManagedClass)
|
||||
elementClass = typeHandle.GetType().ManagedClass->GetNative();
|
||||
else
|
||||
elementClass = Scripting::FindClassNative(elementTypename);
|
||||
if (!elementClass)
|
||||
{
|
||||
const ScriptingType& type = typeHandle.GetType();
|
||||
if (type.Type == ScriptingTypes::Script || type.Type == ScriptingTypes::Class || (type.ManagedClass && !mono_class_is_valuetype(type.ManagedClass->GetNative())))
|
||||
LOG(Error, "Invalid type to box {0}", value.Type);
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t elementAlign;
|
||||
const int32 elementSize = mono_class_value_size(elementClass, &elementAlign);
|
||||
managed = mono_array_new(mono_domain_get(), elementClass, array.Count());
|
||||
if (mono_class_is_enum(elementClass))
|
||||
{
|
||||
// Array of Enums
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
{
|
||||
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]));
|
||||
auto data = (uint64)array[i];
|
||||
Platform::MemoryCopy(mono_array_addr_with_size(managed, elementSize, i), &data, elementSize);
|
||||
}
|
||||
else
|
||||
}
|
||||
else if (mono_class_is_valuetype(elementClass))
|
||||
{
|
||||
// Array of Structures
|
||||
VariantType elementType = UnboxVariantType(mono_class_get_type(elementClass));
|
||||
switch (elementType.Type)
|
||||
{
|
||||
// TODO: Boxing value-type Variant array
|
||||
MISSING_CODE("Boxing value-type Variant array");
|
||||
case VariantType::Bool:
|
||||
case VariantType::Int:
|
||||
case VariantType::Uint:
|
||||
case VariantType::Int64:
|
||||
case VariantType::Uint64:
|
||||
case VariantType::Float:
|
||||
case VariantType::Double:
|
||||
case VariantType::Vector2:
|
||||
case VariantType::Vector3:
|
||||
case VariantType::Vector4:
|
||||
case VariantType::Color:
|
||||
case VariantType::Guid:
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::Quaternion:
|
||||
case VariantType::Rectangle:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Int16:
|
||||
case VariantType::Uint16:
|
||||
// Optimized boxing of raw data type
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
Platform::MemoryCopy(mono_array_addr_with_size(managed, elementSize, i), &array[i].AsData, elementSize);
|
||||
break;
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Transform:
|
||||
case VariantType::Ray:
|
||||
case VariantType::Matrix:
|
||||
// Optimized boxing of raw data type
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
Platform::MemoryCopy(mono_array_addr_with_size(managed, 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 MonoObject* but use raw managed object to prevent additional boxing here
|
||||
MonoObject* boxed = type.Struct.Box(array[i].AsBlob.Data);
|
||||
Platform::MemoryCopy(mono_array_addr_with_size(managed, elementSize, i), mono_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
|
||||
LOG(Error, "Invalid type to box {0}", value.Type);
|
||||
{
|
||||
// Array of Objects
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
mono_array_setref(managed, i, BoxVariant(array[i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// object[]
|
||||
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]));
|
||||
@@ -503,7 +657,7 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
|
||||
return nullptr;
|
||||
}
|
||||
case VariantType::ManagedObject:
|
||||
return (MonoObject*)value;
|
||||
return value.AsUint ? mono_gchandle_get_target(value.AsUint) : nullptr;
|
||||
case VariantType::Typename:
|
||||
{
|
||||
const auto klass = Scripting::FindClassNative((StringAnsiView)value);
|
||||
@@ -635,6 +789,13 @@ MonoClass* MUtils::GetClass(const VariantType& value)
|
||||
case VariantType::Matrix:
|
||||
return stdTypes.MatrixClass->GetNative();
|
||||
case VariantType::Array:
|
||||
if (value.TypeName)
|
||||
{
|
||||
const StringAnsiView elementTypename(value.TypeName, StringUtils::Length(value.TypeName) - 2);
|
||||
mclass = Scripting::FindClass(elementTypename);
|
||||
if (mclass)
|
||||
return mono_array_class_get(mclass->GetNative(), 1);
|
||||
}
|
||||
return mono_array_class_get(mono_get_object_class(), 1);
|
||||
case VariantType::ManagedObject:
|
||||
return mono_get_object_class();
|
||||
@@ -791,8 +952,6 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
|
||||
return &value.AsDouble;
|
||||
case MONO_TYPE_STRING:
|
||||
return MUtils::ToString((StringView)value);
|
||||
// TODO: MONO_TYPE_PTR
|
||||
// TODO: MONO_TYPE_BYREF
|
||||
case MONO_TYPE_VALUETYPE:
|
||||
{
|
||||
MonoClass* klass = type.GetNative()->data.klass;
|
||||
@@ -873,19 +1032,18 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
|
||||
object = nullptr;
|
||||
return object;
|
||||
}
|
||||
// TODO: MONO_TYPE_VAR
|
||||
// TODO: MONO_TYPE_ARRAY
|
||||
// TODO: MONO_TYPE_GENERICINST
|
||||
// TODO: MONO_TYPE_TYPEDBYREF
|
||||
case MONO_TYPE_OBJECT:
|
||||
return BoxVariant(value);
|
||||
case MONO_TYPE_SZARRAY:
|
||||
if (value.Type == VariantType::Array)
|
||||
{
|
||||
// TODO: box Variant array into C# Array
|
||||
}
|
||||
return nullptr;
|
||||
// TODO: MONO_TYPE_ENUM
|
||||
case MONO_TYPE_ARRAY:
|
||||
{
|
||||
if (value.Type.Type != VariantType::Array)
|
||||
return nullptr;
|
||||
MonoObject* object = BoxVariant(value);
|
||||
if (object && !mono_class_is_subclass_of(mono_object_get_class(object), mono_array_class_get(type.GetNative()->data.klass, 1), false))
|
||||
object = nullptr;
|
||||
return object;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user