From 405ae519dd2f24f136681be4785a749627178f2e Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Mon, 23 Jan 2023 18:41:12 +0100 Subject: [PATCH] Refactor generated bindings P/Invoke to support name mangling for symbols --- Source/Editor/Scripting/ScriptsBuilder.cpp | 3 - Source/Engine/Animations/AnimationGraph.cs | 6 +- Source/Engine/Core/Compiler.h | 4 - Source/Engine/Level/Level.cpp | 2 - Source/Engine/Networking/NetworkManager.cpp | 1 - .../Platform/Windows/WindowsClipboard.cpp | 1 - Source/Engine/Render2D/Render2D.cpp | 4 - .../InternalCalls/EngineInternalCalls.cpp | 139 ++++++++---------- .../Bindings/BindingsGenerator.CSharp.cs | 14 +- .../Bindings/BindingsGenerator.Cpp.cs | 89 ++++++----- .../Flax.Build/Bindings/BindingsGenerator.cs | 4 +- .../Tools/Flax.Build/Bindings/FunctionInfo.cs | 1 + .../Flax.Build/Utilities/CppNameMangling.cs | 55 +++++++ 13 files changed, 187 insertions(+), 136 deletions(-) create mode 100644 Source/Tools/Flax.Build/Utilities/CppNameMangling.cs diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 70d8fdca4..12e1b2a2d 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -176,7 +176,6 @@ bool ScriptsBuilder::IsReady() void ScriptsBuilder::MarkWorkspaceDirty() { - SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_MarkWorkspaceDirty") ScopeLock scopeLock(_locker); _lastSourceCodeEdited = DateTime::Now(); _wasProjectStructureChanged = true; @@ -184,7 +183,6 @@ void ScriptsBuilder::MarkWorkspaceDirty() void ScriptsBuilder::CheckForCompile() { - SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_CheckForCompile") ScopeLock scopeLock(_locker); if (IsSourceDirty()) Compile(); @@ -207,7 +205,6 @@ void ScriptsBuilderImpl::onScriptsReloadEnd() void ScriptsBuilder::Compile() { - SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_Compile") ScopeLock scopeLock(_locker); // Request compile job diff --git a/Source/Engine/Animations/AnimationGraph.cs b/Source/Engine/Animations/AnimationGraph.cs index e90a7be78..9bba0858b 100644 --- a/Source/Engine/Animations/AnimationGraph.cs +++ b/Source/Engine/Animations/AnimationGraph.cs @@ -260,15 +260,15 @@ namespace FlaxEngine #region Internal Calls - [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_HasConnection")] + [LibraryImport("FlaxEngine", EntryPoint = "AnimGraphInternal_HasConnection")] [return: MarshalAs(UnmanagedType.U1)] internal static partial bool Internal_HasConnection(ref AnimationGraph.CustomNode.Context context, int boxId); - [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_GetInputValue")] + [LibraryImport("FlaxEngine", EntryPoint = "AnimGraphInternal_GetInputValue")] [return: MarshalUsing(typeof(FlaxEngine.ManagedHandleMarshaller))] internal static partial object Internal_GetInputValue(ref AnimationGraph.CustomNode.Context context, int boxId); - [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_GetOutputImpulseData")] + [LibraryImport("FlaxEngine", EntryPoint = "AnimGraphInternal_GetOutputImpulseData")] internal static partial IntPtr Internal_GetOutputImpulseData(ref AnimationGraph.CustomNode.Context context); #endregion diff --git a/Source/Engine/Core/Compiler.h b/Source/Engine/Core/Compiler.h index 7ea30244a..9a33b8758 100644 --- a/Source/Engine/Core/Compiler.h +++ b/Source/Engine/Core/Compiler.h @@ -32,8 +32,6 @@ #pragma clang diagnostic ignored "-Wnull-dereference" #pragma clang diagnostic ignored "-Winvalid-noreturn" -#define SCRIPTING_EXPORT(name) - #elif defined(__GNUC__) #define DLLEXPORT __attribute__ ((__visibility__ ("default"))) @@ -88,8 +86,6 @@ #pragma warning(disable: 4251) -#define SCRIPTING_EXPORT(name) __pragma(comment(linker, "/EXPORT:" #name "=" __FUNCDNAME__)) - #else #pragma error "Unknown compiler." diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index f531e1ed4..45518efd1 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -1323,7 +1323,6 @@ bool Level::SaveAllScenes() void Level::SaveAllScenesAsync() { - SCRIPTING_EXPORT("FlaxEngine.Level::Internal_SaveAllScenesAsync") ScopeLock lock(_sceneActionsLocker); for (int32 i = 0; i < Scenes.Count(); i++) _sceneActions.Enqueue(New(Scenes[i])); @@ -1428,7 +1427,6 @@ bool Level::UnloadAllScenes() void Level::UnloadAllScenesAsync() { - SCRIPTING_EXPORT("FlaxEngine.Level::Internal_UnloadAllScenesAsync") ScopeLock lock(_sceneActionsLocker); _sceneActions.Enqueue(New()); } diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index de62063cf..3a1984fe0 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -304,7 +304,6 @@ bool NetworkManager::StartHost() void NetworkManager::Stop() { - SCRIPTING_EXPORT("FlaxEngine.Networking.NetworkManager::Internal_Stop"); if (Mode == NetworkManagerMode::Offline && State == NetworkConnectionState::Offline) return; PROFILE_CPU(); diff --git a/Source/Engine/Platform/Windows/WindowsClipboard.cpp b/Source/Engine/Platform/Windows/WindowsClipboard.cpp index 0bc23cfc6..455d3a303 100644 --- a/Source/Engine/Platform/Windows/WindowsClipboard.cpp +++ b/Source/Engine/Platform/Windows/WindowsClipboard.cpp @@ -16,7 +16,6 @@ typedef struct _DROPFILES void WindowsClipboard::Clear() { - SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear"); OpenClipboard(nullptr); EmptyClipboard(); CloseClipboard(); diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index d252d6706..1fd384d44 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -700,7 +700,6 @@ void Render2D::Begin(GPUContext* context, GPUTextureView* output, GPUTextureView void Render2D::End() { - SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_End") RENDER2D_CHECK_RENDERING_STATE; ASSERT(Context != nullptr && Output != nullptr); ASSERT(GUIShader != nullptr); @@ -816,7 +815,6 @@ void Render2D::PeekTransform(Matrix3x3& transform) void Render2D::PopTransform() { - SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopTransform") RENDER2D_CHECK_RENDERING_STATE; ASSERT(TransformLayersStack.HasItems()); @@ -858,7 +856,6 @@ void Render2D::PeekClip(Rectangle& clipRect) void Render2D::PopClip() { - SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopClip") RENDER2D_CHECK_RENDERING_STATE; ClipLayersStack.Pop(); @@ -880,7 +877,6 @@ void Render2D::PeekTint(Color& tint) void Render2D::PopTint() { - SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopTint") RENDER2D_CHECK_RENDERING_STATE; TintLayersStack.Pop(); diff --git a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp index 91b4291e3..e50df903a 100644 --- a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp +++ b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -#include "Engine/Platform/FileSystem.h" #include "Engine/Animations/Graph/AnimGraph.h" #include "Engine/Scripting/InternalCalls.h" #include "Engine/Scripting/MException.h" @@ -8,82 +7,69 @@ #if !COMPILE_WITHOUT_CSHARP -namespace UtilsInternal +#if USE_MONO && !USE_NETCORE +DEFINE_INTERNAL_CALL(MonoObject*) UtilsInternal_ExtractArrayFromList(MonoObject* obj) { - MonoObject* ExtractArrayFromList(MonoObject* obj) - { -#if USE_MONO - auto klass = mono_object_get_class(obj); - auto field = mono_class_get_field_from_name(klass, "_items"); - MonoObject* o; - mono_field_get_value(obj, field, &o); - return o; -#else - SCRIPTING_EXPORT("FlaxEngine.Utils::Internal_ExtractArrayFromList") - return nullptr; + auto klass = mono_object_get_class(obj); + auto field = mono_class_get_field_from_name(klass, "_items"); + MonoObject* o; + mono_field_get_value(obj, field, &o); + return o; +} #endif - } + +DEFINE_INTERNAL_CALL(void) PlatformInternal_MemoryCopy(void* dst, const void* src, uint64 size) +{ + Platform::MemoryCopy(dst, src, size); } -namespace DebugLogHandlerInternal +DEFINE_INTERNAL_CALL(void) PlatformInternal_MemoryClear(void* dst, uint64 size) { - void LogWrite(LogType level, MonoString* msgObj) - { - SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_LogWrite") - StringView msg; - MUtils::ToString(msgObj, msg); - Log::Logger::Write(level, msg); - } - - void Log(LogType level, MonoString* msgObj, ScriptingObject* obj, MonoString* stackTrace) - { - SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_Log") - - if (msgObj == nullptr) - return; - - // Get info - StringView msg; - MUtils::ToString(msgObj, msg); - //const String objName = obj ? obj->ToString() : String::Empty; - - // Send event - // TODO: maybe option for build to threat warnings and errors as fatal errors? - //const String logMessage = String::Format(TEXT("Debug:{1} {2}"), objName, *msg); - Log::Logger::Write(level, msg); - } - - - void LogException(MonoException* exception, ScriptingObject* obj) - { - SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_LogException") -#if USE_MONO - if (exception == nullptr) - return; - - // Get info - MException ex(exception); - const String objName = obj ? obj->ToString() : String::Empty; - - // Print exception including inner exceptions - // TODO: maybe option for build to threat warnings and errors as fatal errors? - ex.Log(LogType::Warning, objName.GetText()); -#endif - } - + Platform::MemoryClear(dst, size); } -namespace FlaxLogWriterInternal +DEFINE_INTERNAL_CALL(int32) PlatformInternal_MemoryCompare(const void* buf1, const void* buf2, uint64 size) { - void WriteStringToLog(MonoString* msgObj) - { - SCRIPTING_EXPORT("FlaxEngine.FlaxLogWriter::Internal_WriteStringToLog") - if (msgObj == nullptr) - return; - StringView msg; - MUtils::ToString(msgObj, msg); - LOG_STR(Info, msg); - } + return Platform::MemoryCompare(buf1, buf2, size); +} + +DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_LogWrite(LogType level, MonoString* msgObj) +{ + StringView msg; + MUtils::ToString(msgObj, msg); + Log::Logger::Write(level, msg); +} + +DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_Log(LogType level, MonoString* msgObj, ScriptingObject* obj, MonoString* stackTrace) +{ + if (msgObj == nullptr) + return; + + // Get info + StringView msg; + MUtils::ToString(msgObj, msg); + //const String objName = obj ? obj->ToString() : String::Empty; + + // Send event + // TODO: maybe option for build to threat warnings and errors as fatal errors? + //const String logMessage = String::Format(TEXT("Debug:{1} {2}"), objName, *msg); + Log::Logger::Write(level, msg); +} + +DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_LogException(MonoException* exception, ScriptingObject* obj) +{ +#if USE_MONO + if (exception == nullptr) + return; + + // Get info + MException ex(exception); + const String objName = obj ? obj->ToString() : String::Empty; + + // Print exception including inner exceptions + // TODO: maybe option for build to threat warnings and errors as fatal errors? + ex.Log(LogType::Warning, objName.GetText()); +#endif } #endif @@ -92,13 +78,14 @@ void registerFlaxEngineInternalCalls() { AnimGraphExecutor::initRuntime(); #if USE_MONO - ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCopy", &Platform::MemoryCopy); - ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryClear", &Platform::MemoryClear); - ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCompare", &Platform::MemoryCompare); + ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCopy", &PlatformInternal_MemoryCopy); + ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryClear", &PlatformInternal_MemoryClear); + ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCompare", &PlatformInternal_MemoryCompare); +#if USE_MONO && !USE_NETCORE ADD_INTERNAL_CALL("FlaxEngine.Utils::Internal_ExtractArrayFromList", &UtilsInternal::ExtractArrayFromList); - ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogWrite", &DebugLogHandlerInternal::LogWrite); - ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_Log", &DebugLogHandlerInternal::Log); - ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogException", &DebugLogHandlerInternal::LogException); - ADD_INTERNAL_CALL("FlaxEngine.FlaxLogWriter::Internal_WriteStringToLog", &FlaxLogWriterInternal::WriteStringToLog); +#endif + ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogWrite", &DebugLogHandlerInternal_LogWrite); + ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_Log", &DebugLogHandlerInternal_Log); + ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogException", &DebugLogHandlerInternal_LogException); #endif } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 26dd7477c..b6dd7aeec 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -17,6 +17,9 @@ namespace Flax.Build.Bindings private static readonly List CSharpUsedNamespacesSorted = new List(); private static readonly List CSharpAdditionalCode = new List(); private static readonly Dictionary CSharpAdditionalCodeCache = new Dictionary(); +#if USE_NETCORE + private static readonly List CSharpEventBindParams = new List() { new FunctionInfo.ParameterInfo() { Name = "bind", Type = new TypeInfo("bool") } }; +#endif public static event Action GenerateCSharpTypeInternals; @@ -512,7 +515,9 @@ namespace Flax.Build.Bindings contents.AppendLine().Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]"); contents.AppendLine().Append(indent).Append("internal static partial "); #else - contents.AppendLine().Append(indent).Append($"[LibraryImport(\"{caller.ParentModule.Module.BinaryModuleName}\", EntryPoint = \"{caller.FullNameManaged}::Internal_{functionInfo.UniqueName}\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]"); + if (string.IsNullOrEmpty(functionInfo.Glue.LibraryEntryPoint)) + throw new Exception($"Function {caller.FullNameNative}::{functionInfo.Name} has missing entry point for library import."); + contents.AppendLine().Append(indent).Append($"[LibraryImport(\"{caller.ParentModule.Module.BinaryModuleName}\", EntryPoint = \"{functionInfo.Glue.LibraryEntryPoint}\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]"); if (!string.IsNullOrEmpty(returnMarshalType)) contents.AppendLine().Append(indent).Append($"[return: {returnMarshalType}]"); contents.AppendLine().Append(indent).Append("internal static partial "); @@ -1000,7 +1005,12 @@ namespace Flax.Build.Bindings contents.Append("IntPtr obj, "); contents.Append("bool bind);"); #else - contents.Append(indent).Append($"[LibraryImport(\"{classInfo.ParentModule.Module.BinaryModuleName}\", EntryPoint = \"{classInfo.FullNameManaged}::Internal_{eventInfo.Name}_Bind\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]").AppendLine(); + string libraryEntryPoint; + if (buildData.Toolchain.Compiler == TargetCompiler.MSVC) + libraryEntryPoint = $"{classInfo.FullNameManaged}::Internal_{eventInfo.Name}_Bind"; // MSVC allows to override exported symbol name + else + libraryEntryPoint = CppNameMangling.MangleFunctionName(buildData, eventInfo.Name + "_ManagedBind", classInfo.FullNameNativeInternal + "Internal", CSharpEventBindParams); + contents.Append(indent).Append($"[LibraryImport(\"{classInfo.ParentModule.Module.BinaryModuleName}\", EntryPoint = \"{libraryEntryPoint}\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]").AppendLine(); contents.Append(indent).Append($"internal static partial void Internal_{eventInfo.Name}_Bind("); if (!eventInfo.IsStatic) contents.Append("IntPtr obj, "); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index ae44a7799..7a2285b21 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -871,7 +871,6 @@ namespace Flax.Build.Bindings return true; if (typeInfo.IsPtr || typeInfo.IsRef || typeInfo.IsArray || typeInfo.IsBitField || (typeInfo.GenericArgs != null && typeInfo.GenericArgs.Count != 0)) return false; -#if !USE_NETCORE if (CSharpNativeToManagedBasicTypes.ContainsKey(typeInfo.Type) || CSharpNativeToManagedBasicTypes.ContainsValue(typeInfo.Type)) return true; var apiType = FindApiTypeInfo(buildData, typeInfo, caller); @@ -880,7 +879,6 @@ namespace Flax.Build.Bindings if (apiType.IsEnum) return true; } -#endif return false; } @@ -899,8 +897,9 @@ namespace Flax.Build.Bindings return true; } - private static void GenerateCppWrapperFunction(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, string callFormat = "{0}({1})") + private static void GenerateCppWrapperFunction(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, string callerName, FunctionInfo functionInfo, string callFormat = "{0}({1})") { +#if !USE_NETCORE // Optimize static function wrappers that match C# internal call ABI exactly // Use it for Engine-internally only because in games this makes it problematic to use the same function name but with different signature that is not visible to scripting if (CurrentModule.Module is EngineModule && callFormat == "{0}({1})" && GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo, caller)) @@ -913,6 +912,7 @@ namespace Flax.Build.Bindings return; } } +#endif // Setup function binding glue to ensure that wrapper method signature matches for C++ and C# functionInfo.Glue = new FunctionInfo.GlueInfo @@ -954,9 +954,22 @@ namespace Flax.Build.Bindings }); } #endif - + + var prevIndent = " "; + var indent = " "; + contents.Append(prevIndent); +#if USE_NETCORE + contents.AppendFormat("DLLEXPORT static {0} {1}(", returnValueType, functionInfo.UniqueName); + string libraryEntryPoint; + if (buildData.Toolchain.Compiler == TargetCompiler.MSVC) + libraryEntryPoint = $"{caller.FullNameManaged}::Internal_{functionInfo.UniqueName}"; // MSVC allows to override exported symbol name + else + libraryEntryPoint = CppNameMangling.MangleFunctionName(buildData, functionInfo.Name, callerName + "Internal", functionInfo.Parameters, functionInfo.Glue.CustomParameters); + functionInfo.Glue.LibraryEntryPoint = libraryEntryPoint; +#else + contents.AppendFormat("static {0} {1}(", returnValueType, functionInfo.UniqueName); +#endif CppInternalCalls.Add(new KeyValuePair(functionInfo.UniqueName, functionInfo.UniqueName)); - contents.AppendFormat(" static {0} {1}(", returnValueType, functionInfo.UniqueName); var separator = false; if (!functionInfo.IsStatic) @@ -1054,14 +1067,15 @@ namespace Flax.Build.Bindings contents.Append(')'); contents.AppendLine(); - contents.AppendLine(" {"); + contents.Append(prevIndent).AppendLine("{"); #if USE_NETCORE - contents.AppendLine(string.Format(" SCRIPTING_EXPORT(\"{0}\")", caller.FullNameManaged + "::Internal_" + functionInfo.UniqueName)); + if (buildData.Toolchain.Compiler == TargetCompiler.MSVC) + contents.Append(indent).AppendLine($"MSVC_FUNC_EXPORT(\"{libraryEntryPoint}\")"); // Export generated function binding under the C# name #endif if (!functionInfo.IsStatic) - contents.AppendLine(" if (obj == nullptr) DebugLog::ThrowNullReference();"); + contents.Append(indent).AppendLine("if (obj == nullptr) DebugLog::ThrowNullReference();"); - string callBegin = " "; + string callBegin = indent; if (functionInfo.Glue.UseReferenceForResult) { callBegin += "*__resultAsRef = "; @@ -1078,7 +1092,7 @@ namespace Flax.Build.Bindings string callReturnCount = ""; if (returnTypeIsContainer) { - callReturnCount = " "; + callReturnCount = indent; if (functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "BytesContainer") callReturnCount += "*__returnCount = {0}.Length();"; else @@ -1132,9 +1146,9 @@ namespace Flax.Build.Bindings if (apiType != null) { if (parameterInfo.IsOut) - contents.AppendFormat(" {1} {0}Temp;", parameterInfo.Name, new TypeInfo(parameterInfo.Type) { IsRef = false }.GetFullNameNative(buildData, caller)).AppendLine(); + contents.Append(indent).AppendFormat("{1} {0}Temp;", parameterInfo.Name, new TypeInfo(parameterInfo.Type) { IsRef = false }.GetFullNameNative(buildData, caller)).AppendLine(); else - contents.AppendFormat(" auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine(); + contents.Append(indent).AppendFormat("auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine(); if (parameterInfo.Type.IsPtr && !parameterInfo.Type.IsRef) callParams += "&"; callParams += parameterInfo.Name; @@ -1143,7 +1157,7 @@ namespace Flax.Build.Bindings // BytesContainer else if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null) { - contents.AppendFormat(" BytesContainer {0}Temp;", parameterInfo.Name).AppendLine(); + contents.Append(indent).AppendFormat("BytesContainer {0}Temp;", parameterInfo.Name).AppendLine(); callParams += parameterInfo.Name; callParams += "Temp"; } @@ -1151,7 +1165,7 @@ namespace Flax.Build.Bindings // Special case for parameter that cannot be passed directly to the function from the wrapper method input parameter (eg. MonoArray* converted into BytesContainer uses as BytesContainer&) else if (CppParamsThatNeedLocalVariable[i]) { - contents.AppendFormat(" auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine(); + contents.Append(indent).AppendFormat("auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine(); if (parameterInfo.Type.IsPtr) callParams += "&"; callParams += parameterInfo.Name; @@ -1166,7 +1180,7 @@ namespace Flax.Build.Bindings #if USE_NETCORE if (!string.IsNullOrEmpty(callReturnCount)) { - contents.Append(" ").Append("const auto& __callTemp = ").Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine(); + contents.Append(indent).Append("const auto& __callTemp = ").Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine(); call = "__callTemp"; contents.Append(string.Format(callReturnCount, call)); contents.AppendLine(); @@ -1210,11 +1224,11 @@ namespace Flax.Build.Bindings { if (apiType.IsClass) { - contents.AppendFormat(" mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine(); + contents.Append(indent).AppendFormat("mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine(); #if USE_NETCORE if (parameterInfo.Type.Type == "Array") { - contents.AppendFormat(" *__{0}Count = {1}.Count();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine(); + contents.Append(indent).AppendFormat("*__{0}Count = {1}.Count();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine(); } #endif continue; @@ -1222,7 +1236,7 @@ namespace Flax.Build.Bindings if (apiType.IsStruct && !apiType.IsPod) { CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h"); - contents.AppendFormat(" {{ auto __temp = {1}; mono_gc_wbarrier_value_copy({0}, &__temp, 1, {2}::TypeInitializer.GetType().ManagedClass->GetNative()); }}", parameterInfo.Name, value, apiType.FullNameNative).AppendLine(); + contents.Append(indent).AppendFormat("{{ auto __temp = {1}; mono_gc_wbarrier_value_copy({0}, &__temp, 1, {2}::TypeInitializer.GetType().ManagedClass->GetNative()); }}", parameterInfo.Name, value, apiType.FullNameNative).AppendLine(); continue; } } @@ -1231,25 +1245,25 @@ namespace Flax.Build.Bindings // BytesContainer if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null) { - contents.AppendFormat(" mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine(); - contents.AppendFormat(" *__{0}Count = {1}.Length();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine(); + contents.Append(indent).AppendFormat("mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine(); + contents.Append(indent).AppendFormat("*__{0}Count = {1}.Length();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine(); continue; } throw new Exception($"Unsupported type of parameter '{parameterInfo}' in method '{functionInfo}' to be passed using 'out'"); } } - contents.AppendFormat(" *{0} = {1};", parameterInfo.Name, value).AppendLine(); + contents.Append(indent).AppendFormat("*{0} = {1};", parameterInfo.Name, value).AppendLine(); } } } if (!useInlinedReturn && !functionInfo.Glue.UseReferenceForResult && !functionInfo.ReturnType.IsVoid) { - contents.Append(" return __result;").AppendLine(); + contents.Append(indent).Append("return __result;").AppendLine(); } - contents.AppendLine(" }"); + contents.Append(prevIndent).AppendLine("}"); contents.AppendLine(); } @@ -1901,14 +1915,13 @@ namespace Flax.Build.Bindings // C# event wrapper binding method (binds/unbinds C# wrapper to C++ delegate) CppInternalCalls.Add(new KeyValuePair(eventInfo.Name + "_Bind", eventInfo.Name + "_ManagedBind")); - contents.AppendFormat(" static void {0}_ManagedBind(", eventInfo.Name); + contents.AppendFormat(" DLLEXPORT static void {0}_ManagedBind(", eventInfo.Name); if (!eventInfo.IsStatic) contents.AppendFormat("{0}* obj, ", classTypeNameNative); contents.Append("bool bind)").AppendLine(); contents.Append(" {").AppendLine(); -#if USE_NETCORE - contents.AppendLine(string.Format(" SCRIPTING_EXPORT(\"{0}\")", classTypeNameManagedInternalCall + "::Internal_" + eventInfo.Name + "_Bind")); -#endif + if (buildData.Toolchain.Compiler == TargetCompiler.MSVC) + contents.AppendLine($" MSVC_FUNC_EXPORT(\"{classTypeNameManaged}::Internal_{eventInfo.Name}_Bind\")"); // Export generated function binding under the C# name contents.Append(" Function _strignBuilderCache; - private static StringBuilder GetStringBuilder() + public static StringBuilder GetStringBuilder() { if (_strignBuilderCache == null || _strignBuilderCache.Count == 0) return new StringBuilder(); @@ -73,7 +73,7 @@ namespace Flax.Build.Bindings return result; } - private static void PutStringBuilder(StringBuilder value) + public static void PutStringBuilder(StringBuilder value) { if (_strignBuilderCache == null) _strignBuilderCache = new List(); diff --git a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs index efadd193e..b16b1313e 100644 --- a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs @@ -60,6 +60,7 @@ namespace Flax.Build.Bindings public struct GlueInfo { public bool UseReferenceForResult; + public string LibraryEntryPoint; public List CustomParameters; } diff --git a/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs b/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs new file mode 100644 index 000000000..772d392ef --- /dev/null +++ b/Source/Tools/Flax.Build/Utilities/CppNameMangling.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; +using Flax.Build.Bindings; + +namespace Flax.Build +{ + /// + /// The utilities for C++ typename mangling. + /// + internal static class CppNameMangling + { + public static string MangleFunctionName(Builder.BuildData buildData, string name, string outerName, List parameters1, List parameters2) + { + if (parameters1 == null || parameters1.Count == 0) + return MangleFunctionName(buildData, name, outerName, parameters2); + if (parameters2 == null || parameters2.Count == 0) + return MangleFunctionName(buildData, name, outerName, parameters1); + var parameters = new List(); + parameters.AddRange(parameters1); + parameters.AddRange(parameters2); + return MangleFunctionName(buildData, name, outerName, parameters); + } + + public static string MangleFunctionName(Builder.BuildData buildData, string name, string outerName, List parameters) + { + if (name.Contains(":")) + throw new NotImplementedException("No nested types mangling support."); + var sb = BindingsGenerator.GetStringBuilder(); + switch (buildData.Toolchain.Compiler) + { + case TargetCompiler.MSVC: + // References: + // https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names + // https://mearie.org/documents/mscmangle/ + sb.Append('?'); + sb.Append(name); + sb.Append('@'); + sb.Append(outerName); + sb.Append('@'); + // TODO: mangle parameters + break; + case TargetCompiler.Clang: + sb.Append("todo"); + break; + default: + throw new InvalidPlatformException(buildData.Platform.Target); + } + var result = sb.ToString(); + BindingsGenerator.PutStringBuilder(sb); + return result; + } + } +}