From 54a103d840992beca873288e6d12b4d00d049936 Mon Sep 17 00:00:00 2001 From: Andrei Gagua Date: Sun, 24 May 2026 13:27:46 +0300 Subject: [PATCH] Upd: Array & Dictionary support for the ObjectInterfaceReferences --- .../CustomEditors/Editors/DictionaryEditor.cs | 2 +- .../Values/DictionaryValueContainer.cs | 13 +- .../Scripting/CodeEditors/RiderCodeEditor.cpp | 33 +++- .../Scripting/Internal/ManagedDictionary.cpp | 156 ++++++++++++++++++ .../Scripting/Internal/ManagedDictionary.h | 156 +----------------- .../Bindings/BindingsGenerator.CSharp.cs | 72 +++++++- .../Bindings/BindingsGenerator.Cpp.cs | 8 +- 7 files changed, 272 insertions(+), 168 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 1fe849f09..7dfce6055 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -260,7 +260,7 @@ namespace FlaxEditor.CustomEditors.Editors var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; var property = panel.AddPropertyItem(new DictionaryItemLabel(this, key)); var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel(); - itemLayout.Object(new DictionaryValueContainer(valuesType, key, Values), overrideEditor); + itemLayout.Object(new DictionaryValueContainer(valuesType, key, Values, attributes), overrideEditor); if (_readOnly && itemLayout.Children.Count > 0) GenericEditor.OnReadOnlyProperty(itemLayout); } diff --git a/Source/Editor/CustomEditors/Values/DictionaryValueContainer.cs b/Source/Editor/CustomEditors/Values/DictionaryValueContainer.cs index b143b44b1..435e5a515 100644 --- a/Source/Editor/CustomEditors/Values/DictionaryValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/DictionaryValueContainer.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.CustomEditors [HideInEditor] public class DictionaryValueContainer : ValueContainer { + private readonly object[] _attributes; + /// /// The key in the collection. /// @@ -36,9 +38,12 @@ namespace FlaxEditor.CustomEditors /// Type of the collection elements. /// The key. /// The collection values. - public DictionaryValueContainer(ScriptType elementType, object key, ValueContainer values) + /// The dictionary property attributes to inherit. + public DictionaryValueContainer(ScriptType elementType, object key, ValueContainer values, object[] attributes = null) : this(elementType, key) { + _attributes = attributes; + Capacity = values.Count; for (int i = 0; i < values.Count; i++) { @@ -123,5 +128,11 @@ namespace FlaxEditor.CustomEditors _hasReferenceValue = true; } } + + /// + public override object[] GetAttributes() + { + return _attributes ?? base.GetAttributes(); + } } } diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index b63815dce..de6001efd 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -14,6 +14,9 @@ #if PLATFORM_WINDOWS #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" +#elif PLATFORM_MAC +#include "Engine/Platform/Apple/AppleUtils.h" +#include #endif namespace @@ -68,10 +71,14 @@ namespace if (!launcherPath.HasChars() || !FileSystem::FileExists(exePath)) return; - if (launchOverridePath != String::Empty) - installations->Add(New(launchOverridePath, versionMember->value.GetText())); - else - installations->Add(New(exePath, versionMember->value.GetText())); + String installPath = launchOverridePath != String::Empty ? launchOverridePath : exePath; + StringUtils::PathRemoveRelativeParts(installPath); + for (RiderInstallation* installation : *installations) + { + if (installation->path == installPath) + return; + } + installations->Add(New(installPath, versionMember->value.GetText())); } #if PLATFORM_WINDOWS @@ -221,17 +228,29 @@ void RiderCodeEditor::FindEditors(Array* output) String applicationSupportFolder; FileSystem::GetSpecialFolderPath(SpecialFolder::ProgramData, applicationSupportFolder); + NSURL* appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:@"com.jetbrains.rider"]; + if (appURL != nullptr) + { + const String appPath = AppleUtils::ToString((CFStringRef)[appURL path]); + SearchDirectory(&installations, appPath / TEXT("Contents/Resources"), appPath); + } + Array subMacDirectories; FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-0/")); FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-1/")); for (const String& directory : subMacDirectories) { - String riderAppDirectory = directory / TEXT("Rider.app/Contents/Resources"); - SearchDirectory(&installations, riderAppDirectory); + String riderAppPath = directory / TEXT("Rider.app"); + SearchDirectory(&installations, riderAppPath / TEXT("Contents/Resources"), riderAppPath); } // Check the local installer version - SearchDirectory(&installations, TEXT("/Applications/Rider.app/Contents/Resources")); + SearchDirectory(&installations, TEXT("/Applications/Rider.app/Contents/Resources"), TEXT("/Applications/Rider.app")); + + String userFolder; + FileSystem::GetSpecialFolderPath(SpecialFolder::Documents, userFolder); + String riderAppPath = userFolder / TEXT("../Applications/Rider.app"); + SearchDirectory(&installations, riderAppPath / TEXT("Contents/Resources"), riderAppPath); #endif for (const String& directory : subDirectories) diff --git a/Source/Engine/Scripting/Internal/ManagedDictionary.cpp b/Source/Engine/Scripting/Internal/ManagedDictionary.cpp index d2f74e054..3cd4bfd8b 100644 --- a/Source/Engine/Scripting/Internal/ManagedDictionary.cpp +++ b/Source/Engine/Scripting/Internal/ManagedDictionary.cpp @@ -15,4 +15,160 @@ MMethod* ManagedDictionary::CreateInstance; MMethod* ManagedDictionary::AddDictionaryItem; MMethod* ManagedDictionary::GetDictionaryKeys; #endif + +ManagedDictionary::ManagedDictionary(MObject* instance) +{ + Instance = instance; + +#if !USE_MONO_AOT + // Cache the thunks of the dictionary helper methods + if (MakeGenericType == nullptr) + { + MClass* scriptingClass = Scripting::GetStaticClass(); + CHECK(scriptingClass); + + MMethod* makeGenericTypeMethod = scriptingClass->GetMethod("MakeGenericType", 2); + CHECK(makeGenericTypeMethod); + MakeGenericType = (MakeGenericTypeThunk)makeGenericTypeMethod->GetThunk(); + + MMethod* createInstanceMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2); + CHECK(createInstanceMethod); + CreateInstance = (CreateInstanceThunk)createInstanceMethod->GetThunk(); + + MMethod* addDictionaryItemMethod = scriptingClass->GetMethod("AddDictionaryItem", 3); + CHECK(addDictionaryItemMethod); + AddDictionaryItem = (AddDictionaryItemThunk)addDictionaryItemMethod->GetThunk(); + + MMethod* getDictionaryKeysItemMethod = scriptingClass->GetMethod("GetDictionaryKeys", 1); + CHECK(getDictionaryKeysItemMethod); + GetDictionaryKeys = (GetDictionaryKeysThunk)getDictionaryKeysItemMethod->GetThunk(); + } +#else + if (MakeGenericType == nullptr) + { + MClass* scriptingClass = Scripting::GetStaticClass(); + CHECK(scriptingClass); + + MakeGenericType = scriptingClass->GetMethod("MakeGenericType", 2); + CHECK(MakeGenericType); + + CreateInstance = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2); + CHECK(CreateInstance); + + AddDictionaryItem = scriptingClass->GetMethod("AddDictionaryItem", 3); + CHECK(AddDictionaryItem); + + GetDictionaryKeys = scriptingClass->GetMethod("GetDictionaryKeys", 1); + CHECK(GetDictionaryKeys); + } +#endif +} + +MTypeObject* ManagedDictionary::GetClass(MType* keyType, MType* valueType) +{ + // Check if the generic type was generated earlier + KeyValueType cacheKey = { keyType, valueType }; + MTypeObject* dictionaryType; + if (CachedTypes.TryGet(cacheKey, dictionaryType)) + return dictionaryType; + + MTypeObject* genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass); +#if USE_NETCORE + MArray* genericArgs = MCore::Array::New(MCore::TypeCache::IntPtr, 2); +#else + MArray* genericArgs = MCore::Array::New(MCore::TypeCache::Object, 2); +#endif + MTypeObject** genericArgsPtr = MCore::Array::GetAddress(genericArgs); + genericArgsPtr[0] = INTERNAL_TYPE_GET_OBJECT(keyType); + genericArgsPtr[1] = INTERNAL_TYPE_GET_OBJECT(valueType); + + MObject* exception = nullptr; +#if !USE_MONO_AOT + dictionaryType = MakeGenericType(nullptr, genericType, genericArgs, &exception); +#else + void* params[2]; + params[0] = genericType; + params[1] = genericArgs; + dictionaryType = (MTypeObject*)MakeGenericType->Invoke(nullptr, params, &exception); +#endif + if (exception) + { + MException ex(exception); + ex.Log(LogType::Error, TEXT("")); + return nullptr; + } + CachedTypes.Add(cacheKey, dictionaryType); + return dictionaryType; +} + +ManagedDictionary ManagedDictionary::New(MType* keyType, MType* valueType) +{ + ManagedDictionary result; + MTypeObject* dictionaryType = GetClass(keyType, valueType); + if (!dictionaryType) + return result; + + MObject* exception = nullptr; +#if !USE_MONO_AOT + MObject* instance = CreateInstance(nullptr, dictionaryType, nullptr, &exception); +#else + void* params[2]; + params[0] = dictionaryType; + params[1] = nullptr; + MObject* instance = CreateInstance->Invoke(nullptr, params, &exception); +#endif + if (exception) + { + MException ex(exception); + ex.Log(LogType::Error, TEXT("")); + return result; + } + + result.Instance = instance; + return result; +} + +void ManagedDictionary::Add(MObject* key, MObject* value) +{ + CHECK(Instance); + + MObject* exception = nullptr; +#if !USE_MONO_AOT + AddDictionaryItem(nullptr, Instance, key, value, &exception); +#else + void* params[3]; + params[0] = Instance; + params[1] = key; + params[2] = value; + AddDictionaryItem->Invoke(Instance, params, &exception); +#endif + if (exception) + { + MException ex(exception); + ex.Log(LogType::Error, TEXT("")); + } +} + +MArray* ManagedDictionary::GetKeys() const +{ + CHECK_RETURN(Instance, nullptr); +#if !USE_MONO_AOT + return GetDictionaryKeys(nullptr, Instance, nullptr); +#else + void* params[1]; + params[0] = Instance; + return (MArray*)GetDictionaryKeys->Invoke(nullptr, params, nullptr); +#endif +} + +MObject* ManagedDictionary::GetValue(MObject* key) const +{ + CHECK_RETURN(Instance, nullptr); + MClass* klass = MCore::Object::GetClass(Instance); + MMethod* getItemMethod = klass->GetMethod("System.Collections.IDictionary.get_Item", 1); + CHECK_RETURN(getItemMethod, nullptr); + void* params[1]; + params[0] = key; + return getItemMethod->Invoke(Instance, params, nullptr); +} #endif diff --git a/Source/Engine/Scripting/Internal/ManagedDictionary.h b/Source/Engine/Scripting/Internal/ManagedDictionary.h index 5e2638af7..f4663ac58 100644 --- a/Source/Engine/Scripting/Internal/ManagedDictionary.h +++ b/Source/Engine/Scripting/Internal/ManagedDictionary.h @@ -57,53 +57,7 @@ private: public: MObject* Instance; - ManagedDictionary(MObject* instance = nullptr) - { - Instance = instance; - -#if !USE_MONO_AOT - // Cache the thunks of the dictionary helper methods - if (MakeGenericType == nullptr) - { - MClass* scriptingClass = Scripting::GetStaticClass(); - CHECK(scriptingClass); - - MMethod* makeGenericTypeMethod = scriptingClass->GetMethod("MakeGenericType", 2); - CHECK(makeGenericTypeMethod); - MakeGenericType = (MakeGenericTypeThunk)makeGenericTypeMethod->GetThunk(); - - MMethod* createInstanceMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2); - CHECK(createInstanceMethod); - CreateInstance = (CreateInstanceThunk)createInstanceMethod->GetThunk(); - - MMethod* addDictionaryItemMethod = scriptingClass->GetMethod("AddDictionaryItem", 3); - CHECK(addDictionaryItemMethod); - AddDictionaryItem = (AddDictionaryItemThunk)addDictionaryItemMethod->GetThunk(); - - MMethod* getDictionaryKeysItemMethod = scriptingClass->GetMethod("GetDictionaryKeys", 1); - CHECK(getDictionaryKeysItemMethod); - GetDictionaryKeys = (GetDictionaryKeysThunk)getDictionaryKeysItemMethod->GetThunk(); - } -#else - if (MakeGenericType == nullptr) - { - MClass* scriptingClass = Scripting::GetStaticClass(); - CHECK(scriptingClass); - - MakeGenericType = scriptingClass->GetMethod("MakeGenericType", 2); - CHECK(MakeGenericType); - - CreateInstance = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2); - CHECK(CreateInstance); - - AddDictionaryItem = scriptingClass->GetMethod("AddDictionaryItem", 3); - CHECK(AddDictionaryItem); - - GetDictionaryKeys = scriptingClass->GetMethod("GetDictionaryKeys", 1); - CHECK(GetDictionaryKeys); - } -#endif - } + ManagedDictionary(MObject* instance = nullptr); template static MObject* ToManaged(const Dictionary& data, MType* keyType, MType* valueType) @@ -154,113 +108,15 @@ public: return result; } - static MTypeObject* GetClass(MType* keyType, MType* valueType) - { - // Check if the generic type was generated earlier - KeyValueType cacheKey = { keyType, valueType }; - MTypeObject* dictionaryType; - if (CachedTypes.TryGet(cacheKey, dictionaryType)) - return dictionaryType; + static MTypeObject* GetClass(MType* keyType, MType* valueType); - MTypeObject* genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass); -#if USE_NETCORE - MArray* genericArgs = MCore::Array::New(MCore::TypeCache::IntPtr, 2); -#else - MArray* genericArgs = MCore::Array::New(MCore::TypeCache::Object, 2); -#endif - MTypeObject** genericArgsPtr = MCore::Array::GetAddress(genericArgs); - genericArgsPtr[0] = INTERNAL_TYPE_GET_OBJECT(keyType); - genericArgsPtr[1] = INTERNAL_TYPE_GET_OBJECT(valueType); + static ManagedDictionary New(MType* keyType, MType* valueType); - MObject* exception = nullptr; -#if !USE_MONO_AOT - dictionaryType = MakeGenericType(nullptr, genericType, genericArgs, &exception); -#else - void* params[2]; - params[0] = genericType; - params[1] = genericArgs; - dictionaryType = (MTypeObject*)MakeGenericType->Invoke(nullptr, params, &exception); -#endif - if (exception) - { - MException ex(exception); - ex.Log(LogType::Error, TEXT("")); - return nullptr; - } - CachedTypes.Add(cacheKey, dictionaryType); - return dictionaryType; - } + void Add(MObject* key, MObject* value); - static ManagedDictionary New(MType* keyType, MType* valueType) - { - ManagedDictionary result; - MTypeObject* dictionaryType = GetClass(keyType, valueType); - if (!dictionaryType) - return result; + MArray* GetKeys() const; - MObject* exception = nullptr; -#if !USE_MONO_AOT - MObject* instance = CreateInstance(nullptr, dictionaryType, nullptr, &exception); -#else - void* params[2]; - params[0] = dictionaryType; - params[1] = nullptr; - MObject* instance = CreateInstance->Invoke(nullptr, params, &exception); -#endif - if (exception) - { - MException ex(exception); - ex.Log(LogType::Error, TEXT("")); - return result; - } - - result.Instance = instance; - return result; - } - - void Add(MObject* key, MObject* value) - { - CHECK(Instance); - - MObject* exception = nullptr; -#if !USE_MONO_AOT - AddDictionaryItem(nullptr, Instance, key, value, &exception); -#else - void* params[3]; - params[0] = Instance; - params[1] = key; - params[2] = value; - AddDictionaryItem->Invoke(Instance, params, &exception); -#endif - if (exception) - { - MException ex(exception); - ex.Log(LogType::Error, TEXT("")); - } - } - - MArray* GetKeys() const - { - CHECK_RETURN(Instance, nullptr); -#if !USE_MONO_AOT - return GetDictionaryKeys(nullptr, Instance, nullptr); -#else - void* params[1]; - params[0] = Instance; - return (MArray*)GetDictionaryKeys->Invoke(nullptr, params, nullptr); -#endif - } - - MObject* GetValue(MObject* key) const - { - CHECK_RETURN(Instance, nullptr); - MClass* klass = MCore::Object::GetClass(Instance); - MMethod* getItemMethod = klass->GetMethod("System.Collections.IDictionary.get_Item", 1); - CHECK_RETURN(getItemMethod, nullptr); - void* params[1]; - params[0] = key; - return getItemMethod->Invoke(Instance, params, nullptr); - } + MObject* GetValue(MObject* key) const; }; inline uint32 GetHash(const ManagedDictionary::KeyValueType& other) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 433dbdda6..604d501ab 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -375,12 +375,16 @@ namespace Flax.Build.Bindings if (arrayApiType != null && arrayApiType.MarshalAs != null) arrayTypeInfo = arrayApiType.MarshalAs; } - return GenerateCSharpNativeToManaged(buildData, arrayTypeInfo, caller) + "[]"; + return GenerateCSharpNativeToManaged(buildData, arrayTypeInfo, caller, marshalling) + "[]"; } // Dictionary if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null) - return string.Format("System.Collections.Generic.Dictionary<{0}, {1}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller, marshalling), GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller, marshalling)); + { + var keyType = marshalling && typeInfo.GenericArgs[0].IsInterfaceRef ? "object" : GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller, marshalling); + var valueType = marshalling && typeInfo.GenericArgs[1].IsInterfaceRef ? "object" : GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller, marshalling); + return string.Format("System.Collections.Generic.Dictionary<{0}, {1}>", keyType, valueType); + } // HashSet if (typeInfo.Type == "HashSet" && typeInfo.GenericArgs != null) @@ -554,11 +558,21 @@ namespace Flax.Build.Bindings { // Convert array that uses different type for marshalling var arrayTypeInfo = typeInfo.GenericArgs[0]; + if (arrayTypeInfo.IsInterfaceRef) + return "{0} != null ? FlaxEngine.Interop.NativeInterop.ManagedArrayToGCHandleArray({0}) : null"; var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller); if (arrayApiType != null && arrayApiType.MarshalAs != null) return $"{{0}}.ConvertArray(x => ({GenerateCSharpNativeToManaged(buildData, arrayApiType.MarshalAs, caller)})x)"; } return string.Empty; + case "Dictionary": + if (typeInfo.GenericArgs != null && typeInfo.GenericArgs.Count == 2 && (typeInfo.GenericArgs[0].IsInterfaceRef || typeInfo.GenericArgs[1].IsInterfaceRef)) + { + var keyConverter = typeInfo.GenericArgs[0].IsInterfaceRef ? "(object)x.Key" : "x.Key"; + var valueConverter = typeInfo.GenericArgs[1].IsInterfaceRef ? "(object)x.Value" : "x.Value"; + return $"{{0}} != null ? System.Linq.Enumerable.ToDictionary({{0}}, x => {keyConverter}, x => {valueConverter}) : null"; + } + return string.Empty; default: // Interface reference property if (typeInfo.IsInterfaceRef) @@ -787,8 +801,20 @@ namespace Flax.Build.Bindings } #endif const string interfaceResultName = "__interfaceResult"; + const string interfaceArrayResultName = "__interfaceArrayResult"; + const string interfaceDictionaryResultName = "__interfaceDictionaryResult"; var returnInterfaceRef = !functionInfo.Glue.UseReferenceForResult && functionInfo.ReturnType.IsInterfaceRef; + var returnInterfaceRefArray = !functionInfo.Glue.UseReferenceForResult && + (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer") && + functionInfo.ReturnType.GenericArgs != null && + functionInfo.ReturnType.GenericArgs.Count != 0 && + functionInfo.ReturnType.GenericArgs[0].IsInterfaceRef; + var returnInterfaceRefDictionary = !functionInfo.Glue.UseReferenceForResult && + functionInfo.ReturnType.Type == "Dictionary" && + functionInfo.ReturnType.GenericArgs != null && + functionInfo.ReturnType.GenericArgs.Count == 2 && + (functionInfo.ReturnType.GenericArgs[0].IsInterfaceRef || functionInfo.ReturnType.GenericArgs[1].IsInterfaceRef); if (functionInfo.Glue.UseReferenceForResult) { @@ -797,6 +823,14 @@ namespace Flax.Build.Bindings { contents.Append("var ").Append(interfaceResultName).Append(" = "); } + else if (returnInterfaceRefArray) + { + contents.Append("var ").Append(interfaceArrayResultName).Append(" = "); + } + else if (returnInterfaceRefDictionary) + { + contents.Append("var ").Append(interfaceDictionaryResultName).Append(" = "); + } else if (!functionInfo.ReturnType.IsVoid) { contents.Append("return "); @@ -872,7 +906,20 @@ namespace Flax.Build.Bindings var managedType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType.GenericArgs[0], caller); contents.Append("; return ").Append(interfaceResultName).Append(" != IntPtr.Zero ? Unsafe.As<").Append(managedType).Append(">(ManagedHandle.FromIntPtr(").Append(interfaceResultName).Append(").Target) : null"); } - if ((functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer") && functionInfo.ReturnType.GenericArgs != null) + else if (returnInterfaceRefArray) + { + var managedType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType.GenericArgs[0].GenericArgs[0], caller); + contents.Append("; return ").Append(interfaceArrayResultName).Append("?.ConvertArray(x => x != IntPtr.Zero ? Unsafe.As<").Append(managedType).Append(">(ManagedHandle.FromIntPtr(x).Target) : null)"); + } + else if (returnInterfaceRefDictionary) + { + var keyTypeInfo = functionInfo.ReturnType.GenericArgs[0]; + var valueTypeInfo = functionInfo.ReturnType.GenericArgs[1]; + var keyConverter = keyTypeInfo.IsInterfaceRef ? $"x.Key != null ? Unsafe.As<{GenerateCSharpNativeToManaged(buildData, keyTypeInfo.GenericArgs[0], caller)}>(x.Key) : null" : "x.Key"; + var valueConverter = valueTypeInfo.IsInterfaceRef ? $"x.Value != null ? Unsafe.As<{GenerateCSharpNativeToManaged(buildData, valueTypeInfo.GenericArgs[0], caller)}>(x.Value) : null" : "x.Value"; + contents.Append("; return ").Append(interfaceDictionaryResultName).Append(" != null ? System.Linq.Enumerable.ToDictionary(").Append(interfaceDictionaryResultName).Append(", x => ").Append(keyConverter).Append(", x => ").Append(valueConverter).Append(") : null"); + } + else if ((functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer") && functionInfo.ReturnType.GenericArgs != null) { // Convert array that uses different type for marshalling var arrayTypeInfo = functionInfo.ReturnType.GenericArgs[0]; @@ -1009,9 +1056,24 @@ namespace Flax.Build.Bindings { GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, memberInfo.Attributes, memberInfo.Comment, true, useUnmanaged, defaultValue, memberInfo.DeprecatedMessage, defaultValueType); var memberType = (memberInfo as FieldInfo)?.Type ?? (memberInfo as PropertyInfo)?.Type; - if (memberType != null && memberType.IsInterfaceRef) + var interfaceRefType = memberType; + if ((memberType?.Type == "Array" || memberType?.Type == "Span" || memberType?.Type == "DataContainer") && + memberType.GenericArgs != null && + memberType.GenericArgs.Count != 0 && + memberType.GenericArgs[0].IsInterfaceRef) { - var attribute = memberType.Type == "SoftObjectInterfaceReference" ? "SoftObjectInterfaceReference" : "ScriptingObjectInterfaceReference"; + interfaceRefType = memberType.GenericArgs[0]; + } + else if (memberType?.Type == "Dictionary" && + memberType.GenericArgs != null && + memberType.GenericArgs.Count == 2 && + (memberType.GenericArgs[0].IsInterfaceRef || memberType.GenericArgs[1].IsInterfaceRef)) + { + interfaceRefType = memberType.GenericArgs[1].IsInterfaceRef ? memberType.GenericArgs[1] : memberType.GenericArgs[0]; + } + if (interfaceRefType != null && interfaceRefType.IsInterfaceRef) + { + var attribute = interfaceRefType.Type == "SoftObjectInterfaceReference" ? "SoftObjectInterfaceReference" : "ScriptingObjectInterfaceReference"; contents.Append(indent).Append("[FlaxEngine.").Append(attribute).AppendLine("]"); } } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index e67bde3af..da53fa78b 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -656,8 +656,8 @@ namespace Flax.Build.Bindings { CppIncludeFiles.Add("Engine/Scripting/Internal/ManagedDictionary.h"); type = "MObject*"; - var keyClass = GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[0], caller, functionInfo); - var valueClass = GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[1], caller, functionInfo); + var keyClass = typeInfo.GenericArgs[0].IsInterfaceRef ? "MCore::TypeCache::Object->GetType()" : GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[0], caller, functionInfo); + var valueClass = typeInfo.GenericArgs[1].IsInterfaceRef ? "MCore::TypeCache::Object->GetType()" : GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[1], caller, functionInfo); return "ManagedDictionary::ToManaged({0}, " + keyClass + ", " + valueClass + ")"; } @@ -1023,8 +1023,8 @@ namespace Flax.Build.Bindings if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null) { CppIncludeFiles.Add("Engine/Scripting/Internal/ManagedDictionary.h"); - var keyClass = GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[0], caller); - var valueClass = GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[1], caller); + var keyClass = typeInfo.GenericArgs[0].IsInterfaceRef ? "MCore::TypeCache::Object->GetType()" : GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[0], caller); + var valueClass = typeInfo.GenericArgs[1].IsInterfaceRef ? "MCore::TypeCache::Object->GetType()" : GenerateCppGetNativeType(buildData, typeInfo.GenericArgs[1], caller); return $"ManagedDictionary::ToManaged({value}, {keyClass}, {valueClass})"; }