Merge remote-tracking branch 'origin/master' into gi

# Conflicts:
#	Source/Editor/Windows/Assets/VisualScriptWindow.cs
This commit is contained in:
Wojciech Figat
2022-05-02 10:38:14 +02:00
42 changed files with 1467 additions and 518 deletions

View File

@@ -222,7 +222,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v
LOG(Error, "Failed to access Visual Script parameter for {0}.", stack.Stack->Instance->ToString());
PrintStack(LogType::Error);
}
if (node->Boxes[2].HasConnection())
if (box->ID == 0 && node->Boxes[2].HasConnection())
eatBox(node, node->Boxes[2].FirstConnection());
break;
}
@@ -1154,17 +1154,19 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
arrayValue.NodeId = node->ID;
arrayValue.BoxId = 1;
arrayValue.Value = tryGetValue(node->GetBox(1), Value::Null);
if (arrayValue.Value.Type.Type != VariantType::Array)
if (arrayValue.Value.Type.Type == VariantType::Array)
{
const int32 count = arrayValue.Value.AsArray().Count();
for (; iteratorValue.Value.AsInt < count; iteratorValue.Value.AsInt++)
{
boxBase = node->GetBox(3);
if (boxBase->HasConnection())
eatBox(node, boxBase->FirstConnection());
}
}
else if (arrayValue.Value.Type.Type != VariantType::Null)
{
OnError(node, boxBase, String::Format(TEXT("Input value {0} is not an array."), arrayValue.Value));
return;
}
const int32 count = arrayValue.Value.AsArray().Count();
for (; iteratorValue.Value.AsInt < count; iteratorValue.Value.AsInt++)
{
boxBase = node->GetBox(3);
if (boxBase->HasConnection())
eatBox(node, boxBase->FirstConnection());
}
boxBase = node->GetBox(6);
if (boxBase->HasConnection())
@@ -1190,6 +1192,88 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
}
break;
}
// Dictionary For Each
case 8:
{
const auto scope = ThreadStacks.Get().Stack->Scope;
int32 iteratorIndex = 0;
for (; iteratorIndex < scope->ReturnedValues.Count(); iteratorIndex++)
{
const auto& e = scope->ReturnedValues[iteratorIndex];
if (e.NodeId == node->ID && e.BoxId == 0)
break;
}
int32 dictionaryIndex = 0;
for (; iteratorIndex < scope->ReturnedValues.Count(); dictionaryIndex++)
{
const auto& e = scope->ReturnedValues[dictionaryIndex];
if (e.NodeId == node->ID && e.BoxId == 1)
break;
}
switch (boxBase->ID)
{
// Loop
case 0:
{
if (iteratorIndex == scope->ReturnedValues.Count())
{
if (dictionaryIndex == scope->ReturnedValues.Count())
dictionaryIndex++;
scope->ReturnedValues.AddOne();
}
if (dictionaryIndex == scope->ReturnedValues.Count())
scope->ReturnedValues.AddOne();
auto& iteratorValue = scope->ReturnedValues[iteratorIndex];
iteratorValue.NodeId = node->ID;
iteratorValue.BoxId = 0;
auto& dictionaryValue = scope->ReturnedValues[dictionaryIndex];
dictionaryValue.NodeId = node->ID;
dictionaryValue.BoxId = 1;
dictionaryValue.Value = tryGetValue(node->GetBox(4), Value::Null);
if (dictionaryValue.Value.Type.Type == VariantType::Dictionary)
{
auto& dictionary = *dictionaryValue.Value.AsDictionary;
iteratorValue.Value = dictionary.Begin().Index();
int32 end = dictionary.End().Index();
while (iteratorValue.Value.AsInt < end)
{
boxBase = node->GetBox(3);
if (boxBase->HasConnection())
eatBox(node, boxBase->FirstConnection());
Dictionary<Variant, Variant>::Iterator it(dictionary, iteratorValue.Value.AsInt);
++it;
iteratorValue.Value.AsInt = it.Index();
}
}
else if (dictionaryValue.Value.Type.Type != VariantType::Null)
{
OnError(node, boxBase, String::Format(TEXT("Input value {0} is not a dictionary."), dictionaryValue.Value));
return;
}
boxBase = node->GetBox(6);
if (boxBase->HasConnection())
eatBox(node, boxBase->FirstConnection());
break;
}
// Key
case 1:
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
value = Dictionary<Variant, Variant>::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key;
break;
// Value
case 2:
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
value = Dictionary<Variant, Variant>::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value;
break;
// Break
case 5:
// Reset loop iterator
if (iteratorIndex != scope->ReturnedValues.Count())
scope->ReturnedValues[iteratorIndex].Value.AsInt = MAX_int32 - 1;
break;
}
break;
}
}
}

View File

@@ -245,6 +245,7 @@ public:
Dictionary& _collection;
int32 _index;
public:
Iterator(Dictionary& collection, const int32 index)
: _collection(collection)
, _index(index)
@@ -257,8 +258,6 @@ public:
{
}
public:
Iterator(const Iterator& i)
: _collection(i._collection)
, _index(i._index)
@@ -272,6 +271,10 @@ public:
}
public:
FORCE_INLINE int32 Index() const
{
return _index;
}
FORCE_INLINE bool IsEnd() const
{

View File

@@ -1679,7 +1679,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector2 left, Vector2 right)
{
return Mathf.NearEqual(left.X, left.X) && Mathf.NearEqual(left.Y, right.Y);
return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y);
}
/// <summary>
@@ -1691,7 +1691,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector2 left, Vector2 right)
{
return !Mathf.NearEqual(left.X, left.X) || !Mathf.NearEqual(left.Y, right.Y);
return !Mathf.NearEqual(left.X, right.X) || !Mathf.NearEqual(left.Y, right.Y);
}
/// <summary>

View File

@@ -1445,7 +1445,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector4 left, Vector4 right)
{
return left.Equals(ref right);
return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y) && Mathf.NearEqual(left.Z, right.Z) && Mathf.NearEqual(left.W, right.W);
}
/// <summary>

View File

@@ -66,7 +66,7 @@ namespace
"FlaxEngine.Ray",// Ray
"FlaxEngine.Matrix",// Matrix
"System.Object[]",// Array
"Dictionary<System.Object,System.Object>",// Dictionary
"System.Collections.Generic.Dictionary`2[System.Object,System.Object]",// Dictionary
"System.Object",// ManagedObject
"System.Type",// Typename
"FlaxEngine.Int2"// Int2
@@ -767,6 +767,12 @@ Variant::Variant(const Array<Variant, HeapAllocation>& v)
new(array)Array<Variant, HeapAllocation>(v);
}
Variant::Variant(Dictionary<Variant, Variant>&& v)
: Type(VariantType::Dictionary)
{
AsDictionary = New<Dictionary<Variant, Variant>>(MoveTemp(v));
}
Variant::Variant(const Dictionary<Variant, Variant>& v)
: Type(VariantType::Dictionary)
{

View File

@@ -228,6 +228,7 @@ public:
explicit Variant(const Matrix& v);
Variant(Array<Variant, HeapAllocation>&& v);
Variant(const Array<Variant, HeapAllocation>& v);
explicit Variant(Dictionary<Variant, Variant, HeapAllocation>&& v);
explicit Variant(const Dictionary<Variant, Variant, HeapAllocation>& v);
explicit Variant(const Span<byte>& v);
explicit Variant(const CommonValue& v);

View File

@@ -210,7 +210,7 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <returns>The child actor or null.</returns>
API_FUNCTION() Actor* GetChild(const MClass* type) const;
API_FUNCTION() Actor* GetChild(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
/// <summary>
/// Gets the child actor of the given type.
@@ -227,7 +227,7 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <returns>The child actors.</returns>
API_FUNCTION() Array<Actor*> GetChildren(const MClass* type) const;
API_FUNCTION() Array<Actor*> GetChildren(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
/// <summary>
/// Gets the child actors of the given type.
@@ -273,7 +273,7 @@ public:
/// </summary>
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
/// <returns>The script or null.</returns>
API_FUNCTION() Script* GetScript(const MClass* type) const;
API_FUNCTION() Script* GetScript(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type) const;
/// <summary>
/// Gets the script of the given type from this actor.
@@ -290,7 +290,7 @@ public:
/// </summary>
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
/// <returns>The scripts.</returns>
API_FUNCTION() Array<Script*> GetScripts(const MClass* type) const;
API_FUNCTION() Array<Script*> GetScripts(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type) const;
/// <summary>
/// Gets the scripts of the given type from this actor.
@@ -702,7 +702,7 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() Actor* FindActor(const MClass* type) const;
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
/// <summary>
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
@@ -719,7 +719,7 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <returns>Script instance if found, null otherwise.</returns>
API_FUNCTION() Script* FindScript(const MClass* type) const;
API_FUNCTION() Script* FindScript(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type) const;
/// <summary>
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).

View File

@@ -362,7 +362,7 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <returns>Found actor or null.</returns>
API_FUNCTION() static Actor* FindActor(const MClass* type);
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
/// <summary>
/// Tries to find the actor of the given type in all the loaded scenes.
@@ -379,7 +379,7 @@ public:
/// </summary>
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
/// <returns>Found script or null.</returns>
API_FUNCTION() static Script* FindScript(const MClass* type);
API_FUNCTION() static Script* FindScript(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type);
/// <summary>
/// Tries to find the script of the given type in all the loaded scenes.
@@ -396,14 +396,14 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <returns>Found actors list.</returns>
API_FUNCTION() static Array<Actor*> GetActors(const MClass* type);
API_FUNCTION() static Array<Actor*> GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
/// <summary>
/// Finds all the scripts of the given type in all the loaded scenes.
/// </summary>
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
/// <returns>Found scripts list.</returns>
API_FUNCTION() static Array<Script*> GetScripts(const MClass* type);
API_FUNCTION() static Array<Script*> GetScripts(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type);
/// <summary>
/// Tries to find scene with given ID.

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;
}

View File

@@ -10,7 +10,6 @@
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/Asset.h"
#include "Engine/Core/Cache.h"
#include "Engine/Debug/DebugLog.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ManagedSerialization.h"
@@ -458,12 +457,11 @@ void ReadStream::ReadVariant(Variant* data)
auto& dictionary = *data->AsDictionary;
dictionary.Clear();
dictionary.EnsureCapacity(count);
Variant key, value;
for (int32 i = 0; i < count; i++)
{
Variant key;
ReadVariant(&key);
ReadVariant(&value);
dictionary.Add(key, value);
ReadVariant(&dictionary[MoveTemp(key)]);
}
break;
}

View File

@@ -133,6 +133,18 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
}
}
break;
// Dictionary
case 14:
{
value = Variant(Dictionary<Variant, Variant>());
String typeName = TEXT("System.Collections.Generic.Dictionary`2[");
typeName += (StringView)node->Values[0];
typeName += ',';
typeName += (StringView)node->Values[1];
typeName += ']';
value.Type.SetTypeName(typeName);
break;
}
default:
break;
}
@@ -1257,6 +1269,8 @@ void VisjectExecutor::ProcessGroupCollections(Box* box, Node* node, Value& value
{
// Array
Variant v = tryGetValue(node->GetBox(0), Value::Null);
if (v.Type.Type == VariantType::Null)
v = Variant(Array<Variant>());
ENSURE(v.Type.Type == VariantType::Array, String::Format(TEXT("Input value {0} is not an array."), v));
auto& array = v.AsArray();
Box* b;
@@ -1359,4 +1373,65 @@ void VisjectExecutor::ProcessGroupCollections(Box* box, Node* node, Value& value
break;
}
}
else if (node->TypeID < 200)
{
// Dictionary
Variant v = tryGetValue(node->GetBox(0), Value::Null);
if (v.Type.Type == VariantType::Null)
v = Variant(Dictionary<Variant, Variant>());
ENSURE(v.Type.Type == VariantType::Dictionary, String::Format(TEXT("Input value {0} is not a dictionary."), v));
auto& dictionary = *v.AsDictionary;
switch (node->TypeID)
{
// Count
case 101:
value = dictionary.Count();
break;
// Contains Key
case 102:
{
Variant inKey = tryGetValue(node->GetBox(1), 0, Value::Null);
value = dictionary.ContainsKey(inKey);
break;
}
// Contains Value
case 103:
{
Variant inValue = tryGetValue(node->GetBox(2), 0, Value::Null);
value = dictionary.ContainsValue(inValue);
break;
}
// Clear
case 104:
dictionary.Clear();
value = MoveTemp(v);
break;
// Remove
case 105:
{
Variant inKey = tryGetValue(node->GetBox(1), 0, Value::Null);
dictionary.Remove(inKey);
value = MoveTemp(v);
break;
}
// Set
case 106:
{
Variant inKey = tryGetValue(node->GetBox(1), 0, Value::Null);
Variant inValue = tryGetValue(node->GetBox(2), 1, Value::Null);
dictionary[MoveTemp(inKey)] = MoveTemp(inValue);
value = MoveTemp(v);
break;
}
// Get
case 107:
{
Variant key = tryGetValue(node->GetBox(1), 0, Value::Null);
Variant* valuePtr = dictionary.TryGet(key);
ENSURE(valuePtr, TEXT("Missing key to get."));
value = MoveTemp(*valuePtr);
break;
}
}
}
}