Add **Dictionaries to Visual Scripting**

This commit is contained in:
Wojtek Figat
2022-04-27 22:47:54 +02:00
parent 3c841b1be1
commit 158c29e598
20 changed files with 852 additions and 114 deletions

View File

@@ -14,6 +14,9 @@
#if USE_MONO
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
/// <summary>
/// Utility interop between C++ and C# for Dictionary collection.
/// </summary>
struct FLAXENGINE_API ManagedDictionary
{
MonoObject* Instance;
@@ -74,17 +77,13 @@ struct FLAXENGINE_API ManagedDictionary
return result;
}
static ManagedDictionary New(MonoType* keyType, MonoType* valueType)
static MonoReflectionType* GetClass(MonoType* keyType, MonoType* valueType)
{
ManagedDictionary result;
auto domain = mono_domain_get();
auto scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, result);
CHECK_RETURN(scriptingClass, nullptr);
auto makeGenericMethod = scriptingClass->GetMethod("MakeGenericType", 2);
CHECK_RETURN(makeGenericMethod, result);
auto createMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
CHECK_RETURN(createMethod, result);
CHECK_RETURN(makeGenericMethod, nullptr);
auto genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass->GetNative());
auto genericArgs = mono_array_new(domain, mono_get_object_class(), 2);
@@ -100,9 +99,25 @@ struct FLAXENGINE_API ManagedDictionary
{
MException ex(exception);
ex.Log(LogType::Error, TEXT(""));
return result;
return nullptr;
}
return (MonoReflectionType*)dictionaryType;
}
static ManagedDictionary New(MonoType* keyType, MonoType* valueType)
{
ManagedDictionary result;
auto dictionaryType = GetClass(keyType, valueType);
if (!dictionaryType)
return result;
auto scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, result);
auto createMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
CHECK_RETURN(createMethod, result);
MObject* exception = nullptr;
void* params[2];
params[0] = dictionaryType;
params[1] = nullptr;
auto instance = createMethod->Invoke(nullptr, params, &exception);

View File

@@ -19,11 +19,42 @@
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Scripting/StdTypesContainer.h"
#include "Engine/Scripting/InternalCalls/ManagedDictionary.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Content/Asset.h"
#if USE_MONO
// Inlined mono private types to access MonoType internals
typedef struct _MonoGenericClass MonoGenericClass;
typedef struct _MonoGenericContext MonoGenericContext;
struct _MonoGenericInst
{
unsigned int id;
unsigned int type_argc : 22;
unsigned int is_open : 1;
MonoType* type_argv[MONO_ZERO_LEN_ARRAY];
};
struct _MonoGenericContext
{
MonoGenericInst* class_inst;
MonoGenericInst* method_inst;
};
struct _MonoGenericClass
{
MonoClass* container_class;
MonoGenericContext context;
unsigned int is_dynamic : 1;
unsigned int is_tb_open : 1;
unsigned int need_sync : 1;
MonoClass* cached_class;
class MonoImageSet* owner;
};
struct _MonoType
{
union
@@ -43,6 +74,21 @@ struct _MonoType
unsigned int pinned : 1;
};
namespace
{
// typeName in format System.Collections.Generic.Dictionary`2[KeyType,ValueType]
void GetDictionaryKeyValueTypes(const StringAnsiView& typeName, MonoClass*& keyClass, MonoClass*& 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::FindClassNative(keyTypename);
valueClass = Scripting::FindClassNative(valueTypename);
}
}
StringView MUtils::ToString(MonoString* str)
{
if (str == nullptr)
@@ -439,6 +485,30 @@ Variant MUtils::UnboxVariant(MonoObject* value)
}
return v;
}
case MONO_TYPE_GENERICINST:
{
if (StringUtils::Compare(mono_class_get_name(klass), "Dictionary`2") == 0 && StringUtils::Compare(mono_class_get_namespace(klass), "System.Collections.Generic") == 0)
{
// Dictionary
ManagedDictionary managed(value);
MonoArray* managedKeys = managed.GetKeys();
auto length = managedKeys ? (int32)mono_array_length(managedKeys) : 0;
Dictionary<Variant, Variant> native;
native.EnsureCapacity(length);
for (int32 i = 0; i < length; i++)
{
MonoObject* keyManaged = mono_array_get(managedKeys, MonoObject*, i);
MonoObject* valueManaged = managed.GetValue(keyManaged);
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged));
}
Variant v(MoveTemp(native));
StringAnsi typeName;
GetClassFullname(klass, typeName);
v.Type.SetTypeName(typeName);
return v;
}
break;
}
}
if (mono_class_is_subclass_of(klass, Asset::GetStaticClass()->GetNative(), false) != 0)
@@ -473,7 +543,6 @@ Variant MUtils::UnboxVariant(MonoObject* value)
}
return Variant(value);
}
// TODO: support any dictionary unboxing
return Variant(value);
}
@@ -642,7 +711,31 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
}
return (MonoObject*)managed;
}
// TODO: VariantType::Dictionary
case VariantType::Dictionary:
{
// Get dictionary key and value types
MonoClass *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(mono_class_get_type(keyClass), mono_class_get_type(valueClass));
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)
@@ -684,7 +777,6 @@ void MUtils::GetClassFullname(MonoObject* obj, MString& fullname)
{
if (obj == nullptr)
return;
MonoClass* monoClass = mono_object_get_class(obj);
GetClassFullname(monoClass, fullname);
}
@@ -693,11 +785,13 @@ void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
{
static MString plusStr("+");
static MString dotStr(".");
MonoClass* nestingClass = mono_class_get_nesting_type(monoClass);
MonoClass* lastClass = monoClass;
// Name
fullname = mono_class_get_name(monoClass);
// Outer class for nested types
MonoClass* nestingClass = mono_class_get_nesting_type(monoClass);
MonoClass* lastClass = monoClass;
while (nestingClass)
{
lastClass = nestingClass;
@@ -705,9 +799,27 @@ void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
nestingClass = mono_class_get_nesting_type(nestingClass);
}
// Namespace
const char* lastClassNamespace = mono_class_get_namespace(lastClass);
if (lastClassNamespace && *lastClassNamespace)
fullname = lastClassNamespace + dotStr + fullname;
// Generic instance arguments
const MonoType* monoType = mono_class_get_type(monoClass);
if (monoType && monoType->type == MONO_TYPE_GENERICINST)
{
fullname += '[';
MString tmp;
for (unsigned int i = 0; i < monoType->data.generic_class->context.class_inst->type_argc; i++)
{
if (i != 0)
fullname += ',';
MonoType* argType = monoType->data.generic_class->context.class_inst->type_argv[i];
GetClassFullname(mono_type_get_class(argType), tmp);
fullname += tmp;
}
fullname += ']';
}
}
void MUtils::GetClassFullname(MonoReflectionType* type, MString& fullname)
@@ -715,7 +827,7 @@ void MUtils::GetClassFullname(MonoReflectionType* type, MString& fullname)
if (!type)
return;
MonoType* monoType = mono_reflection_type_get_type(type);
MonoClass* monoClass = mono_type_get_class(monoType);
MonoClass* monoClass = mono_class_from_mono_type(monoType);
GetClassFullname(monoClass, fullname);
}
@@ -729,7 +841,7 @@ MonoClass* MUtils::GetClass(MonoReflectionType* type)
if (!type)
return nullptr;
MonoType* monoType = mono_reflection_type_get_type(type);
return mono_type_get_class(monoType);
return mono_class_from_mono_type(monoType);
}
MonoClass* MUtils::GetClass(const VariantType& value)
@@ -805,6 +917,17 @@ MonoClass* MUtils::GetClass(const VariantType& value)
return mono_array_class_get(mclass->GetNative(), 1);
}
return mono_array_class_get(mono_get_object_class(), 1);
case VariantType::Dictionary:
{
MonoClass *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(mono_class_get_type(keyClass), mono_class_get_type(valueClass)));
}
case VariantType::ManagedObject:
return mono_get_object_class();
default: ;
@@ -1052,6 +1175,15 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
object = nullptr;
return object;
}
case MONO_TYPE_GENERICINST:
{
if (value.Type.Type == VariantType::Null)
return nullptr;
MonoObject* object = BoxVariant(value);
if (object && !mono_class_is_subclass_of(mono_object_get_class(object), mono_class_from_mono_type(type.GetNative()), false))
object = nullptr;
return object;
}
default:
break;
}